diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml index 528ebc740d8..6c98df57073 100644 --- a/.github/workflows/verify-push.yaml +++ b/.github/workflows/verify-push.yaml @@ -55,6 +55,7 @@ jobs: - contribs/socnetsim - contribs/sumo - contribs/pseudosimulation + - contribs/railsim - contribs/roadpricing - contribs/analysis - contribs/eventsBasedPTRouter diff --git a/contribs/README.md b/contribs/README.md index cf682e61410..f29f739c4ef 100644 --- a/contribs/README.md +++ b/contribs/README.md @@ -41,6 +41,7 @@ The MATSim core development team cannot make any guarantee that these extensions | [parking](parking/README.md) | Parking infrastructure and supply constraints | [protobuf](protobuf/README.md) | Protocol buffer implementation and converter for the MATSim event infrastructure | [pseudosimulation](pseudosimulation/README.md) | Pseudo-simulation to speed-up simulation times +| [railsim](railsim/README.md) | A large-scale hybrid micro- and mesoscopic simulation approach for railway operation | [roadpricing](roadpricing/README.md) | Functionality to simulate different road-pricing scenarios in MATSim | [shared_mobility](shared_mobility/README.md) | Simulate human-driven shared mobility (i.e., micromobility) | [signals](signals/README.md) | Simulate traffic lights microscopically diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/AdjustActivityToLinkDistances.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/AdjustActivityToLinkDistances.java index 33bb953f305..d0daecccda7 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/AdjustActivityToLinkDistances.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/AdjustActivityToLinkDistances.java @@ -134,6 +134,7 @@ public Integer call() throws Exception { v = new Coord(coord.getX() - y * m, coord.getY() + x * m); } + v = CoordUtils.round(v); mapping.put(act.getCoord(), v); } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/ExtractHomeCoordinates.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/ExtractHomeCoordinates.java index e8f343ffa1f..9ceef42f3e4 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/ExtractHomeCoordinates.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/ExtractHomeCoordinates.java @@ -8,6 +8,7 @@ import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.CsvOptions; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.utils.geometry.CoordUtils; import picocli.CommandLine; import java.nio.file.Path; @@ -15,10 +16,10 @@ import java.util.Map; @CommandLine.Command( - name = "extract-home-coordinates", - description = "Extract the home coordinates of a person" + name = "extract-home-coordinates", + description = "Extract the home coordinates of a person" ) -public class ExtractHomeCoordinates implements MATSimAppCommand { +public final class ExtractHomeCoordinates implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(ExtractHomeCoordinates.class); @@ -34,6 +35,29 @@ public class ExtractHomeCoordinates implements MATSimAppCommand { @CommandLine.Mixin private CsvOptions options = new CsvOptions(); + /** + * Set and return home coordinate of this person. Can be null if no home activity is known. + */ + public static Coord setHomeCoordinate(Person person) { + for (Plan plan : person.getPlans()) { + for (PlanElement planElement : plan.getPlanElements()) { + if (planElement instanceof Activity) { + String actType = ((Activity) planElement).getType(); + if (actType.startsWith("home")) { + Coord homeCoord = CoordUtils.round(((Activity) planElement).getCoord()); + + person.getAttributes().putAttribute("home_x", homeCoord.getX()); + person.getAttributes().putAttribute("home_y", homeCoord.getY()); + + return homeCoord; + } + } + } + } + + return null; + } + @Override public Integer call() throws Exception { @@ -42,24 +66,9 @@ public Integer call() throws Exception { Map coords = new LinkedHashMap<>(); for (Person person : population.getPersons().values()) { - outer: - for (Plan plan : person.getPlans()) { - for (PlanElement planElement : plan.getPlanElements()) { - if (planElement instanceof Activity) { - String actType = ((Activity) planElement).getType(); - if (actType.startsWith("home")) { - Coord homeCoord = ((Activity) planElement).getCoord(); - coords.put(person, homeCoord); - - person.getAttributes().putAttribute("home_x", homeCoord.getX()); - person.getAttributes().putAttribute("home_y", homeCoord.getY()); - - break outer; - } - } - } - - } + Coord coord = setHomeCoordinate(person); + if (coord != null) + coords.put(person, coord); } if (csv != null) { diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/ResolveGridCoordinates.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/ResolveGridCoordinates.java index 7c223d08484..c2cacd46992 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/ResolveGridCoordinates.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/ResolveGridCoordinates.java @@ -14,6 +14,7 @@ import org.matsim.application.options.ShpOptions; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.geotools.MGC; import picocli.CommandLine; @@ -93,7 +94,7 @@ public Integer call() throws Exception { Coord newCoord = mapping.getOrDefault(coord, null); if (newCoord == null) { - newCoord = landuse.select(crs.getInputCRS(), + newCoord = CoordUtils.round(landuse.select(crs.getInputCRS(), () -> { double x = rnd.nextDouble(-gridResolution / 2, gridResolution / 2); double y = rnd.nextDouble(-gridResolution / 2, gridResolution / 2); @@ -103,7 +104,7 @@ public Integer call() throws Exception { else return new Coord(coord.getX() + x, coord.getY() + y); } - ); + )); mapping.put(coord, newCoord); } diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java index dc6efe3e196..0c72be12c3b 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java @@ -111,8 +111,8 @@ private enum SmallScaleCommercialTrafficType { commercialPersonTraffic, goodsTraffic, completeSmallScaleCommercialTraffic } - @CommandLine.Parameters(arity = "1", paramLabel = "INPUT", description = "Path to the freight data directory") - private Path inputDataDirectory; + @CommandLine.Parameters(arity = "1", paramLabel = "INPUT", description = "Path to the config for small scale commercial generation") + private Path configPath; @CommandLine.Option(names = "--sample", description = "Scaling factor of the small scale commercial traffic (0, 1)", required = true) private double sample; @@ -166,11 +166,11 @@ public static void main(String[] args) { public Integer call() throws Exception { Configurator.setLevel("org.matsim.core.utils.geometry.geotools.MGC", Level.ERROR); - String modelName = inputDataDirectory.getFileName().toString(); + String modelName = configPath.getParent().getFileName().toString(); String sampleName = SmallScaleCommercialTrafficUtils.getSampleNameOfOutputFolder(sample); - Config config = readAndCheckConfig(inputDataDirectory, modelName, sampleName, output); + Config config = readAndCheckConfig(configPath, modelName, sampleName, output); output = Path.of(config.controler().getOutputDirectory()); @@ -185,6 +185,8 @@ public Integer call() throws Exception { throw new Exception( "You set that existing models should included to the new model. This is only possible for a creation of the new carrier file and not by using an existing."); freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); + if (config.vehicles() != null && freightConfigGroup.getCarriersVehicleTypesFile() == null) + freightConfigGroup.setCarriersVehicleTypesFile(config.vehicles().getVehiclesFile()); log.info("Load carriers from: " + freightConfigGroup.getCarriersFile()); FreightUtils.loadCarriersAccordingToFreightConfig(scenario); } @@ -194,6 +196,8 @@ public Integer call() throws Exception { throw new Exception( "You set that existing models should included to the new model. This is only possible for a creation of the new carrier file and not by using an existing."); freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); + if (config.vehicles() != null && freightConfigGroup.getCarriersVehicleTypesFile() == null) + freightConfigGroup.setCarriersVehicleTypesFile(config.vehicles().getVehiclesFile()); log.info("Load carriers from: " + freightConfigGroup.getCarriersFile()); FreightUtils.loadCarriersAccordingToFreightConfig(scenario); solveSeparatedVRPs(scenario, null); @@ -209,6 +213,7 @@ public Integer call() throws Exception { if (!Files.exists(shapeFileZonePath)) { throw new Exception("Required districts shape file {} not found" + shapeFileZonePath.toString()); } + Path inputDataDirectory = Path.of(config.getContext().toURI()).getParent(); HashMap> resultingDataPerZone = LanduseBuildingAnalysis .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, usedLanduseConfiguration.toString(), shapeFileLandusePath, shapeFileZonePath, @@ -221,13 +226,13 @@ public Integer call() throws Exception { case commercialPersonTraffic, goodsTraffic -> createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, usedSmallScaleCommercialTrafficType.toString(), - inputDataDirectory, includeExistingModels); + includeExistingModels); case completeSmallScaleCommercialTraffic -> { createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "commercialPersonTraffic", - inputDataDirectory, includeExistingModels); + includeExistingModels); includeExistingModels = false; // because already included in the step before createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "goodsTraffic", - inputDataDirectory, includeExistingModels); + includeExistingModels); } default -> throw new RuntimeException("No traffic type selected."); } @@ -393,7 +398,6 @@ private void solveSeparatedVRPs(Scenario originalScenario, Map> resultingDataPerZone, Map, Link>> regionLinksMap, String smallScaleCommercialTrafficType, - Path inputDataDirectory, boolean includeExistingModels) throws Exception { ArrayList modesORvehTypes; @@ -413,7 +417,7 @@ else if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, smallScaleCommercialTrafficType); if (includeExistingModels) { - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, smallScaleCommercialTrafficType, trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); } @@ -425,9 +429,9 @@ else if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) /** * Reads and checks config if all necessary parameter are set. */ - private Config readAndCheckConfig(Path inputDataDirectory, String modelName, String sampleName, Path output) throws Exception { + private Config readAndCheckConfig(Path configPath, String modelName, String sampleName, Path output) throws Exception { - Config config = ConfigUtils.loadConfig(inputDataDirectory.resolve("config_demand.xml").toString()); + Config config = ConfigUtils.loadConfig(configPath.toString()); if (output == null || output.toString().isEmpty()) config.controler().setOutputDirectory(Path.of(config.controler().getOutputDirectory()).resolve(modelName) .resolve(usedSmallScaleCommercialTrafficType.toString() + "_" + sampleName + "pct" + "_" diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java index a4bb68a202a..8054c953605 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysis.java @@ -104,7 +104,7 @@ static HashMap> createInputDataDistribution(Pat } } } - log.info("Data distribution for " + resultingDataPerZone.size() + " zones was imported from ", + log.info("Data distribution for " + resultingDataPerZone.size() + " zones was imported from " + existingDataDistribution); Files.copy(existingDataDistribution, outputFileInOutputFolder, StandardCopyOption.COPY_ATTRIBUTES); } @@ -211,7 +211,7 @@ private static void createResultingDataForLanduseInZones( resultingNumberPerCategory, Double::sum); } if (totalEmployeesInCategoriesPerZone.get(investigationArea).getDouble(zoneId) != 0) - resultingDataPerZone.get(zoneId).mergeDouble("Employee", + resultingDataPerZone.get(zoneId).mergeDouble("Employee", totalEmployeesInCategoriesPerZone.get(investigationArea).getDouble(zoneId), Double::sum); } } @@ -337,7 +337,7 @@ static void analyzeBuildingType(List buildingsFeatures, .point2Coord(((Geometry) singleBuildingFeature.getDefaultGeometry()).getCentroid()); String singleZone = indexZones.query(centroidPointOfBuildingPolygon); String buildingType = String.valueOf(singleBuildingFeature.getAttribute("type")); - if (buildingType.equals("") || buildingType.equals("null") || buildingType.equals("yes")) { + if (buildingType.isEmpty() || buildingType.equals("null") || buildingType.equals("yes")) { buildingType = indexLanduse.query(centroidPointOfBuildingPolygon); buildingTypes = new String[] { buildingType }; } else { 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 caa108c5042..9d7ab8ad305 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java @@ -281,11 +281,11 @@ static String getSampleNameOfOutputFolder(double sample) { * scenario reduces the demand of the small scale commercial traffic. The * dispersedTraffic will be added additionally. */ - static void readExistingModels(Scenario scenario, double sampleScenario, Path inputDataDirectory, + static void readExistingModels(Scenario scenario, double sampleScenario, Map, Link>> regionLinksMap) throws Exception { - String locationOfExistingModels = inputDataDirectory.resolve("existingModels") - .resolve("existingModels.csv").toString(); + Path existingModelsFolder = Path.of(scenario.getConfig().getContext().toURI()).getParent().resolve("existingModels"); + String locationOfExistingModels = existingModelsFolder.resolve("existingModels.csv").toString(); CSVParser parse = CSVFormat.Builder.create(CSVFormat.DEFAULT).setDelimiter('\t').setHeader() .setSkipHeaderRecord(true).build().parse(IOUtils.getBufferedReader(locationOfExistingModels)); for (CSVRecord record : parse) { @@ -304,8 +304,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in vehicleType = null; final String modelMode = record.get("networkMode"); - Path scenarioLocation = inputDataDirectory.resolve("existingModels") - .resolve(modelName); + Path scenarioLocation = existingModelsFolder.resolve(modelName); if (!Files.exists(scenarioLocation.resolve("output_carriers.xml.gz"))) throw new Exception("For the existing model " + modelName + " no carrierFile exists. The carrierFile should have the name 'output_carriers.xml.gz'"); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java index 555f365c5c9..9ba27596185 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/LanduseBuildingAnalysisTest.java @@ -84,12 +84,12 @@ public void testReadOfDataDistributionPerZoneAndBuildingAnalysis() throws IOExce Assert.assertTrue(categories.containsKey("Employee Traffic/Parcels")); Assert.assertTrue(categories.containsKey("Employee Tertiary Sector Rest")); - employeeSum += categories.getDouble("Employee Primary Sector"); - employeeSum += categories.getDouble("Employee Construction"); - employeeSum += categories.getDouble("Employee Secondary Sector Rest"); - employeeSum += categories.getDouble("Employee Retail"); - employeeSum += categories.getDouble("Employee Traffic/Parcels"); - employeeSum += categories.getDouble("Employee Tertiary Sector Rest"); + employeeSum += (int) categories.getDouble("Employee Primary Sector"); + employeeSum += (int) categories.getDouble("Employee Construction"); + employeeSum += (int) categories.getDouble("Employee Secondary Sector Rest"); + employeeSum += (int) categories.getDouble("Employee Retail"); + employeeSum += (int) categories.getDouble("Employee Traffic/Parcels"); + employeeSum += (int) categories.getDouble("Employee Tertiary Sector Rest"); Assert.assertEquals(categories.getDouble("Employee"), employeeSum, MatsimTestUtils.EPSILON); @@ -276,12 +276,12 @@ public void testLanduseDistribution() throws IOException { Assert.assertTrue(categories.containsKey("Employee Traffic/Parcels")); Assert.assertTrue(categories.containsKey("Employee Tertiary Sector Rest")); - employeeSum += categories.getDouble("Employee Primary Sector"); - employeeSum += categories.getDouble("Employee Construction"); - employeeSum += categories.getDouble("Employee Secondary Sector Rest"); - employeeSum += categories.getDouble("Employee Retail"); - employeeSum += categories.getDouble("Employee Traffic/Parcels"); - employeeSum += categories.getDouble("Employee Tertiary Sector Rest"); + employeeSum += (int) categories.getDouble("Employee Primary Sector"); + employeeSum += (int) categories.getDouble("Employee Construction"); + employeeSum += (int) categories.getDouble("Employee Secondary Sector Rest"); + employeeSum += (int) categories.getDouble("Employee Retail"); + employeeSum += (int) categories.getDouble("Employee Traffic/Parcels"); + employeeSum += (int) categories.getDouble("Employee Tertiary Sector Rest"); Assert.assertEquals(categories.getDouble("Employee"), employeeSum, MatsimTestUtils.EPSILON); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java index 7862852d026..7e05a64a48e 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java @@ -48,14 +48,13 @@ public class RunGenerateSmallScaleCommercialTrafficTest { @Test public void testMainRunAndResults() { - String inputDataDirectory = utils.getPackageInputDirectory(); + String inputDataDirectory = utils.getPackageInputDirectory() + "config_demand.xml"; String output = utils.getOutputDirectory(); String sample = "0.1"; String jspritIterations = "2"; String creationOption = "createNewCarrierFile"; String landuseConfiguration = "useExistingDataDistribution"; String smallScaleCommercialTrafficType = "commercialPersonTraffic"; - String includeExistingModels = "true"; String zoneShapeFileName = utils.getPackageInputDirectory() + "/shp/testZones.shp"; String buildingsShapeFileName = utils.getPackageInputDirectory() + "/shp/testBuildings.shp"; String landuseShapeFileName = utils.getPackageInputDirectory() + "/shp/testLanduse.shp"; diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java index 13475f3086a..1d2109fcec7 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java @@ -398,13 +398,14 @@ public void testAddingExistingScenarios() throws Exception { config.global().setCoordinateSystem("EPSG:4326"); config.network().setInputFile(networkPath); config.network().setInputCRS("EPSG:4326"); + config.setContext(inputDataDirectory.resolve("config.xml").toUri().toURL()); Scenario scenario = ScenarioUtils.loadScenario(config); HashMap>> buildingsPerZone = new HashMap<>(); Map, Link>> regionLinksMap = GenerateSmallScaleCommercialTrafficDemand .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem()), buildingsPerZone); - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); Assert.assertEquals(3, FreightUtils.getCarriers(scenario).getCarriers().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, FreightUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().size(), MatsimTestUtils.EPSILON); @@ -463,13 +464,14 @@ public void testAddingExistingScenariosWithSample() throws Exception { config.global().setCoordinateSystem("EPSG:4326"); config.network().setInputFile(networkPath); config.network().setInputCRS("EPSG:4326"); + config.setContext(inputDataDirectory.resolve("config.xml").toUri().toURL()); Scenario scenario = ScenarioUtils.loadScenario(config); HashMap>> buildingsPerZone = new HashMap<>(); Map, Link>> regionLinksMap = GenerateSmallScaleCommercialTrafficDemand .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem()), buildingsPerZone); - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); Assert.assertEquals(2, FreightUtils.getCarriers(scenario).getCarriers().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, FreightUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().size(), MatsimTestUtils.EPSILON); @@ -523,6 +525,7 @@ public void testReducingDemandAfterAddingExistingScenarios_goods() throws Except config.global().setCoordinateSystem("EPSG:4326"); config.network().setInputFile(networkPath); config.network().setInputCRS("EPSG:4326"); + config.setContext(inputDataDirectory.resolve("config.xml").toUri().toURL()); Scenario scenario = ScenarioUtils.loadScenario(config); TrafficVolumeGeneration.setInputParameters(usedTrafficType); @@ -540,7 +543,7 @@ public void testReducingDemandAfterAddingExistingScenarios_goods() throws Except .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem()), buildingsPerZone); - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, usedTrafficType, trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); @@ -682,6 +685,7 @@ public void testReducingDemandAfterAddingExistingScenarios_commercialPersonTraff config.global().setCoordinateSystem("EPSG:4326"); config.network().setInputFile(networkPath); config.network().setInputCRS("EPSG:4326"); + config.setContext(inputDataDirectory.resolve("config.xml").toUri().toURL()); Scenario scenario = ScenarioUtils.loadScenario(config); TrafficVolumeGeneration.setInputParameters(usedTrafficType); @@ -699,7 +703,7 @@ public void testReducingDemandAfterAddingExistingScenarios_commercialPersonTraff .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem()), buildingsPerZone); - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, usedTrafficType, trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScore.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScore.java new file mode 100644 index 00000000000..3bf791201d4 --- /dev/null +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScore.java @@ -0,0 +1,7 @@ +package org.matsim.contrib.bicycle; + +import org.matsim.api.core.v01.network.Link; + +public interface AdditionalBicycleLinkScore{ + double computeLinkBasedScore( Link link ); +} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScoreDefaultImpl.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScoreDefaultImpl.java new file mode 100644 index 00000000000..4a38c748dd7 --- /dev/null +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/AdditionalBicycleLinkScoreDefaultImpl.java @@ -0,0 +1,53 @@ +package org.matsim.contrib.bicycle; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.config.ConfigUtils; + +public final class AdditionalBicycleLinkScoreDefaultImpl implements AdditionalBicycleLinkScore { + + private final double marginalUtilityOfInfrastructure_m; + private final double userDefinedNetworkAttributeDefaultValue; + private final double marginalUtilityOfComfort_m; + private final double marginalUtilityOfGradient_m_100m; + private final double marginalUtilityOfUserDefinedNetworkAttribute_m; + private final String nameOfUserDefinedNetworkAttribute; + @Inject AdditionalBicycleLinkScoreDefaultImpl( Scenario scenario ) { + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( scenario.getConfig(), BicycleConfigGroup.class ); + this.marginalUtilityOfInfrastructure_m = bicycleConfigGroup.getMarginalUtilityOfInfrastructure_m(); + this.marginalUtilityOfComfort_m = bicycleConfigGroup.getMarginalUtilityOfComfort_m(); + this.marginalUtilityOfGradient_m_100m = bicycleConfigGroup.getMarginalUtilityOfGradient_m_100m(); + this.marginalUtilityOfUserDefinedNetworkAttribute_m = bicycleConfigGroup.getMarginalUtilityOfUserDefinedNetworkAttribute_m(); + this.nameOfUserDefinedNetworkAttribute = bicycleConfigGroup.getUserDefinedNetworkAttributeName(); + this.userDefinedNetworkAttributeDefaultValue = bicycleConfigGroup.getUserDefinedNetworkAttributeDefaultValue(); + + } + @Override public double computeLinkBasedScore( Link link ){ + String surface = (String) link.getAttributes().getAttribute(BicycleUtils.SURFACE ); + String type = (String) link.getAttributes().getAttribute("type" ); + String cyclewaytype = (String) link.getAttributes().getAttribute(BicycleUtils.CYCLEWAY ); + + double distance = link.getLength(); + + double comfortFactor = BicycleUtils.getComfortFactor(surface ); + double comfortScore = marginalUtilityOfComfort_m * (1. - comfortFactor) * distance; + + double infrastructureFactor = BicycleUtils.getInfrastructureFactor(type, cyclewaytype ); + double infrastructureScore = marginalUtilityOfInfrastructure_m * (1. - infrastructureFactor) * distance; + + double gradient = BicycleUtils.getGradient( link ); + double gradientScore = marginalUtilityOfGradient_m_100m * gradient * distance; + + String userDefinedNetworkAttributeString; + double userDefinedNetworkAttributeScore = 0.; + if ( nameOfUserDefinedNetworkAttribute != null) { + userDefinedNetworkAttributeString = BicycleUtils.getUserDefinedNetworkAttribute( link, nameOfUserDefinedNetworkAttribute ); + double userDefinedNetworkAttributeFactor = BicycleUtils.getUserDefinedNetworkAttributeFactor(userDefinedNetworkAttributeString, userDefinedNetworkAttributeDefaultValue ); + userDefinedNetworkAttributeScore = marginalUtilityOfUserDefinedNetworkAttribute_m * (1. - userDefinedNetworkAttributeFactor) * distance; + } + + return (infrastructureScore + comfortScore + gradientScore + userDefinedNetworkAttributeScore); + } +} + diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleConfigGroup.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleConfigGroup.java index ea2c59864b6..ad79f3afe49 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleConfigGroup.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleConfigGroup.java @@ -52,7 +52,7 @@ public double getMaxBicycleSpeedForRouting() { private double marginalUtilityOfUserDefinedNetworkAttribute; private String userDefinedNetworkAttributeName; private double userDefinedNetworkAttributeDefaultValue; - private BicycleScoringType bicycleScoringType = BicycleScoringType.legBased; +// private BicycleScoringType bicycleScoringType = BicycleScoringType.legBased; private double maxBicycleSpeedForRouting = 25.0/3.6; private String bicycleMode = "bicycle"; private boolean motorizedInteraction = false; @@ -127,13 +127,13 @@ public BicycleConfigGroup setUserDefinedNetworkAttributeDefaultValue(double valu public double getUserDefinedNetworkAttributeDefaultValue() { return this.userDefinedNetworkAttributeDefaultValue; } - public BicycleConfigGroup setBicycleScoringType( final BicycleScoringType value ) { - this.bicycleScoringType = value; - return this; - } - public BicycleScoringType getBicycleScoringType() { - return this.bicycleScoringType; - } +// public BicycleConfigGroup setBicycleScoringType( final BicycleScoringType value ) { +// this.bicycleScoringType = value; +// return this; +// } +// public BicycleScoringType getBicycleScoringType() { +// return this.bicycleScoringType; +// } @StringSetter( MAX_BICYCLE_SPEED_FOR_ROUTING ) @Deprecated @@ -142,7 +142,7 @@ public BicycleConfigGroup setMaxBicycleSpeedForRouting( final double value ) { return this; } - public enum BicycleScoringType {legBased, @Deprecated linkBased} +// public enum BicycleScoringType {legBased, @Deprecated linkBased} @StringGetter( BICYCLE_MODE ) public String getBicycleMode() { return this.bicycleMode; diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkScoring.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkScoring.java deleted file mode 100644 index f0d6e2e54af..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkScoring.java +++ /dev/null @@ -1,178 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* * - * * - * *********************************************************************** * - * * - * copyright : (C) 2008 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ -package org.matsim.contrib.bicycle; - -import org.apache.logging.log4j.LogManager; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.events.Event; -import org.matsim.api.core.v01.events.LinkEnterEvent; -import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent; -import org.matsim.api.core.v01.events.VehicleLeavesTrafficEvent; -import org.matsim.api.core.v01.network.Link; -import org.matsim.core.events.algorithms.Vehicle2DriverEventHandler; -import org.matsim.core.gbl.Gbl; -import org.matsim.core.scoring.SumScoringFunction; -import org.matsim.core.scoring.functions.ModeUtilityParameters; -import org.matsim.core.scoring.functions.ScoringParameters; -import org.matsim.vehicles.Vehicle; - -/** - * @author dziemke - * - * This is an alternative to BicycleLegScoring. Currently yields slightly different scores than BicyleLegScoring. - * This link-based scoring should be used when true times spent on an individual link are relevant - * and for the scoring of the interaction with motorized traffic. - */ -class BicycleLinkScoring implements SumScoringFunction.ArbitraryEventScoring { - - private final ScoringParameters params; - private final Scenario scenario; - private final BicycleConfigGroup bicycleConfigGroup; - - private Vehicle2DriverEventHandler vehicle2Driver = new Vehicle2DriverEventHandler(); - private Id previousLink; - private double previousLinkRelativePosition; - private double previousLinkEnterTime; - private double score; - private int carCountOnLink; - - private static int ccc=0 ; - - BicycleLinkScoring(ScoringParameters params, Scenario scenario, BicycleConfigGroup bicycleConfigGroup) { - this.params = params; - this.scenario = scenario; - this.bicycleConfigGroup = bicycleConfigGroup; - } - - @Override public void finish() {} - - @Override - public double getScore() { - return score; - } - - @Override - public void handleEvent(Event event) { - if (event instanceof VehicleEntersTrafficEvent) { - VehicleEntersTrafficEvent vehEvent = (VehicleEntersTrafficEvent) event; - - // Establish connection between driver and vehicle - vehicle2Driver.handleEvent(vehEvent); - - // No LinkEnterEvent on first link of a leg - previousLink = vehEvent.getLinkId(); - carCountOnLink = 0; - previousLinkRelativePosition = vehEvent.getRelativePositionOnLink(); - previousLinkEnterTime =vehEvent.getTime(); - - } - if (event instanceof VehicleLeavesTrafficEvent) { - VehicleLeavesTrafficEvent vehEvent = (VehicleLeavesTrafficEvent) event; - - Id vehId = vehEvent.getVehicleId(); - double enterTime = previousLinkEnterTime; - double travelTime = vehEvent.getTime() - enterTime; - calculateScoreForPreviousLink(vehEvent.getLinkId(), enterTime, vehId, travelTime, previousLinkRelativePosition); - - // End connection between driver and vehicle - vehicle2Driver.handleEvent(vehEvent); - } - if (event instanceof LinkEnterEvent) { - // This only works since ScoringFunctionsForPopulation passes link events to persons; quite new; dz, june'18 - // Otherwise ArbitraryEventScoring only handles events that are instance of HasPersonId, which is not the case for LinkEnterEvents - LinkEnterEvent linkEnterEvent = (LinkEnterEvent) event; - - Id vehId = linkEnterEvent.getVehicleId(); - double enterTime = previousLinkEnterTime; - double travelTime = linkEnterEvent.getTime() - enterTime; - calculateScoreForPreviousLink(previousLink, enterTime, vehId, travelTime, previousLinkRelativePosition); - - previousLink = linkEnterEvent.getLinkId(); - carCountOnLink = 0; - previousLinkRelativePosition = 0.; - previousLinkEnterTime = linkEnterEvent.getTime(); - } - if ( event instanceof MotorizedInteractionEvent ) { - if ( ((MotorizedInteractionEvent) event).getLinkId().equals(previousLink )){ - this.carCountOnLink++; - } - } - - } - - private void calculateScoreForPreviousLink(Id linkId, Double enterTime, Id vehId, double travelTime, double relativeLinkEnterPosition) { - if (relativeLinkEnterPosition != 1.0) { - // Link link = scenario.getNetwork().getLinks().get(linkId); - // Person person = scenario.getPopulation().getPersons().get(vehicle2Driver.getDriverOfVehicle(vehId)); - // Vehicle vehicle = scenario.getVehicles().getVehicles().get(vehId); - - double carScoreOffset = -(this.carCountOnLink * 0.04); - this.score += carScoreOffset; - // LOG.warn("----- link = " + linkId + " -- car score offset = " + carScoreOffset); - - double scoreOnLink = BicycleUtilityUtils.computeLinkBasedScore(scenario.getNetwork().getLinks().get(linkId), - bicycleConfigGroup.getMarginalUtilityOfComfort_m(), - bicycleConfigGroup.getMarginalUtilityOfInfrastructure_m(), - bicycleConfigGroup.getMarginalUtilityOfGradient_m_100m(), - bicycleConfigGroup.getMarginalUtilityOfUserDefinedNetworkAttribute_m(), - bicycleConfigGroup.getUserDefinedNetworkAttributeName(), - bicycleConfigGroup.getUserDefinedNetworkAttributeDefaultValue()); - // LOG.warn("----- link = " + linkId + " -- scoreOnLink = " + scoreOnLink); - this.score += scoreOnLink; - - double timeDistanceBasedScoreComponent = computeTimeDistanceBasedScoreComponent(travelTime, scenario.getNetwork().getLinks().get(linkId).getLength()); - // LOG.warn("----- link = " + linkId + " -- timeDistanceBasedScoreComponent = " + timeDistanceBasedScoreComponent); - this.score += timeDistanceBasedScoreComponent; - } - else { - double timeDistanceBasedScoreComponent = computeTimeDistanceBasedScoreComponent(travelTime, 0.); - this.score += timeDistanceBasedScoreComponent; - } - } - - - // Copied and adapted from CharyparNagelLegScoring - private double computeTimeDistanceBasedScoreComponent( double travelTime, double dist ) { - double tmpScore = 0.0; - ModeUtilityParameters modeParams = this.params.modeParams.get(bicycleConfigGroup.getBicycleMode()); - if (modeParams == null) { - throw new RuntimeException("no scoring parameters are defined for " + bicycleConfigGroup.getBicycleMode()) ; - } - tmpScore += travelTime * modeParams.marginalUtilityOfTraveling_s; - if (modeParams.marginalUtilityOfDistance_m != 0.0 || modeParams.monetaryDistanceCostRate != 0.0) { - if ( Double.isNaN(dist) ) { - if ( ccc<10 ) { - ccc++ ; - LogManager.getLogger(this.getClass()).warn("distance is NaN. Will make score of this plan NaN. Possible reason: Simulation does not report " + - "a distance for this trip. Possible reason for that: mode is teleported and router does not " + - "write distance into plan. Needs to be fixed or these plans will die out.") ; - if ( ccc==10 ) { - LogManager.getLogger(this.getClass()).warn(Gbl.FUTURE_SUPPRESSED) ; - } - } - } - tmpScore += modeParams.marginalUtilityOfDistance_m * dist; - tmpScore += modeParams.monetaryDistanceCostRate * this.params.marginalUtilityOfMoney * dist; - } - tmpScore += modeParams.constant; - return tmpScore; - } - -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleModule.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleModule.java index 920e3f2afda..15b2d7f42a9 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleModule.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleModule.java @@ -48,33 +48,17 @@ public void install() { // * link speeds are computed via a plugin handler to the DefaultLinkSpeedCalculator. If the plugin handler returns a speed, it is // used, otherwise the default speed is used. This has the advantage that multiple plugins can register such special link speed calculators. - addTravelTimeBinding(bicycleConfigGroup.getBicycleMode()).to(BicycleTravelTime.class).in(Singleton.class); addTravelDisutilityFactoryBinding(bicycleConfigGroup.getBicycleMode()).to(BicycleTravelDisutilityFactory.class).in(Singleton.class); - switch ( bicycleConfigGroup.getBicycleScoringType() ) { - case legBased -> { - this.addEventHandlerBinding().to( BicycleScoreEventsCreator.class ); - } - case linkBased -> { - // yyyy the leg based scoring was moved to score events, so that it does not change the scoring function. For the - // link based scoring, this has not yet been done. It seems to me that the link based scoring is needed for the - // motorized interaction. However, from a technical point of vew it should be possible to use the score events as - // well, since they are computed link-by-link. That is, optimally the link based scoring would go away completely, - // and only motorized interaction would be switched on or off. kai, jun'23 - - bindScoringFunctionFactory().to(BicycleScoringFunctionFactory.class).in(Singleton.class); - } - default -> throw new IllegalStateException( "Unexpected value: " + bicycleConfigGroup.getBicycleScoringType() ); - } + this.addEventHandlerBinding().to( BicycleScoreEventsCreator.class ); + // (the motorized interaction is in the BicycleScoreEventsCreator) + + this.bind( AdditionalBicycleLinkScore.class ).to( AdditionalBicycleLinkScoreDefaultImpl.class ); bind( BicycleLinkSpeedCalculator.class ).to( BicycleLinkSpeedCalculatorDefaultImpl.class ) ; // this is still needed because the bicycle travel time calculator for routing needs to use the same bicycle speed as the mobsim. kai, jun'23 - if (bicycleConfigGroup.isMotorizedInteraction()) { - addMobsimListenerBinding().to(MotorizedInteractionEngine.class); - } - this.installOverridingQSimModule( new AbstractQSimModule(){ @Override protected void configureQSim(){ this.addLinkSpeedCalculator().to( BicycleLinkSpeedCalculator.class ); diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoreEventsCreator.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoreEventsCreator.java index 0b1b1d84ba2..e82f524c711 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoreEventsCreator.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoreEventsCreator.java @@ -25,10 +25,9 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.events.LinkLeaveEvent; -import org.matsim.api.core.v01.events.PersonScoreEvent; -import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent; -import org.matsim.api.core.v01.events.VehicleLeavesTrafficEvent; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.events.handler.LinkEnterEventHandler; import org.matsim.api.core.v01.events.handler.LinkLeaveEventHandler; import org.matsim.api.core.v01.events.handler.VehicleEntersTrafficEventHandler; import org.matsim.api.core.v01.events.handler.VehicleLeavesTrafficEventHandler; @@ -48,72 +47,115 @@ */ class BicycleScoreEventsCreator implements // SumScoringFunction.LegScoring, SumScoringFunction.ArbitraryEventScoring - VehicleEntersTrafficEventHandler, LinkLeaveEventHandler,VehicleLeavesTrafficEventHandler + VehicleEntersTrafficEventHandler, LinkEnterEventHandler, LinkLeaveEventHandler,VehicleLeavesTrafficEventHandler { +// yyyy The car interaction is still somewhat primitive -- a vehicle leaving a link gets a penalty that is multiplied with the number of cars that +// are on the link at that moment. Evidently, this could be improved, by counting the cars that actually overtake the bicycle. Not very difficult +// ... + private static final Logger log = LogManager.getLogger( BicycleScoreEventsCreator.class ) ; - private final double marginalUtilityOfInfrastructure_m; - private final double marginalUtilityOfComfort_m; - private final double marginalUtilityOfGradient_m_100m; - private final double marginalUtilityOfUserDefinedNetworkAttribute_m; - private final String nameOfUserDefinedNetworkAttribute; - private final double userDefinedNetworkAttributeDefaultValue; - private final String bicycleMode; private final Network network; private final EventsManager eventsManager; + private final AdditionalBicycleLinkScore additionalBicycleLinkScore; + private final String bicycleMode; - Vehicle2DriverEventHandler vehicle2driver = new Vehicle2DriverEventHandler(); - private Map,Id> firstLinkIdMap = new LinkedHashMap<>(); + private final Vehicle2DriverEventHandler vehicle2driver = new Vehicle2DriverEventHandler(); + private final Map,Id> firstLinkIdMap = new LinkedHashMap<>(); + private final Map,String> modeFromVehicle = new LinkedHashMap<>(); + private final Map,Double>> numberOfVehiclesOnLinkByMode = new LinkedHashMap<>(); + private final BicycleConfigGroup bicycleConfig; - @Inject BicycleScoreEventsCreator( Scenario scenario, EventsManager eventsManager ) { + @Inject BicycleScoreEventsCreator( Scenario scenario, EventsManager eventsManager, AdditionalBicycleLinkScore additionalBicycleLinkScore ) { this.eventsManager = eventsManager; - this.network = scenario.getNetwork(); - - BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( scenario.getConfig(), BicycleConfigGroup.class ); - this.marginalUtilityOfInfrastructure_m = bicycleConfigGroup.getMarginalUtilityOfInfrastructure_m(); - this.marginalUtilityOfComfort_m = bicycleConfigGroup.getMarginalUtilityOfComfort_m(); - this.marginalUtilityOfGradient_m_100m = bicycleConfigGroup.getMarginalUtilityOfGradient_m_100m(); - this.marginalUtilityOfUserDefinedNetworkAttribute_m = bicycleConfigGroup.getMarginalUtilityOfUserDefinedNetworkAttribute_m(); - this.nameOfUserDefinedNetworkAttribute = bicycleConfigGroup.getUserDefinedNetworkAttributeName(); - this.userDefinedNetworkAttributeDefaultValue = bicycleConfigGroup.getUserDefinedNetworkAttributeDefaultValue(); - this.bicycleMode = bicycleConfigGroup.getBicycleMode(); + this.additionalBicycleLinkScore = additionalBicycleLinkScore; + this.bicycleConfig = ConfigUtils.addOrGetModule( scenario.getConfig(), BicycleConfigGroup.class ); + this.bicycleMode = bicycleConfig.getBicycleMode(); } @Override public void reset( int iteration ){ vehicle2driver.reset( iteration ); } + @Override public void handleEvent( VehicleEntersTrafficEvent event ){ vehicle2driver.handleEvent( event ); - // --- + this.firstLinkIdMap.put( event.getVehicleId(), event.getLinkId() ); + + if ( this.bicycleConfig.isMotorizedInteraction() ){ + modeFromVehicle.put( event.getVehicleId(), event.getNetworkMode() ); + + // inc count by one: + numberOfVehiclesOnLinkByMode.putIfAbsent( event.getNetworkMode(), new LinkedHashMap<>() ); + Map, Double> map = numberOfVehiclesOnLinkByMode.get( event.getNetworkMode() ); + map.merge( event.getLinkId(), 1., Double::sum ); + } + } + + @Override public void handleEvent( LinkEnterEvent event ) { + if ( this.bicycleConfig.isMotorizedInteraction() ){ + // inc count by one: + String mode = this.modeFromVehicle.get( event.getVehicleId() ); + numberOfVehiclesOnLinkByMode.putIfAbsent( mode, new LinkedHashMap<>() ); + Map, Double> map = numberOfVehiclesOnLinkByMode.get( mode ); + map.merge( event.getLinkId(), 1., Double::sum ); + } } + @Override public void handleEvent( LinkLeaveEvent event ){ + if ( this.bicycleConfig.isMotorizedInteraction() ){ + // dec count by one: + String mode = this.modeFromVehicle.get( event.getVehicleId() ); + numberOfVehiclesOnLinkByMode.putIfAbsent( mode, new LinkedHashMap<>() ); + Map, Double> map = numberOfVehiclesOnLinkByMode.get( mode ); + Gbl.assertIf( map.merge( event.getLinkId(), -1., Double::sum ) >= 0 ); + } + if ( vehicle2driver.getDriverOfVehicle( event.getVehicleId() ) != null ){ - // can happen on first link. + double amount = additionalBicycleLinkScore.computeLinkBasedScore( network.getLinks().get( event.getLinkId() ) ); + + if ( this.bicycleConfig.isMotorizedInteraction() ) { + // yyyy this is the place where instead a data structure would need to be build that counts interaction with every car + // that entered the link after the bicycle, and left it before. kai, jul'23 + var carCounts = this.numberOfVehiclesOnLinkByMode.get( TransportMode.car ); + if ( carCounts != null ){ + amount -= 0.004 * carCounts.getOrDefault( event.getLinkId(), 0. ); + } + } - Link link = network.getLinks().get( event.getLinkId() ); - double amount = BicycleUtilityUtils.computeLinkBasedScore( link, marginalUtilityOfComfort_m, marginalUtilityOfInfrastructure_m, - marginalUtilityOfGradient_m_100m, marginalUtilityOfUserDefinedNetworkAttribute_m, nameOfUserDefinedNetworkAttribute, - userDefinedNetworkAttributeDefaultValue); final Id driverOfVehicle = vehicle2driver.getDriverOfVehicle( event.getVehicleId() ); Gbl.assertNotNull( driverOfVehicle ); this.eventsManager.processEvent( new PersonScoreEvent( event.getTime(), driverOfVehicle, amount, "bicycleAdditionalLinkScore" ) ); + } else { + log.warn( "no driver found for vehicleId=" + event.getVehicleId() + "; not clear why this could happen"); } } + @Override public void handleEvent( VehicleLeavesTrafficEvent event ){ vehicle2driver.handleEvent( event ); - // --- + if ( this.bicycleConfig.isMotorizedInteraction() ){ + // dec count by one: + String mode = this.modeFromVehicle.get( event.getVehicleId() ); + numberOfVehiclesOnLinkByMode.putIfAbsent( mode, new LinkedHashMap<>() ); + Map, Double> map = numberOfVehiclesOnLinkByMode.get( mode ); + Gbl.assertIf( map.merge( event.getLinkId(), -1., Double::sum ) >= 0. ); + } if ( vehicle2driver.getDriverOfVehicle( event.getVehicleId() ) != null ){ if( !Objects.equals( this.firstLinkIdMap.get( event.getVehicleId() ), event.getLinkId() ) ){ - Link link = network.getLinks().get( event.getLinkId() ); - double amount = BicycleUtilityUtils.computeLinkBasedScore( link, marginalUtilityOfComfort_m, marginalUtilityOfInfrastructure_m, - marginalUtilityOfGradient_m_100m, marginalUtilityOfUserDefinedNetworkAttribute_m, nameOfUserDefinedNetworkAttribute, - userDefinedNetworkAttributeDefaultValue ); + // what is this good for? maybe that bicycles that enter and leave on the same link should not receive the additional score? kai, jul'23 + + // yyyy in the link based scoring, it actually uses event.getReleativePositionOnLink. Good idea! kai, jul'23 + + double amount = additionalBicycleLinkScore.computeLinkBasedScore( network.getLinks().get( event.getLinkId() ) ); + final Id driverOfVehicle = vehicle2driver.getDriverOfVehicle( event.getVehicleId() ); Gbl.assertNotNull( driverOfVehicle ); this.eventsManager.processEvent( new PersonScoreEvent( event.getTime(), driverOfVehicle, amount, "bicycleAdditionalLinkScore" ) ); } + } else { + log.warn( "no driver found for vehicleId=" + event.getVehicleId() + "; not clear why this could happen" ); } + // --- } } diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoringFunctionFactory.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoringFunctionFactory.java deleted file mode 100644 index 0ee6aeaab5a..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleScoringFunctionFactory.java +++ /dev/null @@ -1,103 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* * - * ScoringFunctionFactory.java * - * * - * *********************************************************************** * - * * - * copyright : (C) 2007 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ -package org.matsim.contrib.bicycle; - -import com.google.inject.Inject; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.events.Event; -import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.bicycle.BicycleConfigGroup.BicycleScoringType; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.events.handler.BasicEventHandler; -import org.matsim.core.scoring.ScoringFunction; -import org.matsim.core.scoring.ScoringFunctionFactory; -import org.matsim.core.scoring.SumScoringFunction; -import org.matsim.core.scoring.functions.*; - -/** - * @author dziemke - */ -/** -* @deprecated -- The {@link BicycleScoringType#linkBased} is already running through {@link BicycleScoreEventsCreator}; for {@link -* BicycleScoringType#legBased} the same should be done. However, the {@link MotorizedInteractionEngine} is also not implemented in a way that it will -* actually work. - */ -final class BicycleScoringFunctionFactory implements ScoringFunctionFactory { - // ok to have this public final when the constructor is package-private/injected: can only used through injection - - @Inject - private ScoringParametersForPerson parameters; - - @Inject - private Scenario scenario; - - @Inject - private EventsManager eventsManager; - - @Inject - private BicycleConfigGroup bicycleConfigGroup; - - @Inject - private BicycleScoringFunctionFactory() { - } - - @Override - public ScoringFunction createNewScoringFunction(Person person) { - SumScoringFunction sumScoringFunction = new SumScoringFunction(); - - final ScoringParameters params = parameters.getScoringParameters(person); - sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(params)) ; - sumScoringFunction.addScoringFunction(new CharyparNagelAgentStuckScoring(params)); - sumScoringFunction.addScoringFunction(new CharyparNagelMoneyScoring( params )); - - BicycleScoringType bicycleScoringType = bicycleConfigGroup.getBicycleScoringType(); - - if (bicycleScoringType == BicycleScoringType.legBased) { - throw new RuntimeException( "this execution path should no longer be used."); -// sumScoringFunction.addScoringFunction(new BicycleLegScoring(params, scenario.getNetwork(), scenario.getConfig().transit().getTransitModes(), bicycleConfigGroup)); - } else if (bicycleScoringType == BicycleScoringType.linkBased) { - BicycleLinkScoring bicycleLinkScoring = new BicycleLinkScoring(params, scenario, bicycleConfigGroup); - sumScoringFunction.addScoringFunction(bicycleLinkScoring); - - CarCounter carCounter = new CarCounter( bicycleLinkScoring ); - eventsManager.addHandler(carCounter); - } else { - throw new IllegalArgumentException("Bicycle scoring type " + bicycleScoringType + " not known."); - } - - return sumScoringFunction; - } - - - private static class CarCounter implements BasicEventHandler{ - private final BicycleLinkScoring bicycleLinkScoring; - - private CarCounter( BicycleLinkScoring bicycleLinkScoring ) { - this.bicycleLinkScoring = bicycleLinkScoring; - } - - @Override - public void handleEvent( Event event ) { - if ( event instanceof MotorizedInteractionEvent ){ - bicycleLinkScoring.handleEvent(event); - } - } - } -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutility.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutility.java index 9748299ad32..be8c5cca418 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutility.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutility.java @@ -102,20 +102,20 @@ public double getLinkTravelDisutility(Link link, double time, Person person, Veh double travelTimeDisutility = marginalCostOfTime_s * travelTime; double distanceDisutility = marginalCostOfDistance_m * distance; - double comfortFactor = BicycleUtilityUtils.getComfortFactor(surface); + double comfortFactor = BicycleUtils.getComfortFactor(surface ); double comfortDisutility = marginalCostOfComfort_m * (1. - comfortFactor) * distance; - double infrastructureFactor = BicycleUtilityUtils.getInfrastructureFactor(type, cyclewaytype); + double infrastructureFactor = BicycleUtils.getInfrastructureFactor(type, cyclewaytype ); double infrastructureDisutility = marginalCostOfInfrastructure_m * (1. - infrastructureFactor) * distance; - double gradientFactor = BicycleUtilityUtils.getGradient(link); + double gradientFactor = BicycleUtils.getGradient(link ); double gradientDisutility = marginalCostOfGradient_m_100m * gradientFactor * distance; double userDefinedNetworkAttritubeDisutility = 0.; if (nameOfUserDefinedNetworkAttribute != null) { String userDefinedNetworkAttributeString = BicycleUtils.getUserDefinedNetworkAttribute(link, nameOfUserDefinedNetworkAttribute); - double userDefinedNetworkAttributeFactor = BicycleUtilityUtils.getUserDefinedNetworkAttributeFactor(userDefinedNetworkAttributeString, - this.userDefinedNetworkAttributeDefaultValue); + double userDefinedNetworkAttributeFactor = BicycleUtils.getUserDefinedNetworkAttributeFactor(userDefinedNetworkAttributeString, + this.userDefinedNetworkAttributeDefaultValue ); userDefinedNetworkAttritubeDisutility = marginalCostOfUserDefinedNetworkAttribute_m * (1. - userDefinedNetworkAttributeFactor) * distance; } @@ -132,11 +132,16 @@ public double getLinkTravelDisutility(Link link, double time, Person person, Veh throw new RuntimeException("you cannot use the randomzing travel disutility without person. If you need this without a person, set" + "sigma to zero.") ; } - normalRndLink = 0.05 * random.nextGaussian(); - // yyyyyy are we sure that this is a good approach? In high resolution networks, this leads to quirky detours ... kai, sep'19 +// normalRndLink = 0.05 * random.nextGaussian(); + // are we sure that this is a good approach? In high resolution networks, this leads to quirky detours ... kai, sep'19 + // --> see below. kai, jul'23 if (person != prevPerson) { prevPerson = person; + normalRndLink = 0.05 * random.nextGaussian(); + // are we sure that this is a good approach? In high resolution networks, this leads to quirky detours ... kai, sep'19 + // --> addressed with moving it to down here, i.e. into the person. Also caused race conditions. kai, jul'23 + logNormalRndDist = Math.exp(sigma * random.nextGaussian()); logNormalRndInf = Math.exp(sigma * random.nextGaussian()); logNormalRndComf = Math.exp(sigma * random.nextGaussian()); diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityV2.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityV2.java deleted file mode 100644 index b80c299324f..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityV2.java +++ /dev/null @@ -1,110 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* * - * * - * *********************************************************************** * - * * - * copyright : (C) 2008 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ -package org.matsim.contrib.bicycle; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Person; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.router.util.TravelDisutility; -import org.matsim.vehicles.Vehicle; - -/** - * @author smetzler, dziemke - */ -// Used version seesm to be BicycleTravelDisutility (without V2) -@Deprecated -class BicycleTravelDisutilityV2 implements TravelDisutility { - private static final Logger LOG = LogManager.getLogger(BicycleTravelDisutilityV2.class); - - private final double marginalCostOfInfrastructure_m; - private final double marginalCostOfComfort_m; - private final double marginalCostOfGradient_m_100m; - - private final Network network; - - private final TravelDisutility timeDistanceDisutility; - - - BicycleTravelDisutilityV2(Network network, TravelDisutility timeDistanceDisutility, BicycleConfigGroup bicycleConfigGroup, PlanCalcScoreConfigGroup cnScoringGroup) { - this.timeDistanceDisutility = timeDistanceDisutility; - - final PlanCalcScoreConfigGroup.ModeParams bicycleParams = cnScoringGroup.getModes().get(bicycleConfigGroup.getBicycleMode()); - if (bicycleParams == null) { - throw new NullPointerException(bicycleConfigGroup.getBicycleMode() + " is not part of the valid mode parameters " + cnScoringGroup.getModes().keySet()); - } - - this.marginalCostOfInfrastructure_m = -(bicycleConfigGroup.getMarginalUtilityOfInfrastructure_m()); - this.marginalCostOfComfort_m = -(bicycleConfigGroup.getMarginalUtilityOfComfort_m()); - this.marginalCostOfGradient_m_100m = -(bicycleConfigGroup.getMarginalUtilityOfGradient_m_100m()); - - // TODO Needed as long as network mode filtering kicks out attributes; remove when possible, dz, sep'17 - // Also see comments in BicycleTravelDisutilityFactory - this.network = network; - } - - - @Override - public double getLinkTravelDisutility(Link link, double time, Person person, Vehicle vehicle) { - // TODO Needed as long as network mode filtering kicks out attributes; remove when possible, dz, sep'17 - Link linkWithAttributes = network.getLinks().get(link.getId()); - - String surface = (String) linkWithAttributes.getAttributes().getAttribute(BicycleUtils.SURFACE); - String type = (String) linkWithAttributes.getAttributes().getAttribute("type"); - String cyclewaytype = (String) linkWithAttributes.getAttributes().getAttribute(BicycleUtils.CYCLEWAY); - - double distance = linkWithAttributes.getLength(); - - double comfortFactor = BicycleUtilityUtils.getComfortFactor(surface); - double comfortDisutility = marginalCostOfComfort_m * (1. - comfortFactor) * distance; - - double infrastructureFactor = BicycleUtilityUtils.getInfrastructureFactor(type, cyclewaytype); - double infrastructureDisutility = marginalCostOfInfrastructure_m * (1. - infrastructureFactor) * distance; - - double gradientFactor = BicycleUtilityUtils.getGradient(linkWithAttributes); - double gradientDisutility = marginalCostOfGradient_m_100m * gradientFactor * distance; - -// LOG.warn("link = " + link.getId() + "-- travelTime = " + travelTime + " -- distance = " + distance + " -- comfortFactor = " -// + comfortFactor + " -- infraFactor = "+ infrastructureFactor + " -- gradient = " + gradientFactor); - - // TODO Gender - // TODO Activity - // TODO Other influence factors - - double linkTimeDistanceDisutility = timeDistanceDisutility.getLinkTravelDisutility(link, time, person, vehicle); - - // New idea - double logNormalRnd = (double) person.getAttributes().getAttribute("logNormalRnd"); - // - - LOG.warn("person = " + person.getId() + " / link = " + linkWithAttributes.getId() + " / infrastructureDisutility = " + infrastructureDisutility + " / comfortDisutility = " - + comfortDisutility + " / gradientDisutility = " + gradientDisutility + " / randomfactor = " + logNormalRnd); - double disutility = linkTimeDistanceDisutility + logNormalRnd * (infrastructureDisutility + comfortDisutility + gradientDisutility); - LOG.warn("---------- disutility = " + disutility); - return disutility; - } - - - @Override - public double getLinkMinimumTravelDisutility(Link link) { - return 0; - } -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtilityUtils.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtilityUtils.java deleted file mode 100644 index d8d2acbf914..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtilityUtils.java +++ /dev/null @@ -1,134 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* * - * * - * *********************************************************************** * - * * - * copyright : (C) 2014 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.bicycle; - -import org.matsim.api.core.v01.network.Link; - -/** - * @author dziemke - */ -class BicycleUtilityUtils { - - static double computeLinkBasedScore( Link link, double marginalUtilityOfComfort_m, double marginalUtilityOfInfrastructure_m, - double marginalUtilityOfGradient_m_100m, double marginalUtilityOfUserDefinedNetworkAttribute_m, - String nameOfUserDefinedNetworkAttribute, double userDefinedNetworkAttributeDefaultValue) { - String surface = (String) link.getAttributes().getAttribute(BicycleUtils.SURFACE); - String type = (String) link.getAttributes().getAttribute("type"); - String cyclewaytype = (String) link.getAttributes().getAttribute(BicycleUtils.CYCLEWAY); - - double distance = link.getLength(); - - double comfortFactor = getComfortFactor(surface); - double comfortScore = marginalUtilityOfComfort_m * (1. - comfortFactor) * distance; - - double infrastructureFactor = getInfrastructureFactor(type, cyclewaytype); - double infrastructureScore = marginalUtilityOfInfrastructure_m * (1. - infrastructureFactor) * distance; - - double gradient = getGradient(link); - double gradientScore = marginalUtilityOfGradient_m_100m * gradient * distance; - - String userDefinedNetworkAttributeString; - double userDefinedNetworkAttributeScore = 0.; - if (nameOfUserDefinedNetworkAttribute != null) { - userDefinedNetworkAttributeString = BicycleUtils.getUserDefinedNetworkAttribute(link, nameOfUserDefinedNetworkAttribute); - double userDefinedNetworkAttributeFactor = getUserDefinedNetworkAttributeFactor(userDefinedNetworkAttributeString, userDefinedNetworkAttributeDefaultValue); - userDefinedNetworkAttributeScore = marginalUtilityOfUserDefinedNetworkAttribute_m * (1. - userDefinedNetworkAttributeFactor) * distance; - } - - return (infrastructureScore + comfortScore + gradientScore + userDefinedNetworkAttributeScore); - } - - static double getGradient(Link link ) { - - if (!link.getFromNode().getCoord().hasZ() || !link.getToNode().getCoord().hasZ()) return 0.; - - var fromZ = link.getFromNode().getCoord().getZ(); - var toZ = link.getToNode().getCoord().getZ(); - var gradient = (toZ - fromZ) / link.getLength(); - // No positive utility for downhill, only negative for uphill - return Math.max(0, gradient); - } - - // TODO Combine this with speeds? - static double getComfortFactor( String surface ) { - - // This method included another if/els branch with some conditions on road types which could never be reached. The following comment was - // written above this branch. Deleting it, because I don't know what it was supposed to do. janek may '23 - // - // For many primary and secondary roads, no surface is specified because they are by default assumed to be is asphalt. - // For tertiary roads street this is not true, e.g. Friesenstr. in Kreuzberg - - if (surface == null) return 1.0; - - return switch (surface) { - case "paved", "asphalt" -> 1.0; - case "concrete:lanes" -> .95; - case "concrete_plates", "concrete:plates", "fine_gravel" -> .9; - case "paving_stones", "paving_stones:35", "paving_stones:30" -> .8; - case "compacted" -> .7; - case "unpaved", "asphalt;paving_stones:35", "bricks", "gravel", "ground" -> .6; - case "sett", "cobblestone;flattened", "cobblestone:flattened" -> .5; - case "cobblestone", "stone", "grass", "compressed", "paving_stones:3" -> .4; - case "cobblestone (bad)", "dirt", "earth", "wood", "pebblestone", "sand" -> .3; - case "concrete" -> .1; - default -> .85; - }; - } - - static double getInfrastructureFactor( String type, String cyclewaytype ) { - - // The method was unreadable before, so I hope I got the logic right. basically this differentiates between explicit cycleway tags, where the - // road type has an influence on the factor, i.e. cycling along a primary road without cycle lane is less attractive compared to a tertiary road. - // On the other hand if cycleways are present the factor is always 0.95, exept the cycle tracks has steps (horrible) or the road type is a - // cycleway anyway (very nice) - // in case there is no road type a medium factor of 0.85 is assigned - // janek may '23 - - if (type == null) return 0.85; - if (hasNoCycleway(cyclewaytype)) { - return switch (type) { - case "trunk" -> 0.05; - case "primary", "primary_link" -> 0.1; - case "secondary", "secondary_link" -> 0.3; - case "tertiary", "tertiary_link" -> 0.4; - case "unclassified" -> 0.9; - default -> 0.95; - }; - } else { - return switch (type) { - case "cycleway", "path" -> 1.0; - case "steps" -> 0.1; - default -> 0.95; - }; - } - } - - private static boolean hasNoCycleway(String cyclewayType) { - return (cyclewayType == null || cyclewayType.equals("no") || cyclewayType.equals("none")); - } - - static double getUserDefinedNetworkAttributeFactor( String userDefinedNetworkAttributeString, double userDefinedNetworkAttributeDefaultValue ) { - double userDefinedNetworkAttributeFactor = userDefinedNetworkAttributeDefaultValue; - - if (userDefinedNetworkAttributeString != null) { - userDefinedNetworkAttributeFactor = Double.parseDouble(userDefinedNetworkAttributeString); - } - return userDefinedNetworkAttributeFactor; - } -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtils.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtils.java index e0b9c212da1..43bf5e63776 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtils.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleUtils.java @@ -19,6 +19,7 @@ package org.matsim.contrib.bicycle; +import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; /** @@ -27,8 +28,6 @@ * @author dziemke */ public final class BicycleUtils { - // I think that this should rather be used through a BicycleUtils class. kai, may'19 - public static final String GRADIENT = "gradient"; public static final String AVERAGE_ELEVATION = "averageElevation"; public static final String SURFACE = "surface"; @@ -51,4 +50,81 @@ public static String getSurface( Link link ){ public static String getUserDefinedNetworkAttribute( Link link, String nameOfUserDefinedNetworkAttribute ) { return (String) link.getAttributes().getAttribute( nameOfUserDefinedNetworkAttribute ); } + + public static AdditionalBicycleLinkScore createDefaultBicycleLinkScore( Scenario scenario ) { + return new AdditionalBicycleLinkScoreDefaultImpl( scenario ); + } + /* package */ static double getGradient(Link link ) { + + if (!link.getFromNode().getCoord().hasZ() || !link.getToNode().getCoord().hasZ()) return 0.; + + var fromZ = link.getFromNode().getCoord().getZ(); + var toZ = link.getToNode().getCoord().getZ(); + var gradient = (toZ - fromZ) / link.getLength(); + // No positive utility for downhill, only negative for uphill + return Math.max(0, gradient); + } + // TODO Combine this with speeds? + /* package */ static double getComfortFactor( String surface ) { + + // This method included another if/els branch with some conditions on road types which could never be reached. The following comment was + // written above this branch. Deleting it, because I don't know what it was supposed to do. janek may '23 + // + // For many primary and secondary roads, no surface is specified because they are by default assumed to be is asphalt. + // For tertiary roads street this is not true, e.g. Friesenstr. in Kreuzberg + + if (surface == null) return 1.0; + + return switch (surface) { + case "paved", "asphalt" -> 1.0; + case "concrete:lanes" -> .95; + case "concrete_plates", "concrete:plates", "fine_gravel" -> .9; + case "paving_stones", "paving_stones:35", "paving_stones:30" -> .8; + case "compacted" -> .7; + case "unpaved", "asphalt;paving_stones:35", "bricks", "gravel", "ground" -> .6; + case "sett", "cobblestone;flattened", "cobblestone:flattened" -> .5; + case "cobblestone", "stone", "grass", "compressed", "paving_stones:3" -> .4; + case "cobblestone (bad)", "dirt", "earth", "wood", "pebblestone", "sand" -> .3; + case "concrete" -> .1; + default -> .85; + }; + } + /* package */ static double getInfrastructureFactor( String type, String cyclewaytype ) { + + // The method was unreadable before, so I hope I got the logic right. basically this differentiates between explicit cycleway tags, where the + // road type has an influence on the factor, i.e. cycling along a primary road without cycle lane is less attractive compared to a tertiary road. + // On the other hand if cycleways are present the factor is always 0.95, exept the cycle tracks has steps (horrible) or the road type is a + // cycleway anyway (very nice) + // in case there is no road type a medium factor of 0.85 is assigned + // janek may '23 + + if (type == null) return 0.85; + if (hasNoCycleway(cyclewaytype)) { + return switch (type) { + case "trunk" -> 0.05; + case "primary", "primary_link" -> 0.1; + case "secondary", "secondary_link" -> 0.3; + case "tertiary", "tertiary_link" -> 0.4; + case "unclassified" -> 0.9; + default -> 0.95; + }; + } else { + return switch (type) { + case "cycleway", "path" -> 1.0; + case "steps" -> 0.1; + default -> 0.95; + }; + } + } + private static boolean hasNoCycleway( String cyclewayType ) { + return (cyclewayType == null || cyclewayType.equals("no") || cyclewayType.equals("none")); + } + /* package */ static double getUserDefinedNetworkAttributeFactor( String userDefinedNetworkAttributeString, double userDefinedNetworkAttributeDefaultValue ) { + double userDefinedNetworkAttributeFactor = userDefinedNetworkAttributeDefaultValue; + + if (userDefinedNetworkAttributeString != null) { + userDefinedNetworkAttributeFactor = Double.parseDouble(userDefinedNetworkAttributeString); + } + return userDefinedNetworkAttributeFactor; + } } diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/Bicycles.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/Bicycles.java deleted file mode 100644 index 33a6c27a7d8..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/Bicycles.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.matsim.contrib.bicycle; - -import org.matsim.core.controler.AllowsConfiguration; - -/** - * @deprecated -- please inline. kai, dec'22 - */ -public class Bicycles { - - /** - * @deprecated -- please inline. kai, dec'22 - */ - public static void addAsOverridingModule(AllowsConfiguration ao) { - ao.addOverridingModule(new BicycleModule()); - } -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEngine.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEngine.java deleted file mode 100644 index d3d91f8489a..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEngine.java +++ /dev/null @@ -1,70 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* * - * * - * *********************************************************************** * - * * - * copyright : (C) 2008 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ -package org.matsim.contrib.bicycle; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.network.Link; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; -import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; -import org.matsim.vehicles.Vehicle; - -import jakarta.inject.Inject; - -/** - * @author dziemke - * @deprecated -- it might be possible to repair this, but as of now it is not working. kai, nov'22 - */ -@Deprecated -final class MotorizedInteractionEngine implements MobsimBeforeSimStepListener { - // ok to have this public final when ctor is package-private/injected: can only be used through injection - - private EventsManager eventsManager; -// private List> links; -// private double startTime; -// private double endTime; -// private double frequency; - - @Inject -// MotorizedInteractionEngine(EventsManager eventsManager, List> links, double startTime, double endTime, double frequency) { - MotorizedInteractionEngine(EventsManager eventsManager) { - this.eventsManager = eventsManager; -// this.links = links; -// this.startTime = startTime; -// this.endTime = endTime; -// this.frequency = frequency; - } - - @Override - public void notifyMobsimBeforeSimStep(MobsimBeforeSimStepEvent e) { - double currentTime = e.getSimulationTime(); - double startTime = 8. * 60 * 60; - double endTime = 12. * 60 * 60; - double frequency = 3.; - Id linkId = Id.createLinkId("6"); // The central link - - - if ((currentTime % frequency == 0) && (currentTime >= startTime) && (currentTime <= endTime)) { -// LOG.info("Current time = " + currentTime + " -- " + currentTime / 3600.); -// for (Id linkId : links) { - eventsManager.processEvent(new MotorizedInteractionEvent(e.getSimulationTime(), linkId, Id.create("evilCar", Vehicle.class))); -// } - } - } -} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/run/RunBicycleExample.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/run/RunBicycleExample.java index f8c139a1aa1..134f8bf421e 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/run/RunBicycleExample.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/run/RunBicycleExample.java @@ -18,20 +18,24 @@ * *********************************************************************** */ package org.matsim.contrib.bicycle.run; +import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.bicycle.AdditionalBicycleLinkScore; import org.matsim.contrib.bicycle.BicycleConfigGroup; import org.matsim.contrib.bicycle.BicycleModule; +import org.matsim.contrib.bicycle.BicycleUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ModeParams; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; -import org.matsim.core.controler.AllowsConfiguration; +import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; import org.matsim.core.scenario.ScenarioUtils; @@ -70,7 +74,7 @@ public static void main(String[] args) { config.controler().setLastIteration(100); // Modify if motorized interaction is used boolean considerMotorizedInteraction = false; - new RunBicycleExample().run(config, considerMotorizedInteraction); + new RunBicycleExample().run(config ); } static void fillConfigWithBicycleStandardValues(Config config) { @@ -105,16 +109,13 @@ static void fillConfigWithBicycleStandardValues(Config config) { config.plansCalcRoute().setNetworkModes(mainModeList); } - public void run(Config config, boolean considerMotorizedInteraction) { + public void run(Config config ) { config.global().setNumberOfThreads(1); config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); config.plansCalcRoute().setRoutingRandomness(3.); BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); - if (considerMotorizedInteraction) { - bicycleConfigGroup.setMotorizedInteraction(considerMotorizedInteraction); - } final String bicycle = bicycleConfigGroup.getBicycleMode(); @@ -134,4 +135,53 @@ public void run(Config config, boolean considerMotorizedInteraction) { controler.run(); } + public void runWithOwnScoring(Config config, boolean considerMotorizedInteraction) { + config.global().setNumberOfThreads(1); + config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.plansCalcRoute().setRoutingRandomness(3.); + + if (considerMotorizedInteraction) { + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); + bicycleConfigGroup.setMotorizedInteraction(considerMotorizedInteraction); + } + + Scenario scenario = ScenarioUtils.loadScenario(config); + + // set config such that the mode vehicles come from vehicles data: + scenario.getConfig().qsim().setVehiclesSource(QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData); + + // now put hte mode vehicles into the vehicles data: + final VehiclesFactory vf = VehicleUtils.getFactory(); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create(TransportMode.car, VehicleType.class ) ) ); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create("bicycle", VehicleType.class ) ).setMaximumVelocity(4.16666666 ).setPcuEquivalents(0.25 ) ); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new BicycleModule() ); + controler.addOverridingModule( new AbstractModule(){ + @Override public void install(){ + this.bind( AdditionalBicycleLinkScore.class ).to( MyAdditionalBicycleLinkScore.class ); + } + } ); + + controler.run(); + } + + private static class MyAdditionalBicycleLinkScore implements AdditionalBicycleLinkScore { + + private final AdditionalBicycleLinkScore delegate; + @Inject MyAdditionalBicycleLinkScore( Scenario scenario ) { + this.delegate = BicycleUtils.createDefaultBicycleLinkScore( scenario ); + } + @Override public double computeLinkBasedScore( Link link ){ + double result = (double) link.getAttributes().getAttribute( "carFreeStatus" ); // from zero to one + + double amount = delegate.computeLinkBasedScore( link ); + + return amount + result ; // or some other way to augment the score + + } + } + + } diff --git a/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleUtilityUtilsTest.java b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleUtilityUtilsTest.java index 23a9a376677..19697a3afaf 100644 --- a/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleUtilityUtilsTest.java +++ b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleUtilityUtilsTest.java @@ -13,31 +13,31 @@ public class BicycleUtilityUtilsTest { @Test public void getGradientNoFromZ() { var link = createLink(new Coord(0, 0), new Coord(100, 0, 100)); - assertEquals(0., BicycleUtilityUtils.getGradient(link), 0.00001); + assertEquals(0., BicycleUtils.getGradient(link ), 0.00001 ); } @Test public void getGradientNoToZ() { var link = createLink(new Coord(0, 0, 100), new Coord(100, 0)); - assertEquals(0., BicycleUtilityUtils.getGradient(link), 0.00001); + assertEquals(0., BicycleUtils.getGradient(link ), 0.00001 ); } @Test public void getGradientFlat() { var link = createLink(new Coord(0, 0, 100), new Coord(100, 0, 100)); - assertEquals(0., BicycleUtilityUtils.getGradient(link), 0.00001); + assertEquals(0., BicycleUtils.getGradient(link ), 0.00001 ); } @Test public void getGradientUphill() { var link = createLink(new Coord(0, 0, 0), new Coord(100, 0, 100)); - assertEquals(1., BicycleUtilityUtils.getGradient(link), 0.00001); + assertEquals(1., BicycleUtils.getGradient(link ), 0.00001 ); } @Test public void getGradientDownhill() { var link = createLink(new Coord(0, 0, 100), new Coord(100, 0, 0)); - assertEquals(0., BicycleUtilityUtils.getGradient(link), 0.00001); + assertEquals(0., BicycleUtils.getGradient(link ), 0.00001 ); } private static Link createLink(Coord from, Coord to) { diff --git a/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/run/BicycleTest.java b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/run/BicycleTest.java index 5fe6e5a46b1..329e40cc0c5 100644 --- a/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/run/BicycleTest.java +++ b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/run/BicycleTest.java @@ -21,7 +21,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; @@ -32,24 +31,26 @@ import org.matsim.api.core.v01.events.handler.LinkEnterEventHandler; import org.matsim.api.core.v01.events.handler.LinkLeaveEventHandler; import org.matsim.api.core.v01.network.Link; +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.PopulationFactory; import org.matsim.contrib.bicycle.BicycleConfigGroup; -import org.matsim.contrib.bicycle.BicycleConfigGroup.BicycleScoringType; import org.matsim.contrib.bicycle.BicycleModule; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ModeParams; -import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource; import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.AllowsConfiguration; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.io.PopulationReader; +import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.collections.CollectionUtils; import org.matsim.testcases.MatsimTestUtils; import org.matsim.utils.eventsfilecomparison.EventsFileComparator; import org.matsim.vehicles.Vehicle; @@ -72,11 +73,13 @@ public class BicycleTest { private static final Logger LOG = LogManager.getLogger(BicycleTest.class); + private static final String bicycleMode = "bicycle"; + @Rule public MatsimTestUtils utils = new MatsimTestUtils(); @Test public void testNormal() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -88,7 +91,7 @@ public void testNormal() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -110,7 +113,7 @@ public void testNormal() { @Test public void testCobblestone() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -124,7 +127,7 @@ public void testCobblestone() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); { Scenario scenarioReference = ScenarioUtils.createScenario( ConfigUtils.createConfig() ); Scenario scenarioCurrent = ScenarioUtils.createScenario( ConfigUtils.createConfig() ); @@ -143,7 +146,7 @@ public void testCobblestone() { @Test public void testPedestrian() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -155,7 +158,7 @@ public void testPedestrian() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -172,7 +175,7 @@ public void testPedestrian() { @Test public void testLane() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -184,7 +187,7 @@ public void testLane() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -201,7 +204,7 @@ public void testLane() { @Test public void testGradient() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -213,7 +216,7 @@ public void testGradient() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -230,7 +233,7 @@ public void testGradient() { @Test public void testGradientLane() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -243,7 +246,7 @@ public void testGradientLane() { config.controler().setLastIteration(0); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -260,7 +263,7 @@ public void testGradientLane() { @Test public void testNormal10It() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); RunBicycleExample.fillConfigWithBicycleStandardValues(config); @@ -275,7 +278,7 @@ public void testNormal10It() { config.controler().setWritePlansInterval(10); config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + new RunBicycleExample().run(config ); LOG.info("Checking MATSim events file ..."); final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; @@ -290,49 +293,156 @@ public void testNormal10It() { assertTrue("Populations are different", PopulationUtils.equalPopulation(scenarioReference.getPopulation(), scenarioCurrent.getPopulation())); } - @Test - public void testMotorizedInteraction() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); - config.addModule(new BicycleConfigGroup()); - RunBicycleExample.fillConfigWithBicycleStandardValues(config); + @Test public void testLinkBasedScoring() { +// { +// Config config = createConfig( 0 ); +// BicycleConfigGroup bicycleConfigGroup = (BicycleConfigGroup) config.getModules().get( "bicycle" ); +// bicycleConfigGroup.setBicycleScoringType( BicycleScoringType.legBased ); +// new RunBicycleExample().run( config ); +// } + Scenario scenarioReference = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + new PopulationReader(scenarioReference).readFile(utils.getInputDirectory() + "output_plans.xml.gz"); + { + Config config2 = createConfig( 0 ); + BicycleConfigGroup bicycleConfigGroup2 = (BicycleConfigGroup) config2.getModules().get( "bicycle" ); +// bicycleConfigGroup2.setBicycleScoringType( BicycleScoringType.linkBased ); + new RunBicycleExample().run( config2 ); + } + Scenario scenarioCurrent = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + new PopulationReader(scenarioCurrent).readFile(utils.getOutputDirectory() + "output_plans.xml.gz"); - // Normal network - config.network().setInputFile("network_normal.xml"); - config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(10); - config.controler().setLastIteration(10); - config.controler().setWriteEventsInterval(10); - config.controler().setWritePlansInterval(10); - config.controler().setCreateGraphs(false); +// LOG.info("Checking MATSim events file ..."); +// final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; +// final String eventsFilenameNew = utils.getOutputDirectory() + "output_events.xml.gz"; +// assertEquals("Different event files.", FILES_ARE_EQUAL, +// new EventsFileComparator().setIgnoringCoordinates( true ).runComparison(eventsFilenameReference, eventsFilenameNew)); - // Activate link-based scoring - BicycleConfigGroup bicycleConfigGroup = (BicycleConfigGroup) config.getModules().get("bicycle"); - bicycleConfigGroup.setBicycleScoringType(BicycleScoringType.linkBased); + for (Id personId : scenarioReference.getPopulation().getPersons().keySet()) { + double scoreReference = scenarioReference.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); + double scoreCurrent = scenarioCurrent.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); + Assert.assertEquals("Scores of persons " + personId + " are different", scoreReference, scoreCurrent, MatsimTestUtils.EPSILON); + } +// assertTrue("Populations are different", PopulationUtils.equalPopulation(scenarioReference.getPopulation(), scenarioCurrent.getPopulation())); + } + @Test public void testLinkVsLegMotorizedScoring() { + // --- withOUT additional car traffic: +// { +// Config config2 = createConfig( 0 ); +// BicycleConfigGroup bicycleConfigGroup2 = ConfigUtils.addOrGetModule( config2, BicycleConfigGroup.class ); +//// bicycleConfigGroup2.setBicycleScoringType( BicycleScoringType.linkBased ); +// bicycleConfigGroup2.setMotorizedInteraction( false ); +// new RunBicycleExample().run( config2 ); +// } + Scenario scenarioReference = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + new PopulationReader(scenarioReference).readFile(utils.getInputDirectory() + "output_plans.xml.gz"); + // --- + // --- WITH additional car traffic: + { + Config config = createConfig( 0 ); + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); +// bicycleConfigGroup.setBicycleScoringType( BicycleScoringType.legBased ); + bicycleConfigGroup.setMotorizedInteraction( true ); - // Interaction with motor vehicles - new RunBicycleExample().run(config, true); + // the following comes from inlining RunBicycleExample, which we need since we need to modify scenario data: + config.global().setNumberOfThreads(1 ); + config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists ); - LOG.info("Checking MATSim events file ..."); - final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; - final String eventsFilenameNew = utils.getOutputDirectory() + "output_events.xml.gz"; - assertEquals("Different event files.", FILES_ARE_EQUAL, - new EventsFileComparator().setIgnoringCoordinates( true ).runComparison(eventsFilenameReference, eventsFilenameNew)); + config.plansCalcRoute().setRoutingRandomness(3. ); - Scenario scenarioReference = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + final String bicycle = bicycleConfigGroup.getBicycleMode(); + + Scenario scenario = ScenarioUtils.loadScenario( config ); + + for( Link link : scenario.getNetwork().getLinks().values() ){ + link.setAllowedModes( CollectionUtils.stringArrayToSet( new String[]{ bicycleMode, TransportMode.car}) ); + } + + // add car traffic: + { + PopulationFactory pf = scenario.getPopulation().getFactory(); + List newPersons = new ArrayList<>(); + for( Person oldPerson : scenario.getPopulation().getPersons().values() ){ + Person newPerson = pf.createPerson( Id.createPersonId( oldPerson.getId() + "_car" ) ); + Plan newPlan = pf.createPlan(); + PopulationUtils.copyFromTo( oldPerson.getSelectedPlan(), newPlan ); + for( Leg leg : TripStructureUtils.getLegs( newPlan ) ){ + leg.setMode( TransportMode.car ); + } + newPerson.addPlan( newPlan ); + newPersons.add( newPerson ); + } + for( Person newPerson : newPersons ){ + scenario.getPopulation().addPerson( newPerson ); + } + } + + // go again back to RunBicycleExample material: + + // set config such that the mode vehicles come from vehicles data: + scenario.getConfig().qsim().setVehiclesSource( VehiclesSource.modeVehicleTypesFromVehiclesData ); + + // now put hte mode vehicles into the vehicles data: + final VehiclesFactory vf = VehicleUtils.getFactory(); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create(TransportMode.car, VehicleType.class ) ) ); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create( bicycle, VehicleType.class ) ) + .setNetworkMode( bicycle ).setMaximumVelocity(4.16666666 ).setPcuEquivalents(0.25 ) ); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new BicycleModule() ); + + controler.run(); + } Scenario scenarioCurrent = ScenarioUtils.createScenario(ConfigUtils.createConfig()); - new PopulationReader(scenarioReference).readFile(utils.getInputDirectory() + "output_plans.xml.gz"); new PopulationReader(scenarioCurrent).readFile(utils.getOutputDirectory() + "output_plans.xml.gz"); - assertTrue("Populations are different", PopulationUtils.equalPopulation(scenarioReference.getPopulation(), scenarioCurrent.getPopulation())); + // --- + // --- + +// LOG.info("Checking MATSim events file ..."); +// final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; +// final String eventsFilenameNew = utils.getOutputDirectory() + "output_events.xml.gz"; +// assertEquals("Different event files.", FILES_ARE_EQUAL, +// new EventsFileComparator().setIgnoringCoordinates( true ).runComparison(eventsFilenameReference, eventsFilenameNew)); + + for (Id personId : scenarioReference.getPopulation().getPersons().keySet()) { + double scoreReference = scenarioReference.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); + double scoreCurrent = scenarioCurrent.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); + Assert.assertEquals("Scores of person=" + personId + " are different", scoreReference, scoreCurrent, MatsimTestUtils.EPSILON); + } +// assertTrue("Populations are different", PopulationUtils.equalPopulation(scenarioReference.getPopulation(), scenarioCurrent.getPopulation())); } +// @Test public void testMotorizedInteraction() { +//// Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); +// Config config = createConfig( 10 ); +// +// // Activate link-based scoring +// BicycleConfigGroup bicycleConfigGroup = (BicycleConfigGroup) config.getModules().get("bicycle"); +//// bicycleConfigGroup.setBicycleScoringType(BicycleScoringType.linkBased); +// +// // Interaction with motor vehicles +// new RunBicycleExample().run(config ); +// +// LOG.info("Checking MATSim events file ..."); +// final String eventsFilenameReference = utils.getInputDirectory() + "output_events.xml.gz"; +// final String eventsFilenameNew = utils.getOutputDirectory() + "output_events.xml.gz"; +// assertEquals("Different event files.", FILES_ARE_EQUAL, +// new EventsFileComparator().setIgnoringCoordinates( true ).runComparison(eventsFilenameReference, eventsFilenameNew)); +// +// Scenario scenarioReference = ScenarioUtils.createScenario(ConfigUtils.createConfig()); +// Scenario scenarioCurrent = ScenarioUtils.createScenario(ConfigUtils.createConfig()); +// new PopulationReader(scenarioReference).readFile(utils.getInputDirectory() + "output_plans.xml.gz"); +// new PopulationReader(scenarioCurrent).readFile(utils.getOutputDirectory() + "output_plans.xml.gz"); +// for (Id personId : scenarioReference.getPopulation().getPersons().keySet()) { +// double scoreReference = scenarioReference.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); +// double scoreCurrent = scenarioCurrent.getPopulation().getPersons().get(personId).getSelectedPlan().getScore(); +// Assert.assertEquals("Scores of persons " + personId + " are different", scoreReference, scoreCurrent, MatsimTestUtils.EPSILON); +// } +// assertTrue("Populations are different", PopulationUtils.equalPopulation(scenarioReference.getPopulation(), scenarioCurrent.getPopulation())); +// } @Test public void testInfrastructureSpeedFactor() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); - - var bicycleConfig = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); - final String bicycleMode = bicycleConfig.getBicycleMode(); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); + config.addModule(new BicycleConfigGroup()); config.controler().setWriteEventsInterval(0); config.controler().setWritePlansInterval(0); @@ -426,8 +536,8 @@ public void install() { @Test public void testInfrastructureSpeedFactorDistanceMoreRelevantThanTravelTime() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); - var bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); config.controler().setWriteEventsInterval(0); config.controler().setWritePlansInterval(0); @@ -507,6 +617,26 @@ public void testInfrastructureSpeedFactorDistanceMoreRelevantThanTravelTime() { Assert.assertEquals("Wrong travel time (bicycle user)", Math.ceil( 10000 / (25. * 0.1 / 3.6) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("6")).get(3), MatsimTestUtils.EPSILON); } + private Config createConfig( int lastIteration ){ + // Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); + Config config = ConfigUtils.createConfig( utils.getClassInputDirectory() ); + config.addModule( new BicycleConfigGroup() ); + RunBicycleExample.fillConfigWithBicycleStandardValues( config ); + + // Normal network + config.network().setInputFile( "network_normal.xml" ); + config.plans().setInputFile( "population_1200.xml" ); + config.controler().setOverwriteFileSetting( OverwriteFileSetting.deleteDirectoryIfExists ); + config.controler().setOutputDirectory( utils.getOutputDirectory() ); + config.controler().setLastIteration( lastIteration ); + config.controler().setLastIteration( lastIteration ); + config.controler().setWriteEventsInterval( 10 ); + config.controler().setWritePlansInterval( 10 ); + config.controler().setCreateGraphs( false ); + return config; + } + + } class LinkDemandEventHandler implements LinkEnterEventHandler, LinkLeaveEventHandler { diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_cobblestone.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_cobblestone.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_cobblestone.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_cobblestone.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_gradient.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_gradient.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_gradient.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_gradient.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_gradient_lane.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_gradient_lane.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_gradient_lane.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_gradient_lane.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_infrastructure-speed-factor.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_infrastructure-speed-factor.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_infrastructure-speed-factor.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_infrastructure-speed-factor.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_lane.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_lane.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_lane.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_lane.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_normal.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_normal.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_normal.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_normal.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/network_pedestrian.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_pedestrian.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/network_pedestrian.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/network_pedestrian.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/population_1200.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_1200.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/population_1200.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_1200.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/population_3.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_3.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/population_3.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_3.xml diff --git a/contribs/bicycle/src/main/resources/bicycle_example/population_4.xml b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_4.xml similarity index 100% rename from contribs/bicycle/src/main/resources/bicycle_example/population_4.xml rename to contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/population_4.xml diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/readme.md b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/readme.md new file mode 100644 index 00000000000..fcfa60187bc --- /dev/null +++ b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/readme.md @@ -0,0 +1 @@ +The input files were originally in ```./src/main/resources/bicycle_example/```. Since we do not want input files in the resource path, I moved it to here. I also copied them into the scenarios directory for ExamplesUtils, but that will have to be deployed before it works. kai, jul'23 diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_events.xml.gz index 18616ca44b3..9cb0d3cf237 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_plans.xml.gz index 718244d4d58..17c8452fd78 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testCobblestone/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_events.xml.gz index e47e8d4f77f..97f24034206 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_plans.xml.gz index 81a125b5531..59e2f55ae6f 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradient/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_events.xml.gz index 2c05fccaab8..a49a018f3d0 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_plans.xml.gz index 7b8e0927dd3..69788484cf9 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_events.xml.gz new file mode 100644 index 00000000000..97f24034206 Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_plans.xml.gz new file mode 100644 index 00000000000..59e2f55ae6f Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testGradientLane/testGradient/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_events.xml.gz index 450f285d600..955cde9e707 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_plans.xml.gz index 9fc8cc70b0d..f41e975b460 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLane/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_events.xml.gz new file mode 100644 index 00000000000..3478209daf4 Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_plans.xml.gz new file mode 100644 index 00000000000..31eeebd5ab5 Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkBasedScoring/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_events.xml.gz new file mode 100644 index 00000000000..e6f996e514c Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_plans.xml.gz new file mode 100644 index 00000000000..6a99b6e8af4 Binary files /dev/null and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testLinkVsLegMotorizedScoring/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testMotorizedInteraction/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testMotorizedInteraction/output_events.xml.gz index 532ec3ef5f8..814ece57bde 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testMotorizedInteraction/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testMotorizedInteraction/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_events.xml.gz index 8e387d4a68c..c45233d6432 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_plans.xml.gz index 6f10118b020..31eeebd5ab5 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_events.xml.gz index cda1f926215..f0c50645e9b 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_plans.xml.gz index f963eb1abaa..3c5b6caa023 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testNormal10It/output_plans.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_events.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_events.xml.gz index d6c408efce1..f5b8f36e29d 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_events.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_events.xml.gz differ diff --git a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_plans.xml.gz b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_plans.xml.gz index a56c4509476..b09a281270f 100644 Binary files a/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_plans.xml.gz and b/contribs/bicycle/test/input/org/matsim/contrib/bicycle/run/BicycleTest/testPedestrian/output_plans.xml.gz differ diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingChangeoverActivity.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingChangeoverActivity.java index 2c5d784e8da..f5771c79f09 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingChangeoverActivity.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingChangeoverActivity.java @@ -25,9 +25,9 @@ public ChargingChangeoverActivity(ChargingTask chargingTask, PassengerHandler pa DynAgent driver, StayTask task, Map, ? extends AcceptedDrtRequest> dropoffRequests, Map, ? extends AcceptedDrtRequest> pickupRequests) { - chargingDelegate = new FixedTimeChargingActivity(chargingTask, task.getEndTime()); - busStopDelegate = new DrtStopActivity(passengerHandler, driver, task, dropoffRequests, pickupRequests, ""); endTime = task.getEndTime(); + chargingDelegate = new FixedTimeChargingActivity(chargingTask, endTime); + busStopDelegate = new DrtStopActivity(passengerHandler, driver, () -> endTime, dropoffRequests, pickupRequests, ""); } @Override diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftDrtActionCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftDrtActionCreator.java index 42c7c18db1a..0f65e1208f0 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftDrtActionCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftDrtActionCreator.java @@ -32,11 +32,11 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now Task task = vehicle.getSchedule().getCurrentTask(); if (task instanceof ShiftBreakTask) { DrtStopTask t = (DrtStopTask)task; - return new DrtStopActivity(passengerHandler, dynAgent, t, t.getDropoffRequests(), t.getPickupRequests(), + return new DrtStopActivity(passengerHandler, dynAgent, t::getEndTime, t.getDropoffRequests(), t.getPickupRequests(), DRT_SHIFT_BREAK_NAME); } else if (task instanceof ShiftChangeOverTask) { DrtStopTask t = (DrtStopTask) task; - return new DrtStopActivity(passengerHandler, dynAgent, t, t.getDropoffRequests(), t.getPickupRequests(), + return new DrtStopActivity(passengerHandler, dynAgent, t::getEndTime, t.getDropoffRequests(), t.getPickupRequests(), DRT_SHIFT_CHANGEOVER_NAME); } else if (task instanceof WaitForShiftStayTask) { return new IdleDynActivity(DRT_SHIFT_WAIT_FOR_SHIFT_NAME, task::getEndTime); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImpl.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImpl.java index 1b5ec6a5d94..98b961e2204 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImpl.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImpl.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtStopTask; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; @@ -100,31 +101,40 @@ public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { Waypoint.Stop s = stops[i] = new Waypoint.Stop(stopTasks.get(i), outgoingOccupancy); outgoingOccupancy -= s.getOccupancyChange(); } + + Waypoint.Stop startStop = startTask != null && STOP.isBaseTypeOf(startTask) + ? new Waypoint.Stop((DrtStopTask) startTask, 0) + : null; - var slackTimes = computeSlackTimes(vehicle, currentTime, stops); + var slackTimes = computeSlackTimes(vehicle, currentTime, stops, startStop); return new VehicleEntry(vehicle, new Waypoint.Start(startTask, start.link, start.time, outgoingOccupancy), - ImmutableList.copyOf(stops), slackTimes); + ImmutableList.copyOf(stops), slackTimes, currentTime); } public boolean isNotEligibleForRequestInsertion(DvrpVehicle vehicle, double currentTime) { return currentTime + lookAhead < vehicle.getServiceBeginTime() || currentTime >= vehicle.getServiceEndTime(); } - static double[] computeSlackTimes(DvrpVehicle vehicle, double now, Waypoint.Stop[] stops) { - double[] slackTimes = new double[stops.length + 1]; + static double[] computeSlackTimes(DvrpVehicle vehicle, double now, Waypoint.Stop[] stops, Waypoint.Stop start) { + double[] slackTimes = new double[stops.length + 2]; //vehicle double slackTime = calcVehicleSlackTime(vehicle, now); - slackTimes[stops.length] = slackTime; + slackTimes[stops.length + 1] = slackTime; //stops for (int i = stops.length - 1; i >= 0; i--) { var stop = stops[i]; slackTime = Math.min(stop.latestArrivalTime - stop.task.getBeginTime(), slackTime); slackTime = Math.min(stop.latestDepartureTime - stop.task.getEndTime(), slackTime); - slackTimes[i] = slackTime; + slackTimes[i + 1] = slackTime; } + + // start + slackTimes[0] = start == null ? slackTime : + Math.min(start.latestDepartureTime - start.task.getEndTime(), slackTime); + return slackTimes; } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleEntry.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleEntry.java index b79c3523f33..df2bd47957b 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleEntry.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/VehicleEntry.java @@ -37,14 +37,16 @@ public interface EntryFactory { public final ImmutableList stops; public final Waypoint.End end; private final double[] slackTimes;// for all insertion points + public final double createTime; public VehicleEntry(DvrpVehicle vehicle, Waypoint.Start start, ImmutableList stops, - double[] slackTimes) { + double[] slackTimes, double createTime) { this.vehicle = vehicle; this.start = start; this.stops = stops; this.end = Waypoint.End.OPEN_END; this.slackTimes = slackTimes; + this.createTime = createTime; } protected VehicleEntry(VehicleEntry that) { @@ -53,6 +55,7 @@ protected VehicleEntry(VehicleEntry that) { this.stops = that.stops; this.end = that.end; this.slackTimes = that.slackTimes; + this.createTime = that.createTime; } public Waypoint getWaypoint(int index) { @@ -64,6 +67,10 @@ public boolean isAfterLastStop(int index) { } public double getSlackTime(int index) { - return slackTimes[index]; + return slackTimes[index + 1]; + } + + public double getStartSlackTime() { + return slackTimes[0]; } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculator.java index 9ff3a41e82d..4e0006320cc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculator.java @@ -152,39 +152,8 @@ private PickupTimeInfo calculatePickupIfSameLink(VehicleEntry vEntry, int pickup return new PickupTimeInfo(departureTime, additionalStopDuration); } else { // case 3: previous waypoint is an ongoing (started) stop - // insertion is the beginning of the planned stop (traditionally) - - /* - * TODO: Generally, DRT is allowed to insert pickups into ongoing stop tasks. - * However, currently, stop tasks have a fixed duration that is never changed. - * This means that a pickup is added to an ongoing task that will end, maybe in - * 20s. The task will then end regardless of the added pickup. The request will, - * hence, have a wait time of 20s although the configured stop duration may be - * 60s. We can even frequently have requests with zero wait time. - * - * To mitigate the problem, the following steps are necessary: - * - Impose here not the stopTask.beginTime as the time at which the request is inserted, - * but impose the current time ("now") as the point of insertion. - * - The underlying StopTimeCalculator should make proper use of this information and - * *extend* the stop task here in the insertion algorithm. - * - This should equally be done in RequestInsertionScheduler where the stop task must be - * extended according to the information given by StopTimeCalculator. - * - The DrtStopActvity should not be based on the endTime of the stop that is known at - * time of construction, but it should depend dynamically on the end time (basically, - * like a stay task). - * - Finally, all of this means that by adding a pickup to an ongoing stop and, potentially, - * shifting the end time of the task, we may shift already assigned pick-ups beyond their - * latestDepartureTime. Testing for this requires to extend the definition of slack times. - * Specifically, VehicleEntryFactoryImpl only generates the slack *after* the start of the - * schedule. In an empty schedule this is the slack from the initial stay to the end time - * of the vehicle service. Hence, we need to introduce a "start slack" that indicates the - * slack at the very beginning of the schedule (i.e., how far can I extend the start stop - * if there is any to maintain a valid timing). - */ - - double insertionTime = stopTask.getBeginTime(); - double departureTime = stopTimeCalculator.updateEndTimeForPickup(vEntry.vehicle, stopTask, - insertionTime, request); + // insertion is a soon as possible (now) + double departureTime = stopTimeCalculator.updateEndTimeForPickup(vEntry.vehicle, stopTask, vEntry.createTime, request); double additionalStopDuration = departureTime - stopTask.getEndTime(); return new PickupTimeInfo(departureTime, additionalStopDuration); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java index 55d381daf01..1316ccde366 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java @@ -27,8 +27,10 @@ import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DetourTimeInfo; import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.PickupDetourInfo; import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.schedule.DrtTaskBaseType; import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.dvrp.schedule.Task; import com.google.common.base.MoreObjects; @@ -180,6 +182,11 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, toPickupDepartureTime + toPickupTT); //TODO stopDuration not included var pickupDetourInfo = detourTimeCalculator.calcPickupDetourInfo(vEntry, pickupInsertion, toPickupTT, fromPickupTT, true, request); + + if (i == 0 && !checkStartSlack(vEntry, request, pickupDetourInfo)) { + // Inserting at schedule start and extending an ongoing stop task further than allowed + return; + } int stopCount = vEntry.stops.size(); // i == j @@ -252,6 +259,26 @@ private Waypoint.Stop currentStop(VehicleEntry entry, int insertionIdx) { private Waypoint.Stop nextStop(VehicleEntry entry, int insertionIdx) { return entry.stops.get(insertionIdx); } + + private boolean checkStartSlack(VehicleEntry vEntry, DrtRequest request, PickupDetourInfo pickupDetourInfo) { + if (vEntry.start.task.isEmpty()) { + return true; + } + + Task startTask = vEntry.start.task.get(); + + if (!DrtTaskBaseType.STOP.isBaseTypeOf(startTask)) { + return true; + } + + DrtStopTask stopTask = (DrtStopTask) startTask; + + if (stopTask.getLink() != request.getFromLink()) { + return true; + } + + return vEntry.getStartSlackTime() >= pickupDetourInfo.departureTime - stopTask.getEndTime(); + } private InsertionWithDetourData createInsertionWithDetourData(DrtRequest request, VehicleEntry vehicleEntry, InsertionPoint pickupInsertion, double fromPickupTT, PickupDetourInfo pickupDetourInfo, int dropoffIdx) { diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java index 6d7a375efe7..e646c761df1 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java @@ -21,6 +21,7 @@ package org.matsim.contrib.drt.passenger; import java.util.Map; +import java.util.function.Supplier; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; @@ -42,11 +43,11 @@ public class DrtStopActivity extends FirstLastSimStepDynActivity implements Pass private final DynAgent driver; private final Map, ? extends AcceptedDrtRequest> dropoffRequests; private final Map, ? extends AcceptedDrtRequest> pickupRequests; - private final double expectedEndTime; + private final Supplier endTime; private int passengersPickedUp = 0; - public DrtStopActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask task, + public DrtStopActivity(PassengerHandler passengerHandler, DynAgent driver, Supplier endTime, Map, ? extends AcceptedDrtRequest> dropoffRequests, Map, ? extends AcceptedDrtRequest> pickupRequests, String activityType) { super(activityType); @@ -54,12 +55,12 @@ public DrtStopActivity(PassengerHandler passengerHandler, DynAgent driver, StayT this.driver = driver; this.dropoffRequests = dropoffRequests; this.pickupRequests = pickupRequests; - this.expectedEndTime = task.getEndTime(); + this.endTime = endTime; } @Override protected boolean isLastStep(double now) { - return passengersPickedUp == pickupRequests.size() && now >= expectedEndTime; + return passengersPickedUp == pickupRequests.size() && now >= endTime.get(); } @Override @@ -72,7 +73,7 @@ protected void beforeFirstStep(double now) { @Override protected void simStep(double now) { - if (now == expectedEndTime) { + if (now == endTime.get()) { for (var request : pickupRequests.values()) { if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { passengersPickedUp++; @@ -83,7 +84,7 @@ protected void simStep(double now) { @Override public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - if (now < expectedEndTime) { + if (now < endTime.get()) { return;// pick up only at the end of stop activity } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java index 9f184486755..5f40c9a80f6 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/DefaultRequestInsertionScheduler.java @@ -126,6 +126,7 @@ private void verifyTimes(String messageStart, double timeFromInsertionData, doub } private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetourData insertionWithDetourData) { + final double now = timer.getTimeOfDay(); var insertion = insertionWithDetourData.insertion; VehicleEntry vehicleEntry = insertion.vehicleEntry; Schedule schedule = vehicleEntry.vehicle.getSchedule(); @@ -164,7 +165,6 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour stayTask.setEndTime(stayTask.getBeginTime());// could get later removed with ScheduleTimingUpdater } else if (STAY.isBaseTypeOf(currentTask)) { stayTask = (DrtStayTask)currentTask; // ongoing stay task - double now = timer.getTimeOfDay(); if (stayTask.getEndTime() > now) { // stop stay task; a new stop/drive task can be inserted now stayTask.setEndTime(now); } @@ -179,15 +179,9 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour // add pickup request to stop task stopTask.addPickupRequest(request); - /* - * TODO: insertionTime should be set to "now" here to avoid adding pickups to - * ongoing tasks "for free" and generating requests with zero wait time. See - * InsertionDetourTimeCalculator.calculatePickupIfSameLink for more details. - */ - - double insertionTime = stopTask.getBeginTime(); + // potentially extend task stopTask.setEndTime(stopTimeCalculator.updateEndTimeForPickup(vehicleEntry.vehicle, stopTask, - insertionTime, request.getRequest())); + now, request.getRequest())); /// ADDED //// TODO this is copied, but has not been updated !!!!!!!!!!!!!!! @@ -285,6 +279,7 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetourData insertionWithDetourData, DrtStopTask pickupTask) { + final double now = timer.getTimeOfDay(); var insertion = insertionWithDetourData.insertion; VehicleEntry vehicleEntry = insertion.vehicleEntry; Schedule schedule = vehicleEntry.vehicle.getSchedule(); @@ -303,15 +298,9 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou // add dropoff request to stop task, and extend the stop task (when incremental stop task duration is used) stopTask.addDropoffRequest(request); - /* - * TODO: insertionTime should be set to "now" here to avoid adding pickups to - * ongoing tasks "for free" and generating requests with zero wait time. See - * InsertionDetourTimeCalculator.calculatePickupIfSameLink for more details. - */ - - double insertionTime = stopTask.getBeginTime(); + // potentially extend task stopTask.setEndTime(stopTimeCalculator.updateEndTimeForDropoff(vehicleEntry.vehicle, stopTask, - insertionTime, request.getRequest())); + now, request.getRequest())); scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, stopTask.getTaskIdx() + 1, stopTask.getEndTime()); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/vrpagent/DrtActionCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/vrpagent/DrtActionCreator.java index a1ced347d8a..1d57080e521 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/vrpagent/DrtActionCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/vrpagent/DrtActionCreator.java @@ -63,7 +63,7 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now case STOP: DrtStopTask t = (DrtStopTask)task; - return new DrtStopActivity(passengerHandler, dynAgent, t, t.getDropoffRequests(), t.getPickupRequests(), + return new DrtStopActivity(passengerHandler, dynAgent, t::getEndTime, t.getDropoffRequests(), t.getPickupRequests(), DRT_STOP_NAME); case STAY: diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImplTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImplTest.java index 45fae689ef2..79bde9d12c8 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImplTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/VehicleDataEntryFactoryImplTest.java @@ -49,34 +49,46 @@ public class VehicleDataEntryFactoryImplTest { @Test public void computeSlackTimes_withStops() { //final stay task not started - vehicle slack time is 50 - assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] { stop0, stop1 })).containsExactly(20, 30, 50); + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] { stop0, stop1 }, null)).containsExactly(20, 20, 30, 50); //final stay task not started - vehicle slack time is 25 and limits the slack times at stop1 - assertThat(computeSlackTimes(vehicle(500, 475), 100, new Stop[] { stop0, stop1 })).containsExactly(20, 25, 25); + assertThat(computeSlackTimes(vehicle(500, 475), 100, new Stop[] { stop0, stop1 }, null)).containsExactly(20, 20, 25, 25); //final stay task not started - vehicle slack time is 10 and limits the slack times at all stops - assertThat(computeSlackTimes(vehicle(500, 490), 100, new Stop[] { stop0, stop1 })).containsExactly(10, 10, 10); + assertThat(computeSlackTimes(vehicle(500, 490), 100, new Stop[] { stop0, stop1 }, null)).containsExactly(10, 10, 10, 10); } @Test public void computeSlackTimes_withoutStops() { //final stay task not started yet - vehicle slack time is 10 - assertThat(computeSlackTimes(vehicle(500, 490), 485, new Stop[] {})).containsExactly(10); + assertThat(computeSlackTimes(vehicle(500, 490), 485, new Stop[] {}, null)).containsExactly(10, 10); //final stay task just started - vehicle slack time is 10 - assertThat(computeSlackTimes(vehicle(500, 490), 490, new Stop[] {})).containsExactly(10); + assertThat(computeSlackTimes(vehicle(500, 490), 490, new Stop[] {}, null)).containsExactly(10, 10); //final stay task half completed - vehicle slack time is 5 - assertThat(computeSlackTimes(vehicle(500, 490), 495, new Stop[] {})).containsExactly(5); + assertThat(computeSlackTimes(vehicle(500, 490), 495, new Stop[] {}, null)).containsExactly(5, 5); //final stay task just completed - vehicle slack time is 0 - assertThat(computeSlackTimes(vehicle(500, 490), 500, new Stop[] {})).containsExactly(0); + assertThat(computeSlackTimes(vehicle(500, 490), 500, new Stop[] {}, null)).containsExactly(0, 0); //final stay task started, but delayed - vehicle slack time is 0 - assertThat(computeSlackTimes(vehicle(500, 510), 510, new Stop[] {})).containsExactly(0); + assertThat(computeSlackTimes(vehicle(500, 510), 510, new Stop[] {}, null)).containsExactly(0, 0); //final stay task planned after vehicle end time - vehicle slack time is 0s - assertThat(computeSlackTimes(vehicle(500, 510), 300, new Stop[] {})).containsExactly(0); + assertThat(computeSlackTimes(vehicle(500, 510), 300, new Stop[] {}, null)).containsExactly(0, 0); + } + + @Test + public void computeSlackTimes_withStart() { + //start without stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] {}, stop0)).containsExactly(30, 50); + + //start without stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] {}, stop1)).containsExactly(30, 50); + + //start with stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] { stop1 }, stop0)).containsExactly(30, 30, 50); } private Stop stop(double beginTime, double latestArrivalTime, double endTime, double latestDepartureTime) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/BestInsertionFinderTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/BestInsertionFinderTest.java index cc7fdac6b04..14b8c359ec5 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/BestInsertionFinderTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/BestInsertionFinderTest.java @@ -132,7 +132,7 @@ private void whenInsertionThenCost(InsertionWithDetourData insertion, double cos private InsertionWithDetourData insertion(String vehicleId, int pickupIdx, int dropoffIdx) { var vehicle = mock(DvrpVehicle.class); when(vehicle.getId()).thenReturn(Id.create(vehicleId, DvrpVehicle.class)); - var vehicleEntry = new VehicleEntry(vehicle, null, null, null); + var vehicleEntry = new VehicleEntry(vehicle, null, null, null, 0); var pickupInsertion = new InsertionGenerator.InsertionPoint(pickupIdx, null, null, null); var dropoffInsertion = new InsertionGenerator.InsertionPoint(dropoffIdx, null, null, null); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java index afb8b62cc58..dcdc662f335 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java @@ -197,7 +197,7 @@ public void acceptedRequest() { var unplannedRequests = requests(request1); double now = 15; - var vehicle1Entry = new VehicleEntry(vehicle1, null, null, null); + var vehicle1Entry = new VehicleEntry(vehicle1, null, null, null, 0); var createEntryCounter = new MutableInt(); VehicleEntry.EntryFactory entryFactory = (vehicle, currentTime) -> { //make sure the right arguments are passed diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionCostCalculatorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionCostCalculatorTest.java index 22af2a9cf15..f9c25dd60cf 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionCostCalculatorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionCostCalculatorTest.java @@ -42,7 +42,7 @@ public class InsertionCostCalculatorTest { @Test public void testCalculate() { - VehicleEntry entry = entry(new double[] { 20, 50 }); + VehicleEntry entry = entry(new double[] { 20, 20, 50 }); var insertion = insertion(entry, 0, 1); //feasible solution @@ -71,7 +71,7 @@ private void assertCalculate(Insertion insertion, DetourTimeInfo detourTimeInfo, } private VehicleEntry entry(double[] slackTimes) { - return new VehicleEntry(null, null, null, slackTimes); + return new VehicleEntry(null, null, null, slackTimes, 0); } private Link link(String id) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorTest.java index 010e531b996..16e708701fd 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorTest.java @@ -234,7 +234,7 @@ private Waypoint.Stop stop(double beginTime, Link link) { } private VehicleEntry entry(Waypoint.Start start, Waypoint.Stop... stops) { - return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null); + return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null, 0); } private InsertionDetourData detourData(double toPickupTT, double fromPickupTT, double toDropoffTT, diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorWithVariableDurationTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorWithVariableDurationTest.java index 37f95db5c83..8f9b037a7dc 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorWithVariableDurationTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionDetourTimeCalculatorWithVariableDurationTest.java @@ -274,7 +274,7 @@ private Waypoint.Stop stop(double beginTime, Link link) { } private VehicleEntry entry(Waypoint.Start start, Waypoint.Stop... stops) { - return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null); + return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null, 0); } private InsertionWithDetourData insertion(VehicleEntry entry, int pickupIdx, int dropoffIdx, diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java index 22da5b22295..4509e5f90ad 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java @@ -207,10 +207,10 @@ public void startEmpty_twoStops_notFullBetweenStops_tightSlackTimes() { Waypoint.Stop stop0 = stop(start.time + TIME_REPLACED_DRIVE, link("stop0"), 1);//pick up 1 pax Waypoint.Stop stop1 = stop(stop0.getDepartureTime() + TIME_REPLACED_DRIVE, link("stop1"), 0);//drop off 1 pax - double[] slackTimes = { 0, // impossible insertions: 00, 01, 02 (pickup at 0 is not possible) + double[] slackTimes = { 0, 0, // impossible insertions: 00, 01, 02 (pickup at 0 is not possible) 500, // additional impossible insertions: 11 (too long total detour); however 12 is possible 1000 }; // 22 is possible - VehicleEntry entry = new VehicleEntry(vehicle, start, ImmutableList.of(stop0, stop1), slackTimes); + VehicleEntry entry = new VehicleEntry(vehicle, start, ImmutableList.of(stop0, stop1), slackTimes, 0); var insertions = new ArrayList(); {//12 @@ -399,8 +399,8 @@ private Waypoint.Stop stop(double beginTime, Link link, int outgoingOccupancy) { } private VehicleEntry entry(Waypoint.Start start, Waypoint.Stop... stops) { - var slackTimes = new double[stops.length + 1]; + var slackTimes = new double[stops.length + 2]; Arrays.fill(slackTimes, Double.POSITIVE_INFINITY); - return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), slackTimes); + return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), slackTimes, 0); } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/DetourPathDataCacheTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/DetourPathDataCacheTest.java index 37ac3a13a1d..283f69b0cf3 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/DetourPathDataCacheTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/DetourPathDataCacheTest.java @@ -132,7 +132,7 @@ private Link link(String id) { private VehicleEntry entry(Link startLink, Link... stopLinks) { return new VehicleEntry(null, new Waypoint.Start(null, startLink, 0, 0), - Arrays.stream(stopLinks).map(this::stop).collect(ImmutableList.toImmutableList()), null); + Arrays.stream(stopLinks).map(this::stop).collect(ImmutableList.toImmutableList()), null, 0); } private Waypoint.Stop stop(Link link) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/KNearestInsertionsAtEndFilterTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/KNearestInsertionsAtEndFilterTest.java index 8711a88a795..e971c5a312e 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/KNearestInsertionsAtEndFilterTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/KNearestInsertionsAtEndFilterTest.java @@ -124,7 +124,7 @@ private Waypoint.Stop stop(double endTime) { private VehicleEntry vehicleEntry(String id, Waypoint.Start start, Waypoint.Stop... stops) { var vehicle = mock(DvrpVehicle.class); when(vehicle.getId()).thenReturn(Id.create(id, DvrpVehicle.class)); - return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), null); + return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), null, 0); } private List filterOneInsertionAtEnd(InsertionWithDetourData... insertions) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java index 0c18bbb7fe4..3d28a85cffd 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java @@ -38,6 +38,7 @@ import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; import org.matsim.contrib.drt.run.DrtControlerCreator; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.drt.stops.CorrectedStopTimeCalculator; import org.matsim.contrib.drt.stops.CumulativeStopTimeCalculator; import org.matsim.contrib.drt.stops.MinimumStopDurationAdapter; import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; @@ -212,6 +213,41 @@ public void testRunDrtStopbasedExample() { verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); } + + @Test + public void testRunDrtStopbasedExampleWithFlexibleStopDuration() { + Id.resetCaches(); + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), + "mielec_stop_based_drt_config.xml"); + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup()); + + config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controler().setOutputDirectory(utils.getOutputDirectory()); + + Controler controller = DrtControlerCreator.createControler(config, false); + + // This snippet adds the correction against wait times smaller than the defined stopDuration + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { + @Override + public void install() { + StopTimeCalculator stopTimeCalculator = new CorrectedStopTimeCalculator(60.0); + bindModal(StopTimeCalculator.class).toInstance(stopTimeCalculator); + } + }); + + controller.run(); + + var expectedStats = Stats.newBuilder() + .rejectionRate(0.05) + .rejections(17) + .waitAverage(261.88) + .inVehicleTravelTimeMean(376.04) + .totalTravelTimeMean(637.93) + .build(); + + verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); + } @Test public void testRunServiceAreabasedExampleWithSpeedUp() { @@ -263,9 +299,9 @@ public void install() { var expectedStats = Stats.newBuilder() .rejectionRate(0.04) .rejections(16) - .waitAverage(278.11) + .waitAverage(278.92) .inVehicleTravelTimeMean(384.6) - .totalTravelTimeMean(662.71) + .totalTravelTimeMean(663.52) .build(); verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); diff --git a/contribs/ev/pom.xml b/contribs/ev/pom.xml index f2c94d726b6..ba04bbce066 100644 --- a/contribs/ev/pom.xml +++ b/contribs/ev/pom.xml @@ -30,5 +30,11 @@ commons-csv 1.10.0 + + one.util + streamex + 0.8.2 + compile + diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileCalculator.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileCalculator.java new file mode 100644 index 00000000000..78e7f6987ef --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileCalculator.java @@ -0,0 +1,82 @@ +package org.matsim.contrib.ev.stats; + +import java.util.HashMap; +import java.util.Map; + +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.common.timeprofile.TimeDiscretizer; +import org.matsim.contrib.ev.EvConfigGroup; +import org.matsim.contrib.ev.EvUnits; +import org.matsim.contrib.ev.charging.ChargingEndEvent; +import org.matsim.contrib.ev.charging.ChargingEndEventHandler; +import org.matsim.contrib.ev.charging.ChargingStartEvent; +import org.matsim.contrib.ev.charging.ChargingStartEventHandler; +import org.matsim.contrib.ev.infrastructure.Charger; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.vehicles.Vehicle; + +import com.google.inject.Inject; + +public class ChargerPowerTimeProfileCalculator implements ChargingStartEventHandler, ChargingEndEventHandler { + + private final Map, double[]> chargerProfiles = new HashMap<>(); + private final Map, Double> chargingStartTimeMap = new HashMap<>(); + private final Map, Double> chargingStartEnergyMap = new HashMap<>(); + + private final TimeDiscretizer timeDiscretizer; + private final double qsimEndTime; + + /** + * Calculation of average power output for each charging station for each charging event. Charging stations without any charging events will not + * be present in the output file. Implementation does only work when the Qsim end time is defined in the config, i.e., will not work for + * extensions drt and taxi or others depending on the ev contrib without a defined Qsim end time. + * @author mattiasingelstrom + */ + @Inject + public ChargerPowerTimeProfileCalculator(Config config) { + int chargeTimeStep = ConfigUtils.addOrGetModule(config, EvConfigGroup.class).chargeTimeStep; + qsimEndTime = ConfigUtils.addOrGetModule(config, QSimConfigGroup.class).getEndTime().orElse(0.0); + timeDiscretizer = new TimeDiscretizer((int)Math.ceil(qsimEndTime), chargeTimeStep); + } + + + public Map, double[]> getChargerProfiles() { + return chargerProfiles; + } + + @Override + public void handleEvent(ChargingStartEvent event) { + chargingStartTimeMap.put(event.getVehicleId(), event.getTime()); + chargingStartEnergyMap.put(event.getVehicleId(), EvUnits.J_to_kWh(event.getCharge())); + } + + @Override + + public void handleEvent(ChargingEndEvent event) { + double chargingTimeIn_h = (event.getTime() - chargingStartTimeMap.get(event.getVehicleId())) / 3600.0; + double averagePowerIn_kW = (EvUnits.J_to_kWh(event.getCharge()) - chargingStartEnergyMap.get(event.getVehicleId())) / chargingTimeIn_h; + increment(averagePowerIn_kW, event.getChargerId(), chargingStartTimeMap.get(event.getVehicleId()), event.getTime()); + } + private void increment(double averagePower, Id chargerId, double chargingStartTime, double chargingEndTime) { + + //If Qsim end time is undefined in config, qsimEndTime will be 0.0 and will therefore not proceed in calculating the power curves. + if (chargingStartTime == chargingEndTime || chargingStartTime >= qsimEndTime || qsimEndTime == 0.0) { + return; + } + chargingEndTime = Math.min(chargingEndTime, qsimEndTime); + + int fromIdx = timeDiscretizer.getIdx(chargingStartTime); + int toIdx = timeDiscretizer.getIdx(chargingEndTime); + + for (int i = fromIdx; i < toIdx; i++) { + double[] chargingVector = chargerProfiles.computeIfAbsent(chargerId, c -> new double[timeDiscretizer.getIntervalCount()]); + chargingVector[i] += averagePower; + } + } + + public TimeDiscretizer getTimeDiscretizer() { + return timeDiscretizer; + } +} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileView.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileView.java new file mode 100644 index 00000000000..d0941954540 --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerPowerTimeProfileView.java @@ -0,0 +1,39 @@ +package org.matsim.contrib.ev.stats; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.tuple.Pair; +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.common.timeprofile.ProfileWriter; + +import java.awt.*; +import java.util.Map; + +public class ChargerPowerTimeProfileView implements ProfileWriter.ProfileView { + private final ChargerPowerTimeProfileCalculator calculator; + + public ChargerPowerTimeProfileView(ChargerPowerTimeProfileCalculator calculator) { + this.calculator = calculator; + + } + @Override + public double[] times() { + return calculator.getTimeDiscretizer().getTimes(); + } + + @Override + public ImmutableMap profiles() { + return calculator.getChargerProfiles() + .entrySet() + .stream() + .sorted(Map.Entry.comparingByKey(Id::compareTo)) + .map(e -> Pair.of(e.getKey().toString(), e.getValue())) + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + public Map seriesPaints() { + return Map.of(); + } + + +} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/EvStatsModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/EvStatsModule.java index 4aa2596c6ca..91f9de2878b 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/EvStatsModule.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/EvStatsModule.java @@ -20,10 +20,14 @@ package org.matsim.contrib.ev.stats; +import com.google.inject.Provider; +import org.matsim.contrib.common.timeprofile.ProfileWriter; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.EvModule; import org.matsim.contrib.ev.charging.ChargingEventSequenceCollector; import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.MatsimServices; +import org.matsim.core.controler.listener.ControlerListener; import org.matsim.core.mobsim.qsim.AbstractQSimModule; import com.google.inject.Inject; @@ -44,11 +48,11 @@ public void install() { @Override protected void configureQSim() { if (evCfg.timeProfiles) { - addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider( SocHistogramTimeProfileCollectorProvider.class); - addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider( IndividualChargeTimeProfileCollectorProvider.class); - addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider( ChargerOccupancyTimeProfileCollectorProvider.class); + addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider(SocHistogramTimeProfileCollectorProvider.class); + addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider(IndividualChargeTimeProfileCollectorProvider.class); + addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider(ChargerOccupancyTimeProfileCollectorProvider.class); addQSimComponentBinding(EvModule.EV_COMPONENT).to(ChargerOccupancyXYDataCollector.class).asEagerSingleton(); - addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider( VehicleTypeAggregatedChargeTimeProfileCollectorProvider.class); + addQSimComponentBinding(EvModule.EV_COMPONENT).toProvider(VehicleTypeAggregatedChargeTimeProfileCollectorProvider.class); bind(ChargerPowerCollector.class).asEagerSingleton(); addMobsimScopeEventHandlerBinding().to(ChargerPowerCollector.class); @@ -61,5 +65,20 @@ protected void configureQSim() { } } }); + bind(ChargerPowerTimeProfileCalculator.class).asEagerSingleton(); + addEventHandlerBinding().to(ChargerPowerTimeProfileCalculator.class); + addControlerListenerBinding().toProvider(new Provider<>() { + @Inject + private ChargerPowerTimeProfileCalculator calculator; + @Inject + private MatsimServices matsimServices; + + @Override + public ControlerListener get() { + var profileView = new ChargerPowerTimeProfileView(calculator); + return new ProfileWriter(matsimServices,"ev",profileView,"charger_power_time_profiles"); + + } + }); } } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierEventsReaders.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierEventsReaders.java index c268d7d1184..20eb9dad5a8 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierEventsReaders.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierEventsReaders.java @@ -36,9 +36,14 @@ public class CarrierEventsReaders { public static Map createCustomEventMappers() { return Map.of( + CarrierServiceStartEvent.EVENT_TYPE, CarrierServiceStartEvent::convert, + CarrierServiceEndEvent.EVENT_TYPE, CarrierServiceEndEvent::convert, + CarrierShipmentDeliveryStartEvent.EVENT_TYPE, CarrierShipmentDeliveryStartEvent::convert, + CarrierShipmentDeliveryEndEvent.EVENT_TYPE, CarrierShipmentDeliveryEndEvent::convert, + CarrierShipmentPickupStartEvent.EVENT_TYPE, CarrierShipmentPickupStartEvent::convert, + CarrierShipmentPickupEndEvent.EVENT_TYPE, CarrierShipmentPickupEndEvent::convert, CarrierTourStartEvent.EVENT_TYPE, CarrierTourStartEvent::convert, // CarrierTourEndEvent.EVENT_TYPE, CarrierTourEndEvent::convert - // more will follow later, KMT feb'23 ); } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceEndEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceEndEvent.java index e1527edc9b0..0c4585932a0 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceEndEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceEndEvent.java @@ -24,6 +24,8 @@ import java.util.Map; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierService; import org.matsim.vehicles.Vehicle; @@ -67,4 +69,17 @@ public Map getAttributes() { attr.put(ATTRIBUTE_SERVICE_DURATION, String.valueOf(serviceDuration)); return attr; } + + public static CarrierServiceEndEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id carrierServiceId = Id.create(attributes.get(ATTRIBUTE_SERVICE_ID), CarrierService.class); + Id locationLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + CarrierService service = CarrierService.Builder.newInstance(carrierServiceId, locationLinkId) + .setServiceDuration(Double.parseDouble(attributes.get(ATTRIBUTE_SERVICE_DURATION))) + .build(); + Id vehicleId = Id.create(attributes.get(ATTRIBUTE_VEHICLE), Vehicle.class); + return new CarrierServiceEndEvent(time, carrierId, service, vehicleId); + } } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceStartEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceStartEvent.java index 719bfc96978..6c8550df63f 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceStartEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierServiceStartEvent.java @@ -22,6 +22,8 @@ package org.matsim.contrib.freight.events; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierService; import org.matsim.vehicles.Vehicle; @@ -77,4 +79,18 @@ public Map getAttributes() { attr.put(ATTRIBUTE_CAPACITYDEMAND, String.valueOf(capacityDemand)); return attr; } + + public static CarrierServiceStartEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id carrierServiceId = Id.create(attributes.get(ATTRIBUTE_SERVICE_ID), CarrierService.class); + Id locationLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + CarrierService service = CarrierService.Builder.newInstance(carrierServiceId, locationLinkId) + .setServiceDuration(Double.parseDouble(attributes.get(ATTRIBUTE_SERVICE_DURATION))) + .setCapacityDemand(Integer.parseInt(attributes.get(ATTRIBUTE_CAPACITYDEMAND))) + .build(); + Id vehicleId = Id.create(attributes.get(ATTRIBUTE_VEHICLE), Vehicle.class); + return new CarrierServiceStartEvent(time, carrierId, service, vehicleId); + } } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryEndEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryEndEvent.java index 9e702f3359d..eb462b77a88 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryEndEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryEndEvent.java @@ -22,6 +22,8 @@ package org.matsim.contrib.freight.events; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierShipment; import org.matsim.vehicles.Vehicle; @@ -76,4 +78,18 @@ public Map getAttributes() { return attr; } + public static CarrierShipmentDeliveryEndEvent convert(GenericEvent event) { + var attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id shipmentId = Id.create(attributes.get(ATTRIBUTE_SHIPMENT_ID), CarrierShipment.class); + Id shipmentTo = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + int size = Integer.parseInt(attributes.get(ATTRIBUTE_CAPACITYDEMAND)); + CarrierShipment shipment = CarrierShipment.Builder.newInstance(shipmentId, null, shipmentTo, size) + .setDeliveryServiceTime(Double.parseDouble(attributes.get(ATTRIBUTE_SERVICE_DURATION))) + .build(); + Id vehicleId = Id.createVehicleId(attributes.get(ATTRIBUTE_VEHICLE)); + return new CarrierShipmentDeliveryEndEvent(time, carrierId, shipment, vehicleId); + } + } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryStartEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryStartEvent.java index 6e097bbe64d..08b77671842 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryStartEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentDeliveryStartEvent.java @@ -22,6 +22,8 @@ package org.matsim.contrib.freight.events; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierShipment; import org.matsim.vehicles.Vehicle; @@ -75,4 +77,18 @@ public Map getAttributes() { return attr; } + public static CarrierShipmentDeliveryStartEvent convert(GenericEvent event) { + var attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id shipmentId = Id.create(attributes.get(ATTRIBUTE_SHIPMENT_ID), CarrierShipment.class); + Id shipmentTo = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + int size = Integer.parseInt(attributes.get(ATTRIBUTE_CAPACITYDEMAND)); + CarrierShipment shipment = CarrierShipment.Builder.newInstance(shipmentId, null, shipmentTo, size) + .setDeliveryServiceTime(Double.parseDouble(attributes.get(ATTRIBUTE_SERVICE_DURATION))) + .build(); + Id vehicleId = Id.createVehicleId(attributes.get(ATTRIBUTE_VEHICLE)); + return new CarrierShipmentDeliveryStartEvent(time, carrierId, shipment, vehicleId); + } + } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupEndEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupEndEvent.java index 664dbfa45c7..e88ead0cb37 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupEndEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupEndEvent.java @@ -22,6 +22,8 @@ package org.matsim.contrib.freight.events; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierShipment; import org.matsim.vehicles.Vehicle; @@ -69,4 +71,18 @@ public Map getAttributes() { attr.put(ATTRIBUTE_CAPACITYDEMAND, String.valueOf(capacityDemand)); return attr; } + + public static CarrierShipmentPickupEndEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id shipmentId = Id.create(attributes.get(ATTRIBUTE_SHIPMENT_ID), CarrierShipment.class); + Id shipmentFrom = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + int shipmentSize = Integer.parseInt(attributes.get(ATTRIBUTE_CAPACITYDEMAND)); + CarrierShipment shipment = CarrierShipment.Builder.newInstance(shipmentId, shipmentFrom, null, shipmentSize) + .setPickupServiceTime(Double.parseDouble(attributes.get(ATTRIBUTE_PICKUP_DURATION))) + .build(); + Id vehicleId = Id.createVehicleId(attributes.get(ATTRIBUTE_VEHICLE)); + return new CarrierShipmentPickupEndEvent(time, carrierId, shipment, vehicleId); + } } diff --git a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupStartEvent.java b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupStartEvent.java index 7285434d134..4f7725e6ece 100644 --- a/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupStartEvent.java +++ b/contribs/freight/src/main/java/org/matsim/contrib/freight/events/CarrierShipmentPickupStartEvent.java @@ -22,6 +22,8 @@ package org.matsim.contrib.freight.events; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.freight.carrier.Carrier; import org.matsim.contrib.freight.carrier.CarrierShipment; import org.matsim.vehicles.Vehicle; @@ -69,4 +71,18 @@ public Map getAttributes() { attr.put(ATTRIBUTE_CAPACITYDEMAND, String.valueOf(capacityDemand)); return attr; } + + public static CarrierShipmentPickupStartEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + Id carrierId = Id.create(attributes.get(ATTRIBUTE_CARRIER_ID), Carrier.class); + Id shipmentId = Id.create(attributes.get(ATTRIBUTE_SHIPMENT_ID), CarrierShipment.class); + Id shipmentFrom = Id.createLinkId(attributes.get(ATTRIBUTE_LINK)); + int shipmentSize = Integer.parseInt(attributes.get(ATTRIBUTE_CAPACITYDEMAND)); + CarrierShipment shipment = CarrierShipment.Builder.newInstance(shipmentId, shipmentFrom, null, shipmentSize) + .setPickupServiceTime(Double.parseDouble(attributes.get(ATTRIBUTE_PICKUP_DURATION))) + .build(); + Id vehicleId = Id.createVehicleId(attributes.get(ATTRIBUTE_VEHICLE)); + return new CarrierShipmentPickupStartEvent(time, carrierId, shipment, vehicleId); + } } diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index 55a9bcd52b2..d340e18bbb6 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -10,8 +10,8 @@ hybridsim - 3.24.2 - 1.57.2 + 3.24.3 + 1.58.0 diff --git a/contribs/pom.xml b/contribs/pom.xml index 987847d56ac..90f504be8fa 100644 --- a/contribs/pom.xml +++ b/contribs/pom.xml @@ -90,6 +90,7 @@ parking protobuf pseudosimulation + railsim roadpricing sbb-extensions shared_mobility diff --git a/contribs/protobuf/pom.xml b/contribs/protobuf/pom.xml index b75f9d59200..7c8509d4e15 100644 --- a/contribs/protobuf/pom.xml +++ b/contribs/protobuf/pom.xml @@ -11,7 +11,7 @@ protobuf - 3.24.2 + 3.24.3 diff --git a/contribs/railsim/README.md b/contribs/railsim/README.md new file mode 100644 index 00000000000..3efbc30fedc --- /dev/null +++ b/contribs/railsim/README.md @@ -0,0 +1,35 @@ +# railsim + +A large-scale hybrid micro- and mesoscopic simulation approach for railway operation. + +Trains behave and interact with links differently than cars: While usually multiple cars can be on one link, +for safety reasons there should be only one train on a track (segment or block) per time. +And while a car is typically located on one link only, a long train may occupy space on multiple links. +To capture these differences and offer a realistic simulation of train traffic within MATSim, +the *railsim* contrib provides a custom QSim-Engine to simulate trains: + +- Trains are spatially expanded along several links. Additional events indicate when the end of the train leaves a link. +- Trains accelerate and decelerate based on predefined vehicle attributes (along a single link or along several links). +- The infrastructure ahead of each train is blocked (reserved train path) depending on the braking distance which is + computed based on the vehicle-specific deceleration and the current speed. +- Capacity effects are modeled at the level of resources. A resource consists of one link or several links. +- Trains may deviate from the network route given in the schedule, e.g. to avoid a blocked track (dispatching, + disposition). + +## Configuration + +See `config.RailsimConfigGroup.java` + +## Enabling railsim in MATSim + +See `RunRailsimExample.java` + +## Specifications + +- [network-specification](docs/network-specification.md) +- [train-specification](docs/train-specification.md) +- [events-specification](docs/events-specification.md) + +## Acknowledgments + +This contrib was initiated and initially funded by the [Swiss Federal Railways](https://www.sbb.ch) (SBB). diff --git a/contribs/railsim/docs/deadlock-avoidance.md b/contribs/railsim/docs/deadlock-avoidance.md new file mode 100644 index 00000000000..5b8a2229823 --- /dev/null +++ b/contribs/railsim/docs/deadlock-avoidance.md @@ -0,0 +1,145 @@ +# Deadlock-Avoidance + +Deadlocks can occur on two scales in the mesoscopic approach of railsim: + +- Locally within a route section with constant capacity: *Deadlock in a route section with bidirectionally shared + constant capacity.* +- Globally due to conflicting route selections in the network: *Deadlock due to sequence of multiple route sections with + different bidirectionally shared capacities (bottleneck).* + +## Local case + +The local case seems easier to solve. A possible approach is described below: + +Before a train travels on a section with constant capacity (successive links with constant capacity, where the speed +limit may vary, hereafter called segment), it must check that not all tracks are already occupied from the opposite +direction. If this is the case, the train must wait in front of the segment before entering. If at least one track from +the opposite direction is free, then the train must check whether tracks in its direction are already in use. If this is +the case, it will be checked that the incoming train is not slowed down by the preceding train if there is sufficient +capacity, so that the faster train can overtake the slower train. If the incoming train is slower than the previous +train anyway, it should enter the same track to keep as much capacity as possible free for the opposite direction. + +The tracks of a segment could be represented as an array of free-again times (maximum of the exit times of the trains on +a track) or zero if the track is free. When a train enters a track it overwrites the free-again time, if its exit time +is after the exit time of the preceding train. + +## Global case + +The global case is more complex to solve, and the problem is described below. The description is based on the segments +defined in the local case, even though they could possibly be omitted for this case, depending on the chosen solution +approach. + +A train on its route reserves segments ahead as far as possible (see *full path reservation*) or alternatively until the +next station with the assumption that there are enough tracks available in the station to pass each other (see *station +to station path reservation*). +If two reserved paths running in opposite directions meet in a segment and the maximum capacity in the segment is used, +then both trains should travel as far as it is still possible to pass each other. So they have to stop before the +bottleneck, where the capacity still allows for a crossing. The train that arrives first at the bottleneck continues its +path, while the other train remains stationary until it no longer sees the opposite train in its reserved path. Then +the waiting train continues its path into the bottleneck. + +This raises a few questions: + +- What happens when several (not just two) reserved paths overlap? No matter how many trains are oncoming, the segment + must allow **one** free track in the opposite direction. +- In situations with multiple overlaps, how do we find the positions where the trains should wait? Last places on the + path where the equivalent capacity is at least 1? +- How do we solve the problem computationally efficiently? + +### Full path reservation + +In case of reservation of the entire route of a train line, i.e. from the origin station to the destination station of +the route, deadlocks are no longer possible on the route. If we have vehicle circuits where a train is waiting at the +destination station for the next departure (and thus blocks a track), again a deadlock would be possible when another +train tries to enter the destination station and no track is available. + +==To Discuss:== this case is probably rather rare and negligible for the time being? If we model the stations as +segments too, then this case should be automatically solved? + +**Key idea** + +We have agents who want to navigate through resources. In the context of railsim, agents are individual vehicles, +resources are `RailLink`s (?), and the full path of an agent is the transit route it takes from the origin to the +destination station. A vehicle can have several transit routes on a simulation day. +Assuming that the current simulation is in a deadlock-free state, the goal is to ensure that the simulation will also be +deadlock-free after a next simulation step. This requires a deadlock avoidance algorithm. The algorithm shall prevent +deadlock state from occurring. + +**Algorithm** + +1. Setup: + - Create a constant 1D-array with total train capacity per resource **CAP** (dim: 1 x n_resources) + - Create a binary matrix for global positions **POS** (dim: n_agents x n_resources) and initialize it with zero. + - Create a binary matrix for all path masks **PATH** (dim: n_agents x n_resources) and calculate for each + agent **a_i** the binary mask for the full path with resources needed to reach the destination. Write mask into + the paths mask matrix **PATH[a_i, ]**. + - Create a set with moving agents **MOV** and a set with agents on a decision point resource **DEC**. Add all agents + to both sets. +2. Start railsim simulation, in each iteration: + 1. Handle moving set agents: + - For each agent in the moving set **MOV** enter the currently occupied resource into the global position + mask **POS[a_i, ]**. + 2. Handle decision set agents: + - Mask the path of each agent in the decision set **DEC** with the global position mask and identify all + potentially conflicting agents. + - Create a new 1D-array (dim: 1 x n_resources) with the sum of paths of all conflicting agents per agent (zero + out non-conflicting agents in **PATH** and sum the columns). + - Subtract the summed path array from the constant available capacity layer **CAP** for each agent. + - If the next resource an agent wants to allocate has at least one free capacity, the agent is added to the + moving set **MOV**, otherwise the agent is removed from the moving set and has to wait on the current resource + for this iteration. + 3. The steps 1 and 2 are repeated for **the situation where the next step is already moved virtually** of all agents + in the moving agent set. If there is a conflict, randomly chose one agent of the conflicting pair and remove it + from the moving set (has to wait in this interation). Do not save the actual movement from this step! + 4. For all agents in the moving set **MOV**, set the current resource in the path matrix to zero + **PATH[a_i, r_c]=0**. and check if the next resource they occupy is a decision point. If it is a decision point, + add the agent to the decision set *DEC*. + 5. Continue railsim iteration (here the actual movement of the agent happens) and repeat. + +**Key benefits** + +The algorithm described above can be fully parallelized. The computation time depends only on the number of agents (n) +and number of resources (m). The computation complexity is O(n2 * m) in the worst case. + +**References** + +This algorithm was designed and implemented by Adrian Egli (SBB) as part of the Flatland project. + +**Railsim context** + +- Resources have to be smaller entities (e.g. `RailLink`) than segments, since otherwise two agents are not allowed to + travel in the same direction. +- Not all resources should trigger the deadlock avoidance algorithm for the agent. For better performance, only when an + agent is on a decision point (decision set of agents), the algorithm is triggered (e.g. first / entering `RailLink` of + a segment with constant capacity). +- The former two points could be combined: + - Each segment consists of the following **directed** `RailLink`s for both directions (2x): An *entering* `RailLink` + , *intermediate* `RailLink`s for each change in VMax (with same capacity), a *leaving* `RailLink`. + - Every `RailLink` has an opposite direction `RailLink`, where the entering link is the leaving link and vice versa. + - The algorithm is only triggered on the **leaving** `RailLink`s. + - Stations could also be modelled as segments (entering, intermediate, leaving links). +- I (mu) am not sure if we still need the combination with the local deadlock case, but I suspect that this is necessary + if the capacity of a segment is greater than 1. We then have to determine which train is on which track to see if the + train can overtake or has to brake. + +### Station to station path reservation + +In the case where the trains only reserve the path to the next station, a deadlock is again possible when entering the +station. It is not guaranteed that the train can enter the station, and a train in the station (travelling in the +opposite direction), could be waiting for the incoming train to free its track. + +==To Discuss:== do we want to see this deadlock or should the simulation should never block in general? + +If the simulation never blocks, a lack of capacity in a station should be visible in the delays of the trains. It is +probably not easy to find the initial cause of the delay, as a delay potentially propagates and affects other trains. +Additional events may be needed to signal and document a train's decision to wait at a particular point and thus deviate +from the timetable. This allows to detect the origins and the course of the delays in an event analysis after the +simulation. + +## References + +The deadlock problem has already been solved in the Flatland project and could be used as a starting point or serve as +inspiration: + +- [flatland_solver_policy/policy/heuristic_policy/shortest_path_deadlock_avoidance_policy/deadlock_avoidance_policy.py](https://github.com/aiAdrian/flatland_solver_policy/blob/main/policy/heuristic_policy/shortest_path_deadlock_avoidance_policy/deadlock_avoidance_policy.py) +- [flatland/flatland/contrib/utils/deadlock_checker.py](https://gitlab.aicrowd.com/flatland/flatland/-/blob/master/flatland/contrib/utils/deadlock_checker.py) diff --git a/contribs/railsim/docs/events-specification.md b/contribs/railsim/docs/events-specification.md new file mode 100644 index 00000000000..e960461b9fe --- /dev/null +++ b/contribs/railsim/docs/events-specification.md @@ -0,0 +1,36 @@ +# Events-Specification + +railsim introduces additional, custom events. This document describes these event types. + +All the additional events use the prefix `railsim`. + +## Event Types + +### RailsimLinkStateChangeEvent + +The generic `RailsimLinkStateChangeEvent` includes information about the new state of a link (or even the track of a +multi-track link). + +Attributes: + +- `state`: `free` or `blocked` +- `vehicleId`: if `state=reserved|blocked`, the id of the vehicle blocking or reserving this link +- `track`: a number (0-based or 1-based?) if the link has multiple tracks + +### RailsimTrainLeavesLinkEvent + +One could argue that setting the link state to `free` would imply the same. I (mr) would still +say it makes sense to have it separate, because depending on the implementation, a link could +remain blocked for a longer time even if the train has already passed (e.g. minimum headway time). + +There is **no** `RailsimTrainEntersLinkEvent`. The regular `LinkEnterEvent` is used to provide the highest +compatibility with existing analysis and visualization tools. + +### RailsimTrainStateEvent + +This event is emitted every time there is a position update for a train and contains detailed information about the +trains position on a single link. + +### RailsimDetourEvent + +This event is emitted when a train is re-routed and contains parts of the routes that have changed. diff --git a/contribs/railsim/docs/network-specification.md b/contribs/railsim/docs/network-specification.md new file mode 100644 index 00000000000..956de2e3da3 --- /dev/null +++ b/contribs/railsim/docs/network-specification.md @@ -0,0 +1,226 @@ +# Network-Specification + +## Introduction + +As trains interact differently with links than regular cars, the typical attributes like `capacity` and `lanes` are not +suitable for describing rail infrastructure. + +railsim uses custom link attributes to describe the essential parts of the rail infrastructure. +This document specifies these custom attributes. + +For the maximum allowed speed on a link, the normal `freespeed` attribute is used. + +## Specification + +We use the prefix `railsim` where it is appropriate. + +### Link Attributes + +#### railsimTrainCapacity + +The number of trains that can be on this link at the same time. If the attribute is not provided, a default of 1 is +used. +railsim supports the microscopic modelling of tracks, where each link represents a single track (`railsimCapacity` = 1 +or default), and a mesoscopic level of modelling, where a link may represent multiple tracks (`railsimCapacity` > 1). + +Example: + +```xml + + + + 3 + + + +``` + +#### railsimResourceId + +The id of a resource, i.e. a segment of links that share a constant capacity. + +This can be used to denote certain blocks of links that can only be reserved as a whole. +One use-case is the modelling of bidirectional links, where one train blocks both directions. +MATSim uses uni-directional links. While on a road, cars might usually be able to pass each other +even on small roads by going very slow and near the edge, but trains cannot. + +This can also be used to model intersections as one resource, which will restrict crossing trains. + +The train capacity will be derived as the minimum of all included links of this resource. +Links that have no resource id will be handled as individual resource. + +#### railsimMinimumTime + +The minimum time ("minimum train headway time") for the switch at the end of the link (toNode). +If no link attribute is provided, a default of 0 is used. + +#### railsimEntry + +Entry link of a station, triggers re-routing and serves as origin. + +#### railsimExit + +Exit link of a station, denotes a destination in re-routing. + +Example: + +```xml + + + + true + + + +``` + +#### railsimSpeed_ + vehicle type + +TODO + +The vehicle-specific freespeed on this link. +Please note that the actual vehicle-type must be used as part of the attribute name, see example. + +Example: + +```xml + + + + 44.444 + 50.0 + + + +``` + +### Node Attributes + +Currently none. + +## Microscopic + +### Moving Block + +Model tracks consisting of short links. Each link, except for the opposite link, has a unique resource ID and a capacity +of 1. + +### Fixed block + +Model blocks of links with a capacity of 1 each and identical resource IDs. + +### Station + +Each platform link has a distinct resource ID, except for the opposite link, and a capacity of 1. Each platform link has +a transit stop facility, which belongs to the same stop area id. Ingoing and outgoing links of the station have entry +and exit attributes. + +### Examples + +#### Single track with contraflow + +Default value of `railsimCapacity` sets an own railsimResourceId for each track. + +```xml + + + + + AB + + + + + AB + + + +``` + +#### Two tracks, each with a single direction + +Default value of `railsimResourceId` sets an own railsimResourceId for each track. + +```xml + + + + + + + +``` + +#### Three tracks, with contraflow in the middle track + +```xml + + + + + + + AB + + + + + AB + + + + + +``` + +#### Two tracks that intersect each other + +If two tracks cross each other, e.g. like in the form of the letter `X` or a plus `+`, a train driving in one direction +effectively also blocks the intersecting tracks, even if they only share a common node, but not a common link. + +There should be no additional link- or node-attributes necessary. The simulation should block the node in the case of +`railsimCapacity = 1`, but not if the capacity is larger than 1. If the node is blocked, no other trains must be able +to cross this node/intersection. + +## Mesoscopic + +### Route + +Model tracks consisting of links with capacities exceeding 1. Opposite links share the same resource ID. There is no +differentiation between moving block and fixed block. A mesoscopic link can only initiate or terminate at points where a +physical track change is feasible. + +### Station + +The station consists of one or several links, including the opposite links, with the same resource ID. The capacity is +larger than 1 and corresponds to the number of tracks. + +### Examples + +#### Section with a capacity of 2 + +```xml + + + + + 2 + + + + + 2 + + + +``` diff --git a/contribs/railsim/docs/train-specification.md b/contribs/railsim/docs/train-specification.md new file mode 100644 index 00000000000..41fcec36403 --- /dev/null +++ b/contribs/railsim/docs/train-specification.md @@ -0,0 +1,41 @@ +# Train-Specification + +## Introduction + +railsim supports the simulation of specific, train-related behavior, e.g. acceleration based on the total weight of a +train. In order to simulate this detailed behavior, additional attributes must be specified per train, i.e. per vehicle +type in MATSim. + +This document specifies these custom attributes. + +## Specification + +### TransitVehicle Attributes + +Default vehicle type attributes for length, maximum velocity and capacity. +Set network mode to rail. + +#### railsimAcceleration + +The vehicle-specific acceleration. Unit: meters per square-seconds \[m/s²] + +#### railsimDeceleration + +The vehicle-specific deceleration. Unit: meters per square-seconds \[m/s²] + +## Examples + +```xml + + + + 0.4 + 0.5 + + + + + + + +``` diff --git a/contribs/railsim/pom.xml b/contribs/railsim/pom.xml new file mode 100644 index 00000000000..345b4fd0cea --- /dev/null +++ b/contribs/railsim/pom.xml @@ -0,0 +1,28 @@ + + + + contrib + org.matsim + 16.0-SNAPSHOT + + 4.0.0 + ch.sbb.matsim.contrib + railsim + + + + org.assertj + assertj-core + test + + + + org.mockito + mockito-core + test + + + + diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimModule.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimModule.java new file mode 100644 index 00000000000..9d25bc4a9a9 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimModule.java @@ -0,0 +1,46 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim; + +import ch.sbb.matsim.contrib.railsim.analysis.linkstates.RailsimLinkStateControlerListener; +import ch.sbb.matsim.contrib.railsim.analysis.trainstates.RailsimTrainStateControlerListener; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimQSimModule; +import com.google.inject.Singleton; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; + +/** + * Railsim module installing all needed component. + */ +public class RailsimModule extends AbstractModule { + + @Override + public void install() { + installQSimModule(new RailsimQSimModule()); + ConfigUtils.addOrGetModule(getConfig(), RailsimConfigGroup.class); + + bind(RailsimLinkStateControlerListener.class).in(Singleton.class); + addControlerListenerBinding().to(RailsimLinkStateControlerListener.class); + + bind(RailsimTrainStateControlerListener.class).in(Singleton.class); + addControlerListenerBinding().to(RailsimTrainStateControlerListener.class); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java new file mode 100644 index 00000000000..6eff2e945f2 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RailsimUtils.java @@ -0,0 +1,158 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim; + +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import org.matsim.api.core.v01.network.Link; +import org.matsim.vehicles.VehicleType; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * Utility class for working with Railsim and its specific attributes. + * + * @author ikaddoura + * @author rakow + * @author munterfi + */ +public final class RailsimUtils { + + public static final String LINK_ATTRIBUTE_RESOURCE_ID = "railsimResourceId"; + public static final String LINK_ATTRIBUTE_CAPACITY = "railsimTrainCapacity"; + public static final String LINK_ATTRIBUTE_MINIMUM_TIME = "railsimMinimumTime"; + public static final String VEHICLE_ATTRIBUTE_ACCELERATION = "railsimAcceleration"; + public static final String VEHICLE_ATTRIBUTE_DECELERATION = "railsimDeceleration"; + + private RailsimUtils() { + } + + /** + * Round number to precision commonly used in Railsim. + */ + public static double round(double d) { + return BigDecimal.valueOf(d).setScale(3, RoundingMode.HALF_EVEN).doubleValue(); + } + + /** + * Return the train capacity for this link, if no link attribute is provided the default is 1. + */ + public static int getTrainCapacity(Link link) { + Object attr = link.getAttributes().getAttribute(LINK_ATTRIBUTE_CAPACITY); + return attr != null ? (int) attr : 1; + } + + /** + * Sets the train capacity for the link. + */ + public static void setTrainCapacity(Link link, int capacity) { + link.getAttributes().putAttribute(LINK_ATTRIBUTE_CAPACITY, capacity); + } + + /** + * Return the minimum time for the switch at the end of the link (toNode); if no link attribute is provided the default is 0. + */ + public static double getMinimumHeadwayTime(Link link) { + Object attr = link.getAttributes().getAttribute(LINK_ATTRIBUTE_MINIMUM_TIME); + return attr != null ? (double) attr : 0; + } + + /** + * Sets the minimum headway time after a link can be released. + */ + public static void setMinimumHeadwayTime(Link link, double time) { + link.getAttributes().putAttribute(LINK_ATTRIBUTE_MINIMUM_TIME, time); + } + + /** + * Resource id or null if there is none. + */ + public static String getResourceId(Link link) { + return (String) link.getAttributes().getAttribute(LINK_ATTRIBUTE_RESOURCE_ID); + } + + /** + * Sets the resource id for the link. + */ + public static void setResourceId(Link link, String resourceId) { + link.getAttributes().putAttribute(LINK_ATTRIBUTE_RESOURCE_ID, resourceId); + } + + /** + * Whether this link is an entry link applicable for re routing. + */ + public static boolean isEntryLink(Link link) { + return Boolean.TRUE.equals(link.getAttributes().getAttribute("railsimEntry")); + } + + /** + * Sets whether this link is an entry link applicable for re-routing. + */ + public static void setEntryLink(Link link, boolean isEntry) { + link.getAttributes().putAttribute("railsimEntry", isEntry); + } + + /** + * Exit link used for re routing. + */ + public static boolean isExitLink(Link link) { + return Boolean.TRUE.equals(link.getAttributes().getAttribute("railsimExit")); + } + + /** + * Sets whether this link is an exit link used for re-routing. + */ + public static void setExitLink(Link link, boolean isExit) { + link.getAttributes().putAttribute("railsimExit", isExit); + } + + /** + * Return the default deceleration time or the vehicle-specific value. + */ + public static double getTrainDeceleration(VehicleType vehicle, RailsimConfigGroup railsimConfigGroup) { + double deceleration = railsimConfigGroup.decelerationDefault; + Object attr = vehicle.getAttributes().getAttribute(VEHICLE_ATTRIBUTE_DECELERATION); + return attr != null ? (double) attr : deceleration; + } + + /** + * Sets the deceleration time for the vehicle type. + */ + public static void setTrainDeceleration(VehicleType vehicle, double deceleration) { + vehicle.getAttributes().putAttribute(VEHICLE_ATTRIBUTE_DECELERATION, deceleration); + } + + /** + * Return the default acceleration time or the vehicle-specific value. + */ + public static double getTrainAcceleration(VehicleType vehicle, RailsimConfigGroup railsimConfigGroup) { + double acceleration = railsimConfigGroup.accelerationDefault; + Object attr = vehicle.getAttributes().getAttribute(VEHICLE_ATTRIBUTE_ACCELERATION); + return attr != null ? (double) attr : acceleration; + } + + /** + * Sets the acceleration time for the vehicle type. + */ + public static void setTrainAcceleration(VehicleType vehicle, double acceleration) { + vehicle.getAttributes().putAttribute(VEHICLE_ATTRIBUTE_ACCELERATION, acceleration); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RunRailsimExample.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RunRailsimExample.java new file mode 100644 index 00000000000..2724b7889b6 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/RunRailsimExample.java @@ -0,0 +1,60 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimQSimModule; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.scenario.ScenarioUtils; + +/** + * Example script that shows how to use railsim included in this contrib. + */ +public final class RunRailsimExample { + + private RunRailsimExample() { + } + + public static void main(String[] args) { + + if (args.length == 0) { + System.err.println("Path to config is required as first argument."); + System.exit(2); + } + + String configFilename = args[0]; + Config config = ConfigUtils.loadConfig(configFilename); + config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + + Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + controler.addOverridingModule(new RailsimModule()); + + // if you have other extensions that provide QSim components, call their configure-method here + controler.configureQSimComponents(components -> new RailsimQSimModule().configure(components)); + + controler.run(); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/PostProcessAnalysis.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/PostProcessAnalysis.java new file mode 100644 index 00000000000..aed580e018f --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/PostProcessAnalysis.java @@ -0,0 +1,81 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis; + +import ch.sbb.matsim.contrib.railsim.analysis.linkstates.RailLinkStateAnalysis; +import ch.sbb.matsim.contrib.railsim.analysis.trainstates.TrainStateAnalysis; +import ch.sbb.matsim.contrib.railsim.eventmappers.RailsimLinkStateChangeEventMapper; +import ch.sbb.matsim.contrib.railsim.eventmappers.RailsimTrainStateEventMapper; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.core.network.io.MatsimNetworkReader; +import org.matsim.core.scenario.ScenarioUtils; + +/** + * Class to generate rail sim csv files from event file. + */ +public final class PostProcessAnalysis { + + private PostProcessAnalysis() { + } + + public static void main(String[] args) { + String eventsFilename; + String networkFilename = null; + if (args.length > 0) { + eventsFilename = args[0]; + } else { + System.err.println("Please provide events filename."); + System.exit(2); + return; + } + if (args.length > 1) { + networkFilename = args[1]; + } + + Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + if (networkFilename != null) { + new MatsimNetworkReader(scenario.getNetwork()).readFile(networkFilename); + } + + RailLinkStateAnalysis linkStateAnalysis = new RailLinkStateAnalysis(); + TrainStateAnalysis trainStateAnalysis = new TrainStateAnalysis(); + + EventsManager events = EventsUtils.createEventsManager(); + events.addHandler(linkStateAnalysis); + events.addHandler(trainStateAnalysis); + events.initProcessing(); + + MatsimEventsReader reader = new MatsimEventsReader(events); + reader.addCustomEventMapper(RailsimLinkStateChangeEvent.EVENT_TYPE, new RailsimLinkStateChangeEventMapper()); + reader.addCustomEventMapper(RailsimTrainStateEvent.EVENT_TYPE, new RailsimTrainStateEventMapper()); + reader.readFile(eventsFilename); + + events.finishProcessing(); + + RailsimCsvWriter.writeLinkStatesCsv(linkStateAnalysis.getEvents(), "railsimLinkStates.csv"); + RailsimCsvWriter.writeTrainStatesCsv(trainStateAnalysis.getEvents(), scenario.getNetwork(), "railsimTrainStates.csv"); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java new file mode 100644 index 00000000000..61462ddebbd --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/RailsimCsvWriter.java @@ -0,0 +1,119 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.utils.io.IOUtils; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; + +/** + * Helper class to write railsim related csv files. + */ +public final class RailsimCsvWriter { + + private RailsimCsvWriter() { + } + + /** + * Write {@link RailsimLinkStateChangeEvent} to a csv file. + */ + public static void writeLinkStatesCsv(List events, String filename) throws UncheckedIOException { + String[] header = {"link", "time", "state", "vehicle", "track"}; + + try (CSVPrinter csv = new CSVPrinter(IOUtils.getBufferedWriter(filename), CSVFormat.DEFAULT.builder().setHeader(header).build())) { + for (RailsimLinkStateChangeEvent event : events) { + csv.print(event.getLinkId().toString()); + csv.print(event.getTime()); + csv.print(event.getState().toString()); + csv.print(event.getVehicleId() != null ? event.getVehicleId().toString() : ""); + csv.print(event.getTrack()); + csv.println(); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + } + + /** + * Write {@link RailsimTrainStateEvent} to a csv file. + */ + public static void writeTrainStatesCsv(List events, Network network, String filename) throws UncheckedIOException { + String[] header = {"vehicle", "time", "acceleration", "speed", "targetSpeed", "headLink", "headPosition", "headX", "headY", "tailLink", "tailPosition", "tailX", "tailY"}; + + try (CSVPrinter csv = new CSVPrinter(IOUtils.getBufferedWriter(filename), CSVFormat.DEFAULT.builder().setHeader(header).build())) { + for (RailsimTrainStateEvent event : events) { + csv.print(event.getVehicleId().toString()); + csv.print(event.getExactTime()); + csv.print(event.getAcceleration()); + csv.print(RailsimUtils.round(event.getSpeed())); + csv.print(RailsimUtils.round(event.getTargetSpeed())); + + csv.print(event.getHeadLink().toString()); + csv.print(RailsimUtils.round(event.getHeadPosition())); + if (network != null) { + Link link = network.getLinks().get(event.getHeadLink()); + if (link != null) { + double fraction = event.getHeadPosition() / link.getLength(); + Coord from = link.getFromNode().getCoord(); + Coord to = link.getToNode().getCoord(); + csv.print(from.getX() + (to.getX() - from.getX()) * fraction); + csv.print(from.getY() + (to.getY() - from.getY()) * fraction); + } + } else { + csv.print(""); + csv.print(""); + } + + csv.print(event.getTailLink().toString()); + csv.print(RailsimUtils.round(event.getTailPosition())); + if (network != null) { + Link link = network.getLinks().get(event.getTailLink()); + if (link != null) { + double fraction = event.getTailPosition() / link.getLength(); + Coord from = link.getFromNode().getCoord(); + Coord to = link.getToNode().getCoord(); + csv.print(from.getX() + (to.getX() - from.getX()) * fraction); + csv.print(from.getY() + (to.getY() - from.getY()) * fraction); + } + } else { + csv.print(""); + csv.print(""); + } + + csv.println(); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailLinkStateAnalysis.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailLinkStateAnalysis.java new file mode 100644 index 00000000000..5f204dc9387 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailLinkStateAnalysis.java @@ -0,0 +1,43 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis.linkstates; + +import ch.sbb.matsim.contrib.railsim.eventhandlers.RailsimLinkStateChangeEventHandler; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handler for {@link RailsimLinkStateChangeEvent}. + */ +public final class RailLinkStateAnalysis implements RailsimLinkStateChangeEventHandler { + + final List events = new ArrayList<>(1000); + + @Override + public void handleEvent(RailsimLinkStateChangeEvent event) { + this.events.add(event); + } + + public List getEvents() { + return events; + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailsimLinkStateControlerListener.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailsimLinkStateControlerListener.java new file mode 100644 index 00000000000..fe37076bfcd --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/linkstates/RailsimLinkStateControlerListener.java @@ -0,0 +1,61 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis.linkstates; + +import ch.sbb.matsim.contrib.railsim.analysis.RailsimCsvWriter; +import com.google.inject.Inject; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.events.IterationStartsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.controler.listener.IterationStartsListener; + +/** + * Controler to automatically write rail sim analysis files. + */ +public final class RailsimLinkStateControlerListener implements IterationEndsListener, IterationStartsListener { + + private RailLinkStateAnalysis analysis; + private OutputDirectoryHierarchy controlerIO; + private final EventsManager eventsManager; + private final Scenario scenario; + + @Inject + RailsimLinkStateControlerListener(Scenario scenario, EventsManager eventsManager, OutputDirectoryHierarchy controlerIO) { + this.eventsManager = eventsManager; + this.controlerIO = controlerIO; + this.scenario = scenario; + this.analysis = new RailLinkStateAnalysis(); + } + + @Override + public void notifyIterationStarts(IterationStartsEvent event) { + this.eventsManager.addHandler(this.analysis); + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + String railLinkStatesCsvFilename = this.controlerIO.getIterationFilename(event.getIteration(), "railsimLinkStates.csv", this.scenario.getConfig().controler().getCompressionType()); + RailsimCsvWriter.writeLinkStatesCsv(this.analysis.events, railLinkStatesCsvFilename); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/RailsimTrainStateControlerListener.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/RailsimTrainStateControlerListener.java new file mode 100644 index 00000000000..49934ca9495 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/RailsimTrainStateControlerListener.java @@ -0,0 +1,62 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis.trainstates; + +import ch.sbb.matsim.contrib.railsim.analysis.RailsimCsvWriter; +import com.google.inject.Inject; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.events.IterationStartsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.controler.listener.IterationStartsListener; + + +/** + * Controler to write {@link ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent} csv files. + */ +public final class RailsimTrainStateControlerListener implements IterationEndsListener, IterationStartsListener { + + private TrainStateAnalysis analysis; + private OutputDirectoryHierarchy controlerIO; + private final EventsManager eventsManager; + private final Scenario scenario; + + @Inject + RailsimTrainStateControlerListener(Scenario scenario, EventsManager eventsManager, OutputDirectoryHierarchy controlerIO) { + this.eventsManager = eventsManager; + this.controlerIO = controlerIO; + this.scenario = scenario; + this.analysis = new TrainStateAnalysis(); + } + + @Override + public void notifyIterationStarts(IterationStartsEvent event) { + this.eventsManager.addHandler(this.analysis); + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + String railLinkStatesCsvFilename = this.controlerIO.getIterationFilename(event.getIteration(), "railsimTrainStates.csv", this.scenario.getConfig().controler().getCompressionType()); + RailsimCsvWriter.writeTrainStatesCsv(this.analysis.events, this.scenario.getNetwork(), railLinkStatesCsvFilename); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/TrainStateAnalysis.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/TrainStateAnalysis.java new file mode 100644 index 00000000000..fcc7531ce43 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/analysis/trainstates/TrainStateAnalysis.java @@ -0,0 +1,43 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.analysis.trainstates; + +import ch.sbb.matsim.contrib.railsim.eventhandlers.RailsimTrainStateEventHandler; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Handler collecting all {@link RailsimTrainStateEvent}s. + */ +public final class TrainStateAnalysis implements RailsimTrainStateEventHandler { + + final List events = new ArrayList<>(1000); + + @Override + public void handleEvent(RailsimTrainStateEvent event) { + this.events.add(event); + } + + public List getEvents() { + return events; + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/config/RailsimConfigGroup.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/config/RailsimConfigGroup.java new file mode 100644 index 00000000000..9599b04c915 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/config/RailsimConfigGroup.java @@ -0,0 +1,73 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.config; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import org.matsim.core.config.Config; +import org.matsim.core.config.ReflectiveConfigGroup; + +import java.util.Set; + +/** + * Config of the Railsim contrib. + */ +public final class RailsimConfigGroup extends ReflectiveConfigGroup { + + public static final String GROUP_NAME = "railsim"; + + @Parameter + @Comment("Comma separated set of modes that are handled by the railsim qsim engine in the simulation. Defaults to 'rail'.") + public String networkModes = "rail"; + + @Parameter + @Comment("Global acceleration in meters per second^2 which is used if there is no value provided in the vehicle attributes (" + RailsimUtils.VEHICLE_ATTRIBUTE_ACCELERATION + ");" + " used to compute the train velocity per link.") + public double accelerationDefault = 0.5; + + @Parameter + @Comment("Global deceleration in meters per second^2 which is used if there is no value provided in the vehicle attributes (" + RailsimUtils.VEHICLE_ATTRIBUTE_DECELERATION + ");" + " used to compute the reserved train path and the train velocity per link.") + public double decelerationDefault = 0.5; + + @Parameter + @Comment("Time interval in seconds a train has to wait until trying again to request a track reservation if the track was blocked by another train.") + public double pollInterval = 10; + + @Parameter + @Comment("Maximum time interval in seconds which is used to update the train position update events.") + public double updateInterval = 10.; + + public RailsimConfigGroup() { + super(GROUP_NAME); + } + + public Set getNetworkModes() { + return Set.of(networkModes.split(",")); + } + + @Override + protected void checkConsistency(Config config) { + super.checkConsistency(config); + for (String mode : getNetworkModes()) { + if (config.qsim().getMainModes().contains(mode)) { + throw new IllegalArgumentException(String.format("Railsim mode '%s' must not be a network mode in qsim.", mode)); + } + } + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimLinkStateChangeEventHandler.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimLinkStateChangeEventHandler.java new file mode 100644 index 00000000000..7fd4ef9cfa4 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimLinkStateChangeEventHandler.java @@ -0,0 +1,34 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.eventhandlers; + +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import org.matsim.core.events.handler.EventHandler; + +/** + * Handler for {@link RailsimLinkStateChangeEvent}. + */ +public interface RailsimLinkStateChangeEventHandler extends EventHandler { + + /** + * Process given event. + */ + void handleEvent(RailsimLinkStateChangeEvent event); +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimTrainStateEventHandler.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimTrainStateEventHandler.java new file mode 100644 index 00000000000..5f4ae3179e6 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventhandlers/RailsimTrainStateEventHandler.java @@ -0,0 +1,34 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.eventhandlers; + +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.matsim.core.events.handler.EventHandler; + +/** + * Event handler for {@link RailsimTrainStateEvent}. + */ +public interface RailsimTrainStateEventHandler extends EventHandler { + + /** + * Handle given event. + */ + void handleEvent(RailsimTrainStateEvent event); +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java new file mode 100644 index 00000000000..97488e97e1d --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimLinkStateChangeEventMapper.java @@ -0,0 +1,52 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.eventmappers; + +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.TrackState; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.vehicles.Vehicle; + +/** + * Converter for railsim events. + */ +public class RailsimLinkStateChangeEventMapper implements MatsimEventsReader.CustomEventMapper { + @Override + public RailsimLinkStateChangeEvent apply(GenericEvent event) { + var attributes = event.getAttributes(); + return new RailsimLinkStateChangeEvent( + event.getTime(), + asId(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_LINK), Link.class), + asId(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_VEHICLE), Vehicle.class), + TrackState.valueOf(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_STATE)), + Integer.parseInt(attributes.get(RailsimLinkStateChangeEvent.ATTRIBUTE_TRACK)) + ); + } + + private static Id asId(String value, Class idClass) { + if (value == null) { + return null; + } + return Id.create(value, idClass); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimTrainStateEventMapper.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimTrainStateEventMapper.java new file mode 100644 index 00000000000..1c118f15856 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/eventmappers/RailsimTrainStateEventMapper.java @@ -0,0 +1,56 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.eventmappers; + +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.vehicles.Vehicle; + +/** + * Convert for {@link RailsimTrainStateEvent}. + */ +public class RailsimTrainStateEventMapper implements MatsimEventsReader.CustomEventMapper { + @Override + public RailsimTrainStateEvent apply(GenericEvent event) { + var attributes = event.getAttributes(); + return new RailsimTrainStateEvent( + event.getTime(), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_EXACT_TIME)), + asId(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_VEHICLE), Vehicle.class), + asId(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_HEAD_LINK), Link.class), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_HEAD_POSITION)), + asId(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_TAIL_LINK), Link.class), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_TAIL_POSITION)), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_SPEED)), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_ACCELERATION)), + Double.parseDouble(attributes.get(RailsimTrainStateEvent.ATTRIBUTE_TARGET_SPEED)) + ); + } + + private static Id asId(String value, Class idClass) { + if (value == null) { + return null; + } + return Id.create(value, idClass); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java new file mode 100644 index 00000000000..f5a84b0e8ff --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimDetourEvent.java @@ -0,0 +1,80 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.events; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.HasVehicleId; +import org.matsim.api.core.v01.network.Link; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; +import org.matsim.vehicles.Vehicle; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Event that occurs when a train was re-routed. + */ +public class RailsimDetourEvent extends Event implements HasVehicleId { + + public static final String EVENT_TYPE = "railsimDetourEvent"; + + private final Id vehicleId; + private final Id entry; + private final Id exit; + private final List> detour; + private final Id newStop; + + public RailsimDetourEvent(double time, Id vehicleId, Id entry, Id exit, List> detour, + Id newStop) { + super(time); + this.vehicleId = vehicleId; + this.entry = entry; + this.exit = exit; + this.detour = detour; + this.newStop = newStop; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public Id getVehicleId() { + return vehicleId; + } + + + @Override + public Map getAttributes() { + Map attributes = super.getAttributes(); + + attributes.put(HasVehicleId.ATTRIBUTE_VEHICLE, vehicleId.toString()); + attributes.put("entry", entry.toString()); + attributes.put("exit", exit.toString()); + attributes.put("detour", detour.stream().map(Object::toString).collect(Collectors.joining(","))); + attributes.put("newStop", Objects.toString(newStop)); + + return attributes; + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java new file mode 100644 index 00000000000..55a73fdf6d7 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimLinkStateChangeEvent.java @@ -0,0 +1,87 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.events; + +import ch.sbb.matsim.contrib.railsim.qsimengine.TrackState; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.HasLinkId; +import org.matsim.api.core.v01.events.HasVehicleId; +import org.matsim.api.core.v01.network.Link; +import org.matsim.vehicles.Vehicle; + +import java.util.Map; + +/** + * Event thrown when the {@link ch.sbb.matsim.contrib.railsim.qsimengine.TrackState} of a {@link Link} changes. + */ +public final class RailsimLinkStateChangeEvent extends Event implements HasLinkId, HasVehicleId { + + public static final String EVENT_TYPE = "railsimLinkStateChangeEvent"; + + public static final String ATTRIBUTE_STATE = "state"; + public static final String ATTRIBUTE_TRACK = "track"; + + private final Id linkId; + private final Id vehicleId; + private final TrackState state; + private final int track; + + public RailsimLinkStateChangeEvent(double time, Id linkId, Id vehicleId, TrackState state, int track) { + super(time); + this.linkId = linkId; + this.vehicleId = vehicleId; + this.state = state; + this.track = track; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public Id getLinkId() { + return linkId; + } + + @Override + public Id getVehicleId() { + return this.vehicleId; + } + + public TrackState getState() { + return state; + } + + public int getTrack() { + return track; + } + + @Override + public Map getAttributes() { + Map attr = super.getAttributes(); + attr.put(ATTRIBUTE_LINK, this.linkId.toString()); + attr.put(ATTRIBUTE_VEHICLE, this.vehicleId.toString()); + attr.put(ATTRIBUTE_STATE, this.state.toString()); + attr.put(ATTRIBUTE_TRACK, String.valueOf(track)); + return attr; + } +} diff --git a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainLeavesLinkEvent.java similarity index 60% rename from contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEvent.java rename to contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainLeavesLinkEvent.java index aee57a21b34..eb2d4038f81 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEvent.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainLeavesLinkEvent.java @@ -1,9 +1,9 @@ /* *********************************************************************** * - * project: org.matsim.* * + * project: org.matsim.* * * * *********************************************************************** * * * - * copyright : (C) 2008 by the members listed in the COPYING, * + * copyright : (C) 2023 by the members listed in the COPYING, * * LICENSE and WARRANTY file. * * email : info at matsim dot org * * * @@ -16,39 +16,54 @@ * See also COPYING, LICENSE and WARRANTY file * * * * *********************************************************************** */ -package org.matsim.contrib.bicycle; + +package ch.sbb.matsim.contrib.railsim.events; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.HasLinkId; +import org.matsim.api.core.v01.events.HasVehicleId; import org.matsim.api.core.v01.network.Link; import org.matsim.vehicles.Vehicle; +import java.util.Map; + /** - * @author dziemke - * @deprecated -- it might be possible to use this, but as of now {@link MotorizedInteractionEngine} is not implemented in a meaningful way. kai, dec'22 + * Event thrown when the very end of a train left a link. */ -public final class MotorizedInteractionEvent extends Event { - // plausible to have this public +public class RailsimTrainLeavesLinkEvent extends Event implements HasLinkId, HasVehicleId { + + public static final String EVENT_TYPE = "railsimTrainLeavesLinkEvent"; - private Id linkId; - private Id vehId; + private final Id linkId; + private final Id vehicleId; - public MotorizedInteractionEvent(double time, Id linkId, Id vehId) { + public RailsimTrainLeavesLinkEvent(double time, Id vehicleId, Id linkId) { super(time); + this.vehicleId = vehicleId; this.linkId = linkId; - this.vehId = vehId; } + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override public Id getLinkId() { return linkId; } - public Id getVehId() { - return vehId; + @Override + public Id getVehicleId() { + return vehicleId; } @Override - public String getEventType() { - return "motorizedInteraction"; + public Map getAttributes() { + Map attr = super.getAttributes(); + attr.put(ATTRIBUTE_VEHICLE, this.vehicleId.toString()); + attr.put(ATTRIBUTE_LINK, this.linkId.toString()); + return attr; } } diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainStateEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainStateEvent.java new file mode 100644 index 00000000000..7fa89816c1b --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/events/RailsimTrainStateEvent.java @@ -0,0 +1,132 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.events; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.HasVehicleId; +import org.matsim.api.core.v01.network.Link; +import org.matsim.vehicles.Vehicle; + +import java.util.Map; + +/** + * Event for currents train position. + */ +public final class RailsimTrainStateEvent extends Event implements HasVehicleId { + + public static final String EVENT_TYPE = "railsimTrainStateEvent"; + + public static final String ATTRIBUTE_EXACT_TIME = "exactTime"; + public static final String ATTRIBUTE_HEAD_LINK = "headLink"; + public static final String ATTRIBUTE_HEAD_POSITION = "headPosition"; + public static final String ATTRIBUTE_TAIL_LINK = "tailLink"; + public static final String ATTRIBUTE_TAIL_POSITION = "tailPosition"; + public static final String ATTRIBUTE_SPEED = "speed"; + public static final String ATTRIBUTE_ACCELERATION = "acceleration"; + public static final String ATTRIBUTE_TARGET_SPEED = "targetSpeed"; + + /** + * Exact time with resolution of 0.001s. + */ + private final double exactTime; + private final Id vehicleId; + private final Id headLink; + private final double headPosition; + private final Id tailLink; + private final double tailPosition; + private final double speed; + private final double acceleration; + private final double targetSpeed; + + public RailsimTrainStateEvent(double time, double exactTime, Id vehicleId, + Id headLink, double headPosition, + Id tailLink, double tailPosition, + double speed, double acceleration, double targetSpeed) { + super(time); + this.exactTime = RailsimUtils.round(exactTime); + this.vehicleId = vehicleId; + this.headLink = headLink; + this.headPosition = headPosition; + this.tailLink = tailLink; + this.tailPosition = tailPosition; + this.speed = speed; + this.acceleration = acceleration; + this.targetSpeed = targetSpeed; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public Id getVehicleId() { + return vehicleId; + } + + public double getExactTime() { + return exactTime; + } + + public double getHeadPosition() { + return headPosition; + } + + public double getTailPosition() { + return tailPosition; + } + + public double getSpeed() { + return speed; + } + + public double getTargetSpeed() { + return this.targetSpeed; + } + + public double getAcceleration() { + return this.acceleration; + } + + public Id getHeadLink() { + return this.headLink; + } + + public Id getTailLink() { + return this.tailLink; + } + + @Override + public Map getAttributes() { + Map attr = super.getAttributes(); + attr.put(ATTRIBUTE_EXACT_TIME, String.valueOf(exactTime)); + attr.put(ATTRIBUTE_VEHICLE, this.vehicleId.toString()); + attr.put(ATTRIBUTE_HEAD_LINK, String.valueOf(headLink)); + attr.put(ATTRIBUTE_HEAD_POSITION, Double.toString(headPosition)); + attr.put(ATTRIBUTE_TAIL_LINK, String.valueOf(tailLink)); + attr.put(ATTRIBUTE_TAIL_POSITION, Double.toString(tailPosition)); + attr.put(ATTRIBUTE_SPEED, Double.toString(speed)); + attr.put(ATTRIBUTE_ACCELERATION, Double.toString(acceleration)); + attr.put(ATTRIBUTE_TARGET_SPEED, Double.toString(targetSpeed)); + return attr; + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java new file mode 100644 index 00000000000..3e27651c038 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/FuzzyUtils.java @@ -0,0 +1,67 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +/** + * Util class for fuzzy comparisons. + */ +final class FuzzyUtils { + + private static final double EPSILON = 1E-5; + + private FuzzyUtils() { + } + + /** + * Returns true if two doubles are approximately equal. + */ + public static boolean equals(double a, double b) { + return a == b || Math.abs(a - b) < EPSILON; + } + + /** + * Returns true if the first double is approximately greater than the second. + */ + public static boolean greaterEqualThan(double a, double b) { + return equals(a, b) || a - b > EPSILON; + } + + /** + * Returns true if the first double is certainly greater than the second. + */ + public static boolean greaterThan(double a, double b) { + return a - b > EPSILON; + } + + /** + * Returns true if the first double is approximately less than the second. + */ + public static boolean lessEqualThan(double a, double b) { + return equals(a, b) || b - a > EPSILON; + } + + /** + * Returns true if the first double is approximately less than the second. + */ + public static boolean lessThan(double a, double b) { + return b - a > EPSILON; + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java new file mode 100644 index 00000000000..d59ae3801a2 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailLink.java @@ -0,0 +1,185 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.HasLinkId; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Objects; + +/** + * Rail links which can has multiple tracks and corresponds to exactly one link. + */ +public final class RailLink implements HasLinkId { + + private final Id id; + + /** + * States per track. + */ + private final TrackState[] state; + + private final boolean isEntryLink; + private final boolean isExitLink; + + /** + * Drivers of each blocked track. + */ + private final MobsimDriverAgent[] blocked; + + final double length; + final double freeSpeed; + final double minimumHeadwayTime; + + /** + * ID of the resource this link belongs to. + */ + @Nullable + final Id resource; + + public RailLink(Link link) { + id = link.getId(); + state = new TrackState[RailsimUtils.getTrainCapacity(link)]; + Arrays.fill(state, TrackState.FREE); + blocked = new MobsimDriverAgent[state.length]; + length = link.getLength(); + freeSpeed = link.getFreespeed(); + minimumHeadwayTime = RailsimUtils.getMinimumHeadwayTime(link); + String resourceId = RailsimUtils.getResourceId(link); + resource = resourceId != null ? Id.create(resourceId, RailResource.class) : null; + isEntryLink = RailsimUtils.isEntryLink(link); + isExitLink = RailsimUtils.isExitLink(link); + } + + @Override + public Id getLinkId() { + return id; + } + + @Nullable + public Id getResourceId() { + return resource; + } + + /** + * Number of tracks on this link. + */ + public int getNumberOfTracks() { + return state.length; + } + + /** + * Returns the allowed freespeed, depending on the context, which is given via driver. + */ + public double getAllowedFreespeed(MobsimDriverAgent driver) { + return Math.min(freeSpeed, driver.getVehicle().getVehicle().getType().getMaximumVelocity()); + } + + /** + * Check if driver has already reserved this link. + */ + public boolean isBlockedBy(MobsimDriverAgent driver) { + for (MobsimDriverAgent reservation : blocked) { + if (reservation == driver) + return true; + } + return false; + } + + /** + * Whether at least one track is free. + */ + boolean hasFreeTrack() { + for (TrackState trackState : state) { + if (trackState == TrackState.FREE) + return true; + } + return false; + } + + /** + * Block a track that was previously reserved. + */ + int blockTrack(MobsimDriverAgent driver) { + for (int i = 0; i < state.length; i++) { + if (state[i] == TrackState.FREE) { + blocked[i] = driver; + state[i] = TrackState.BLOCKED; + return i; + } + } + throw new IllegalStateException("No track was free."); + } + + /** + * Release a non-free track to be free again. + */ + int releaseTrack(MobsimDriverAgent driver) { + for (int i = 0; i < state.length; i++) { + if (blocked[i] == driver) { + state[i] = TrackState.FREE; + blocked[i] = null; + return i; + } + } + throw new AssertionError("Driver " + driver + " has not reserved the track."); + } + + + /** + * Entry link of a station relevant for re-routing. + */ + public boolean isEntryLink() { + return isEntryLink; + } + + /** + * Exit link of a station for re-routing. + */ + public boolean isExitLink() { + return isExitLink; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RailLink link = (RailLink) o; + return Objects.equals(id, link.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + return "RailLink{" + + "id=" + id + + ", resource=" + resource + + '}'; + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java new file mode 100644 index 00000000000..ded83867f7d --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResource.java @@ -0,0 +1,62 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * A resource representing multiple {@link RailLink}. + */ +public class RailResource { + + /** + * Links belonging to this resource. + */ + final List links; + + /** + * Agents holding this resource exclusively. + */ + final Set reservations; + + /** + * Maximum number of reservations. + */ + int capacity; + + public RailResource(List links) { + this.links = links; + this.reservations = new HashSet<>(); + this.capacity = links.stream().mapToInt(RailLink::getNumberOfTracks).min().orElseThrow(); + } + + /** + * Whether an agent is able to block this resource. + */ + boolean hasCapacity() { + return reservations.size() < capacity; + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java new file mode 100644 index 00000000000..6f00f25b9d6 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailResourceManager.java @@ -0,0 +1,200 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import jakarta.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.core.mobsim.qsim.QSim; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Class responsible for managing and blocking resources and segments of links. + */ +public final class RailResourceManager { + + private final EventsManager eventsManager; + + /** + * Rail links. + */ + private final Map, RailLink> links; + + private final Map, RailResource> resources; + + @Inject + public RailResourceManager(QSim qsim) { + this(qsim.getEventsManager(), ConfigUtils.addOrGetModule(qsim.getScenario().getConfig(), RailsimConfigGroup.class), qsim.getScenario().getNetwork()); + } + + /** + * Construct resources from network. + */ + public RailResourceManager(EventsManager eventsManager, RailsimConfigGroup config, Network network) { + this.eventsManager = eventsManager; + this.links = new IdMap<>(Link.class, network.getLinks().size()); + + Set modes = config.getNetworkModes(); + for (Map.Entry, ? extends Link> e : network.getLinks().entrySet()) { + if (e.getValue().getAllowedModes().stream().anyMatch(modes::contains)) + this.links.put(e.getKey(), new RailLink(e.getValue())); + } + + Map, List> collect = links.values().stream() + .filter(l -> l.resource != null) + .collect(Collectors.groupingBy(l -> l.resource, Collectors.toList()) + ); + + resources = new IdMap<>(RailResource.class, collect.size()); + for (Map.Entry, List> e : collect.entrySet()) { + resources.put(e.getKey(), new RailResource(e.getValue())); + } + } + + /** + * Get single link that belongs to an id. + */ + public RailLink getLink(Id id) { + return links.get(id); + } + + /** + * Return the resource for a given id. + */ + public RailResource getResource(Id id) { + if (id == null) return null; + return resources.get(id); + } + + /** + * Try to block a resource for a specific driver. + * + * @return true if the resource is now blocked or was blocked for this driver already. + */ + private boolean tryBlockResource(RailResource resource, MobsimDriverAgent driver) { + + if (resource.reservations.contains(driver)) + return true; + + if (resource.hasCapacity()) { + resource.reservations.add(driver); + return true; + } + + return false; + } + + /** + * Try to release a resource, but only if none of the links are blocked anymore by this driver. + * + * @return whether driver is still blocking this resource. + */ + private boolean tryReleaseResource(RailResource resource, MobsimDriverAgent driver) { + + if (resource.links.stream().noneMatch(l -> l.isBlockedBy(driver))) { + resource.reservations.remove(driver); + return true; + } + + return false; + } + + /** + * Try to block a track and the underlying resource and return whether it was successful. + */ + public boolean tryBlockTrack(double time, MobsimDriverAgent driver, RailLink link) { + + if (link.isBlockedBy(driver)) + return true; + + Id resourceId = link.getResourceId(); + if (resourceId != null) { + + RailResource resource = getResource(resourceId); + + // resource is required + if (!tryBlockResource(resource, driver)) { + return false; + } + } + + if (link.hasFreeTrack()) { + int track = link.blockTrack(driver); + eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), + driver.getVehicle().getId(), TrackState.BLOCKED, track)); + + return true; + } + + return false; + } + + /** + * Checks whether a link or underlying resource has remaining capacity. + */ + public boolean hasCapacity(Id link) { + + RailLink l = getLink(link); + + if (!l.hasFreeTrack()) + return false; + + RailResource res = getResource(l.getResourceId()); + if (res != null) { + return res.hasCapacity(); + } + + return true; + } + + /** + * Whether a driver already reserved a link or would be able to reserve it. + */ + public boolean isBlockedBy(RailLink link, MobsimDriverAgent driver) { + // If a link is blocked, the resource must be blocked as well + return link.isBlockedBy(driver); + } + + /** + * Release a non-free track to be free again. + */ + public void releaseTrack(double time, MobsimDriverAgent driver, RailLink link) { + int track = link.releaseTrack(driver); + eventsManager.processEvent(new RailsimLinkStateChangeEvent(Math.ceil(time), link.getLinkId(), driver.getVehicle().getId(), + TrackState.FREE, track)); + + // Release held resources + if (link.getResourceId() != null) { + tryReleaseResource(getResource(link.getResourceId()), driver); + } + + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java new file mode 100644 index 00000000000..62437d0bfa7 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalc.java @@ -0,0 +1,284 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class holding static calculation methods related to state (updates). + */ +final class RailsimCalc { + + private RailsimCalc() { + } + + /** + * Calculate traveled distance given initial speed and constant acceleration. + */ + static double calcTraveledDist(double speed, double elapsedTime, double acceleration) { + return speed * elapsedTime + (elapsedTime * elapsedTime * acceleration / 2); + } + + /** + * Inverse of {@link #calcTraveledDist(double, double, double)}, solves for distance, returns needed time. + */ + static double solveTraveledDist(double speed, double dist, double acceleration) { + if (acceleration == 0) + return dist / speed; + + return (Math.sqrt(2 * acceleration * dist + speed * speed) - speed) / acceleration; + } + + /** + * Calculate time needed to advance distance {@code dist}. Depending on acceleration and max speed. + */ + static double calcRequiredTime(TrainState state, double dist) { + + if (FuzzyUtils.equals(dist, 0)) + return 0; + + if (state.acceleration == 0) + return state.speed == 0 ? Double.POSITIVE_INFINITY : dist / state.speed; + + if (state.acceleration > 0) { + + double accelTime = (state.targetSpeed - state.speed) / state.acceleration; + + double d = calcTraveledDist(state.speed, accelTime, state.acceleration); + + // The required distance is reached during acceleration + if (d > dist) { + return solveTraveledDist(state.speed, dist, state.acceleration); + + } else + // Time for accel plus remaining dist at max speed + return accelTime + (dist - d) / state.targetSpeed; + + } else { + + double decelTime = -(state.speed - state.targetSpeed) / state.acceleration; + + // max distance that can be reached + double max = calcTraveledDist(state.speed, decelTime, state.acceleration); + + if (FuzzyUtils.equals(dist, max)) { + return decelTime; + } else if (dist <= max) { + return solveTraveledDist(state.speed, dist, state.acceleration); + } else + return Double.POSITIVE_INFINITY; + } + } + + /** + * Calculate the maximum speed that can be reached under the condition that speed must be reduced to {@code allowedSpeed} + * again after traveled {@code dist}. + */ + static SpeedTarget calcTargetSpeed(double dist, double acceleration, double deceleration, + double currentSpeed, double allowedSpeed, double finalSpeed) { + + // Calculation is simplified if target is the same + if (FuzzyUtils.equals(allowedSpeed, finalSpeed)) { + return new SpeedTarget(finalSpeed, Double.POSITIVE_INFINITY); + } + + double timeDecel = (allowedSpeed - finalSpeed) / deceleration; + double distDecel = calcTraveledDist(allowedSpeed, timeDecel, -deceleration); + + // No further acceleration needed + if (FuzzyUtils.equals(currentSpeed, allowedSpeed)) { + double decelDist = dist - distDecel; + + // Start to stop now + if (FuzzyUtils.equals(decelDist, 0)) { + return new SpeedTarget(finalSpeed, 0); + } + + // Decelerate later + return new SpeedTarget(allowedSpeed, decelDist); + } + + + assert FuzzyUtils.greaterEqualThan(allowedSpeed, currentSpeed) : "Current speed must be lower than allowed"; + assert FuzzyUtils.greaterEqualThan(allowedSpeed, finalSpeed) : "Final speed must be smaller than target"; + + double timeAccel = (allowedSpeed - currentSpeed) / acceleration; + double distAccel = calcTraveledDist(currentSpeed, timeAccel, acceleration); + + // there is enough distance to accelerate to the target speed + if (FuzzyUtils.lessThan(distAccel + distDecel, dist)) { + return new SpeedTarget(allowedSpeed, dist - distDecel); + } + + double nom = 2 * acceleration * deceleration * dist + + acceleration * finalSpeed * finalSpeed + + deceleration * currentSpeed * currentSpeed; + + double v = Math.sqrt(nom / (acceleration + deceleration)); + + timeDecel = (v - finalSpeed) / deceleration; + distDecel = calcTraveledDist(v, timeDecel, -deceleration); + + return new SpeedTarget(v, dist - distDecel); + } + + + /** + * Calculate the deceleration needed to arrive at {@code targetSpeed} exactly after {@code dist}. + * + * @return negative acceleration, always a negative number. + */ + static double calcTargetDecel(double dist, double targetSpeed, double currentSpeed) { + return -(currentSpeed * currentSpeed - targetSpeed * targetSpeed) / (2 * dist); + } + + /** + * Calculate the maximum speed that can be achieved if trains want to stop after dist. + */ + static double calcTargetSpeedForStop(double dist, double acceleration, double deceleration, double currentSpeed) { + + double nom = 2 * acceleration * deceleration * dist + + deceleration * currentSpeed * currentSpeed; + + return Math.sqrt(nom / (acceleration + deceleration)); + } + + /** + * Calculate when the reservation function should be triggered. + * Should return {@link Double#POSITIVE_INFINITY} if this distance is far in the future and can be checked at later point. + * + * @param state current train state + * @param currentLink the link where the train head is on + * @return travel distance after which reservations should be updated. + */ + static double nextLinkReservation(TrainState state, RailLink currentLink) { + + // on way to pt stop, no need to reserve anymore + if (state.isStop(currentLink.getLinkId()) && FuzzyUtils.lessThan(state.headPosition, currentLink.length)) + return Double.POSITIVE_INFINITY; + + double assumedSpeed = calcPossibleMaxSpeed(state); + + // time needed for full stop + double stopTime = assumedSpeed / state.train.deceleration(); + + assert stopTime > 0 : "Stop time can not be negative"; + + // safety distance + double safety = RailsimCalc.calcTraveledDist(assumedSpeed, stopTime, -state.train.deceleration()); + + int idx = state.routeIdx; + double dist = currentLink.length - state.headPosition; + + RailLink nextLink = null; + // need to check beyond safety distance + while (FuzzyUtils.lessEqualThan(dist, safety * 2) && idx < state.route.size()) { + nextLink = state.route.get(idx++); + + if (!nextLink.isBlockedBy(state.driver)) + return dist - safety; + + // No reservation beyond pt stop + if (state.isStop(nextLink.getLinkId())) + break; + + dist += nextLink.length; + } + + // No reservation needed after the end + if (idx == state.route.size() || (nextLink != null && state.isStop(nextLink.getLinkId()))) + return Double.POSITIVE_INFINITY; + + return dist - safety; + } + + /** + * Links that need to be blocked or otherwise stop needs to be initiated. + */ + static List calcLinksToBlock(TrainState state, RailLink currentLink) { + + List result = new ArrayList<>(); + + // Currently driving to pt stop + if (state.isStop(currentLink.getLinkId()) && FuzzyUtils.lessThan(state.headPosition, currentLink.length)) + return result; + + double assumedSpeed = calcPossibleMaxSpeed(state); + double stopTime = assumedSpeed / state.train.deceleration(); + + // safety distance + double safety = RailsimCalc.calcTraveledDist(assumedSpeed, stopTime, -state.train.deceleration()); + + int idx = state.routeIdx; + + // dist to next + double dist = currentLink.length - state.headPosition; + + while (FuzzyUtils.lessEqualThan(dist, safety) && idx < state.route.size()) { + RailLink nextLink = state.route.get(idx++); + result.add(nextLink); + dist += nextLink.length; + + // Beyond pt stop links don't need to be reserved + if (state.isStop(nextLink.getLinkId())) + break; + } + + return result; + } + + /** + * Whether re-routing should be tried. + * + * @param upcoming the upcoming links the train tried to block. + */ + static boolean considerReRouting(List upcoming, RailLink currentLink) { + return currentLink.isEntryLink() || upcoming.stream().anyMatch(RailLink::isEntryLink); + } + + /** + * Maximum speed of the next upcoming links. + */ + private static double calcPossibleMaxSpeed(TrainState state) { + + double safety = RailsimCalc.calcTraveledDist(state.train.maxVelocity(), state.train.maxVelocity() / state.train.deceleration(), -state.train.deceleration()); + double maxSpeed = state.allowedMaxSpeed; + + double dist = 0; + for (int i = 0; i < state.route.size() && dist < safety; i++) { + RailLink link = state.route.get(i); + maxSpeed = Math.max(maxSpeed, link.getAllowedFreespeed(state.driver)); + + dist += link.length; + } + + return maxSpeed; + } + + record SpeedTarget(double targetSpeed, double decelDist) implements Comparable { + + @Override + public int compareTo(SpeedTarget o) { + return Double.compare(decelDist, o.decelDist); + } + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDriverAgentFactory.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDriverAgentFactory.java new file mode 100644 index 00000000000..6a68c0ffd59 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimDriverAgentFactory.java @@ -0,0 +1,71 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.mobsim.qsim.pt.AbstractTransitDriverAgent; +import org.matsim.core.mobsim.qsim.pt.TransitDriverAgentFactory; +import org.matsim.core.mobsim.qsim.pt.TransitDriverAgentImpl; +import org.matsim.core.mobsim.qsim.pt.TransitStopAgentTracker; +import org.matsim.pt.Umlauf; +import org.matsim.pt.transitSchedule.api.TransitStopArea; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Factory to create specific drivers for the rail engine. + */ +public class RailsimDriverAgentFactory implements TransitDriverAgentFactory { + + private final Set modes; + private final Map, List> stopAreas; + + @Inject + public RailsimDriverAgentFactory(Config config, Scenario scenario) { + this.modes = ConfigUtils.addOrGetModule(config, RailsimConfigGroup.class).getNetworkModes(); + + this.stopAreas = scenario.getTransitSchedule().getFacilities().values().stream() + .filter(t -> t.getStopAreaId() != null) + .collect(Collectors.groupingBy(TransitStopFacility::getStopAreaId, Collectors.toList())); + } + + @Override + public AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf, InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker) { + + String mode = umlauf.getUmlaufStuecke().get(0).getRoute().getTransportMode(); + + if (this.modes.contains(mode)) { + return new RailsimTransitDriverAgent(stopAreas, umlauf, mode, transitStopAgentTracker, internalInterface); + } + + return new TransitDriverAgentImpl(umlauf, TransportMode.car, transitStopAgentTracker, internalInterface); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java new file mode 100644 index 00000000000..6573ed1b47e --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngine.java @@ -0,0 +1,764 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.LinkEnterEvent; +import org.matsim.api.core.v01.events.LinkLeaveEvent; +import org.matsim.api.core.v01.events.PersonEntersVehicleEvent; +import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.core.mobsim.framework.Steppable; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; +import org.matsim.vehicles.VehicleType; + +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.events.RailsimDetourEvent; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainLeavesLinkEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.TrainDisposition; + +/** + * Engine to simulate train movement. + */ +final class RailsimEngine implements Steppable { + + private static final Logger log = LogManager.getLogger(RailsimEngine.class); + private final EventsManager eventsManager; + private final RailsimConfigGroup config; + private final List activeTrains = new ArrayList<>(); + private final Queue updateQueue = new PriorityQueue<>(); + private final RailResourceManager resources; + private final TrainDisposition disposition; + + RailsimEngine(EventsManager eventsManager, RailsimConfigGroup config, RailResourceManager resources, TrainDisposition disposition) { + this.eventsManager = eventsManager; + this.config = config; + this.resources = resources; + this.disposition = disposition; + } + + @Override + public void doSimStep(double time) { + + UpdateEvent update = updateQueue.peek(); + + // Update loop over all required state updates + while (update != null && update.plannedTime <= time) { + updateQueue.poll(); + + // Use planned time here, otherwise there will be inaccuracies + updateState(update.plannedTime, update); + + // Add the update event again + if (update.type != UpdateEvent.Type.IDLE) { + updateQueue.add(update); + } + + update = updateQueue.peek(); + } + + if (time % config.updateInterval == 0.) { + updateAllPositions(time); + } + } + + /** + * Update the current state of all trains, even if no update would be needed. + */ + public void updateAllStates(double time) { + + // Process all waiting events first + doSimStep(time); + + // Potential duplication of position update events here, if time matches updateInterval + updateAllPositions(time); + } + + /** + * Handle the departure of a train. + */ + public boolean handleDeparture(double now, MobsimDriverAgent agent, Id linkId, NetworkRoute route) { + + log.debug("Train {} is departing at {}", agent.getVehicle(), now); + + // Queue the update event + // NO events can be generated here, or temporal ordering is not guaranteed + // (departures are handled before event queue is processed) + + List list = route.getLinkIds().stream().map(resources::getLink).collect(Collectors.toList()); + list.add(0, resources.getLink(linkId)); + list.add(resources.getLink(route.getEndLinkId())); + + VehicleType type = agent.getVehicle().getVehicle().getType(); + TrainState state = new TrainState(agent, new TrainInfo(type, config), now, linkId, list); + + state.train.checkConsistency(); + + activeTrains.add(state); + + disposition.onDeparture(now, state.driver, state.route); + + updateQueue.add(new UpdateEvent(state, UpdateEvent.Type.DEPARTURE)); + + return true; + } + + private void updateAllPositions(double time) { + for (TrainState train : activeTrains) { + if (train.timestamp < time) + updateState(time, new UpdateEvent(train, UpdateEvent.Type.POSITION)); + } + } + + private void createEvent(Event event) { + // Because of the 1s update interval, events need to be rounded to the current simulation step + event.setTime(Math.ceil(event.getTime())); + this.eventsManager.processEvent(event); + } + + private void updateState(double time, UpdateEvent event) { + + // Do different updates depending on the type + switch (event.type) { + case DEPARTURE -> updateDeparture(time, event); + case POSITION -> { + updatePosition(time, event); + decideNextUpdate(event); + } + case SPEED_CHANGE -> updateSpeed(time, event); + case ENTER_LINK -> enterLink(time, event); + case LEAVE_LINK -> leaveLink(time, event); + case BLOCK_TRACK -> blockTrack(time, event); + case WAIT_FOR_RESERVATION -> checkTrackReservation(time, event); + case UNBLOCK_LINK -> { + unblockTrack(time, event.state, event.unblockLink); + // event will be removed + event.type = UpdateEvent.Type.IDLE; + } + default -> throw new IllegalStateException("Unhandled update type " + event.type); + } + } + + private void updateSpeed(double time, UpdateEvent event) { + + TrainState state = event.state; + + updatePosition(time, event); + + decideTargetSpeed(event, state); + + createEvent(state.asEvent(time)); + + decideNextUpdate(event); + } + + private void blockTrack(double time, UpdateEvent event) { + + TrainState state = event.state; + + updatePosition(time, event); + + if (!blockLinkTracks(time, state)) { + + decideTargetSpeed(event, state); + + event.checkReservation = time + config.pollInterval; + decideNextUpdate(event); + + } else { + event.checkReservation = -1; + decideNextUpdate(event); + } + } + + private void checkTrackReservation(double time, UpdateEvent event) { + + TrainState state = event.state; + + // train might be at the end of route already + RailLink nextLink = state.isRouteAtEnd() ? null : state.route.get(state.routeIdx); + + boolean allBlocked = blockLinkTracks(time, state); + + // Driver can advance if the next link is already free + if (allBlocked || (nextLink != null && nextLink.isBlockedBy(state.driver))) { + + if (allBlocked) + event.checkReservation = -1; + else { + event.checkReservation = time + config.pollInterval; + } + + // Train already waits at the end of previous link + if (event.waitingForLink) { + + enterLink(time, event); + event.waitingForLink = false; + + } else { + + updatePosition(time, event); + decideTargetSpeed(event, state); + decideNextUpdate(event); + + } + + } else { + + event.checkReservation = time + config.pollInterval; + + // If train is already standing still and waiting, there is no update needed. + if (event.waitingForLink) { + event.plannedTime = time + config.pollInterval; + } else { + decideNextUpdate(event); + } + } + } + + private void updateDeparture(double time, UpdateEvent event) { + + TrainState state = event.state; + state.timestamp = time; + + state.allowedMaxSpeed = retrieveAllowedMaxSpeed(state); + + RailLink firstLink = resources.getLink(state.headLink); + + state.headPosition = firstLink.length; + state.tailPosition = firstLink.length - state.train.length(); + + // reserve links and start if first one is free + if (blockLinkTracks(time, state) || resources.isBlockedBy(firstLink, state.driver)) { + + createEvent(new PersonEntersVehicleEvent(time, state.driver.getId(), state.driver.getVehicle().getId())); + createEvent(new VehicleEntersTrafficEvent(time, state.driver.getId(), + state.headLink, state.driver.getVehicle().getId(), state.driver.getMode(), 1.0)); + + state.timestamp = time; + + double stopTime = 0; + if (state.isStop(state.headLink)) { + stopTime = handleTransitStop(time, state); + } + + // Train departs at the very end of the first link + state.routeIdx = 1; + + createEvent(state.asEvent(time)); + + if (stopTime <= 0) { + // Call enter link logic immediately + enterLink(time, event); + } else { + event.plannedTime = time + stopTime; + event.type = UpdateEvent.Type.ENTER_LINK; + } + + } else { + // vehicle will wait and call departure again + event.plannedTime += config.pollInterval; + } + } + + /** + * Reserve links in advance as necessary. + */ + private boolean blockLinkTracks(double time, TrainState state) { + + List links = RailsimCalc.calcLinksToBlock(state, resources.getLink(state.headLink)); + + if (links.isEmpty()) + return true; + + if (state.pt != null && RailsimCalc.considerReRouting(links, resources.getLink(state.headLink))) { + + int start = -1; + int end = -1; + RailLink entry = null; + RailLink exit = null; + + for (int i = Math.max(0, state.routeIdx - 1); i < state.route.size(); i++) { + RailLink l = state.route.get(i); + + if (l.isEntryLink()) { + entry = l; + start = i; + } else if (start > -1 && l.isBlockedBy(state.driver)) { + // check if any link beyond entry is already blocked + // if that is the case re-route is not possible anymore + break; + } else if (start > -1 && l.isExitLink()) { + exit = l; + end = i; + break; + } + } + + // there might be no exit link if this is the end of the route + // exit will be set to null if re-route is too late + // network could be wrong as well, but hard to verify + if (exit != null) { + + // check if this route is different + List subRoute = state.route.subList(start + 1, end); + + List detour = disposition.requestRoute(time, state.pt, subRoute, entry, exit); + + if (detour != null && !subRoute.equals(detour)) { + + TransitStopFacility newStop = state.pt.addDetour(subRoute, detour); + + subRoute.clear(); + subRoute.addAll(detour); + + if (newStop != null) { + state.nextStop = newStop; + } + + createEvent(new RailsimDetourEvent( + time, state.driver.getVehicle().getId(), + entry.getLinkId(), exit.getLinkId(), + detour.stream().map(RailLink::getLinkId).toList(), + newStop != null ? newStop.getId() : null + )); + + // Block links again using the updated route + links = RailsimCalc.calcLinksToBlock(state, resources.getLink(state.headLink)); + + } + } + } + + List blocked = disposition.blockRailSegment(time, state.driver, links); + + // Only continue successfully if all requested link have been blocked + return links.size() == blocked.size(); + } + + private void enterLink(double time, UpdateEvent event) { + + TrainState state = event.state; + + updatePosition(time, event); + + // current head link is the pt stop, which means the train is at the end of the link when this is called + if (!event.waitingForLink && state.isStop(state.headLink)) { + + double stopTime = handleTransitStop(time, state); + + assert stopTime >= 0 : "Stop time must be positive"; + assert FuzzyUtils.equals(state.speed, 0) : "Speed must be 0 at pt stop, but was " + state.speed; + + // Same event is re-scheduled after stopping, + event.plannedTime = time + stopTime; + + return; + } + + // Arrival at destination + if (!event.waitingForLink && state.isRouteAtEnd()) { + + assert FuzzyUtils.equals(state.speed, 0) : "Speed must be 0 at end, but was " + state.speed; + + // Free all reservations + for (RailLink link : state.route) { + if (link.isBlockedBy(state.driver)) { + disposition.unblockRailLink(time, state.driver, link); + } + } + + state.driver.notifyArrivalOnLinkByNonNetworkMode(state.headLink); + state.driver.endLegAndComputeNextState(Math.ceil(time)); + + activeTrains.remove(state); + + event.type = UpdateEvent.Type.IDLE; + return; + } + + // Train stopped and reserves next links + if (FuzzyUtils.equals(state.speed, 0) && !blockLinkTracks(time, state)) { + + RailLink currentLink = state.route.get(state.routeIdx); + // If this linked is blocked the driver can continue + if (!currentLink.isBlockedBy(state.driver)) { + event.waitingForLink = true; + event.type = UpdateEvent.Type.WAIT_FOR_RESERVATION; + event.plannedTime = time + config.pollInterval; + return; + } + } + + // On route departure the head link is null + createEvent(new LinkLeaveEvent(time, state.driver.getVehicle().getId(), state.headLink)); + + // Get link and increment + state.headPosition = 0; + state.headLink = state.route.get(state.routeIdx++).getLinkId(); + + state.driver.notifyMoveOverNode(state.headLink); + createEvent(new LinkEnterEvent(time, state.driver.getVehicle().getId(), state.headLink)); + + RailLink link = resources.getLink(state.headLink); + + assert link.isBlockedBy(state.driver) : "Link has to be blocked by driver when entered"; + + decideTargetSpeed(event, state); + + createEvent(state.asEvent(time)); + + decideNextUpdate(event); + } + + private void leaveLink(double time, UpdateEvent event) { + + TrainState state = event.state; + + RailLink nextTailLink = null; + // Find the next link in the route + for (int i = state.routeIdx; i >= 1; i--) { + if (state.route.get(i - 1).getLinkId().equals(state.tailLink)) { + nextTailLink = state.route.get(i); + } + } + + Objects.requireNonNull(nextTailLink, "Could not find next link in route"); + + updatePosition(time, event); + + createEvent(new RailsimTrainLeavesLinkEvent(time, state.driver.getVehicle().getId(), state.tailLink)); + + RailLink tailLink = resources.getLink(state.tailLink); + + state.tailLink = nextTailLink.getLinkId(); + state.tailPosition = 0; + + decideTargetSpeed(event, state); + + decideNextUpdate(event); + + if (tailLink.minimumHeadwayTime == 0) + unblockTrack(time, state, tailLink); + else + updateQueue.add(new UpdateEvent(state, tailLink, time)); + } + + /** + * Unblocks a link. + */ + private void unblockTrack(double time, TrainState state, RailLink unblockLink) { + disposition.unblockRailLink(time, state.driver, unblockLink); + } + + /** + * Update position within a link and decides on next update. + */ + private void updatePosition(double time, UpdateEvent event) { + + TrainState state = event.state; + + double elapsed = time - state.timestamp; + + if (elapsed == 0) + return; + + double accelTime = (state.targetSpeed - state.speed) / state.acceleration; + + double dist; + if (state.acceleration == 0) { + dist = state.speed * elapsed; + + } else if (accelTime < elapsed) { + + // Travelled distance under constant acceleration + dist = RailsimCalc.calcTraveledDist(state.speed, accelTime, state.acceleration); + + // Remaining time at constant speed + if (state.acceleration > 0) + dist += RailsimCalc.calcTraveledDist(state.targetSpeed, elapsed - accelTime, 0); + + // Target speed was reached + state.speed = state.targetSpeed; + state.acceleration = 0; + + } else { + + // Acceleration was constant the whole time + dist = RailsimCalc.calcTraveledDist(state.speed, elapsed, state.acceleration); + state.speed = state.speed + elapsed * state.acceleration; + + if (FuzzyUtils.equals(state.speed, state.targetSpeed)) { + state.speed = state.targetSpeed; + state.acceleration = 0; + } + + } + + assert FuzzyUtils.greaterEqualThan(dist, 0) : "Travel distance must be positive, but was" + dist; + + state.headPosition += dist; + state.tailPosition += dist; + + if (Double.isFinite(state.targetDecelDist)) { + state.targetDecelDist -= dist; + } + + // When trains are put into the network their tail may be longer than the current link + // this assertion may not hold depending on the network, should possibly be removed + assert state.routeIdx <= 2 || FuzzyUtils.greaterEqualThan(state.tailPosition, 0) : "Illegal state update. Tail position should not be negative"; + + assert FuzzyUtils.lessEqualThan(state.headPosition, resources.getLink(state.headLink).length) : "Illegal state update. Head position must be smaller than link length"; + assert FuzzyUtils.greaterEqualThan(state.headPosition, 0) : "Head position must be positive"; + assert FuzzyUtils.lessEqualThan(state.speed, state.allowedMaxSpeed) : "Speed must be less equal than the allowed speed"; + + state.timestamp = time; + + // Only emit events on certain occasions + if (event.type == UpdateEvent.Type.ENTER_LINK || event.type == UpdateEvent.Type.LEAVE_LINK || event.type == UpdateEvent.Type.POSITION || event.type == UpdateEvent.Type.SPEED_CHANGE) + createEvent(state.asEvent(time)); + } + + /** + * Handle transit stop and update the state. + * + * @return stop time + */ + private double handleTransitStop(double time, TrainState state) { + + assert state.pt != null : "Pt driver must be present"; + + // Time needs to be rounded to current sim step + double stopTime = state.pt.handleTransitStop(state.nextStop, Math.ceil(time)); + state.nextStop = state.pt.getNextTransitStop(); + + return stopTime; + } + + /** + * Decide which update is the earliest and needs to be the next. + */ + private void decideNextUpdate(UpdateEvent event) { + + TrainState state = event.state; + RailLink currentLink = resources.getLink(state.headLink); + + // (1) max speed reached + double accelDist = Double.POSITIVE_INFINITY; + if (state.acceleration > 0 && FuzzyUtils.greaterThan(state.targetSpeed, state.speed)) { + accelDist = RailsimCalc.calcTraveledDist(state.speed, (state.targetSpeed - state.speed) / state.acceleration, state.acceleration); + } + + // (2) start deceleration + double decelDist = state.targetDecelDist; + + assert FuzzyUtils.greaterEqualThan(decelDist, 0) : "Deceleration distance must be larger than 0, but was " + decelDist; + + // (3) next link needs reservation + double reserveDist = Double.POSITIVE_INFINITY; + if (!state.isRouteAtEnd() && !event.isAwaitingReservation()) { + reserveDist = RailsimCalc.nextLinkReservation(state, currentLink); + + if (reserveDist < 0) + reserveDist = 0; + + // Outside of block track the reserve distance is always greater 0 + // infinite loops would occur otherwise + if (!(event.type != UpdateEvent.Type.BLOCK_TRACK || FuzzyUtils.greaterThan(reserveDist, 0))) { + // There are here for debugging + List tmp = RailsimCalc.calcLinksToBlock(state, currentLink); + double r = RailsimCalc.nextLinkReservation(state, currentLink); + + throw new AssertionError("Reserve distance must be positive, but was " + r); + } + } + + // (4) tail link changes + double tailDist = resources.getLink(state.tailLink).length - state.tailPosition; + + // (5) head link changes + double headDist = currentLink.length - state.headPosition; + + assert FuzzyUtils.greaterEqualThan(tailDist, 0) : "Tail distance must be positive"; + assert FuzzyUtils.greaterEqualThan(headDist, 0) : "Head distance must be positive"; + + // Find the earliest required update + + double dist; + if (reserveDist <= accelDist && reserveDist <= decelDist && reserveDist <= tailDist && reserveDist <= headDist) { + dist = reserveDist; + event.type = UpdateEvent.Type.BLOCK_TRACK; + } else if (accelDist <= decelDist && accelDist <= reserveDist && accelDist <= tailDist && accelDist <= headDist) { + dist = accelDist; + event.type = UpdateEvent.Type.SPEED_CHANGE; + } else if (decelDist <= accelDist && decelDist <= reserveDist && decelDist <= tailDist && decelDist <= headDist) { + dist = decelDist; + event.type = UpdateEvent.Type.SPEED_CHANGE; + } else if (tailDist <= decelDist && tailDist <= reserveDist && tailDist <= headDist) { + dist = tailDist; + event.type = UpdateEvent.Type.LEAVE_LINK; + } else { + dist = headDist; + event.type = UpdateEvent.Type.ENTER_LINK; + } + + assert FuzzyUtils.greaterEqualThan(dist, 0) : "Distance for next update must be positive"; + + // dist is the minimum of all supplied distances + event.plannedTime = state.timestamp + RailsimCalc.calcRequiredTime(state, dist); + + // There could be old reservations events that need to be checked first + if (event.isAwaitingReservation() && event.checkReservation < state.timestamp) { + event.checkReservation = state.timestamp; + } + + // insert reservation event if necessary + if (event.isAwaitingReservation() && event.plannedTime > event.checkReservation) { + + event.type = UpdateEvent.Type.WAIT_FOR_RESERVATION; + event.plannedTime = event.checkReservation; + } + + assert Double.isFinite(event.plannedTime) : "Planned update time must be finite, but was " + event.plannedTime; + assert event.plannedTime >= state.timestamp : "Planned time must be after current time"; + + } + + /** + * Calculates possible target speed, consequential acceleration depending on current state. + */ + private void decideTargetSpeed(UpdateEvent event, TrainState state) { + + // Distance to next link + RailLink currentLink = resources.getLink(state.headLink); + + double dist = currentLink.length - state.headPosition; + state.allowedMaxSpeed = retrieveAllowedMaxSpeed(state); + + // Lookahead window + double window = RailsimCalc.calcTraveledDist(state.allowedMaxSpeed, state.allowedMaxSpeed / state.train.deceleration(), + -state.train.deceleration()) + currentLink.length; + + double minAllowed = state.allowedMaxSpeed; + + state.targetSpeed = state.allowedMaxSpeed; + state.targetDecelDist = Double.POSITIVE_INFINITY; + + for (int i = state.routeIdx; i <= state.route.size(); i++) { + + RailLink link; + double allowed; + if (i == state.route.size()) { + link = null; + allowed = 0; + } else { + link = state.route.get(i); + + // If the previous link is a transit stop the speed needs to be 0 at the next link + // train stops at the very end of a link + if (i > 0 && state.isStop(state.route.get(i - 1).getLinkId())) + allowed = 0; + else if (!resources.isBlockedBy(link, state.driver)) + allowed = 0; + else + allowed = link.getAllowedFreespeed(state.driver); + } + + // Only need to consider if speed is lower than the allowed speed + if (!FuzzyUtils.equals(dist, 0) && allowed <= minAllowed) { + RailsimCalc.SpeedTarget target = RailsimCalc.calcTargetSpeed(dist, state.train.acceleration(), state.train.deceleration(), + state.speed, state.allowedMaxSpeed, Math.min(state.allowedMaxSpeed, allowed)); + + assert FuzzyUtils.greaterEqualThan(target.decelDist(), 0) : "Decel dist must be greater than 0, or stopping is not possible"; + + if (FuzzyUtils.equals(target.decelDist(), 0)) { + + // Need to decelerate now + state.targetSpeed = allowed; + state.targetDecelDist = Double.POSITIVE_INFINITY; + break; + } else if (target.decelDist() < state.targetDecelDist && target.targetSpeed() <= state.targetSpeed) { + + // Decelerate later + state.targetSpeed = target.targetSpeed(); + state.targetDecelDist = target.decelDist(); + + } else if (target.targetSpeed() > state.targetSpeed && !Double.isFinite(state.targetDecelDist)) { + // Acceleration is required + state.targetSpeed = target.targetSpeed(); + state.targetDecelDist = target.decelDist(); + } + } + + if (link != null) + dist += link.length; + + if (dist >= window) + break; + + minAllowed = allowed; + } + + // Calc accel depending on target + if (FuzzyUtils.equals(state.speed, state.targetSpeed)) { + state.acceleration = 0; + } else if (FuzzyUtils.lessThan(state.speed, state.targetSpeed)) { + state.acceleration = state.train.acceleration(); + } else { + state.acceleration = -state.train.deceleration(); + } + + assert FuzzyUtils.equals(state.targetSpeed, state.speed) || state.acceleration != 0 : "Acceleration must be set if target speed is different than current"; + assert FuzzyUtils.greaterThan(state.targetDecelDist, 0) : "Target decel must be greater than 0 after updating"; + + } + + /** + * Allowed speed for the train. + */ + private double retrieveAllowedMaxSpeed(TrainState state) { + + double maxSpeed = resources.getLink(state.headLink).getAllowedFreespeed(state.driver); + + for (int i = state.routeIdx - 1; i >= 0; i--) { + RailLink link = state.route.get(i); + maxSpeed = Math.min(maxSpeed, link.getAllowedFreespeed(state.driver)); + if (link.getLinkId().equals(state.tailLink)) { + break; + } + } + + return maxSpeed; + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java new file mode 100644 index 00000000000..88ee0b22cbc --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimEngine.java @@ -0,0 +1,123 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.TrainDisposition; +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Route; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.core.mobsim.framework.PlanAgent; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.interfaces.DepartureHandler; +import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine; +import org.matsim.core.mobsim.qsim.interfaces.NetsimLink; +import org.matsim.core.mobsim.qsim.pt.TransitStopAgentTracker; +import org.matsim.core.mobsim.qsim.qnetsimengine.QLinkI; +import org.matsim.core.population.routes.NetworkRoute; + +import java.util.Set; + +/** + * QSim Engine to integrate microscopically simulated train movement. + */ +public class RailsimQSimEngine implements DepartureHandler, MobsimEngine { + + private final QSim qsim; + private final RailsimConfigGroup config; + private final RailResourceManager res; + private final TrainDisposition disposition; + private final Set modes; + private final TransitStopAgentTracker agentTracker; + private InternalInterface internalInterface; + + private RailsimEngine engine; + + @Inject + public RailsimQSimEngine(QSim qsim, RailResourceManager res, TrainDisposition disposition, TransitStopAgentTracker agentTracker) { + this.qsim = qsim; + this.config = ConfigUtils.addOrGetModule(qsim.getScenario().getConfig(), RailsimConfigGroup.class); + this.res = res; + this.disposition = disposition; + this.modes = config.getNetworkModes(); + this.agentTracker = agentTracker; + } + + @Override + public void setInternalInterface(InternalInterface internalInterface) { + this.internalInterface = internalInterface; + } + + @Override + public void onPrepareSim() { + engine = new RailsimEngine(qsim.getEventsManager(), config, res, disposition); + } + + @Override + public void afterSim() { + + } + + @Override + public void doSimStep(double time) { + engine.doSimStep(time); + } + + @Override + public boolean handleDeparture(double now, MobsimAgent agent, Id linkId) { + + if (!modes.contains(agent.getMode())) return false; + + NetsimLink link = qsim.getNetsimNetwork().getNetsimLink(linkId); + + // Lots of implicit type checking here to get the required information from the agent + if (!(agent instanceof MobsimDriverAgent driver)) { + throw new IllegalStateException("Departing agent " + agent.getId() + " is not a DriverAgent"); + } + if (!(agent instanceof PlanAgent plan)) { + throw new IllegalStateException("Agent " + agent + " is not of type PlanAgent and therefore incompatible."); + } + PlanElement el = plan.getCurrentPlanElement(); + if (!(el instanceof Leg leg)) { + throw new IllegalStateException("Plan element of agent " + agent + " is not a leg with a route."); + } + Route route = leg.getRoute(); + if (!(route instanceof NetworkRoute networkRoute)) { + throw new IllegalStateException("A network route is required for agent " + agent + "."); + } + + // Vehicles were inserted into the qsim as parked + // Remove them as soon as we depart + if (link instanceof QLinkI qLink) { + qLink.unregisterAdditionalAgentOnLink(agent.getId()); + qLink.removeParkedVehicle(driver.getVehicle().getId()); + } + + return engine.handleDeparture(now, driver, linkId, networkRoute); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java new file mode 100644 index 00000000000..77e4e04133b --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimQSimModule.java @@ -0,0 +1,58 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.TrainDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; +import com.google.inject.multibindings.OptionalBinder; +import org.matsim.core.mobsim.qsim.AbstractQSimModule; +import org.matsim.core.mobsim.qsim.components.QSimComponentsConfig; +import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigurator; +import org.matsim.core.mobsim.qsim.pt.TransitDriverAgentFactory; + +/** + * Module to install railsim functionality. + */ +public class RailsimQSimModule extends AbstractQSimModule implements QSimComponentsConfigurator { + + public static final String COMPONENT_NAME = "Railsim"; + + @Override + public void configure(QSimComponentsConfig components) { + components.addNamedComponent(COMPONENT_NAME); + } + + @Override + protected void configureQSim() { + bind(RailsimQSimEngine.class).asEagerSingleton(); + + bind(TrainRouter.class).asEagerSingleton(); + bind(RailResourceManager.class).asEagerSingleton(); + + // This Interface might be replaced with other implementations + bind(TrainDisposition.class).to(SimpleDisposition.class).asEagerSingleton(); + + addQSimComponentBinding(COMPONENT_NAME).to(RailsimQSimEngine.class); + + OptionalBinder.newOptionalBinder(binder(), TransitDriverAgentFactory.class) + .setBinding().to(RailsimDriverAgentFactory.class); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java new file mode 100644 index 00000000000..be2ebcb6ffb --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTransitDriverAgent.java @@ -0,0 +1,117 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.mobsim.qsim.pt.TransitDriverAgentImpl; +import org.matsim.core.mobsim.qsim.pt.TransitStopAgentTracker; +import org.matsim.pt.Umlauf; +import org.matsim.pt.transitSchedule.api.TransitStopArea; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; + +import java.util.List; +import java.util.Map; + +/** + * Railsim specific transit driver that can be re-routed. + */ +public final class RailsimTransitDriverAgent extends TransitDriverAgentImpl { + + private static final Logger log = LogManager.getLogger(RailsimTransitDriverAgent.class); + + /** + * Contains the original stop if it was overwritten by a detour. + */ + private TransitStopFacility overwrittenStop; + + private final Map, List> stopAreas; + + public RailsimTransitDriverAgent(Map, List> stopAreas, Umlauf umlauf, String transportMode, TransitStopAgentTracker thisAgentTracker, InternalInterface internalInterface) { + super(umlauf, transportMode, thisAgentTracker, internalInterface); + this.stopAreas = stopAreas; + } + + /** + * Add a detour to this driver schedule. + * + * @return next transit stop, if it needs to be changed. + */ + public TransitStopFacility addDetour(List original, List detour) { + + TransitStopFacility nextStop = getNextTransitStop(); + + // Adjust the link index so that it fits to the new size + // the original route inside this agent is not updated because it is currently not necessary + // after the detour the link index should be consistent again + setNextLinkIndex(getNextLinkIndex() + (original.size() - detour.size())); + + if (nextStop != null) { + + Id areaId = nextStop.getStopAreaId(); + + boolean adjust = false; + for (RailLink link : original) { + if (nextStop.getLinkId().equals(link.getLinkId())) { + adjust = true; + break; + } + } + + // pt stop needs to be remapped + if (adjust) { + + List inArea = stopAreas.get(areaId); + + for (TransitStopFacility stop : inArea) { + for (RailLink d : detour) { + + if (stop.getLinkId().equals(d.getLinkId())) { + this.overwrittenStop = nextStop; + return stop; + } + } + } + + log.warn("Could not re-route vehicle {} to a replacement transit stop", getVehicle().getId()); + } + } + + return null; + } + + @Override + public double handleTransitStop(TransitStopFacility stop, double now) { + + // This function will call the API with the original stop as if no reroute has happened + + // use the original stop exactly one time + if (overwrittenStop != null) { + stop = overwrittenStop; + double t = super.handleTransitStop(stop, now); + overwrittenStop = null; + return t; + } + + return super.handleTransitStop(stop, now); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java new file mode 100644 index 00000000000..976d2d2e9ad --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrackState.java @@ -0,0 +1,32 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +/** + * Current state of a track. + */ +public enum TrackState { + FREE, + + /** + * Blocked tracks that are exclusively available for trains. + */ + BLOCKED +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java new file mode 100644 index 00000000000..9d395d785d0 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainInfo.java @@ -0,0 +1,60 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import org.matsim.api.core.v01.Id; +import org.matsim.vehicles.VehicleType; + +/** + * Non-mutable static information for a single train. + */ +record TrainInfo( + Id id, + double length, + double maxVelocity, + double acceleration, + double deceleration, + double maxDeceleration +) { + + TrainInfo(VehicleType vehicle, RailsimConfigGroup config) { + this( + vehicle.getId(), + vehicle.getLength(), + vehicle.getMaximumVelocity(), + RailsimUtils.getTrainAcceleration(vehicle, config), + RailsimUtils.getTrainDeceleration(vehicle, config), + RailsimUtils.getTrainDeceleration(vehicle, config) + ); + } + + public void checkConsistency() { + if (!Double.isFinite(maxVelocity) || maxVelocity <= 0) + throw new IllegalArgumentException("Train of type " + id + " does not have a finite maximumVelocity."); + + if (!Double.isFinite(acceleration) || acceleration <= 0) + throw new IllegalArgumentException("Train of type " + id + " does not have a finite and positive acceleration."); + + if (!Double.isFinite(deceleration) || deceleration <= 0) + throw new IllegalArgumentException("Train of type " + id + " does not have a finite and positive deceleration."); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java new file mode 100644 index 00000000000..1c0f66cb969 --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/TrainState.java @@ -0,0 +1,167 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Stores the mutable current state of a train. + */ +final class TrainState { + + /** + * Driver of the train. + */ + final MobsimDriverAgent driver; + + /** + * Transit agent, if this is a pt transit. + */ + @Nullable + final RailsimTransitDriverAgent pt; + + /** + * Next transit stop. + */ + @Nullable + TransitStopFacility nextStop; + + /** + * Train specific parameters. + */ + final TrainInfo train; + + /** + * Route of this train. + */ + final List route; + + /** + * Current index in the list of route links. + */ + int routeIdx; + + /** + * Time of this state. + */ + double timestamp; + + /** + * The link the where the head of the train is on. Can be null if not yet departed. + */ + @Nullable + Id headLink; + + /** + * Current link the very end of the train is on. Can be null if not yet departed. + */ + @Nullable + Id tailLink; + + /** + * Trains target speed. + */ + double targetSpeed; + + /** + * Train plans to decelerate after this distance. + */ + double targetDecelDist; + + /** + * Current allowed speed, which depends on train type, links, but not on other trains or speed needed to stop. + */ + double allowedMaxSpeed; + + /** + * Distance in meters away from the {@code headLink}s {@code fromNode}. + */ + double headPosition; + + /** + * * Distance in meters away from the {@code tailLink}s {@code fromNode}. + */ + double tailPosition; + + /** + * Speed in m/s. + */ + double speed; + + /** + * Current Acceleration, (or deceleration if negative). + */ + double acceleration; + + TrainState(MobsimDriverAgent driver, TrainInfo train, double timestamp, @Nullable Id linkId, List route) { + this.driver = driver; + this.pt = driver instanceof RailsimTransitDriverAgent ptDriver ? ptDriver : null; + this.nextStop = pt != null ? pt.getNextTransitStop() : null; + this.train = train; + this.route = route; + this.timestamp = timestamp; + this.headLink = linkId; + this.tailLink = linkId; + this.targetDecelDist = Double.POSITIVE_INFINITY; + } + + @Override + public String toString() { + return "TrainState{" + + "driver=" + driver.getId() + + ", timestamp=" + timestamp + + ", headLink=" + headLink + + ", tailLink=" + tailLink + + ", targetSpeed=" + targetSpeed + + ", allowedMaxSpeed=" + allowedMaxSpeed + + ", headPosition=" + headPosition + + ", tailPosition=" + tailPosition + + ", speed=" + speed + + ", acceleration=" + acceleration + + '}'; + } + + + boolean isRouteAtEnd() { + return routeIdx >= route.size(); + } + + /** + * Check whether to stop at certain link. + */ + boolean isStop(Id link) { + return nextStop != null && nextStop.getLinkId().equals(link); + } + + RailsimTrainStateEvent asEvent(double time) { + return new RailsimTrainStateEvent(time, time, driver.getVehicle().getId(), + headLink, headPosition, + tailLink, tailPosition, + speed, acceleration, targetSpeed); + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java new file mode 100644 index 00000000000..52776792c9b --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/UpdateEvent.java @@ -0,0 +1,110 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import java.util.Objects; + +/** + * Internal event that describes, when a {@link TrainState} is supposed to be updated. + */ +final class UpdateEvent implements Comparable { + + final TrainState state; + double plannedTime; + Type type; + + /** + * Timestamp when next reservation will be checked. + */ + double checkReservation = -1; + + /** + * Whether train is waiting on the very link end for the next to be unblocked. + */ + boolean waitingForLink; + + /** + * Stores a link that is to be released. + */ + final RailLink unblockLink; + + UpdateEvent(TrainState state, Type type) { + this.state = state; + this.plannedTime = state.timestamp; + this.type = type; + this.waitingForLink = false; + this.unblockLink = null; + } + + UpdateEvent(TrainState state, RailLink unblockLink, double time) { + this.state = state; + this.unblockLink = unblockLink; + this.plannedTime = time + unblockLink.minimumHeadwayTime; + this.type = Type.UNBLOCK_LINK; + } + + @Override + public int compareTo(UpdateEvent o) { + int compare = Double.compare(plannedTime, o.plannedTime); + + if (compare == 0) + return state.driver.getId().compareTo(o.state.driver.getId()); + + return compare; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UpdateEvent that = (UpdateEvent) o; + return Objects.equals(state, that.state); + } + + @Override + public int hashCode() { + return Objects.hash(state); + } + + /** + * This train currently waits for a reservation for blocked tracks. + */ + boolean isAwaitingReservation() { + return checkReservation >= 0; + } + + /** + * The type of the requested update. + */ + enum Type { + + IDLE, + DEPARTURE, + POSITION, + ENTER_LINK, + LEAVE_LINK, + BLOCK_TRACK, + WAIT_FOR_RESERVATION, + SPEED_CHANGE, + UNBLOCK_LINK + + } + +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java new file mode 100644 index 00000000000..99418a7547b --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/SimpleDisposition.java @@ -0,0 +1,89 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailResourceManager; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimTransitDriverAgent; +import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; +import jakarta.inject.Inject; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +/** + * Simple disposition without deadlock avoidance. + */ +public class SimpleDisposition implements TrainDisposition { + + private final RailResourceManager resources; + private final TrainRouter router; + + @Inject + public SimpleDisposition(RailResourceManager resources, TrainRouter router) { + this.resources = resources; + this.router = router; + } + + @Override + public void onDeparture(double time, MobsimDriverAgent driver, List route) { + // Nothing to do. + } + + @Nullable + @Override + public List requestRoute(double time, RailsimTransitDriverAgent driver, List segment, RailLink entry, RailLink exit) { + + // Only re-routes if the link segment is occupied + for (RailLink link : segment) { + if (!resources.isBlockedBy(link, driver) && !resources.hasCapacity(link.getLinkId())) + return router.calcRoute(entry, exit); + } + + return null; + } + + @Override + public List blockRailSegment(double time, MobsimDriverAgent driver, List segment) { + + List blocked = new ArrayList<>(); + + // Iterate all links that need to be blocked + for (RailLink link : segment) { + + // Check if single link can be reserved + if (resources.tryBlockTrack(time, driver, link)) { + blocked.add(link); + } else + break; + } + + return blocked; + } + + @Override + public void unblockRailLink(double time, MobsimDriverAgent driver, RailLink link) { + + // put resource handling into release track + resources.releaseTrack(time, driver, link); + } +} diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java new file mode 100644 index 00000000000..eb178dc663c --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/disposition/TrainDisposition.java @@ -0,0 +1,64 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine.disposition; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimTransitDriverAgent; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Disposition, handling route and track reservations. + */ +public interface TrainDisposition { + + /** + * Method invoked when a train is departing. + */ + void onDeparture(double time, MobsimDriverAgent driver, List route); + + /** + * Called by the driver when an entry link is within stop distance. + * + * @param segment the original link segment between entry and exit + * @return the route change, or null if nothing should be changed + */ + @Nullable + default List requestRoute(double time, RailsimTransitDriverAgent driver, List segment, + RailLink entry, RailLink exit) { + return null; + } + + /** + * Train is reaching the given links and is trying to block them. + * + * @return links of the request that are exclusively blocked for the train. + */ + List blockRailSegment(double time, MobsimDriverAgent driver, List segment); + + /** + * Inform the resource manager that the train has passed a link that can now be unblocked. + * This needs to be called after track states have been updated already. + */ + void unblockRailLink(double time, MobsimDriverAgent driver, RailLink link); + +} 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 new file mode 100644 index 00000000000..34af433a24e --- /dev/null +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java @@ -0,0 +1,84 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine.router; + +import ch.sbb.matsim.contrib.railsim.qsimengine.RailLink; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailResourceManager; +import jakarta.inject.Inject; +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.Person; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.router.FastDijkstraFactory; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.TravelDisutility; +import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.matsim.vehicles.Vehicle; + +import java.util.List; + +/** + * Calculates unblocked route between two {@link RailLink}. + */ +public final class TrainRouter { + + private final Network network; + private final RailResourceManager resources; + private final LeastCostPathCalculator lpc; + + @Inject + public TrainRouter(QSim qsim, RailResourceManager resources) { + this(qsim.getScenario().getNetwork(), resources); + } + + public TrainRouter(Network network, RailResourceManager resources) { + this.network = network; + this.resources = resources; + + // uses the full network, which should not be slower than filtered network as long as dijkstra is used + this.lpc = new FastDijkstraFactory(false).createPathCalculator(network, new DisUtility(), new FreeSpeedTravelTime()); + } + + /** + * Calculate the shortest path between two links. + */ + public List calcRoute(RailLink from, RailLink to) { + + Node fromNode = network.getLinks().get(from.getLinkId()).getToNode(); + Node toNode = network.getLinks().get(to.getLinkId()).getFromNode(); + + LeastCostPathCalculator.Path path = lpc.calcLeastCostPath(fromNode, toNode, 0, null, null); + + return path.links.stream().map(l -> resources.getLink(l.getId())).toList(); + } + + private final class DisUtility implements TravelDisutility { + @Override + public double getLinkTravelDisutility(Link link, double time, Person person, Vehicle vehicle) { + return resources.hasCapacity(link.getId()) ? 0 : 1; + } + + @Override + public double getLinkMinimumTravelDisutility(Link link) { + return 0; + } + } +} 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 new file mode 100644 index 00000000000..7148d935cb3 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java @@ -0,0 +1,455 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.integration; + +import ch.sbb.matsim.contrib.railsim.RailsimModule; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import ch.sbb.matsim.contrib.railsim.qsimengine.RailsimQSimModule; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.api.experimental.events.VehicleArrivesAtFacilityEvent; +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.scenario.ScenarioUtils; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.examples.ExamplesUtils; +import org.matsim.pt.transitSchedule.api.Departure; +import org.matsim.pt.transitSchedule.api.TransitLine; +import org.matsim.pt.transitSchedule.api.TransitRoute; +import org.matsim.pt.transitSchedule.api.TransitScheduleFactory; +import org.matsim.testcases.MatsimTestUtils; +import org.matsim.testcases.utils.EventsCollector; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class RailsimIntegrationTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void testMicroSimpleBiDirectionalTrack() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microSimpleBiDirectionalTrack")); + } + + @Test + public void testMesoUniDirectionalVaryingCapacities() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoUniDirectionalVaryingCapacities")); + + // print events of train1 for debugging + List train1events = filterTrainEvents(collector, "train1"); + train1events.forEach(System.out::println); + + // calculation of expected time for train1: acceleration = 0.5, length = 100 + // route: - station: 2.77777m/s + // - link: 50000m, 13.8889m/s + // - station: 200m, 2.777777m/s + // - link: 50000m, 13.8889m/s + // - station: 200m, 2.777777m/s + + double departureTime = 8 * 3600; + double trainLength = 100; + double acceleration = 0.5; + double stationSpeed = 2.7777777777777777; + double stationLength = 200; + double linkSpeed = 13.8889; + double linkLength = 50000; + + double currentTime = departureTime; + assertTrainState(currentTime, 0, 0, 0, stationLength, train1events); + + // train starts in the station, accelerates to station speed and continues until the train has left the station link + assertTrainState(currentTime, 0, stationSpeed, acceleration, 0, train1events); + + double accTime1 = timeToAccelerate(0, stationSpeed, acceleration); + double accDistance1 = distanceTravelled(0, acceleration, accTime1); + currentTime += accTime1; + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, accDistance1, train1events); + + double cruiseTime2 = timeForDistance(trainLength - accDistance1, stationSpeed); + double cruiseDistance2 = distanceTravelled(stationSpeed, 0, cruiseTime2); // should be = trainLength - accDistance1 + currentTime += cruiseTime2; + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, accDistance1 + cruiseDistance2, train1events); + + // train further accelerates to link speed + double accTime3 = timeToAccelerate(stationSpeed, linkSpeed, acceleration); + double accDistance3 = distanceTravelled(stationSpeed, acceleration, accTime3); + currentTime += accTime3; + assertTrainState(currentTime, linkSpeed, linkSpeed, 0, accDistance1 + cruiseDistance2 + accDistance3, train1events); + + // train can cruise with link speed until it needs to decelerate for next station + double decTime5 = timeToAccelerate(linkSpeed, stationSpeed, -acceleration); + double decDistance5 = distanceTravelled(linkSpeed, -acceleration, decTime5); + + double cruiseDistance4 = linkLength - accDistance1 - cruiseDistance2 - accDistance3 - decDistance5; + double cruiseTime4 = timeForDistance(cruiseDistance4, linkSpeed); + currentTime += cruiseTime4; + assertTrainState(currentTime, linkSpeed, linkSpeed, 0, linkLength - decDistance5, train1events); + // start deceleration + assertTrainState(currentTime, linkSpeed, stationSpeed, -acceleration, linkLength - decDistance5, train1events); + currentTime += decTime5; + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, linkLength, train1events); + + // Trains stops at station in the middle, calculated directly + currentTime = 32524.2; + assertTrainState(currentTime, 0, 0, 0, stationLength, train1events); + + double accelAfterStation = timeToAccelerate(0, stationSpeed, acceleration); + double distAfterStation = distanceTravelled(0, acceleration, accelAfterStation); + + currentTime += accelAfterStation; + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, distAfterStation, train1events); + + // train passes station completely + double leaveStation = timeForDistance(trainLength - distAfterStation, stationSpeed); + + currentTime += leaveStation; + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, trainLength, train1events); + + // train can accelerate again to link speed + double accTime7 = timeToAccelerate(stationSpeed, linkSpeed, acceleration); + double accDistance7 = distanceTravelled(stationSpeed, acceleration, accTime7); + currentTime += accTime7; + assertTrainState(currentTime, linkSpeed, linkSpeed, 0, trainLength + accDistance7, train1events); + + // train can cruise with link speed until it needs to decelerate for final station + double decTime9 = timeToAccelerate(linkSpeed, stationSpeed, -acceleration); + double decDistance9 = distanceTravelled(linkSpeed, -acceleration, decTime9); + + double cruiseDistance8 = linkLength - trainLength - accDistance7 - decDistance9; + double cruiseTime8 = timeForDistance(cruiseDistance8, linkSpeed); + + currentTime += cruiseTime8 + decTime9; + + // end of link, entering station + assertTrainState(currentTime, stationSpeed, stationSpeed, 0, linkLength, train1events); + + // train can cruise into station link until it needs to fully brake + double decTime11 = timeToAccelerate(stationSpeed, 0, -acceleration); + double decDistance11 = distanceTravelled(stationSpeed, -acceleration, decTime11); + + double cruiseDistance10 = stationLength - decDistance11; + double cruiseTime10 = timeForDistance(cruiseDistance10, stationSpeed); + + currentTime += cruiseTime10; + assertTrainState(currentTime, stationSpeed, 0, -acceleration, cruiseDistance10, train1events); + + // final train arrival + currentTime += decTime11; + assertTrainState(currentTime, 0, 0, 0, stationLength, train1events); + } + + @Test + public void testMicroTrackOppositeTraffic() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microTrackOppositeTraffic")); + } + + @Test + public void testMicroTrackOppositeTrafficMany() { + // multiple trains, one slow train + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microTrackOppositeTrafficMany")); + } + + @Test + public void testMesoTwoSources() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoTwoSources")); + } + + @Test + public void testMesoTwoSourcesComplex() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoTwoSourcesComplex")); + } + + @Test + public void testScenarioMesoGenfBernAllTrains() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "scenarioMesoGenfBern")); + } + + @Test + public void testScenarioMesoGenfBernOneTrain() { + + // Remove vehicles except the first one + Consumer filter = scenario -> { + + Set> remove = scenario.getTransitVehicles().getVehicles().values().stream() + .filter(v -> !v.getId().toString().equals("Bummelzug_GE_BE_train_0")).map(Vehicle::getId).collect(Collectors.toSet()); + + for (TransitLine line : scenario.getTransitSchedule().getTransitLines().values()) { + + for (TransitRoute route : line.getRoutes().values()) { + + Collection values = new ArrayList<>(route.getDepartures().values()); + for (Departure departure : values) { + + if (remove.contains(departure.getVehicleId())) route.removeDeparture(departure); + } + } + } + + remove.forEach(v -> scenario.getTransitVehicles().removeVehicle(v)); + }; + + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "scenarioMesoGenfBern"), filter); + + } + + @Test + public void testMicroThreeUniDirectionalTracks() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microThreeUniDirectionalTracks")); + } + + @Test + public void testMicroTrainFollowingConstantSpeed() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microTrainFollowingConstantSpeed")); + } + + // This test is similar to testMicroTrainFollowingConstantSpeed but with varying speed levels along the corridor. + @Test + public void testMicroTrainFollowingVaryingSpeed() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microTrainFollowingVaryingSpeed")); + } + + @Test + public void testMicroStationSameLink() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microStationSameLink")); + } + + @Test + public void testMicroStationDifferentLink() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microStationDifferentLink")); + } + + @Test + public void testMicroJunctionCross() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microJunctionCross")); + } + + @Test + public void testMicroJunctionY() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microJunctionY")); + } + + @Test + public void testMesoStationCapacityOne() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoStationCapacityOne")); + } + + @Test + public void testMesoStationCapacityTwo() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoStationCapacityTwo")); + } + + @Test + public void testMesoStations() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "mesoStations")); + } + + @Test + public void testMicroSimpleUniDirectionalTrack() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microSimpleUniDirectionalTrack")); + + for (Event event : collector.getEvents()) { + if (event.getEventType().equals(VehicleArrivesAtFacilityEvent.EVENT_TYPE)) { + + VehicleArrivesAtFacilityEvent vehicleArrivesEvent = (VehicleArrivesAtFacilityEvent) event; + if (vehicleArrivesEvent.getVehicleId().toString().equals("train1") && vehicleArrivesEvent.getFacilityId().toString() + .equals("stop_3-4")) { + + Assert.assertEquals("The arrival time of train1 at stop_3-4 has changed.", 29594., event.getTime(), MatsimTestUtils.EPSILON); + } + } + } + } + + @Test + public void testMicroStationRerouting() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microStationRerouting")); + collector.getEvents().forEach(System.out::println); + // Checks end times + assertTrainState(30854, 0, 0, 0, 400, filterTrainEvents(collector, "train1")); + // 1min later + assertTrainState(30914, 0, 0, 0, 400, filterTrainEvents(collector, "train2")); + // 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")); + } + + @Test + public void testMicroStationReroutingConcurrent() { + Consumer filter = scenario -> { + + TransitScheduleFactory f = scenario.getTransitSchedule().getFactory(); + + for (TransitLine line : scenario.getTransitSchedule().getTransitLines().values()) { + + for (TransitRoute route : line.getRoutes().values()) { + + Collection values = new ArrayList<>(route.getDepartures().values()); + values.forEach(route::removeDeparture); + + // Re-create departure for same time + for (Departure departure : values) { + Departure d = f.createDeparture(departure.getId(), 8 * 3600); + d.setVehicleId(departure.getVehicleId()); + route.addDeparture(d); + } + } + } + }; + + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microStationRerouting"), filter); + } + + @Test + public void testScenarioKelheim() { + + URL base = ExamplesUtils.getTestScenarioURL("kelheim"); + + Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(base, "config.xml")); + config.controler().setLastIteration(0); + config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controler().setCreateGraphs(false); + config.controler().setDumpDataAtEnd(false); + + Scenario scenario = ScenarioUtils.loadScenario(config); + + // Convert all pt to rail + for (Link link : scenario.getNetwork().getLinks().values()) { + if (link.getAllowedModes().contains(TransportMode.car)) link.setAllowedModes(Set.of(TransportMode.car, "ride", "freight")); + + if (link.getAllowedModes().contains(TransportMode.pt)) { + link.setFreespeed(50); + link.setAllowedModes(Set.of("rail")); + } + } + + // Maximum velocity must be configured + for (VehicleType type : scenario.getTransitVehicles().getVehicleTypes().values()) { + type.setMaximumVelocity(30); + type.setLength(100); + } + + SnzActivities.addScoringParams(config); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new RailsimModule()); + controler.configureQSimComponents(components -> new RailsimQSimModule().configure(components)); + + controler.run(); + } + + @Test + public void testScenarioMicroMesoCombination() { + EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "scenarioMicroMesoCombination")); + } + + private EventsCollector runSimulation(File scenarioDir) { + return runSimulation(scenarioDir, null); + } + + private EventsCollector runSimulation(File scenarioDir, Consumer f) { + Config config = ConfigUtils.loadConfig(new File(scenarioDir, "config.xml").toString()); + + config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controler().setDumpDataAtEnd(true); + config.controler().setCreateGraphs(false); + config.controler().setLastIteration(0); + + Scenario scenario = ScenarioUtils.loadScenario(config); + + if (f != null) f.accept(scenario); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new RailsimModule()); + controler.configureQSimComponents(components -> new RailsimQSimModule().configure(components)); + + EventsCollector collector = new EventsCollector(); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + this.addEventHandlerBinding().toInstance(collector); + } + }); + + controler.run(); + + return collector; + } + + private double timeToAccelerate(double v0, double v, double a) { + return (v - v0) / a; + } + + private double distanceTravelled(double v0, double a, double t) { + return 0.5 * a * t * t + v0 * t; + } + + private double timeForDistance(double d, double v) { + return d / v; + } + + private void assertTrainState(double time, double speed, double targetSpeed, double acceleration, double headPosition, List events) { + + RailsimTrainStateEvent prev = null; + for (RailsimTrainStateEvent event : events) { + + if (event.getTime() > Math.ceil(time)) { + Assert.fail( + String.format("No matching event found for time %f, speed %f pos %f, Closest event is%s", time, speed, headPosition, prev)); + } + + // If all assertions are true, returns successfully + try { + Assert.assertEquals(Math.ceil(time), event.getTime(), 1e-7); + Assert.assertEquals(speed, event.getSpeed(), 1e-5); + Assert.assertEquals(targetSpeed, event.getTargetSpeed(), 1e-7); + Assert.assertEquals(acceleration, event.getAcceleration(), 1e-5); + Assert.assertEquals(headPosition, event.getHeadPosition(), 1e-5); + return; + } catch (AssertionError e) { + // Check further events in loop + } + + prev = event; + } + } + + private List filterTrainEvents(EventsCollector collector, String train) { + return collector.getEvents().stream().filter(event -> event instanceof RailsimTrainStateEvent).map(event -> (RailsimTrainStateEvent) event) + .filter(event -> event.getVehicleId().toString().equals(train)).toList(); + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/SnzActivities.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/SnzActivities.java new file mode 100644 index 00000000000..9c21fd13385 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/SnzActivities.java @@ -0,0 +1,91 @@ +/* + * Avoid dependency on vsp contrib, copy from: + * https://github.com/matsim-org/matsim-libs/blob/b2305e5e0f744b357486c8bbab253bb7c38aaad4/contribs/vsp/src/main/java/org/matsim/contrib/vsp/scenario/SnzActivities.java + */ +package ch.sbb.matsim.contrib.railsim.integration; + +import org.matsim.core.config.Config; +import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; + +/** + * Defines available activities and open- and closing times in Snz scenarios at vsp. + */ +public enum SnzActivities { + + home, + other, + visit, + accomp_children, + accomp_other, + + educ_kiga(7, 17), + educ_primary(7, 16), + educ_secondary(7, 17), + educ_tertiary(7, 22), + educ_higher(7, 19), + educ_other(7, 22), + + work(6, 20), + business(8, 20), + errands(8, 20), + + leisure(9, 27), + restaurant(8, 27), + shop_daily(8, 20), + shop_other(8, 20); + + /** + * Start time of an activity in hours, can be -1 if not defined. + */ + private final double start; + + /** + * End time of an activity in hours, can be -1 if not defined. + */ + private final double end; + + SnzActivities(double start, double end) { + this.start = start; + this.end = end; + } + + SnzActivities() { + this.start = -1; + this.end = -1; + } + + + /** + * Apply start and end time to params. + */ + public PlanCalcScoreConfigGroup.ActivityParams apply(PlanCalcScoreConfigGroup.ActivityParams params) { + if (start >= 0) + params = params.setOpeningTime(start * 3600.); + if (end >= 0) + params = params.setClosingTime(end * 3600.); + + return params; + } + + /** + * Add activity params for the scenario config. + */ + public static void addScoringParams(Config config) { + + for (SnzActivities value : SnzActivities.values()) { + for (long ii = 600; ii <= 97200; ii += 600) { + config.planCalcScore().addActivityParams(value.apply(new PlanCalcScoreConfigGroup.ActivityParams(value.name() + "_" + ii).setTypicalDuration(ii))); + } + } + + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("car interaction").setTypicalDuration(60)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("ride interaction").setTypicalDuration(60)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("bike interaction").setTypicalDuration(60)); + + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("other").setTypicalDuration(600 * 3)); + + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("freight_start").setTypicalDuration(60 * 15)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("freight_end").setTypicalDuration(60 * 15)); + + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventAssert.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventAssert.java new file mode 100644 index 00000000000..7614ac66c3f --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventAssert.java @@ -0,0 +1,29 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import org.assertj.core.api.AbstractAssert; +import org.matsim.api.core.v01.events.Event; + +class EventAssert extends AbstractAssert { + protected EventAssert(Event event, Class selfType) { + super(event, selfType); + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java new file mode 100644 index 00000000000..726b229ba80 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/EventsAssert.java @@ -0,0 +1,57 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.assertj.core.api.AbstractCollectionAssert; +import org.assertj.core.api.Condition; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; + +import java.util.Collection; +import java.util.stream.StreamSupport; + +class EventsAssert extends AbstractCollectionAssert, Event, EventAssert> { + protected EventsAssert(Collection events, Class selfType) { + super(events, selfType); + } + + public EventsAssert hasTrainState(String veh, double time, double headPosition, double speed) { + return haveExactly(1, + new Condition<>(event -> + (event instanceof RailsimTrainStateEvent ev) + && ev.getVehicleId().equals(Id.createVehicleId(veh)) + && FuzzyUtils.equals(ev.getTime(), time) + && FuzzyUtils.equals(ev.getHeadPosition(), headPosition) + && FuzzyUtils.equals(ev.getSpeed(), speed), + String.format("event with veh %s time %.0f headPosition: %.2f speed: %.2f", veh, time, headPosition, speed)) + ); + } + + @Override + protected EventAssert toAssert(Event value, String description) { + return null; + } + + @Override + protected EventsAssert newAbstractIterableAssert(Iterable iterable) { + return new EventsAssert(StreamSupport.stream(iterable.spliterator(), false).toList(), EventsAssert.class); + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java new file mode 100644 index 00000000000..e969aa3cb2c --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimCalcTest.java @@ -0,0 +1,150 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import org.assertj.core.data.Offset; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RailsimCalcTest { + + @Test + public void testCalcAndSolveTraveledDist() { + + assertThat(RailsimCalc.calcTraveledDist(5, 2, 0)) + .isEqualTo(10); + + assertThat(RailsimCalc.solveTraveledDist(5, 15, 0)) + .isEqualTo(3); + + double d = RailsimCalc.calcTraveledDist(5, 3, 1); + + assertThat(d) + .isEqualTo(19.5); + + assertThat(RailsimCalc.solveTraveledDist(5, 19.5, 1)) + .isEqualTo(3); + + + } + + @Test + public void testCalcAndSolveTraveledDistNegative() { + + double d = RailsimCalc.calcTraveledDist(5, 5, -1); + + assertThat(d).isEqualTo(12.5); + + assertThat(RailsimCalc.calcTraveledDist(5, 10, -1)) + .isEqualTo(0); + + double t = RailsimCalc.solveTraveledDist(5, 12.5, -1); + + assertThat(t) + .isEqualTo(5); + + } + + @Test + public void testMaxSpeed() { + + double dist = 1000; + + double current = 5; + double f = 0; + + RailsimCalc.SpeedTarget res = RailsimCalc.calcTargetSpeed(dist, 0.5, 0.5, + 5, 30, 0); + + double timeDecel = (res.targetSpeed() - f) / 0.5; + double distDecel = RailsimCalc.calcTraveledDist(res.targetSpeed(), timeDecel, -0.5); + + double timeAccel = (res.targetSpeed() - current) / 0.5; + double distAccel = RailsimCalc.calcTraveledDist(5, timeAccel, 0.5); + + assertThat(distDecel + distAccel) + .isCloseTo(dist, Offset.offset(0.001)); + + } + + @Test + public void testCalcTargetDecel() { + + double d = RailsimCalc.calcTargetDecel(1000, 0, 10); + + assertThat(RailsimCalc.calcTraveledDist(10, -10 / d, d)) + .isCloseTo(1000, Offset.offset(0.001)); + + d = RailsimCalc.calcTargetDecel(1000, 5, 10); + + assertThat(RailsimCalc.calcTraveledDist(10, -5 / d, d)) + .isCloseTo(1000, Offset.offset(0.001)); + + } + + @Test + public void testCalcTargetSpeed() { + + RailsimCalc.SpeedTarget target = RailsimCalc.calcTargetSpeed(100, 0.5, 0.5, 0, 23, 0); + + + double t = RailsimCalc.solveTraveledDist(0, 50, 0.5); + + // Train can not reach target speed and accelerates until 50m + assertThat(target.decelDist()) + .isCloseTo(50, Offset.offset(0.0001)); + + assertThat(RailsimCalc.calcTraveledDist(target.targetSpeed(), t, -0.5)) + .isCloseTo(50, Offset.offset(0.0001)); + + + target = RailsimCalc.calcTargetSpeed(200, 0.5, 0.5, 13, 13, 0); + + assertThat(target.targetSpeed()) + .isCloseTo(13, Offset.offset(0.0001)); + + // assume travelling at max speed for 31m + assertThat(target.decelDist()) + .isCloseTo(31, Offset.offset(0.0001)); + + t = RailsimCalc.solveTraveledDist(13, 200 - 31, -0.5); + + // speed is 0 after decelerating rest of the distance + assertThat(13 + t * -0.5) + .isCloseTo(0, Offset.offset(0.001)); + + } + + @Test + public void testCalcTargetSpeedForStop() { + + double v = RailsimCalc.calcTargetSpeedForStop(1000, 0.5, 0.5, 0); + + double accelTime = v / 0.5; + + double d1 = RailsimCalc.calcTraveledDist(0, accelTime, 0.5); + double d2 = RailsimCalc.calcTraveledDist(v, accelTime, -0.5); + + assertThat(d1 + d2) + .isCloseTo(1000, Offset.offset(0.0001)); + + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java new file mode 100644 index 00000000000..a993b46fd49 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimEngineTest.java @@ -0,0 +1,274 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.RailsimUtils; +import ch.sbb.matsim.contrib.railsim.config.RailsimConfigGroup; +import ch.sbb.matsim.contrib.railsim.qsimengine.disposition.SimpleDisposition; +import ch.sbb.matsim.contrib.railsim.qsimengine.router.TrainRouter; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.testcases.MatsimTestUtils; + +import javax.annotation.Nullable; +import java.io.File; +import java.util.function.Consumer; + +public class RailsimEngineTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + private EventsManager eventsManager; + private RailsimTestUtils.EventCollector collector; + + @Before + public void setUp() { + eventsManager = EventsUtils.createEventsManager(); + collector = new RailsimTestUtils.EventCollector(); + + eventsManager.addHandler(collector); + eventsManager.initProcessing(); + } + + private RailsimTestUtils.Holder getTestEngine(String network, @Nullable Consumer f) { + Network net = NetworkUtils.readNetwork(new File(utils.getPackageInputDirectory(), network).toString()); + RailsimConfigGroup config = new RailsimConfigGroup(); + + collector.clear(); + + if (f != null) { + for (Link link : net.getLinks().values()) { + f.accept(link); + } + } + RailResourceManager res = new RailResourceManager(eventsManager, config, net); + TrainRouter router = new TrainRouter(net, res); + + return new RailsimTestUtils.Holder(new RailsimEngine(eventsManager, config, res, new SimpleDisposition(res, router)), net); + } + + private RailsimTestUtils.Holder getTestEngine(String network) { + return getTestEngine(network, null); + } + + @Test + public void testSimple() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroBi.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "train", 0, "l1-2", "l5-6"); + + test.doSimStepUntil(400); + + RailsimTestUtils.assertThat(collector) + .hasSizeGreaterThan(5) + .hasTrainState("train", 144, 0, 44) + .hasTrainState("train", 234, 2000, 0); + + test = getTestEngine("networkMicroBi.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "train", 0, "l1-2", "l5-6"); + + test.doStateUpdatesUntil(400, 1); + + RailsimTestUtils.assertThat(collector) + .hasSizeGreaterThan(5) + .hasTrainState("train", 144, 0, 44) + .hasTrainState("train", 234, 2000, 0); + + } + + @Test + public void testCongested() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroBi.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 0, "l1-2", "l5-6"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 60, "l1-2", "l5-6"); + + test.doSimStepUntil(600); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("cargo", 359, 2000, 0) + .hasTrainState("regio", 474, 2000, 0); + + } + + @Test + public void testCongestedWithHeadway() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroBi.xml", l -> RailsimUtils.setMinimumHeadwayTime(l, 60)); + + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo", 0, "l1-2", "l5-6"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 60, "l1-2", "l5-6"); + + test.doSimStepUntil(600); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("cargo", 359, 2000, 0) + .hasTrainState("regio", 485, 2000, 0); + + } + + + @Test + public void testOpposite() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroBi.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1", 0, "l1-2", "l7-8"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "l8-7", "l2-1"); + + test.doSimStepUntil(600); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1", 293, 600, 0) + .hasTrainState("regio2", 358, 1000, 0); + + + test = getTestEngine("networkMicroBi.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1", 0, "l1-2", "l7-8"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "l8-7", "l2-1"); + + test.doStateUpdatesUntil(600, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1", 293, 600, 0) + .hasTrainState("regio2", 358, 1000, 0); + + } + + @Test + public void testVaryingSpeedOne() { + + RailsimTestUtils.Holder test = getTestEngine("networkMesoUni.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "t1_IN-t1_OUT", "t3_IN-t3_OUT"); + + test.doSimStepUntil(10000); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 7599, 0, 2.7777777) + .hasTrainState("regio", 7674, 200, 0); + + test = getTestEngine("networkMesoUni.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio", 0, "t1_IN-t1_OUT", "t3_IN-t3_OUT"); + + test.doStateUpdatesUntil(10000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio", 7599, 0, 2.7777777) + .hasTrainState("regio", 7674, 200, 0); + + } + + @Test + public void testVaryingSpeedMany() { + + RailsimTestUtils.Holder test = getTestEngine("networkMesoUni.xml"); + + for (int i = 0; i < 10; i++) { + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio" + i, 60 * i, "t1_IN-t1_OUT", "t3_IN-t3_OUT"); + } + + test.doSimStepUntil(30000); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio0", 7599, 0, 2.7777777) + .hasTrainState("regio0", 7674, 200, 0) + .hasTrainState("regio1", 7734, 200, 0) + .hasTrainState("regio9", 23107, 200, 0); + + test = getTestEngine("networkMesoUni.xml"); + + for (int i = 0; i < 10; i++) { + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio" + i, 60 * i, "t1_IN-t1_OUT", "t3_IN-t3_OUT"); + } + + test.doStateUpdatesUntil(30000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio0", 7599, 0, 2.7777777) + .hasTrainState("regio0", 7674, 200, 0) + .hasTrainState("regio1", 7734, 200, 0) + .hasTrainState("regio9", 23107, 200, 0); + + } + + @Test + public void testTrainFollowing() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroUni.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "1-2", "20-21"); + + test.doSimStepUntil(5000); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1", 1138, 1000, 0) + .hasTrainState("regio2", 1517, 1000, 0); + + test = getTestEngine("networkMicroUni.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio1", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Regio, "regio2", 0, "1-2", "20-21"); + + test.doStateUpdatesUntil(5000, 1); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("regio1", 1138, 1000, 0) + .hasTrainState("regio2", 1517, 1000, 0); + } + + @Test + public void testMicroTrainFollowingVaryingSpeed() { + + RailsimTestUtils.Holder test = getTestEngine("networkMicroVaryingSpeed.xml"); + + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo1", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo2", 15, "1-2", "20-21"); + + test.doSimStepUntil(3000); +// test.debugFiles(collector, "microVarying"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("cargo1", 1278, 1000, 0) + .hasTrainState("cargo2", 2033, 1000, 0); + + // Same test with state updates + test = getTestEngine("networkMicroVaryingSpeed.xml"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo1", 0, "1-2", "20-21"); + RailsimTestUtils.createDeparture(test, TestVehicle.Cargo, "cargo2", 15, "1-2", "20-21"); + test.doStateUpdatesUntil(3000, 1); +// test.debugFiles(collector, "microVarying_detailed"); + + RailsimTestUtils.assertThat(collector) + .hasTrainState("cargo1", 1278, 1000, 0) + .hasTrainState("cargo2", 2033, 1000, 0); + + + } +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java new file mode 100644 index 00000000000..63f5249cef6 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/RailsimTestUtils.java @@ -0,0 +1,175 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +import ch.sbb.matsim.contrib.railsim.analysis.RailsimCsvWriter; +import ch.sbb.matsim.contrib.railsim.events.RailsimLinkStateChangeEvent; +import ch.sbb.matsim.contrib.railsim.events.RailsimTrainStateEvent; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.events.algorithms.EventWriterXML; +import org.matsim.core.events.handler.BasicEventHandler; +import org.matsim.core.mobsim.framework.MobsimDriverAgent; +import org.matsim.core.mobsim.qsim.interfaces.MobsimVehicle; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.population.routes.RouteUtils; +import org.matsim.core.router.DijkstraFactory; +import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.matsim.vehicles.MatsimVehicleReader; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.Vehicles; +import org.mockito.Answers; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * Helper class for test cases. + */ +public class RailsimTestUtils { + + static Map vehicles = new EnumMap<>(TestVehicle.class); + + static { + + Vehicles veh = VehicleUtils.createVehiclesContainer(); + + MatsimVehicleReader reader = new MatsimVehicleReader(veh); + reader.readURL(RailsimTestUtils.class.getResource("/trainVehicleTypes.xml")); + + vehicles.put(TestVehicle.Sprinter, veh.getVehicleTypes().get(Id.create("Sprinter", VehicleType.class))); + vehicles.put(TestVehicle.Express, veh.getVehicleTypes().get(Id.create("Express", VehicleType.class))); + vehicles.put(TestVehicle.Regio, veh.getVehicleTypes().get(Id.create("Regio", VehicleType.class))); + vehicles.put(TestVehicle.Cargo, veh.getVehicleTypes().get(Id.create("Cargo", VehicleType.class))); + } + + /** + * Create a departure within the engine. Route will be determined automatically. + */ + public static void createDeparture(Holder test, TestVehicle type, String veh, double time, String from, String to) { + + DijkstraFactory f = new DijkstraFactory(); + LeastCostPathCalculator lcp = f.createPathCalculator(test.network(), new OnlyTimeDependentTravelDisutility(new FreeSpeedTravelTime()), + new FreeSpeedTravelTime()); + + Link fromLink = test.network.getLinks().get(Id.createLinkId(from)); + Link toLink = test.network.getLinks().get(Id.createLinkId(to)); + + LeastCostPathCalculator.Path path = lcp.calcLeastCostPath(fromLink.getFromNode(), toLink.getToNode(), 0, null, null); + NetworkRoute route = RouteUtils.createNetworkRoute(path.links.stream().map(Link::getId).toList()); + + System.out.println("Creating departure with route" + route); + + // Setup mocks for driver and vehicle + Id vehicleId = Id.createVehicleId(veh); + + MobsimDriverAgent driver = Mockito.mock(MobsimDriverAgent.class, Answers.RETURNS_MOCKS); + MobsimVehicle mobVeh = Mockito.mock(MobsimVehicle.class, Answers.RETURNS_MOCKS); + + Vehicle vehicle = VehicleUtils.createVehicle(vehicleId, vehicles.get(type)); + + Mockito.when(mobVeh.getVehicle()).thenReturn(vehicle); + Mockito.when(mobVeh.getId()).thenReturn(vehicleId); + Mockito.when(driver.getVehicle()).thenReturn(mobVeh); + + test.engine.handleDeparture(time, driver, route.getStartLinkId(), route); + } + + /** + * Collect events during testing + */ + public static class EventCollector implements BasicEventHandler { + + List events = new ArrayList<>(); + + @Override + public void handleEvent(Event event) { + System.out.println(event); + events.add(event); + } + + public void clear() { + events.clear(); + } + + public void dump(String out) { + EventWriterXML writer = new EventWriterXML(out); + events.forEach(writer::handleEvent); + writer.closeFile(); + } + + } + + public record Holder(RailsimEngine engine, Network network) { + + /** + * Step at one second until time is reached. + */ + public void doSimStepUntil(double time) { + for (double t = 0; t < time; t++) { + engine().doSimStep(t); + } + } + + /** + * Call state updates until time is reached with fixed interval. + */ + public void doStateUpdatesUntil(double time, double interval) { + + for (double t = 0; t < time; t += interval) { + engine().updateAllStates(t); + } + } + + public void debugFiles(EventCollector collector, String out) { + RailsimCsvWriter.writeTrainStatesCsv( + collector.events.stream().filter(ev -> ev instanceof RailsimTrainStateEvent) + .map(ev -> (RailsimTrainStateEvent) ev) + .toList(), + network, + out + "_trainStates.csv" + ); + + RailsimCsvWriter.writeLinkStatesCsv( + collector.events.stream().filter(ev -> ev instanceof RailsimLinkStateChangeEvent) + .map(ev -> (RailsimLinkStateChangeEvent) ev) + .toList(), + out + "_linkStates.csv" + ); + } + } + + /** + * Helper method for event assertions. + */ + static EventsAssert assertThat(EventCollector events) { + return new EventsAssert(events.events, EventsAssert.class); + } + +} diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/TestVehicle.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/TestVehicle.java new file mode 100644 index 00000000000..0094ab53c15 --- /dev/null +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/qsimengine/TestVehicle.java @@ -0,0 +1,30 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * 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 * + * * + * *********************************************************************** */ + +package ch.sbb.matsim.contrib.railsim.qsimengine; + +/** + * Vehicle types that are defined for the test cases. + */ +public enum TestVehicle { + Sprinter, + Express, + Regio, + Cargo +} diff --git a/contribs/railsim/src/test/resources/trainVehicleTypes.xml b/contribs/railsim/src/test/resources/trainVehicleTypes.xml new file mode 100644 index 00000000000..9e70862fe68 --- /dev/null +++ b/contribs/railsim/src/test/resources/trainVehicleTypes.xml @@ -0,0 +1,50 @@ + + + + + + + 0.7 + 0.7 + + + + + + + + + + + 0.7 + 0.7 + + + + + + + + + + 0.5 + 0.5 + + + + + + + + + + 0.2 + 0.2 + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/trainNetwork.xml new file mode 100644 index 00000000000..f27451d8c21 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/trainNetwork.xml @@ -0,0 +1,73 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 999 + + + + + + + 999 + + + + + 999 + + + + + + + 1 + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitSchedule.xml new file mode 100644 index 00000000000..4926a4cecb6 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitSchedule.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitVehicles.xml new file mode 100644 index 00000000000..de84fed864e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityOne/transitVehicles.xml @@ -0,0 +1,32 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/trainNetwork.xml new file mode 100644 index 00000000000..3d91ff4b191 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/trainNetwork.xml @@ -0,0 +1,73 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 999 + + + + + + + 999 + + + + + 999 + + + + + + + 2 + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitSchedule.xml new file mode 100644 index 00000000000..4926a4cecb6 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitSchedule.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitVehicles.xml new file mode 100644 index 00000000000..de84fed864e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStationCapacityTwo/transitVehicles.xml @@ -0,0 +1,32 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/trainNetwork.xml new file mode 100644 index 00000000000..c278432ef21 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/trainNetwork.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + + 2 + + + + + 2 + + + + + 2 + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitSchedule.xml new file mode 100644 index 00000000000..cac9c1a1ebf --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitSchedule.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitVehicles.xml new file mode 100644 index 00000000000..1002d65f560 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoStations/transitVehicles.xml @@ -0,0 +1,28 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/trainNetwork.xml new file mode 100644 index 00000000000..49ed9222e98 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/trainNetwork.xml @@ -0,0 +1,124 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 999 + + + + + + 1 + + + + + 999 + + + + + + 999 + + + + + 999 + + + + + + 999 + + + + + 999 + + + + + + 999 + + + + + 999 + + + + + + bottleneck + 1 + + + + + bottleneck + 1 + + + + + + 999 + + + + + 999 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitSchedule.xml new file mode 100644 index 00000000000..92e5e3b25ae --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitSchedule.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitVehicles.xml new file mode 100644 index 00000000000..a74a653efb2 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSources/transitVehicles.xml @@ -0,0 +1,36 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/trainNetwork.xml new file mode 100644 index 00000000000..c68b481d038 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/trainNetwork.xml @@ -0,0 +1,82 @@ + + + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitSchedule.xml new file mode 100644 index 00000000000..09d5c02e458 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitSchedule.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitVehicles.xml new file mode 100644 index 00000000000..76d6e266237 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoTwoSourcesComplex/transitVehicles.xml @@ -0,0 +1,36 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/config.xml new file mode 100644 index 00000000000..fd5d50bb1b6 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/config.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/trainNetwork.xml new file mode 100644 index 00000000000..db1e86a0c4f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/trainNetwork.xml @@ -0,0 +1,57 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 2 + + + + + 999 + + + + + 5 + + + + + 999 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitSchedule.xml new file mode 100644 index 00000000000..3db3029cd5d --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitSchedule.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitVehicles.xml new file mode 100644 index 00000000000..262b655b7e3 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/mesoUniDirectionalVaryingCapacities/transitVehicles.xml @@ -0,0 +1,36 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/config.xml new file mode 100644 index 00000000000..b6491cf5f34 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/config.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/trainNetwork.xml new file mode 100644 index 00000000000..2f3d6815f1e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/trainNetwork.xml @@ -0,0 +1,106 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + c1 + + + + + c1 + + + + + + + + + + + + + c1 + + + + + c1 + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitSchedule.xml new file mode 100644 index 00000000000..c9add0e102c --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitSchedule.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitVehicles.xml new file mode 100644 index 00000000000..8a32c984f1a --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionCross/transitVehicles.xml @@ -0,0 +1,28 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/trainNetwork.xml new file mode 100644 index 00000000000..0bfe206efc8 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/trainNetwork.xml @@ -0,0 +1,94 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 1 + + + + + 1 + + + + + 999 + + + + + 1 + + + + + 1 + + + + + 3 + + + + + 1 + + + + + 999 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitSchedule.xml new file mode 100644 index 00000000000..8642858618f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitSchedule.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitVehicles.xml new file mode 100644 index 00000000000..5c4aa7acf39 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microJunctionY/transitVehicles.xml @@ -0,0 +1,48 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/config.xml new file mode 100644 index 00000000000..53f168e5c17 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/config.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/trainNetwork.xml new file mode 100644 index 00000000000..193f08d66a3 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/trainNetwork.xml @@ -0,0 +1,78 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + l45 + + + + + l45 + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitSchedule.xml new file mode 100644 index 00000000000..748f927f15f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitSchedule.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitVehicles.xml new file mode 100644 index 00000000000..20ebec4e74b --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleBiDirectionalTrack/transitVehicles.xml @@ -0,0 +1,30 @@ + + + + + + + 5.0 + serial + 5.0 + 0.5 + 0.5 + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/config.xml new file mode 100644 index 00000000000..be63f574384 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/config.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/trainNetwork.xml new file mode 100644 index 00000000000..52ae1533206 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/trainNetwork.xml @@ -0,0 +1,50 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitSchedule.xml new file mode 100644 index 00000000000..88106a724e8 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitSchedule.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitVehicles.xml new file mode 100644 index 00000000000..00506a3afee --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microSimpleUniDirectionalTrack/transitVehicles.xml @@ -0,0 +1,26 @@ + + + + + + + 0.2 + 0.3 + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/trainNetwork.xml new file mode 100644 index 00000000000..aed5d10a9f7 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/trainNetwork.xml @@ -0,0 +1,425 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + r1 + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + r1 + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitSchedule.xml new file mode 100644 index 00000000000..113ee046b2c --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitSchedule.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitVehicles.xml new file mode 100644 index 00000000000..f942a748536 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationDifferentLink/transitVehicles.xml @@ -0,0 +1,27 @@ + + + + + + + 0.2 + 0.2 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml new file mode 100644 index 00000000000..1ebe818b2c5 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/trainNetwork.xml @@ -0,0 +1,108 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + + 2 + true + + + + + + + + + + + + + + 2 + true + + + + + 2 + + + + + + 2 + true + + + + + 2 + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitSchedule.xml new file mode 100644 index 00000000000..5e5f412f9fb --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitSchedule.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitVehicles.xml new file mode 100644 index 00000000000..e0e618e9b8e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationRerouting/transitVehicles.xml @@ -0,0 +1,30 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/trainNetwork.xml new file mode 100644 index 00000000000..3119cbdaf75 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/trainNetwork.xml @@ -0,0 +1,423 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitSchedule.xml new file mode 100644 index 00000000000..44cb586edf2 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitSchedule.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitVehicles.xml new file mode 100644 index 00000000000..5bdf728353f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationSameLink/transitVehicles.xml @@ -0,0 +1,28 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/trainNetwork.xml new file mode 100644 index 00000000000..5721ac18c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/trainNetwork.xml @@ -0,0 +1,188 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + 5 + + + + + 1 + + + + + 1 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitSchedule.xml new file mode 100644 index 00000000000..0d6c5472cca --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitSchedule.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitVehicles.xml new file mode 100644 index 00000000000..7e98d72953d --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microThreeUniDirectionalTracks/transitVehicles.xml @@ -0,0 +1,28 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/trainNetwork.xml new file mode 100644 index 00000000000..46f01336767 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/trainNetwork.xml @@ -0,0 +1,79 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 5 + + + + + + + + + t2_A-t2_B + 1 + + + + + t2_A-t2_B + 1 + + + + + + + + + 5 + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitSchedule.xml new file mode 100644 index 00000000000..cc3851daffe --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitSchedule.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitVehicles.xml new file mode 100644 index 00000000000..7d91bf38fee --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTraffic/transitVehicles.xml @@ -0,0 +1,36 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/trainNetwork.xml new file mode 100644 index 00000000000..46f01336767 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/trainNetwork.xml @@ -0,0 +1,79 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 5 + + + + + + + + + t2_A-t2_B + 1 + + + + + t2_A-t2_B + 1 + + + + + + + + + 5 + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitSchedule.xml new file mode 100644 index 00000000000..5d12af79465 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitSchedule.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitVehicles.xml new file mode 100644 index 00000000000..b359674db22 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrackOppositeTrafficMany/transitVehicles.xml @@ -0,0 +1,54 @@ + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + 5.0 + serial + 5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/trainNetwork.xml new file mode 100644 index 00000000000..840d9311577 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/trainNetwork.xml @@ -0,0 +1,169 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitSchedule.xml new file mode 100644 index 00000000000..673009a1ed0 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitSchedule.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitVehicles.xml new file mode 100644 index 00000000000..893a13e980f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingConstantSpeed/transitVehicles.xml @@ -0,0 +1,46 @@ + + + + + + + 0.4 + 0.4 + + + + + + + + + + + + + + + + + + 0.1 + 0.1 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/trainNetwork.xml new file mode 100644 index 00000000000..9094a12079c --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/trainNetwork.xml @@ -0,0 +1,170 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitSchedule.xml new file mode 100644 index 00000000000..673009a1ed0 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitSchedule.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitVehicles.xml new file mode 100644 index 00000000000..893a13e980f --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microTrainFollowingVaryingSpeed/transitVehicles.xml @@ -0,0 +1,46 @@ + + + + + + + 0.4 + 0.4 + + + + + + + + + + + + + + + + + + 0.1 + 0.1 + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/config.xml new file mode 100644 index 00000000000..c7ac9dffc64 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/config.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/trainNetwork.xml.gz b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/trainNetwork.xml.gz new file mode 100644 index 00000000000..17ecdf4768f Binary files /dev/null and b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/trainNetwork.xml.gz differ diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitSchedule.xml.gz b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitSchedule.xml.gz new file mode 100644 index 00000000000..5989dd29a2d Binary files /dev/null and b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitSchedule.xml.gz differ diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitVehicles.xml.gz b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitVehicles.xml.gz new file mode 100644 index 00000000000..25ad72a9773 Binary files /dev/null and b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMesoGenfBern/transitVehicles.xml.gz differ diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/config.xml new file mode 100644 index 00000000000..245af478051 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/config.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/trainNetwork.xml new file mode 100644 index 00000000000..0f47de2b222 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/trainNetwork.xml @@ -0,0 +1,327 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + 12 + true + + + + + a + 12 + true + + + + + + + ab1 + + + + + ab1 + + + + + + ab2_1 + + + + + ab2_1 + + + + + + ab2_2 + + + + + ab2_2 + + + + + + ab2_3 + + + + + ab2_3 + + + + + + + b + 3 + true + + + + + b + 3 + true + + + + + + + bc + 2 + true + + + + + bc + 2 + true + + + + + + + c1 + + + + + c1 + + + + + + c1_c2_1 + + + + + c1_c2_1 + + + + + + c2 + + + + + c2 + + + + + + c1_c2_2 + + + + + c1_c2_2 + + + + + + c2_c3_1 + + + + + c2_c3_1 + + + + + + c3 + + + + + c3 + + + + + + c2_c3_2 + + + + + c2_c3_2 + + + + + + + cd + 2 + true + + + + + cd + 2 + true + + + + + + + d + 6 + + + + + d + 6 + + + + + + + ce_1 + 2 + true + + + + + ce_1 + 2 + true + + + + + ce_2 + 2 + + + + + ce_2 + 2 + + + + + + + e + 6 + true + + + + + e + 6 + true + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitSchedule.xml new file mode 100644 index 00000000000..1f4d16bb781 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitSchedule.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitVehicles.xml new file mode 100644 index 00000000000..c6faae55120 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/scenarioMicroMesoCombination/transitVehicles.xml @@ -0,0 +1,54 @@ + + + + + + + 0.3 + 0.3 + + + + + + + + + + + 0.4 + 0.4 + + + + + + + + + + + 0.1 + 0.1 + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml new file mode 100644 index 00000000000..e1296d25fea --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMesoUni.xml @@ -0,0 +1,58 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 2 + + + + + 999 + + + + + 5 + + + + + 999 + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroBi.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroBi.xml new file mode 100644 index 00000000000..4596bfb98cd --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroBi.xml @@ -0,0 +1,78 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + l45 + + + + + l45 + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroUni.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroUni.xml new file mode 100644 index 00000000000..d7d57e30f32 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroUni.xml @@ -0,0 +1,173 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroVaryingSpeed.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroVaryingSpeed.xml new file mode 100644 index 00000000000..9094a12079c --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/qsimengine/networkMicroVaryingSpeed.xml @@ -0,0 +1,170 @@ + + + + + + Atlantis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 5 + + + + + + + diff --git a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitDriverAgentFactory.java b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitDriverAgentFactory.java index e5c7ac247af..665e6f50e0c 100644 --- a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitDriverAgentFactory.java +++ b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitDriverAgentFactory.java @@ -18,23 +18,19 @@ */ public class SBBTransitDriverAgentFactory implements TransitDriverAgentFactory { - private final InternalInterface internalInterface; - private final TransitStopAgentTracker transitStopAgentTracker; private final Set deterministicModes; - SBBTransitDriverAgentFactory(InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker, Set deterministicModes) { - this.internalInterface = internalInterface; - this.transitStopAgentTracker = transitStopAgentTracker; + SBBTransitDriverAgentFactory(Set deterministicModes) { this.deterministicModes = deterministicModes; } @Override - public AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf) { + public AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf, InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker) { String mode = umlauf.getUmlaufStuecke().get(0).getRoute().getTransportMode(); if (this.deterministicModes.contains(mode)) { - return new SBBTransitDriverAgent(umlauf, mode, this.transitStopAgentTracker, this.internalInterface); + return new SBBTransitDriverAgent(umlauf, mode,transitStopAgentTracker, internalInterface); } - return new TransitDriverAgentImpl(umlauf, TransportMode.car, this.transitStopAgentTracker, this.internalInterface); + return new TransitDriverAgentImpl(umlauf, TransportMode.car, transitStopAgentTracker, internalInterface); } } diff --git a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngine.java b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngine.java index 96efff76eaf..643dfef1950 100644 --- a/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngine.java +++ b/contribs/sbb-extensions/src/main/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngine.java @@ -83,14 +83,14 @@ public class SBBTransitQSimEngine extends TransitQSimEngine /*implements Departu private boolean createLinkEvents = false; @Inject - public SBBTransitQSimEngine(QSim qSim, ReplanningContext context) { - super(qSim, new SimpleTransitStopHandlerFactory(), new ReconstructingUmlaufBuilder(qSim.getScenario())); + public SBBTransitQSimEngine(QSim qSim, ReplanningContext context, TransitStopAgentTracker agentTracker, TransitDriverAgentFactory networkDriverFactory) { + super(qSim, new SimpleTransitStopHandlerFactory(), new ReconstructingUmlaufBuilder(qSim.getScenario()), agentTracker, networkDriverFactory); this.qSim = qSim; this.context = context; this.config = ConfigUtils.addOrGetModule(qSim.getScenario().getConfig(), SBBTransitConfigGroup.GROUP_NAME, SBBTransitConfigGroup.class); this.ptConfig = qSim.getScenario().getConfig().transit(); this.schedule = qSim.getScenario().getTransitSchedule(); - this.agentTracker = new TransitStopAgentTracker(qSim.getEventsManager()); + this.agentTracker = agentTracker; if (this.config.getCreateLinkEventsInterval() > 0) { this.linkEventQueue = new PriorityQueue<>(); this.linksCache = new ConcurrentHashMap<>(); @@ -116,8 +116,8 @@ public void setTransitStopHandlerFactory(final TransitStopHandlerFactory stopHan @Override public void setInternalInterface(InternalInterface internalInterface) { this.internalInterface = internalInterface; - this.deterministicDriverFactory = new SBBTransitDriverAgentFactory(internalInterface, this.agentTracker, this.config.getDeterministicServiceModes()); - this.networkDriverFactory = new DefaultTransitDriverAgentFactory(internalInterface, this.agentTracker); + this.deterministicDriverFactory = new SBBTransitDriverAgentFactory(this.config.getDeterministicServiceModes()); + this.networkDriverFactory = new DefaultTransitDriverAgentFactory(); } @Override @@ -230,9 +230,9 @@ private void createVehiclesAndDrivers() { private void createAndScheduleDriver(Vehicle veh, Umlauf umlauf, boolean isDeterministic) { AbstractTransitDriverAgent driver; if (isDeterministic) { - driver = this.deterministicDriverFactory.createTransitDriver(umlauf); + driver = this.deterministicDriverFactory.createTransitDriver(umlauf, internalInterface, agentTracker); } else { - driver = this.networkDriverFactory.createTransitDriver(umlauf); + driver = this.networkDriverFactory.createTransitDriver(umlauf, internalInterface, agentTracker); } TransitQVehicle qVeh = new TransitQVehicle(veh); qVeh.setDriver(driver); diff --git a/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngineTest.java b/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngineTest.java index 1fb69377aee..a3c596481a3 100644 --- a/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngineTest.java +++ b/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/SBBTransitQSimEngineTest.java @@ -34,6 +34,8 @@ import org.matsim.core.mobsim.qsim.PopulationModule; import org.matsim.core.mobsim.qsim.QSim; import org.matsim.core.mobsim.qsim.QSimBuilder; +import org.matsim.core.mobsim.qsim.pt.DefaultTransitDriverAgentFactory; +import org.matsim.core.mobsim.qsim.pt.TransitStopAgentTracker; import org.matsim.core.utils.collections.CollectionUtils; import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitRouteStop; @@ -58,7 +60,7 @@ public void testDriver() { EventsManager eventsManager = EventsUtils.createEventsManager(f.config); QSim qSim = new QSimBuilder(f.config).useDefaults().build(f.scenario, eventsManager); - SBBTransitQSimEngine trEngine = new SBBTransitQSimEngine(qSim, null); + SBBTransitQSimEngine trEngine = new SBBTransitQSimEngine(qSim, null, new TransitStopAgentTracker(eventsManager), new DefaultTransitDriverAgentFactory()); qSim.addMobsimEngine(trEngine); trEngine.insertAgentsIntoMobsim(); diff --git a/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/TestQSimModule.java b/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/TestQSimModule.java index f57e5e54ab6..14d2c36a06a 100644 --- a/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/TestQSimModule.java +++ b/contribs/sbb-extensions/src/test/java/ch/sbb/matsim/mobsim/qsim/pt/TestQSimModule.java @@ -4,10 +4,13 @@ package ch.sbb.matsim.mobsim.qsim.pt; +import com.google.inject.Provides; +import com.google.inject.Singleton; import org.matsim.core.config.Config; +import org.matsim.core.events.EventsUtils; import org.matsim.core.mobsim.qsim.AbstractQSimModule; -import org.matsim.core.mobsim.qsim.pt.ComplexTransitStopHandlerFactory; -import org.matsim.core.mobsim.qsim.pt.TransitStopHandlerFactory; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.pt.*; import org.matsim.core.replanning.ReplanningContext; /** @@ -29,8 +32,15 @@ public TestQSimModule(Config config) { protected void configureQSim() { bind(ReplanningContext.class).toInstance(context); bind(TransitStopHandlerFactory.class).to(ComplexTransitStopHandlerFactory.class); + bind(TransitDriverAgentFactory.class).to(DefaultTransitDriverAgentFactory.class); } + @Provides + @Singleton + public TransitStopAgentTracker transitStopAgentTracker(QSim qSim) { + return new TransitStopAgentTracker(qSim.getEventsManager()); + } + public static final class DummyReplanningContext implements ReplanningContext { private int iteration = 0; diff --git a/contribs/vsp/pom.xml b/contribs/vsp/pom.xml index 3db82269372..f7d6c310905 100644 --- a/contribs/vsp/pom.xml +++ b/contribs/vsp/pom.xml @@ -174,7 +174,7 @@ org.openjfx javafx-graphics - 20.0.2 + 21 com.graphhopper diff --git a/examples/scenarios/bicycle_example/network_cobblestone.xml b/examples/scenarios/bicycle_example/network_cobblestone.xml new file mode 100644 index 00000000000..c45ce2072e1 --- /dev/null +++ b/examples/scenarios/bicycle_example/network_cobblestone.xml @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + cobblestone + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_gradient.xml b/examples/scenarios/bicycle_example/network_gradient.xml new file mode 100644 index 00000000000..0ba9f200eb9 --- /dev/null +++ b/examples/scenarios/bicycle_example/network_gradient.xml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_gradient_lane.xml b/examples/scenarios/bicycle_example/network_gradient_lane.xml new file mode 100644 index 00000000000..07bb4138066 --- /dev/null +++ b/examples/scenarios/bicycle_example/network_gradient_lane.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_infrastructure-speed-factor.xml b/examples/scenarios/bicycle_example/network_infrastructure-speed-factor.xml new file mode 100644 index 00000000000..e696324228f --- /dev/null +++ b/examples/scenarios/bicycle_example/network_infrastructure-speed-factor.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 0.1 + + + + + primary + 1.0 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 1.0 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + primary + 0.1 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_lane.xml b/examples/scenarios/bicycle_example/network_lane.xml new file mode 100644 index 00000000000..a08df8b34a4 --- /dev/null +++ b/examples/scenarios/bicycle_example/network_lane.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + lane + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_normal.xml b/examples/scenarios/bicycle_example/network_normal.xml new file mode 100644 index 00000000000..061c09fd30a --- /dev/null +++ b/examples/scenarios/bicycle_example/network_normal.xml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/network_pedestrian.xml b/examples/scenarios/bicycle_example/network_pedestrian.xml new file mode 100644 index 00000000000..4973f40fa45 --- /dev/null +++ b/examples/scenarios/bicycle_example/network_pedestrian.xml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + pedestrian + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + primary + 1.0 + + + + + + + \ No newline at end of file diff --git a/examples/scenarios/bicycle_example/population_1200.xml b/examples/scenarios/bicycle_example/population_1200.xml new file mode 100644 index 00000000000..d9c5928bf71 --- /dev/null +++ b/examples/scenarios/bicycle_example/population_1200.xml @@ -0,0 +1,21610 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/scenarios/bicycle_example/population_3.xml b/examples/scenarios/bicycle_example/population_3.xml new file mode 100644 index 00000000000..9b2b3417ddd --- /dev/null +++ b/examples/scenarios/bicycle_example/population_3.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/scenarios/bicycle_example/population_4.xml b/examples/scenarios/bicycle_example/population_4.xml new file mode 100644 index 00000000000..278093f03ea --- /dev/null +++ b/examples/scenarios/bicycle_example/population_4.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java index 8181560dd17..1301c6daeb1 100644 --- a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java @@ -32,6 +32,9 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -234,10 +237,11 @@ private static void checkParamFieldValidity(Field field) { private static final Set> ALLOWED_PARAMETER_TYPES = Set.of(String.class, Float.class, Double.class, Integer.class, Long.class, Boolean.class, Character.class, Byte.class, Short.class, Float.TYPE, Double.TYPE, - Integer.TYPE, Long.TYPE, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE); + Integer.TYPE, Long.TYPE, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, LocalTime.class, + LocalDate.class, LocalDateTime.class); private static final String HINT = " Valid types are String, primitive types and their wrapper classes," - + " enumerations, List and Set." + + " enumerations, List and Set, LocalTime, LocalDate, LocalDateTime" + " Other types are fine as parameters, but you will need to implement conversion strategies" + " in corresponding StringGetters andStringSetters."; @@ -342,6 +346,12 @@ private Object fromString(String value, Class type, @Nullable Field paramFiel return null; } else if (type.equals(String.class)) { return value; + } else if (type.equals(LocalTime.class)) { + return LocalTime.parse(value); + } else if (type.equals(LocalDate.class)) { + return LocalDate.parse(value); + } else if (type.equals(LocalDateTime.class)) { + return LocalDateTime.parse(value); } else if (type.equals(Float.class) || type.equals(Float.TYPE)) { return Float.parseFloat(value); } else if (type.equals(Double.class) || type.equals(Double.TYPE)) { diff --git a/matsim/src/main/java/org/matsim/core/controler/OutputDirectoryHierarchy.java b/matsim/src/main/java/org/matsim/core/controler/OutputDirectoryHierarchy.java index 0e719b1dc8e..39e389ab155 100644 --- a/matsim/src/main/java/org/matsim/core/controler/OutputDirectoryHierarchy.java +++ b/matsim/src/main/java/org/matsim/core/controler/OutputDirectoryHierarchy.java @@ -144,6 +144,13 @@ public final String getIterationFilename(final int iteration, final String filen return s.toString(); } + public String getIterationFilename(int iteration, String filename, ControlerConfigGroup.CompressionType compression) { + if (compression == null) { + return getIterationFilename(iteration, filename); + } + return getIterationFilename(iteration, filename + compression.fileEnding); + } + public final String getIterationFilename(int iteration, Controler.DefaultFiles file) { return getIterationFilename(iteration, file, this.defaultCompressionType); } @@ -173,6 +180,13 @@ public final String getOutputFilename(final String filename) { return s.toString(); } + public String getOutputFilename(String filename, ControlerConfigGroup.CompressionType compression) { + if (compression == null) { + return getOutputFilename(filename); + } + return getOutputFilename(filename + compression.fileEnding); + } + public final String getOutputFilenameWithOutputPrefix(final String filename) { return getOutputFilename(Controler.OUTPUT_PREFIX + filename); } diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/AbstractTransitDriverAgent.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/AbstractTransitDriverAgent.java index 6555514f4c1..d7dd5939154 100644 --- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/AbstractTransitDriverAgent.java +++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/AbstractTransitDriverAgent.java @@ -65,7 +65,7 @@ public abstract class AbstractTransitDriverAgent implements TransitDriverAgent, private final PassengerAccessEgressImpl accessEgress; - /* package */ MobsimAgent.State state = MobsimAgent.State.ACTIVITY ; + /* package */ MobsimAgent.State state = MobsimAgent.State.ACTIVITY ; // yy not so great: implicit instantiation at activity. kai, nov'11 @Override public final MobsimAgent.State getState() { @@ -147,6 +147,17 @@ public final void notifyMoveOverNode(Id nextLinkId) { this.nextLinkIndex++; } + protected final int getNextLinkIndex() { + return nextLinkIndex; + } + + /** + * Overwrite the current link index. May be used by implementing classes, but should be handled with care. + */ + protected final void setNextLinkIndex(int idx) { + nextLinkIndex = idx; + } + @Override public final TransitStopFacility getNextTransitStop() { if (this.nextStop == null) { @@ -165,7 +176,7 @@ public double handleTransitStop(final TransitStopFacility stop, final double now TransitRoute route = this.getTransitRoute(); List stopsToCome = route.getStops().subList(stopIterator.nextIndex(), route.getStops().size()); /* - * If there are passengers leaving or entering, the stop time must be not greater than 1.0 in order to let them (de-)board every second. + * If there are passengers leaving or entering, the stop time must be not greater than 1.0 in order to let them (de-)board every second. * If a stopTime greater than 1.0 is used, this method is not necessarily triggered by the qsim, so (de-)boarding will not happen. Dg, 10-2012 */ double stopTime = this.accessEgress.calculateStopTimeAndTriggerBoarding(getTransitRoute(), getTransitLine(), this.vehicle, stop, stopsToCome, now); @@ -202,7 +213,7 @@ public void notifyArrivalOnLinkByNonNetworkMode(final Id linkId) { /**Design comments:
    *
  • Keeping this for the time being, since the derived methods somehow need to get the selected plan. Might * keep track of the selected plan directly, but someone would need to look more into the design. kai, jun'11 - *
  • For that reason, I made the method package-private. There is, however, probably not much harm to make + *
  • For that reason, I made the method package-private. There is, however, probably not much harm to make * it public again as long as it is not part of the PlanDriverAgent interface. kai, jun'11 *
*/ @@ -276,7 +287,7 @@ private void depart(final double now) { private void assertAllStopsServed() { if (this.nextStop != null) { - RuntimeException e = new RuntimeException("Transit vehicle is not yet at last stop! vehicle-id = " + RuntimeException e = new RuntimeException("Transit vehicle is not yet at last stop! vehicle-id = " + this.vehicle.getVehicle().getId() + "; next-stop = " + this.nextStop.getStopFacility().getId()); log.error(e); throw e; @@ -377,17 +388,17 @@ public void setStartLinkId(final Id linkId) { public void setRouteDescription(String routeDescription) { throw new UnsupportedOperationException("read only route."); } - + @Override public String getRouteDescription() { return this.delegate.getRouteDescription(); } - + @Override public String getRouteType() { return this.delegate.getRouteType(); } - + @Override @Deprecated public double getDistance() { @@ -437,4 +448,4 @@ public NetworkRouteWrapper clone() { } -} \ No newline at end of file +} diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/DefaultTransitDriverAgentFactory.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/DefaultTransitDriverAgentFactory.java index 07a6a5eb3de..f38e55d3195 100644 --- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/DefaultTransitDriverAgentFactory.java +++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/DefaultTransitDriverAgentFactory.java @@ -28,17 +28,8 @@ */ public class DefaultTransitDriverAgentFactory implements TransitDriverAgentFactory { - private final InternalInterface internalInterface; - private final TransitStopAgentTracker transitStopAgentTracker; - - public DefaultTransitDriverAgentFactory(InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker) { - this.internalInterface = internalInterface; - this.transitStopAgentTracker = transitStopAgentTracker; - } - - @Override - public AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf) { + public AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf, InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker) { return new TransitDriverAgentImpl(umlauf, TransportMode.car, transitStopAgentTracker, internalInterface); } diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitDriverAgentFactory.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitDriverAgentFactory.java index 2ea71d38b27..2268fd5d0ff 100644 --- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitDriverAgentFactory.java +++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitDriverAgentFactory.java @@ -20,6 +20,7 @@ package org.matsim.core.mobsim.qsim.pt; import org.matsim.core.api.internal.MatsimFactory; +import org.matsim.core.mobsim.qsim.InternalInterface; import org.matsim.pt.Umlauf; /** @@ -27,6 +28,6 @@ */ public interface TransitDriverAgentFactory extends MatsimFactory { - AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf); + AbstractTransitDriverAgent createTransitDriver(Umlauf umlauf, InternalInterface internalInterface, TransitStopAgentTracker transitStopAgentTracker); } diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitEngineModule.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitEngineModule.java index bcbcf85b57e..a6d75867864 100644 --- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitEngineModule.java +++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitEngineModule.java @@ -21,7 +21,15 @@ package org.matsim.core.mobsim.qsim.pt; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Provides; +import com.google.inject.Singleton; +import com.google.inject.multibindings.OptionalBinder; +import org.matsim.core.config.Config; +import org.matsim.core.gbl.Gbl; import org.matsim.core.mobsim.qsim.AbstractQSimModule; +import org.matsim.core.mobsim.qsim.QSim; import org.matsim.pt.ReconstructingUmlaufBuilder; import org.matsim.pt.UmlaufBuilder; @@ -42,5 +50,13 @@ protected void configureQSim() { bind( UmlaufBuilder.class ).to( ReconstructingUmlaufBuilder.class ); + OptionalBinder.newOptionalBinder(binder(), TransitDriverAgentFactory.class) + .setDefault().to( DefaultTransitDriverAgentFactory.class ); + } + + @Provides + @Singleton + public TransitStopAgentTracker transitStopAgentTracker(QSim qSim) { + return new TransitStopAgentTracker(qSim.getEventsManager()); } } diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitQSimEngine.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitQSimEngine.java index 190cd06be13..d0e04b20752 100644 --- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitQSimEngine.java +++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/pt/TransitQSimEngine.java @@ -54,7 +54,7 @@ * @author mrieser * @author mzilske */ -public class TransitQSimEngine implements DepartureHandler, MobsimEngine, AgentSource, HasAgentTracker { +public class TransitQSimEngine implements DepartureHandler, MobsimEngine, AgentSource, HasAgentTracker { private Collection ptDrivers; @@ -80,27 +80,32 @@ public TransitAgentTriesToTeleportException(String message) { private TransitStopHandlerFactory stopHandlerFactory = new SimpleTransitStopHandlerFactory(); - private TransitDriverAgentFactory transitDriverFactory; + private final TransitDriverAgentFactory transitDriverFactory; private InternalInterface internalInterface = null ; @Override public void setInternalInterface( InternalInterface internalInterface ) { this.internalInterface = internalInterface ; - transitDriverFactory = new DefaultTransitDriverAgentFactory(internalInterface, agentTracker); } TransitQSimEngine(QSim queueSimulation) { - this(queueSimulation, new SimpleTransitStopHandlerFactory(), new ReconstructingUmlaufBuilder(queueSimulation.getScenario()) ); + this(queueSimulation, new SimpleTransitStopHandlerFactory(), + new ReconstructingUmlaufBuilder(queueSimulation.getScenario()), + new TransitStopAgentTracker(queueSimulation.getEventsManager()), + new DefaultTransitDriverAgentFactory()); } @Inject - public TransitQSimEngine(QSim queueSimulation, TransitStopHandlerFactory stopHandlerFactory, UmlaufBuilder umlaufBuilder) { + public TransitQSimEngine(QSim queueSimulation, TransitStopHandlerFactory stopHandlerFactory, + UmlaufBuilder umlaufBuilder, TransitStopAgentTracker tracker, + TransitDriverAgentFactory transitDriverFactory) { this.qSim = queueSimulation; this.schedule = queueSimulation.getScenario().getTransitSchedule(); this.umlaufBuilder = umlaufBuilder; - this.agentTracker = new TransitStopAgentTracker(this.qSim.getEventsManager()); + this.agentTracker = tracker; this.stopHandlerFactory = stopHandlerFactory; + this.transitDriverFactory = transitDriverFactory; } // For tests (which create an Engine, and externally create Agents as well). @@ -154,7 +159,7 @@ private UmlaufCache getOrCreateUmlaufCache(final Scenario scenario) { private AbstractTransitDriverAgent createAndScheduleVehicleAndDriver(Umlauf umlauf, Vehicle vehicle) { TransitQVehicle veh = new TransitQVehicle(vehicle); - AbstractTransitDriverAgent driver = this.transitDriverFactory.createTransitDriver(umlauf); + AbstractTransitDriverAgent driver = this.transitDriverFactory.createTransitDriver(umlauf, internalInterface, agentTracker); veh.setDriver(driver); veh.setStopHandler(this.stopHandlerFactory.createTransitStopHandler(veh.getVehicle())); driver.setVehicle(veh); @@ -205,10 +210,6 @@ public void setTransitStopHandlerFactory(final TransitStopHandlerFactory stopHan this.stopHandlerFactory = stopHandlerFactory; } - public void setAbstractTransitDriverFactory(final TransitDriverAgentFactory abstractTransitDriverFactory) { - this.transitDriverFactory = abstractTransitDriverFactory; - } - @Override public void doSimStep(double time) { // Nothing to do here. diff --git a/matsim/src/main/java/org/matsim/core/population/io/AbstractPopulationWriterHandler.java b/matsim/src/main/java/org/matsim/core/population/io/AbstractPopulationWriterHandler.java index 9208db8e875..95a99b66e50 100644 --- a/matsim/src/main/java/org/matsim/core/population/io/AbstractPopulationWriterHandler.java +++ b/matsim/src/main/java/org/matsim/core/population/io/AbstractPopulationWriterHandler.java @@ -77,7 +77,6 @@ else if (pe instanceof Leg) { } this.endPerson(writer); this.writeSeparator(writer); - writer.flush(); } public abstract void startPerson(final Person person, final BufferedWriter out) throws IOException; diff --git a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV0.java b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV0.java index 0009ba521e6..057684fb2f6 100644 --- a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV0.java +++ b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV0.java @@ -75,6 +75,7 @@ public void startPlans(final Population plans, final BufferedWriter out) throws @Override public void endPlans(final BufferedWriter out) throws IOException { out.write("\n"); + out.flush(); } ////////////////////////////////////////////////////////////////////// diff --git a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV4.java b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV4.java index 493ec680d06..c66dd9f57cc 100644 --- a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV4.java +++ b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV4.java @@ -82,6 +82,7 @@ public void startPlans(final Population plans, final BufferedWriter out) throws @Override public void endPlans(final BufferedWriter out) throws IOException { out.write("\n"); + out.flush(); } ////////////////////////////////////////////////////////////////////// diff --git a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV5.java b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV5.java index 7d1c42fe0b9..5ca46309074 100644 --- a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV5.java +++ b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV5.java @@ -99,12 +99,12 @@ else if (pe instanceof Leg) { } PopulationWriterHandlerImplV5.endPerson(out); this.writeSeparator(out); - out.flush(); } @Override public void endPlans(final BufferedWriter out) throws IOException { out.write("\n"); + out.flush(); } private static void startPerson(final Person person, final BufferedWriter out) throws IOException { diff --git a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV6.java b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV6.java index 67ff8fada0a..7d23d6c976a 100644 --- a/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV6.java +++ b/matsim/src/main/java/org/matsim/core/population/io/PopulationWriterHandlerImplV6.java @@ -119,12 +119,12 @@ else if (pe instanceof Leg) { } PopulationWriterHandlerImplV6.endPerson(out); this.writeSeparator(out); - out.flush(); } @Override public void endPlans(final BufferedWriter out) throws IOException { out.write("\n"); + out.flush(); } private void startPerson(final Person person, final BufferedWriter out) throws IOException { diff --git a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java index fcc54693e19..a662f53d5ad 100644 --- a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java @@ -22,6 +22,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -61,6 +64,9 @@ public void testDumpAndRead() { dumpedModule.charField = 'z'; dumpedModule.byteField = 78; dumpedModule.booleanField = true; + dumpedModule.localTimeField = LocalTime.of(23, 59, 59); + dumpedModule.localDateField = LocalDate.of(2022, 12, 31); + dumpedModule.localDateTimeField = LocalDateTime.of(2022, 12, 31, 23, 59, 59); dumpedModule.enumListField = List.of(MyEnum.VALUE1, MyEnum.VALUE2); dumpedModule.enumSetField = Set.of(MyEnum.VALUE2); dumpedModule.setField = ImmutableSet.of("a", "b", "c"); @@ -154,6 +160,9 @@ public void testComments() { expectedComments.put("charField", "char"); expectedComments.put("byteField", "byte"); expectedComments.put("booleanField", "boolean"); + expectedComments.put("localTimeField", "local time"); + expectedComments.put("localDateField", "local date"); + expectedComments.put("localDateTimeField", "local datetime"); expectedComments.put("enumField", "Possible values: VALUE1,VALUE2"); expectedComments.put("enumListField", "list of enum"); expectedComments.put("enumSetField", "set of enum"); @@ -439,6 +448,18 @@ private static class MyModule extends ReflectiveConfigGroup { @Parameter private boolean booleanField; + @Comment("local time") + @Parameter + private LocalTime localTimeField; + + @Comment("local date") + @Parameter + private LocalDate localDateField; + + @Comment("local datetime") + @Parameter + private LocalDateTime localDateTimeField; + @Comment("set") @Parameter private Set setField; diff --git a/pom.xml b/pom.xml index 5a1f03471cf..275d32a0877 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ org.apache.commons commons-compress - 1.23.0 + 1.24.0 commons-logging @@ -185,7 +185,7 @@ com.google.errorprone error_prone_annotations - 2.21.1 + 2.22.0 @@ -308,7 +308,7 @@ net.bytebuddy byte-buddy - 1.14.7 + 1.14.8 test @@ -325,7 +325,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.4.0 + 3.4.1 enforce @@ -412,7 +412,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.6.0 org.codehaus.mojo