diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b76b8957033..9878d8651cc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,11 @@ updates: directory: "/" schedule: interval: "daily" + - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" + groups: + github-actions: + patterns: ["*"] diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml index 0119e935f34..20ffe41971a 100644 --- a/.github/workflows/code-coverage.yaml +++ b/.github/workflows/code-coverage.yaml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..021bae1c67c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,49 @@ +name: CodeQL + +on: + push: + branches: 'master' + +jobs: + analyze: + name: Analyze + runs-on: 'ubuntu-latest' + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'zulu' + cache: 'maven' + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: 'java' + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:java" diff --git a/.github/workflows/deploy-on-pr-merge.yaml b/.github/workflows/deploy-on-pr-merge.yaml index 80253f2b817..332a3dfe178 100644 --- a/.github/workflows/deploy-on-pr-merge.yaml +++ b/.github/workflows/deploy-on-pr-merge.yaml @@ -10,14 +10,14 @@ on: jobs: deploy-snapshot: name: deploy PR-labelled version - # for PR-labelled deployment -- only if closed by merging - if: github.event_name == 'push' || github.event.pull_request.merged == true + # only if PR closed by merging + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 @@ -25,7 +25,7 @@ jobs: java-version: 17 distribution: 'zulu' cache: 'maven' - server-id: ${{ github.event_name == 'push' && 'matsim-snapshots' || 'matsim-releases' }} #choose mvn repo + server-id: 'matsim-releases' server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD @@ -43,5 +43,10 @@ jobs: MAVEN_USERNAME: ${{ secrets.REPOMATSIM_USERNAME }} MAVEN_PASSWORD: ${{ secrets.REPOMATSIM_TOKEN }} + - name: Submit Dependency Graph + # Generate a complete dependency graph and submit the graph to the GitHub repository. + # The goal is to improve security alerts from dependabot, because dependabot is not able to compute the complete dependency graph. + uses: advanced-security/maven-dependency-submission-action@v3 + env: MAVEN_OPTS: -Xmx2g diff --git a/.github/workflows/deploy-on-release-created.yaml b/.github/workflows/deploy-on-release-created.yaml index 529b904e7ea..7c37d3c6f7d 100644 --- a/.github/workflows/deploy-on-release-created.yaml +++ b/.github/workflows/deploy-on-release-created.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 diff --git a/.github/workflows/deploy-weekly.yaml b/.github/workflows/deploy-weekly.yaml index cdaee1af74b..0861a80ff91 100644 --- a/.github/workflows/deploy-weekly.yaml +++ b/.github/workflows/deploy-weekly.yaml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 diff --git a/.github/workflows/full-integration.yaml b/.github/workflows/full-integration.yaml index 3d344f825ad..c831df5ec61 100644 --- a/.github/workflows/full-integration.yaml +++ b/.github/workflows/full-integration.yaml @@ -1,6 +1,7 @@ name: full-integration on: + workflow_dispatch: schedule: - cron: '30 0 * * *' # daily at 0:30 UTC @@ -13,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - name: Prepare git @@ -21,7 +22,7 @@ jobs: run: git config --global core.autocrlf false - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml index a7c563a6ed1..f2a5b59d8ee 100644 --- a/.github/workflows/verify-push.yaml +++ b/.github/workflows/verify-push.yaml @@ -55,21 +55,22 @@ jobs: - contribs/socnetsim - contribs/sumo - contribs/pseudosimulation + - contribs/railsim - contribs/roadpricing - contribs/analysis - - contribs/eventsBasedPTRouter - contribs/hybridsim - contribs/informed-mode-choice - contribs/otfvis - contribs/osm - contribs/application + - contribs/simwrapper - contribs/sbb-extensions - contribs/simulatedannealing - benchmark steps: - name: Checkout git repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Detect changes against master # we only want to build matsim (module) if changes are not limited to contribs diff --git a/benchmark/src/main/java/org/matsim/benchmark/Benchmark.java b/benchmark/src/main/java/org/matsim/benchmark/Benchmark.java index 9c11ff9f623..addf157f821 100644 --- a/benchmark/src/main/java/org/matsim/benchmark/Benchmark.java +++ b/benchmark/src/main/java/org/matsim/benchmark/Benchmark.java @@ -32,7 +32,7 @@ public class Benchmark { public static void main(String[] args) throws IOException { Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("benchmark"), "config.xml")); Controler ctl = new Controler(config); - ctl.getConfig().controler().setCreateGraphs(false); + ctl.getConfig().controller().setCreateGraphs(false); ctl.run(); } diff --git a/contribs/README.md b/contribs/README.md index b49518c7257..f29f739c4ef 100644 --- a/contribs/README.md +++ b/contribs/README.md @@ -7,44 +7,46 @@ Please note that these extensions are usually maintained and provided by single The MATSim core development team cannot make any guarantee that these extensions will be kept up-to-date and compatible with future releases, instead the maintainers themselves are responsible for this task. -| Contrib | Short description | -|---------------------------------------------------------|-------------------------------------------------| -| [accessibility](accessibility/README.md) | Measuring accessibility in policy evaluation -| accidents | -| [analysis](analysis/README.md) | Collection of matsim analysis tools -| [application](application/README.md) | Build MATSim scenarios with preparation and analysis pipelines -| [av](av/README.md) | Automated (autonomous) vehicle contrib -| bicycle | -| [cadytsIntegration](cadytsIntegration/README.md) | Calibration of dynamics traffic simulations -| [carsharing](carsharing/README.md) | Simulate carsharing modes like station based or freefloatin +| Contrib | Short description | +|--------------------------------------------------------------------------|-------------------------------------------------| +| [accessibility](accessibility/README.md) | Measuring accessibility in policy evaluation +| accidents | +| [analysis](analysis/README.md) | Collection of matsim analysis tools +| [application](application/README.md) | Build MATSim scenarios with preparation and analysis pipelines +| [av](av/README.md) | Automated (autonomous) vehicle contrib +| bicycle | +| [cadytsIntegration](cadytsIntegration/README.md) | Calibration of dynamics traffic simulations +| [carsharing](carsharing/README.md) | Simulate carsharing modes like station based or freefloatin | [commercialTrafficApplications](commercialTrafficApplications/README.md) | Collection of applications of the freight contrib in combination with one or more other contribs -| common | -| [decongestion](decongestion/README.md) | Compute tolls accordingly in order to reduce delays -| [discrete_mode_choice](discrete_mode_choice/README.md) | Fine-grained and custom mode choice behaviour -| [drt](drt/README.md) | Demand-responsive transport simulation -| [drt-extensions](drt-extensions/README.md) | Extensions for demand-responsive transport simulation -| [dvrp](dvrp/README.md) | Tool for solving (dynamic) vehicle routing problem -| [emissions](emissions/README.md) | Tool for exhaust emission calculation -| [ev](ev/README.md) | Electric Vehicle functionality -| eventsBasedPTRouter | -| [freight](freight/README.md) | Package that plugs freight algorithms -| [hybridsim](hybridsim/README.md) | -| integration | +| common | +| [decongestion](decongestion/README.md) | Compute tolls accordingly in order to reduce delays +| [discrete_mode_choice](discrete_mode_choice/README.md) | Fine-grained and custom mode choice behaviour +| [drt](drt/README.md) | Demand-responsive transport simulation +| [drt-extensions](drt-extensions/README.md) | Extensions for demand-responsive transport simulation +| [dvrp](dvrp/README.md) | Tool for solving (dynamic) vehicle routing problem +| [emissions](emissions/README.md) | Tool for exhaust emission calculation +| [ev](ev/README.md) | Electric Vehicle functionality +| eventsBasedPTRouter | +| [freight](freight/README.md) | Package that plugs freight algorithms +| [hybridsim](hybridsim/README.md) | +| integration | | [informed-mode-choice](informed-mode-choice/README.md) | Mode choice algorithms based on trip utility estimation -| [locationchoice](locationchoice/README.md) | Location choice for discretionary activities -| [matrixbasedptrouter](matrixbasedptrouter/README.md) | Calculate PT travel times using a set of stops and a pre-calculated travel time -| [minibus](minibus/README.md) | Runs an adaptive "minibus" model to serve given demand -| [multimodal](multimodal/README.md) | Enables multi-modal simulation -| [noise](noise/README.md) | Tools to compute various noise metrics (emissions, imission, exposure costs) -| osm | -| [otfvis](otfvis/README.md) | Visualiser for Matsim scenarios -| [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 -| [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 -| [socnetsim](socnetsim/README.md) | Social network simulation -| [sumo](sumo/README.md) | Converter and integrations for [SUMO](https://sumo.dlr.de/]) -| [taxi](taxi/README.md) | Taxi service functionality -| vsp | +| [locationchoice](locationchoice/README.md) | Location choice for discretionary activities +| [matrixbasedptrouter](matrixbasedptrouter/README.md) | Calculate PT travel times using a set of stops and a pre-calculated travel time +| [minibus](minibus/README.md) | Runs an adaptive "minibus" model to serve given demand +| [multimodal](multimodal/README.md) | Enables multi-modal simulation +| [noise](noise/README.md) | Tools to compute various noise metrics (emissions, imission, exposure costs) +| osm | +| [otfvis](otfvis/README.md) | Visualiser for Matsim scenarios +| [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 +| [simwrapper](simwrapper/README.md) | Creates dashboards automatically with [SimWrapper](https://simwrapper.github.io/) +| [socnetsim](socnetsim/README.md) | Social network simulation +| [sumo](sumo/README.md) | Converter and integrations for [SUMO](https://sumo.dlr.de/]) +| [taxi](taxi/README.md) | Taxi service functionality +| vsp | diff --git a/contribs/accessibility/pom.xml b/contribs/accessibility/pom.xml index 300f8fb1bd1..45d1f56723b 100644 --- a/contribs/accessibility/pom.xml +++ b/contribs/accessibility/pom.xml @@ -52,7 +52,7 @@ org.openstreetmap.osmosis osmosis-core - 0.48.3 + ${osmosis.version} @@ -65,7 +65,7 @@ org.openstreetmap.osmosis osmosis-xml - 0.48.3 + ${osmosis.version} org.matsim.contrib diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationNairobiLandUseLocalCopy.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationNairobiLandUseLocalCopy.java index ef0f7fdc022..91e6e1c72ca 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationNairobiLandUseLocalCopy.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationNairobiLandUseLocalCopy.java @@ -39,26 +39,26 @@ */ class AccessibilityComputationNairobiLandUseLocalCopy { public static final Logger LOG = LogManager.getLogger(AccessibilityComputationNairobiLandUseLocalCopy.class); - + public static void main(String[] args) { int tileSize_m = 500; boolean push2Geoserver = false; // Set true for run on server boolean createQGisOutput = true; // Set false for run on server - + final Config config = ConfigUtils.createConfig(new AccessibilityConfigGroup()); - + Envelope envelope = new Envelope(246000, 271000, 9853000, 9863000); // Central part of Nairobi String scenarioCRS = "EPSG:21037"; // EPSG:21037 = Arc 1960 / UTM zone 37S, for Nairobi, Kenya - + config.network().setInputFile("../nairobi/data/nairobi/input/2015-10-15_network.xml"); config.facilities().setInputFile("../nairobi/data/land_use/Nairobi_LU_2010/facilities.xml"); String runId = "ke_nairobi_landuse_hexagons_" + tileSize_m; - config.controler().setOutputDirectory("../nairobi/data/nairobi/output/" + runId + "_lcpt_par4_car_tr-7_500/"); - config.controler().setRunId(runId); - - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(0); - + config.controller().setOutputDirectory("../nairobi/data/nairobi/output/" + runId + "_lcpt_par4_car_tr-7_500/"); + config.controller().setRunId(runId); + + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); + AccessibilityConfigGroup acg = ConfigUtils.addOrGetModule(config, AccessibilityConfigGroup.class); acg.setAreaOfAccessibilityComputation(AreaOfAccesssibilityComputation.fromBoundingBoxHexagons); acg.setEnvelope(envelope); @@ -69,17 +69,17 @@ public static void main(String[] args) { acg.setOutputCrs(scenarioCRS); //acg.setUseParallelization(false); - + ConfigUtils.setVspDefaults(config); - + final Scenario scenario = ScenarioUtils.loadScenario(config); - + // final List activityTypes = Arrays.asList(new String[]{"educational", "commercial", "industrial", "recreational", "water_body"}); final List activityTypes = Arrays.asList(new String[]{"educational"}); final ActivityFacilities densityFacilities = AccessibilityUtils.createFacilityForEachLink(Labels.DENSITIY, scenario.getNetwork()); - + final Controler controler = new Controler(scenario); - + for (String activityType : activityTypes) { AccessibilityModule module = new AccessibilityModule(); module.setConsideredActivityType(activityType); @@ -88,17 +88,17 @@ public static void main(String[] args) { module.setCreateQGisOutput(createQGisOutput); controler.addOverridingModule(module); } - + controler.run(); - + if (createQGisOutput) { final Integer range = 9; // In the current implementation, this must always be 9 final Double lowerBound = -3.5; // (upperBound - lowerBound) ideally nicely divisible by (range - 2) final Double upperBound = 3.5; final int populationThreshold = (int) (10 / (1000/tileSize_m * 1000/tileSize_m)); // People per km^2 or roads (?) - + String osName = System.getProperty("os.name"); - String workingDirectory = config.controler().getOutputDirectory(); + String workingDirectory = config.controller().getOutputDirectory(); for (String actType : activityTypes) { String actSpecificWorkingDirectory = workingDirectory + actType + "/"; for (Modes4Accessibility mode : acg.getIsComputingMode()) { diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationShutdownListener.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationShutdownListener.java index a3f82abfa44..39637e1228f 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationShutdownListener.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityComputationShutdownListener.java @@ -30,11 +30,10 @@ 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.Node; import org.matsim.contrib.accessibility.utils.AggregationObject; import org.matsim.contrib.accessibility.utils.ProgressBar; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.events.ShutdownEvent; import org.matsim.core.controler.listener.ShutdownListener; import org.matsim.core.gbl.Gbl; @@ -58,9 +57,9 @@ final class AccessibilityComputationShutdownListener implements ShutdownListener private final ArrayList zoneDataExchangeListeners = new ArrayList<>(); private AccessibilityConfigGroup acg; - private final PlanCalcScoreConfigGroup cnScoringGroup; + private final ScoringConfigGroup cnScoringGroup; + - public AccessibilityComputationShutdownListener(Scenario scenario, ActivityFacilities measuringPoints, ActivityFacilities opportunities, String outputDirectory) { this.measuringPoints = measuringPoints; @@ -69,7 +68,7 @@ public AccessibilityComputationShutdownListener(Scenario scenario, ActivityFacil this.outputDirectory = outputDirectory; this.acg = ConfigUtils.addOrGetModule(scenario.getConfig(), AccessibilityConfigGroup.GROUP_NAME, AccessibilityConfigGroup.class); - this.cnScoringGroup = scenario.getConfig().planCalcScore(); + this.cnScoringGroup = scenario.getConfig().scoring(); if (cnScoringGroup.getOrCreateModeParams(TransportMode.car).getMarginalUtilityOfDistance() != 0.) { LOG.error("Marginal utility of distance for car different from zero, but not used in accessibility computations"); @@ -241,10 +240,10 @@ private void writeCSVFile(String adaptedOutputDirectory) { writer.writeField(facility.getCoord().getX()); writer.writeField(facility.getCoord().getY()); writer.writeField(tuple.getSecond()); - + for (String mode : getModes() ) { final double value = accessibilitiesMap.get(tuple).get(mode); - if (!Double.isNaN(value)) { + if (!Double.isNaN(value)) { writer.writeField(value) ; } else { writer.writeField(Double.NaN) ; diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityFromEvents.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityFromEvents.java index 476471ebd8c..b200e3a46f0 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityFromEvents.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityFromEvents.java @@ -58,7 +58,7 @@ private AccessibilityFromEvents( Scenario scenario, String eventsFile, List map = new LinkedHashMap<>( ) ; EventsManager events = EventsUtils.createEventsManager(); - for( String mode : scenario.getConfig().plansCalcRoute().getNetworkModes() ){ + for( String mode : scenario.getConfig().routing().getNetworkModes() ){ TravelTimeCalculator.Builder builder = new TravelTimeCalculator.Builder( scenario.getNetwork() ); builder.setCalculateLinkTravelTimes( true ); builder.setCalculateLinkToLinkTravelTimes( false ); @@ -74,7 +74,7 @@ public void run() { install( new ScenarioByInstanceModule( scenario ) ) ; install( new TripRouterModule() ) ; install( new TimeInterpretationModule() ); - for( String mode : getConfig().plansCalcRoute().getNetworkModes() ){ + for( String mode : getConfig().routing().getNetworkModes() ){ addTravelTimeBinding( mode ).toInstance( map.get(mode) ); } install( new TravelDisutilityModule() ) ; diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityModule.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityModule.java index 19c98283c02..c4677dd3fd3 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityModule.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityModule.java @@ -139,7 +139,7 @@ public ControlerListener get() { } AccessibilityUtils.assignAdditionalFacilitiesDataToMeasurePoint(measuringPoints, measurePointGeometryMap, additionalFacs); - String outputDirectory = scenario.getConfig().controler().getOutputDirectory() + "/" + activityType; + String outputDirectory = scenario.getConfig().controller().getOutputDirectory() + "/" + activityType; AccessibilityComputationShutdownListener accessibilityShutdownListener = new AccessibilityComputationShutdownListener(scenario, measuringPoints, opportunities, outputDirectory); // for (Modes4Accessibility mode : acg.getIsComputingMode()) { @@ -150,14 +150,14 @@ public ControlerListener get() { final TravelDisutilityFactory travelDisutilityFactory = travelDisutilityFactories.get(TransportMode.car); Gbl.assertNotNull(travelDisutilityFactory); calculator = new NetworkModeAccessibilityExpContributionCalculator(mode, new FreeSpeedTravelTime(), travelDisutilityFactory, scenario); - } else if ( config.plansCalcRoute().getNetworkModes().contains( mode ) ) { + } else if ( config.routing().getNetworkModes().contains( mode ) ) { final TravelTime nwModeTravelTime = travelTimes.get(mode); Gbl.assertNotNull(nwModeTravelTime); final TravelDisutilityFactory nwModeTravelDisutility = travelDisutilityFactories.get(mode); Gbl.assertNotNull( nwModeTravelDisutility ); calculator = new NetworkModeAccessibilityExpContributionCalculator(mode, nwModeTravelTime, nwModeTravelDisutility, scenario); } else if ( TransportMode.pt.equals( mode ) ){ - calculator = new SwissRailRaptorAccessibilityContributionCalculator( mode, config.planCalcScore(), scenario ); + calculator = new SwissRailRaptorAccessibilityContributionCalculator( mode, config.scoring(), scenario ); } else if ( Modes4Accessibility.matrixBasedPt.name().equals( mode ) ) { throw new RuntimeException("currently not supported because implementation not consistent with guice grapher. kai, sep'19") ; // calculator = new LeastCostPathCalculatorAccessibilityContributionCalculator( @@ -175,7 +175,7 @@ public ControlerListener get() { if ( travelDisutilityFactory==null ) { throw new RuntimeException("mode=" + mode + "; travelDisutilityFactory is null!") ; } - calculator = new TripRouterAccessibilityContributionCalculator(mode, tripRouter, config.planCalcScore(), scenario, + calculator = new TripRouterAccessibilityContributionCalculator(mode, tripRouter, config.scoring(), scenario, travelTime, travelDisutilityFactory ); } @@ -195,7 +195,7 @@ public ControlerListener get() { } Set additionalFacInfo = additionalFacs.keySet(); accessibilityShutdownListener.addFacilityDataExchangeListener(new GeoserverUpdater(acg.getOutputCrs(), - config.controler().getRunId() + "_" + activityType, measurePointGeometryMap, additionalFacInfo, + config.controller().getRunId() + "_" + activityType, measurePointGeometryMap, additionalFacInfo, outputDirectory, pushing2Geoserver, createQGisOutput)); } diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java index 0de040c489c..db9adc47ccd 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/AccessibilityUtils.java @@ -34,7 +34,7 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.NetworkConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.gbl.Gbl; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.TransportModeNetworkFilter; @@ -65,7 +65,7 @@ public static final Map, AggregationObject> aggregat // yyyy this method ignores the "capacities" of the facilities. kai, mar'14 // for now, we decided not to add "capacities" as it is not needed for current projects. dz, feb'16 - double walkSpeed_m_h = config.plansCalcRoute().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600.; + double walkSpeed_m_h = config.routing().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600.; AccessibilityConfigGroup acg = ConfigUtils.addOrGetModule(config, AccessibilityConfigGroup.GROUP_NAME, AccessibilityConfigGroup.class); LOG.info("Aggregating " + opportunities.getFacilities().size() + " opportunities with same nearest node..."); @@ -76,11 +76,11 @@ public static final Map, AggregationObject> aggregat double distance_m = NetworkUtils.getEuclideanDistance(opportunity.getCoord(), nearestNode.getCoord()); // in MATSim this is [utils/h]: cnScoringGroup.getTravelingWalk_utils_hr() - cnScoringGroup.getPerforming_utils_hr() - double walkBetaTT_utils_h = config.planCalcScore().getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - - config.planCalcScore().getPerforming_utils_hr(); // default values: -12 = (-6.) - (6.) + double walkBetaTT_utils_h = config.scoring().getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() + - config.scoring().getPerforming_utils_hr(); // default values: -12 = (-6.) - (6.) double VjkWalkTravelTime = walkBetaTT_utils_h * (distance_m / walkSpeed_m_h); - double expVjk = Math.exp(config.planCalcScore().getBrainExpBeta() * VjkWalkTravelTime); + double expVjk = Math.exp(config.scoring().getBrainExpBeta() * VjkWalkTravelTime); // add Vjk to sum AggregationObject jco = opportunityClusterMap.get(nearestNode.getId()); // Why "jco"? @@ -142,16 +142,16 @@ public static Network createModeSpecificSubNetwork(Network network, String mode, } - public static double getModeSpecificConstantForAccessibilities(String mode, PlanCalcScoreConfigGroup planCalcScoreConfigGroup) { + public static double getModeSpecificConstantForAccessibilities(String mode, ScoringConfigGroup scoringConfigGroup) { double modeSpecificConstant; if (mode.equals(Modes4Accessibility.freespeed.name())) { - modeSpecificConstant = planCalcScoreConfigGroup.getModes().get(TransportMode.car).getConstant(); + modeSpecificConstant = scoringConfigGroup.getModes().get(TransportMode.car).getConstant(); } else { - modeSpecificConstant = planCalcScoreConfigGroup.getModes().get(mode).getConstant(); + modeSpecificConstant = scoringConfigGroup.getModes().get(mode).getConstant(); } return modeSpecificConstant; } - + /** * Collects all facilities of a given type that have been loaded to the sceanrio. */ @@ -191,8 +191,8 @@ public static List collectAllFacilityOptionTypes(Scenario scenario) { public static void combineDifferentActivityOptionTypes(final Scenario scenario, String combinedType, final List activityOptionsToBeIncluded) { - ActivityOption markerOption = new ActivityOptionImpl(combinedType); - + ActivityOption markerOption = new ActivityOptionImpl(combinedType); + // Memorize all facilities that have certain activity options in a activity facilities container final ActivityFacilities consideredFacilities = FacilitiesUtils.createActivityFacilities(); for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) { @@ -205,7 +205,7 @@ public static void combineDifferentActivityOptionTypes(final Scenario scenario, } } } - + // Add marker option to facilities to be considered for (ActivityFacility facility : consideredFacilities.getFacilities().values()) { facility.addActivityOption(markerOption); @@ -227,16 +227,16 @@ public static final ActivityFacilities createFacilityForEachLink(String facility public static final ActivityFacilities createFacilityFromBuildingShapefile(String shapeFileName, String identifierCaption, String numberOfHouseholdsCaption) { ShapeFileReader shapeFileReader = new ShapeFileReader(); Collection features = shapeFileReader.readFileAndInitialize(shapeFileName); - + ActivityFacilities facilities = FacilitiesUtils.createActivityFacilities("DensitiyFacilities"); ActivityFacilitiesFactory aff = facilities.getFactory(); - + for (SimpleFeature feature : features) { String featureId = (String) feature.getAttribute(identifierCaption); Integer numberOfHouseholds = Integer.parseInt((String) feature.getAttribute(numberOfHouseholdsCaption)); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Coord coord = CoordUtils.createCoord(geometry.getCentroid().getX(), geometry.getCentroid().getY()); - + for (int i = 0; i < numberOfHouseholds; i++) { ActivityFacility facility = aff.createActivityFacility(Id.create(featureId + "_" + i, ActivityFacility.class), coord); facilities.addActivityFacility(facility); @@ -255,7 +255,7 @@ public static ActivityFacilities createMeasuringPointsFromNetworkBounds(Network double xMax = boundingBox.getXMax(); double yMin = boundingBox.getYMin(); double yMax = boundingBox.getYMax(); - + ActivityFacilities measuringPoints = GridUtils.createGridLayerByGridSizeByBoundingBoxV2(xMin, yMin, xMax, yMax, cellSize); return measuringPoints; } @@ -263,7 +263,7 @@ public static ActivityFacilities createMeasuringPointsFromNetworkBounds(Network /** * Calculates the sum of the values of a given list. - * + * * @param valueList * @return sum */ @@ -279,7 +279,7 @@ public static double calculateSum(List valueList) { /** * Calculates Gini coefficient of the values of a given values. The Gini Coefficient is equals to the half of * the relative mean absolute difference (RMD). - * + * * @see * @see * @param valueList @@ -289,7 +289,7 @@ public static double calculateGiniCoefficient(List valueList) { int numberOfValues = valueList.size(); double sumOfValues = calculateSum(valueList); double arithmeticMean = sumOfValues / numberOfValues; - + double sumOfAbsoluteDifferences = 0.; for (double i : valueList) { for (double j : valueList) { @@ -310,24 +310,24 @@ public static double calculateGiniCoefficient(List valueList) { public static ActivityFacilities createFacilitiesFromPlans(Population population) { ActivityFacilitiesFactory aff = new ActivityFacilitiesFactoryImpl(); ActivityFacilities facilities = FacilitiesUtils.createActivityFacilities(); - + for(Person person : population.getPersons().values()) { for(Plan plan : person.getPlans()) { Id personId = person.getId(); - + for (PlanElement planElement : plan.getPlanElements()) { if (planElement instanceof Activity) { Activity activity = (Activity) planElement; - + Coord coord= activity.getCoord(); if (coord == null) { throw new NullPointerException("Activity does not have any coordinates."); } - + String activityType = activity.getType(); - + // In case an agent visits the same activity location twice, create another activity facility with a modified ID - Integer i = 1; + Integer i = 1; Id facilityId = Id.create(activityType + "_" + personId.toString() + "_" + i.toString(), ActivityFacility.class); while (facilities.getFacilities().containsKey(facilityId)) { i++; @@ -335,7 +335,7 @@ public static ActivityFacilities createFacilitiesFromPlans(Population population } ActivityFacility facility = aff.createActivityFacility(facilityId, activity.getCoord()); - + facility.addActivityOption(aff.createActivityOption(activityType)); facilities.addActivityFacility(facility); // log.info("Created activity with option of type " + activityType + " and ID " + facilityId + "."); @@ -353,7 +353,7 @@ public static String getDate() { String monthStr = month + ""; if (month < 10) monthStr = "0" + month; - String date = cal.get(Calendar.YEAR) + "-" + String date = cal.get(Calendar.YEAR) + "-" + monthStr + "-" + cal.get(Calendar.DAY_OF_MONTH); return date; } @@ -363,16 +363,16 @@ public static void assignAdditionalFacilitiesDataToMeasurePoint(ActivityFaciliti Map additionalFacilityData) { LOG.info("Start assigning additional facilities data to measure point."); GeometryFactory geometryFactory = new GeometryFactory(); - + for (ActivityFacilities additionalDataFacilities : additionalFacilityData.values()) { // Iterate over all additional data collections String additionalDataName = additionalDataFacilities.getName(); int additionalDataFacilitiesToAssign = additionalDataFacilities.getFacilities().size(); - + for (Id measurePointId : measurePoints.getFacilities().keySet()) { // Iterate over all measure points ActivityFacility measurePoint = measurePoints.getFacilities().get(measurePointId); measurePoint.getAttributes().putAttribute(additionalDataName, 0); Geometry geometry = measurePointGeometryMap.get(measurePointId); - + for (ActivityFacility facility : additionalDataFacilities.getFacilities().values()) { // Iterate over additional-data facilities Point point = geometryFactory.createPoint(new Coordinate(facility.getCoord().getX(), facility.getCoord().getY())); if (geometry.contains(point)) { diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/ConstantSpeedAccessibilityExpContributionCalculator.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/ConstantSpeedAccessibilityExpContributionCalculator.java index 329155e30e4..90179912f8d 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/ConstantSpeedAccessibilityExpContributionCalculator.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/ConstantSpeedAccessibilityExpContributionCalculator.java @@ -29,7 +29,7 @@ import org.matsim.contrib.accessibility.utils.Distances; import org.matsim.contrib.accessibility.utils.NetworkUtil; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.TransportModeNetworkFilter; import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; @@ -54,14 +54,14 @@ final class ConstantSpeedAccessibilityExpContributionCalculator implements Acces // private Network network; private Network subNetwork; private Scenario scenario; - + private double logitScaleParameter; - + private double betaModeTT; // in MATSim this is [utils/h]: cnScoringGroup.getTravelingBike_utils_hr() - cnScoringGroup.getPerforming_utils_hr() private double betaModeTD; // in MATSim this is 0 !!! since getMonetaryDistanceCostRateBike doesn't exist: private double constMode; private double modeSpeed_m_h = -1; - + private double betaWalkTT; private double betaWalkTD; private double walkSpeed_m_h; @@ -77,27 +77,27 @@ public ConstantSpeedAccessibilityExpContributionCalculator(final String mode, fi this.scenario = scenario; this.config = scenario.getConfig(); - final PlanCalcScoreConfigGroup planCalcScoreConfigGroup = config.planCalcScore() ; + final ScoringConfigGroup scoringConfigGroup = config.scoring() ; - if (planCalcScoreConfigGroup.getOrCreateModeParams(mode).getMonetaryDistanceRate() != 0.) { + if (scoringConfigGroup.getOrCreateModeParams(mode).getMonetaryDistanceRate() != 0.) { LOG.error("Monetary distance cost rate for " + mode + " different from zero, but not used in accessibility computations"); } - logitScaleParameter = planCalcScoreConfigGroup.getBrainExpBeta(); + logitScaleParameter = scoringConfigGroup.getBrainExpBeta(); - if (config.plansCalcRoute().getTeleportedModeSpeeds().get(mode) == null) { + if (config.routing().getTeleportedModeSpeeds().get(mode) == null) { LOG.error("No teleported mode speed for mode " + mode + " set."); } - this.modeSpeed_m_h = config.plansCalcRoute().getTeleportedModeSpeeds().get(mode) * 3600.; + this.modeSpeed_m_h = config.routing().getTeleportedModeSpeeds().get(mode) * 3600.; - final PlanCalcScoreConfigGroup.ModeParams modeParams = planCalcScoreConfigGroup.getOrCreateModeParams(mode); - betaModeTT = modeParams.getMarginalUtilityOfTraveling() - planCalcScoreConfigGroup.getPerforming_utils_hr(); + final ScoringConfigGroup.ModeParams modeParams = scoringConfigGroup.getOrCreateModeParams(mode); + betaModeTT = modeParams.getMarginalUtilityOfTraveling() - scoringConfigGroup.getPerforming_utils_hr(); betaModeTD = modeParams.getMarginalUtilityOfDistance(); constMode = modeParams.getConstant(); - betaWalkTT = planCalcScoreConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - planCalcScoreConfigGroup.getPerforming_utils_hr(); - betaWalkTD = planCalcScoreConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfDistance(); - this.walkSpeed_m_h = config.plansCalcRoute().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600; + betaWalkTT = scoringConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - scoringConfigGroup.getPerforming_utils_hr(); + betaWalkTD = scoringConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfDistance(); + this.walkSpeed_m_h = config.routing().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600; } @@ -124,14 +124,14 @@ public void initialize(ActivityFacilities measuringPoints, ActivityFacilities op } - + @Override public void notifyNewOriginNode(Id fromNodeId, Double departureTime) { this.fromNode = subNetwork.getNodes().get(fromNodeId); this.lcptTravelDistance.calculate(subNetwork, fromNode, departureTime); } - + @Override public double computeContributionOfOpportunity(ActivityFacility origin, Map, AggregationObject> aggregatedOpportunities, Double departureTime) { diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/LeastCostPathCalculatorAccessibilityContributionCalculator.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/LeastCostPathCalculatorAccessibilityContributionCalculator.java index 74dbdfeffa2..ee526dafc6d 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/LeastCostPathCalculatorAccessibilityContributionCalculator.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/LeastCostPathCalculatorAccessibilityContributionCalculator.java @@ -5,7 +5,7 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Node; import org.matsim.contrib.accessibility.utils.AggregationObject; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.facilities.ActivityFacilities; import org.matsim.facilities.ActivityFacility; @@ -18,7 +18,7 @@ */ final class LeastCostPathCalculatorAccessibilityContributionCalculator implements AccessibilityContributionCalculator { private final LeastCostPathCalculator leastCostPathCalculator; - private final PlanCalcScoreConfigGroup planCalcScoreConfigGroup; + private final ScoringConfigGroup scoringConfigGroup; private Node fromNode; private Double departureTime; private Scenario scenario; @@ -27,8 +27,8 @@ final class LeastCostPathCalculatorAccessibilityContributionCalculator implement Map, AggregationObject> aggregatedOpportunities; - public LeastCostPathCalculatorAccessibilityContributionCalculator(PlanCalcScoreConfigGroup planCalcScoreConfigGroup, LeastCostPathCalculator leastCostPathCalculator, Scenario scenario) { - this.planCalcScoreConfigGroup = planCalcScoreConfigGroup; + public LeastCostPathCalculatorAccessibilityContributionCalculator(ScoringConfigGroup scoringConfigGroup, LeastCostPathCalculator leastCostPathCalculator, Scenario scenario) { + this.scoringConfigGroup = scoringConfigGroup; this.leastCostPathCalculator = leastCostPathCalculator; this.scenario = scenario; } @@ -55,7 +55,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, for (final AggregationObject destination : aggregatedOpportunities.values()) { LeastCostPathCalculator.Path path = leastCostPathCalculator.calcLeastCostPath(fromNode, (Node) destination.getNearestBasicLocation(), departureTime, null, null); - expSum += destination.getSum() * Math.exp(planCalcScoreConfigGroup.getBrainExpBeta() * path.travelCost); + expSum += destination.getSum() * Math.exp(scoringConfigGroup.getBrainExpBeta() * path.travelCost); } return expSum; } @@ -64,7 +64,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, @Override public LeastCostPathCalculatorAccessibilityContributionCalculator duplicate() { LeastCostPathCalculatorAccessibilityContributionCalculator leastCostPathCalculatorAccessibilityContributionCalculator = - new LeastCostPathCalculatorAccessibilityContributionCalculator(this.planCalcScoreConfigGroup, this.leastCostPathCalculator, this.scenario); + new LeastCostPathCalculatorAccessibilityContributionCalculator(this.scoringConfigGroup, this.leastCostPathCalculator, this.scenario); return leastCostPathCalculatorAccessibilityContributionCalculator; } diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/NetworkModeAccessibilityExpContributionCalculator.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/NetworkModeAccessibilityExpContributionCalculator.java index cfaf0906478..ff5aee3d9fa 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/NetworkModeAccessibilityExpContributionCalculator.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/NetworkModeAccessibilityExpContributionCalculator.java @@ -12,7 +12,7 @@ import org.matsim.contrib.accessibility.utils.*; import org.matsim.contrib.roadpricing.RoadPricingScheme; import org.matsim.core.config.groups.NetworkConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.gbl.Gbl; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.TransportModeNetworkFilter; @@ -40,7 +40,7 @@ final class NetworkModeAccessibilityExpContributionCalculator implements Accessi private final Scenario scenario; private final TravelDisutility travelDisutility; - private final PlanCalcScoreConfigGroup planCalcScoreConfigGroup; + private final ScoringConfigGroup scoringConfigGroup; private final NetworkConfigGroup networkConfigGroup; private Network subNetwork; @@ -68,7 +68,7 @@ public NetworkModeAccessibilityExpContributionCalculator(String mode, final Trav Gbl.assertNotNull(travelDisutilityFactory); this.travelDisutility = travelDisutilityFactory.createTravelDisutility(travelTime); - planCalcScoreConfigGroup = scenario.getConfig().planCalcScore(); + scoringConfigGroup = scenario.getConfig().scoring(); networkConfigGroup = scenario.getConfig().network(); RoadPricingScheme scheme = (RoadPricingScheme) scenario.getScenarioElement( RoadPricingScheme.ELEMENT_NAME ); @@ -78,9 +78,9 @@ public NetworkModeAccessibilityExpContributionCalculator(String mode, final Trav //FastMultiNodeDijkstraFactory fastMultiNodeDijkstraFactory = new FastMultiNodeDijkstraFactory(true); //this.multiNodePathCalculator = (MultiNodePathCalculator) fastMultiNodeDijkstraFactory.createPathCalculator(network, travelDisutility, travelTime); - betaWalkTT = planCalcScoreConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - planCalcScoreConfigGroup.getPerforming_utils_hr(); + betaWalkTT = scoringConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - scoringConfigGroup.getPerforming_utils_hr(); - this.walkSpeed_m_s = scenario.getConfig().plansCalcRoute().getTeleportedModeSpeeds().get(TransportMode.walk); + this.walkSpeed_m_s = scenario.getConfig().routing().getTeleportedModeSpeeds().get(TransportMode.walk); } @@ -137,7 +137,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, double congestedCarUtilityRoad2Node = -travelDisutility.getLinkTravelDisutility(nearestLink, departureTime, null, null) * distanceFraction; // Combine all utility components (using the identity: exp(a+b) = exp(a) * exp(b)) - double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, planCalcScoreConfigGroup); + double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, scoringConfigGroup); for (final AggregationObject destination : aggregatedOpportunities.values()) { @@ -151,7 +151,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, // Pre-computed effect of all opportunities reachable from destination network node double sumExpVjkWalk = destination.getSum(); - expSum += Math.exp(this.planCalcScoreConfigGroup.getBrainExpBeta() * (walkUtilityMeasuringPoint2Road + modeSpecificConstant + expSum += Math.exp(this.scoringConfigGroup.getBrainExpBeta() * (walkUtilityMeasuringPoint2Road + modeSpecificConstant + congestedCarUtilityRoad2Node + congestedCarUtility)) * sumExpVjkWalk; } return expSum; diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/SwissRailRaptorAccessibilityContributionCalculator.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/SwissRailRaptorAccessibilityContributionCalculator.java index b64c6fa7b4e..6fcd4466276 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/SwissRailRaptorAccessibilityContributionCalculator.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/SwissRailRaptorAccessibilityContributionCalculator.java @@ -25,7 +25,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.accessibility.utils.AggregationObject; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.gbl.Gbl; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.facilities.*; @@ -44,7 +44,7 @@ class SwissRailRaptorAccessibilityContributionCalculator implements Accessibilit private static final Logger LOG = LogManager.getLogger( SwissRailRaptorAccessibilityContributionCalculator.class ); private SwissRailRaptor raptor; private String mode; - private PlanCalcScoreConfigGroup planCalcScoreConfigGroup; + private ScoringConfigGroup scoringConfigGroup; private Scenario scenario; Map, ArrayList> aggregatedMeasurePoints; @@ -58,7 +58,7 @@ class SwissRailRaptorAccessibilityContributionCalculator implements Accessibilit Map, Collection> stopsPerAggregatedOpportunity = new LinkedHashMap<>(); - public SwissRailRaptorAccessibilityContributionCalculator(String mode, PlanCalcScoreConfigGroup planCalcScoreConfigGroup, Scenario scenario) { + public SwissRailRaptorAccessibilityContributionCalculator(String mode, ScoringConfigGroup scoringConfigGroup, Scenario scenario) { this.mode = mode; TransitSchedule schedule = scenario.getTransitSchedule(); @@ -69,12 +69,12 @@ public SwissRailRaptorAccessibilityContributionCalculator(String mode, PlanCalcS this.raptorData = SwissRailRaptorData.create(schedule, null, raptorConfig, ptNetwork, null); this.raptor = new SwissRailRaptor.Builder(raptorData, scenario.getConfig()).build(); - this.planCalcScoreConfigGroup = planCalcScoreConfigGroup; + this.scoringConfigGroup = scoringConfigGroup; this.scenario = scenario; - this.betaWalkTT = planCalcScoreConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - planCalcScoreConfigGroup.getPerforming_utils_hr(); + this.betaWalkTT = scoringConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - scoringConfigGroup.getPerforming_utils_hr(); - this.walkSpeed_m_h = scenario.getConfig().plansCalcRoute().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600.; + this.walkSpeed_m_h = scenario.getConfig().routing().getTeleportedModeSpeeds().get(TransportMode.walk) * 3600.; } @@ -167,8 +167,8 @@ public double computeContributionOfOpportunity(ActivityFacility origin, //check whether direct walk time is cheaper travelCost = Math.min(travelCost, directWalkCost); - double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, planCalcScoreConfigGroup); - expSum += Math.exp(this.planCalcScoreConfigGroup.getBrainExpBeta() * (-travelCost + modeSpecificConstant)); + double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, scoringConfigGroup); + expSum += Math.exp(this.scoringConfigGroup.getBrainExpBeta() * (-travelCost + modeSpecificConstant)); } return expSum; } @@ -177,7 +177,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, @Override public SwissRailRaptorAccessibilityContributionCalculator duplicate() { SwissRailRaptorAccessibilityContributionCalculator swissRailRaptorAccessibilityContributionCalculator = - new SwissRailRaptorAccessibilityContributionCalculator(this.mode, this.planCalcScoreConfigGroup, this.scenario); + new SwissRailRaptorAccessibilityContributionCalculator(this.mode, this.scoringConfigGroup, this.scenario); swissRailRaptorAccessibilityContributionCalculator.aggregatedMeasurePoints = this.aggregatedMeasurePoints; swissRailRaptorAccessibilityContributionCalculator.aggregatedOpportunities = this.aggregatedOpportunities; swissRailRaptorAccessibilityContributionCalculator.stopsPerAggregatedOpportunity = this.stopsPerAggregatedOpportunity; diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TravelTimeBasedTravelDisutility.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TravelTimeBasedTravelDisutility.java index 34ee8375753..6a70a5911fa 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TravelTimeBasedTravelDisutility.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TravelTimeBasedTravelDisutility.java @@ -18,30 +18,30 @@ * *********************************************************************** */ /** - * + * */ package org.matsim.contrib.accessibility; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.router.util.TravelDisutility; import org.matsim.core.router.util.TravelTime; import org.matsim.vehicles.Vehicle; /** * A simple cost calculator which only respects time to calculate generalized costs - * This is based on org.matsim.core.router.costcalculators.TravelTimeAndDistanceBasedTravelDisutility + * This is based on org.matsim.core.router.costcalculators.TravelTimeAndDistanceBasedTravelDisutility * * @author mrieser, thomas */ class TravelTimeBasedTravelDisutility implements TravelDisutility{ - + protected final TravelTime timeCalculator; private final double marginalCostOfTime; - public TravelTimeBasedTravelDisutility(final TravelTime timeCalculator, PlanCalcScoreConfigGroup cnScoringGroup) { + public TravelTimeBasedTravelDisutility(final TravelTime timeCalculator, ScoringConfigGroup cnScoringGroup) { this.timeCalculator = timeCalculator; /* Usually, the travel-utility should be negative (it's a disutility) * but the cost should be positive. Thus negate the utility. diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TripRouterAccessibilityContributionCalculator.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TripRouterAccessibilityContributionCalculator.java index 0f3dece8928..2f027d9863c 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TripRouterAccessibilityContributionCalculator.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/TripRouterAccessibilityContributionCalculator.java @@ -38,7 +38,7 @@ import org.matsim.contrib.accessibility.utils.Distances; import org.matsim.contrib.accessibility.utils.NetworkUtil; import org.matsim.core.config.groups.NetworkConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.gbl.Gbl; import org.matsim.core.network.NetworkUtils; import org.matsim.core.router.TripRouter; @@ -60,7 +60,7 @@ class TripRouterAccessibilityContributionCalculator implements AccessibilityCont private static final Logger LOG = LogManager.getLogger( TripRouterAccessibilityContributionCalculator.class ); private TripRouter tripRouter ; private String mode; - private PlanCalcScoreConfigGroup planCalcScoreConfigGroup; + private ScoringConfigGroup scoringConfigGroup; private NetworkConfigGroup networkConfigGroup; private Scenario scenario; @@ -79,18 +79,18 @@ class TripRouterAccessibilityContributionCalculator implements AccessibilityCont private final TravelTime travelTime; - public TripRouterAccessibilityContributionCalculator(String mode, TripRouter tripRouter, PlanCalcScoreConfigGroup planCalcScoreConfigGroup, Scenario scenario, - TravelTime travelTime, TravelDisutilityFactory travelDisutilityFactory) { + public TripRouterAccessibilityContributionCalculator(String mode, TripRouter tripRouter, ScoringConfigGroup scoringConfigGroup, Scenario scenario, + TravelTime travelTime, TravelDisutilityFactory travelDisutilityFactory) { LOG.warn("This is currently heavliy oriented on the need of car-based computatations. Revise beofre using for other modes!"); this.mode = mode; this.tripRouter = tripRouter; - this.planCalcScoreConfigGroup = planCalcScoreConfigGroup; + this.scoringConfigGroup = scoringConfigGroup; this.networkConfigGroup = scenario.getConfig().network(); this.scenario = scenario; - betaWalkTT = planCalcScoreConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - planCalcScoreConfigGroup.getPerforming_utils_hr(); + betaWalkTT = scoringConfigGroup.getModes().get(TransportMode.walk).getMarginalUtilityOfTraveling() - scoringConfigGroup.getPerforming_utils_hr(); - this.walkSpeed_m_s = scenario.getConfig().plansCalcRoute().getTeleportedModeSpeeds().get(TransportMode.walk); + this.walkSpeed_m_s = scenario.getConfig().routing().getTeleportedModeSpeeds().get(TransportMode.walk); this.travelTime = travelTime; this.travelDisutilityFactory = travelDisutilityFactory; @@ -152,21 +152,21 @@ public double computeContributionOfOpportunity(ActivityFacility origin, // Note: The following computation where the end link length is added is only correct once the end link is removed from the route // in the NetworkRoutingModule (route.setDistance(RouteUtils.calcDistance(route, 1.0, 0.0, this.network));) - if (this.planCalcScoreConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfDistance() != 0.) { + if (this.scoringConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfDistance() != 0.) { LOG.warn("A computation including a marginal utility of distance will only be correct if the route time/distance" + "inconsistency in the NetworkRoutingModule is solved."); } - utility += (leg.getRoute().getDistance() + endLinkLength) * this.planCalcScoreConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfDistance(); - utility += (leg.getRoute().getTravelTime().seconds() + estimatedEndLinkTT) * this.planCalcScoreConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfTraveling() / 3600.; - utility += -(leg.getRoute().getTravelTime().seconds() + estimatedEndLinkTT) * this.planCalcScoreConfigGroup.getPerforming_utils_hr() / 3600.; + utility += (leg.getRoute().getDistance() + endLinkLength) * this.scoringConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfDistance(); + utility += (leg.getRoute().getTravelTime().seconds() + estimatedEndLinkTT) * this.scoringConfigGroup.getModes().get(leg.getMode()).getMarginalUtilityOfTraveling() / 3600.; + utility += -(leg.getRoute().getTravelTime().seconds() + estimatedEndLinkTT) * this.scoringConfigGroup.getPerforming_utils_hr() / 3600.; } // Utility based on opportunities that are attached to destination node double sumExpVjkWalk = destination.getSum(); // exp(beta * a) * exp(beta * b) = exp(beta * (a+b)) - double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, planCalcScoreConfigGroup); - expSum += Math.exp(this.planCalcScoreConfigGroup.getBrainExpBeta() * (utility + modeSpecificConstant + walkUtilityMeasuringPoint2Road + congestedCarUtilityRoad2Node)) * sumExpVjkWalk; + double modeSpecificConstant = AccessibilityUtils.getModeSpecificConstantForAccessibilities(mode, scoringConfigGroup); + expSum += Math.exp(this.scoringConfigGroup.getBrainExpBeta() * (utility + modeSpecificConstant + walkUtilityMeasuringPoint2Road + congestedCarUtilityRoad2Node)) * sumExpVjkWalk; } return expSum; @@ -176,7 +176,7 @@ public double computeContributionOfOpportunity(ActivityFacility origin, @Override public TripRouterAccessibilityContributionCalculator duplicate() { TripRouterAccessibilityContributionCalculator tripRouterAccessibilityContributionCalculator = - new TripRouterAccessibilityContributionCalculator(this.mode, this.tripRouter, this.planCalcScoreConfigGroup, + new TripRouterAccessibilityContributionCalculator(this.mode, this.tripRouter, this.scoringConfigGroup, this.scenario, this.travelTime, this.travelDisutilityFactory); tripRouterAccessibilityContributionCalculator.subNetwork = this.subNetwork; tripRouterAccessibilityContributionCalculator.aggregatedMeasurePoints = this.aggregatedMeasurePoints; diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/run/RunAccessibilityExample.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/run/RunAccessibilityExample.java index 53d94a2c1de..7db337954bd 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/run/RunAccessibilityExample.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/run/RunAccessibilityExample.java @@ -46,7 +46,7 @@ public static void main(String[] args) { throw new RuntimeException("No config.xml file provided. The config file needs to reference a network file and a facilities file.") ; } Config config = ConfigUtils.loadConfig(args[0]); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); AccessibilityConfigGroup accConfig = ConfigUtils.addOrGetModule(config, AccessibilityConfigGroup.class ) ; accConfig.setComputingAccessibilityForMode(Modes4Accessibility.freespeed, true); @@ -54,11 +54,11 @@ public static void main(String[] args) { run(scenario); // The run method is extracted so that a test can operate on it. } - + public static void run(final Scenario scenario) { List activityTypes = AccessibilityUtils.collectAllFacilityOptionTypes(scenario); LOG.info("The following activity types were found: " + activityTypes); - + Controler controler = new Controler(scenario); for (final String actType : activityTypes) { // Add an overriding module for each activity type. final AccessibilityModule module = new AccessibilityModule(); diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GnuplotScriptWriter.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GnuplotScriptWriter.java index d9c1c029634..797211edc2b 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GnuplotScriptWriter.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/GnuplotScriptWriter.java @@ -34,7 +34,7 @@ */ public final class GnuplotScriptWriter { private static final Logger log = LogManager.getLogger( GnuplotScriptWriter.class ) ; - + private GnuplotScriptWriter() {} // do not instantiate public static void createGnuplotScript(Config config, List activityTypes) { @@ -49,37 +49,37 @@ public static void createGnuplotScript(Config config, List activityTypes log.error("skipping everything except freespeed for debugging purposes; remove in production code. dz, nov'14") ; continue ; } - + // the following might be made configurable if this is ever re-used: Boolean doPopulationWeightedPlot = true ; Boolean doNonPopulationWeightedPlot = true ; - - + + if (doPopulationWeightedPlot == false && doNonPopulationWeightedPlot == false) { throw new RuntimeException("At least one plot (pop-weighted or non-pop-weighted) needs to be activated."); } - + // produce gnuplot scripts try { - BufferedWriter writer = IOUtils.getBufferedWriter( config.controler().getOutputDirectory() + "/" + actType + BufferedWriter writer = IOUtils.getBufferedWriter( config.controller().getOutputDirectory() + "/" + actType + "/" + mode + "/t.gpl" ) ; // pm3d is an splot style for drawing palette-mapped 3d and 4d data as color/gray maps and surfaces (docu p.134) // To use pm3d coloring to generate a two-dimensional plot rather than a 3D surface, use set view map // or set pm3d map (p.135) writer.write("set pm3d map\n"); - + // flush { begin | center | end } writer.write("set pm3d flush begin\n") ; - + // corners2color { mean|geomean|median|min|max|c1|c2|c3|c4 } writer.write("set pm3d corners2color c1\n"); - + // The set style data command changes the default plotting style for data plots (docu p.148) // There are many plotting styles available in gnuplot. They are listed alphabetically below. The commands // set style data and set style function change the default plotting style for subsequent plot and splot // commands. (docu p.42) writer.write("set style data pm3d\n"); - + // Palette is a color storage for use by pm3d, filled color contours or polygons, color histograms, color gradient // background, and whatever it is or it will be implemented (docu p.137) // Gray-to-rgb mapping can be manually set by use of palette defined: A color gradient is defined and used @@ -87,86 +87,86 @@ public static void createGnuplotScript(Config config, List activityTypes // space [0,1]x[0,1]x[0,1]. You must specify the gray values and the corresponding RGB values between which // linear interpolation will be done (docu p.139) writer.write("set palette defined ( 0. '#ff0000', 0.82 '#ff0000', 0.86 '#00ff00', 0.9 '#0000ff', 1.0 '#0000ff' )\n"); - + // The set zrange command sets the range that will be displayed on the z axis. The zrange is used only by // splot and is ignored by plot (docu p.166) writer.write("#set zrange [-40:10]\n"); - + // The set cbrange command sets the range of values which are colored using the current palette by styles // with pm3d, with image and with palette. Values outside of the color range use color of the nearest // extreme (docu p.167) writer.write("#set cbrange [-0:10]\n"); - + // gnuplot supports many different graphics devices. Use set terminal to tell gnuplot what kind of output // to generate (docu p.152) // This terminal produces files in the Adobe Portable Document Format (PDF), useable for printing or display // with tools like Acrobat Reader (docu p.206) //writer.write("set term pdf size 25cm,20cm\n"); writer.write("set term pdf font 'Helvetica,6' size 25cm,20cm\n"); - + // The set view command sets the viewing angle for splots. It controls how the 3D coordinates of the plot are // mapped into the 2D screen space. It provides controls for both rotation and scaling of the plotted data, but // supports orthographic projections only (docu p.156) writer.write("#set view 45,30;\n"); writer.write("\n") ; - + // see docu p.137 writer.write("# set palette model HSV functions gray, 1, 1\n"); writer.write("\n"); - + // New user-defined variables and functions of one through twelve variables may be declared and used anywhere, // including on the plot command itself (docu p.30) // define minimum and maximum functions writer.write("min(a,b) = (a < b) ? a : b\n"); writer.write("max(a,b) = (a < b) ? b : a\n"); - + // define two variables writer.write("accmin=3 ; # accessibilities below this are red\n"); writer.write("accmax=9 ; # accessibilities above this are blue. max is around 12\n"); - + // define a function to determine the shade of gray. gray(acc) will have a value from 0 through 1 // Acc values equal to or below accmin lead to 0; acc values equal to or above accmax lead to 1 writer.write("gray(acc) = 2.*min( 1, max(0 , (acc-accmin)/(accmax-accmin) ) ) ;\n") ; writer.write("# I have no idea why this needs to be multiplied by 2. kai, feb'14\n") ; writer.write("\n") ; - + // consider population density // define two variables writer.write("densmax=1000 ; # 2726 is the maximum value in NMB\n") ; writer.write("maxwhite = 240 ; # 255 means that we go all the way to full white\n") ; - + while (doPopulationWeightedPlot == true || doNonPopulationWeightedPlot == true) { // define a function that gets the higher the smaller the population density // maxwhite*1 for pop dens = 0; maxwhite*0 for pop dens higher than densmax if (doPopulationWeightedPlot == true) { writer.write("val(dens) = max(0,maxwhite*(densmax-dens)/densmax) ;\n") ; - + // By default, screens are displayed to the standard output. The set output command redirects the display // to the specified file or device (docu p.132) writer.write("set out 'accessibility-pop-weighted.pdf'\n"); - + // The set title command produces a plot title that is centered at the top of the plot. set title is a special // case of set label (docu p.155) //writer.write("set title 'accessibility to " + actType + " by " + mode + " (population-weighted)'\n") ; - + doPopulationWeightedPlot = false; - + } else if (doNonPopulationWeightedPlot == true) { writer.write("val(dens) = 0. ; # unset this comment to get the non-pop-weighted version (for paper)\n"); writer.write("set out 'accessibility.pdf'\n"); //writer.write("set title 'accessibility to " + actType + " by " + mode + " (non-population-weighted)'\n") ; - + doNonPopulationWeightedPlot = false; } writer.write("\n") ; - + // define three color functions // so far not clear to me where the functions come from, dz mai14 writer.write("blue(acc,dens) = min(255, val(dens)+255*max(0,1.-2.*gray(acc)) ) ;\n") ; writer.write("green(acc,dens) = min(255, val(dens)+255*max(0,min(2.*gray(acc),2.-2.*gray(acc))) ) ;\n") ; writer.write("red(acc,dens) = min(255, val(dens)+255*max(0,2.*gray(acc)-1) ) ;\n") ; writer.write("\n") ; - + // define color function based on accessibility and population density // int(x) = integer part of x, truncated toward zero (docu p.26) // Example: rgb(r,g,b) = 65536 * int(r) + 256 * int(g) + int(b) (docu p.35) @@ -175,8 +175,8 @@ public static void createGnuplotScript(Config config, List activityTypes writer.write("\n") ; writer.write("unset colorbox ; # useless with lc rgb variable\n") ; writer.write("\n") ; // end new - - // plot csv file based on three values; first two are coordinates; third takes into account accessibility and + + // plot csv file based on three values; first two are coordinates; third takes into account accessibility and // population density and is calculated based on above-defined rgb formula // The "lc rgbcolor variable" tells the program to read RGB color information for each line in the data file. // This requires a corresponding additional column in the using specifier. The extra column is interpreted as a @@ -186,27 +186,27 @@ public static void createGnuplotScript(Config config, List activityTypes // writer.write("splot \"accessibilities.csv\" u 1:2:(rgb($3,$8)) lc rgb variable\n"); // writer.write("splot \"accessibilities.csv\" u 1:2:(rgb($3,$8)) notitle lc rgb variable\n"); } - + writer.close(); } catch (Exception ee ) { - ee.printStackTrace(); + ee.printStackTrace(); throw new RuntimeException( "writing t.gpl did not work") ; } - - + + // start running gnuplot with above-created script - + // If working on a Windows system, it is important that the environment variable PATH includes the gnuplot folder // so that gnuplot can be run from the folder where the data is stored (otherwise the relative paths won't work) String cmd = "gnuplot t.gpl"; - + // Doesn't work if root of directory is not passed - String stdoutFileName = config.controler().getOutputDirectory() + actType + "/" + mode + "/gnuplot.log"; + String stdoutFileName = config.controller().getOutputDirectory() + actType + "/" + mode + "/gnuplot.log"; // String stdoutFileName = config.controler().getOutputDirectory() + "gnuplot.log"; int timeout = 99999; - + // 4th argument = workingDirectory. Since we are working with relative paths, workingDirectory needs to be passed. - ExeRunner.run(cmd, stdoutFileName, timeout, config.controler().getOutputDirectory() + actType + "/" + mode); + ExeRunner.run(cmd, stdoutFileName, timeout, config.controller().getOutputDirectory() + actType + "/" + mode); } } } diff --git a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/LeastCostPathTreeExtended.java b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/LeastCostPathTreeExtended.java index 0378f9c7f71..74d2dd7d216 100644 --- a/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/LeastCostPathTreeExtended.java +++ b/contribs/accessibility/src/main/java/org/matsim/contrib/accessibility/utils/LeastCostPathTreeExtended.java @@ -18,7 +18,7 @@ * *********************************************************************** */ /** - * + * */ package org.matsim.contrib.accessibility.utils; @@ -36,7 +36,7 @@ import org.matsim.contrib.matrixbasedptrouter.utils.TempDirectoryUtil; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.network.NetworkUtils; import org.matsim.core.router.util.TravelDisutility; @@ -54,17 +54,17 @@ * accumulate distance and toll separately along the route. *

* To re-iterate: This class does not lead to a different tree than the base class. - * + * * @author thomas - * + * */ public final class LeastCostPathTreeExtended extends LeastCostPathTree{ - + protected static final Logger log = LogManager.getLogger(LeastCostPathTreeExtended.class); - + private Map, NodeDataExtended> nodeDataExt = null; private RoadPricingScheme scheme = null; - + /** * constructor * @param controler Controler, to get the RoadPricingScheme if available @@ -75,13 +75,13 @@ public LeastCostPathTreeExtended(final TravelTime tt, final TravelDisutility td, } /** - * + * * @param network * @param origin * @param time */ public final void calculateExtended(final Network network, final Node origin, final double time) { - + this.nodeDataExt = new ConcurrentHashMap, NodeDataExtended>((int) (network.getNodes().size() * 1.1), 0.95f); if(this.nodeDataExt.get(origin.getId()) == null){ NodeDataExtended nde = new NodeDataExtended(); @@ -89,17 +89,17 @@ public final void calculateExtended(final Network network, final Node origin, fi nde.toll = 0.; this.nodeDataExt.put(origin.getId(), nde); } - + calculate(network, origin, time); } - + /** - * @param link - * @param currTime + * @param link + * @param currTime */ @Override protected final void additionalComputationsHook( final Link link, final double currTime ) { - + Node fromNode = link.getFromNode(); // get current distance and toll so far NodeDataExtended nde = nodeDataExt.get( fromNode.getId() ); @@ -113,10 +113,10 @@ protected final void additionalComputationsHook( final Link link, final double c if(cost != null) toll = cost.amount; } - + double visitDistance = currDistance + link.getLength(); double visitToll = currToll + toll; - + // put new nodes into nodeDataExtended Node toNode = link.getToNode(); NodeDataExtended ndeNew = this.nodeDataExt.get( toNode.getId() ); @@ -126,19 +126,19 @@ protected final void additionalComputationsHook( final Link link, final double c } ndeNew.visit(visitDistance, visitToll); } - + // //////////////////////////////////////////////////////////////////// // get methods // //////////////////////////////////////////////////////////////////// - + public final Map, NodeDataExtended> getTreeExtended() { return this.nodeDataExt; } - + // //////////////////////////////////////////////////////////////////// // inner classes // //////////////////////////////////////////////////////////////////// - + public static class NodeDataExtended { private double distance = 0.; // meter private double toll = 0.; // money @@ -161,11 +161,11 @@ public final double getToll() { return this.toll; } } - + // //////////////////////////////////////////////////////////////////// - // testing + // testing // //////////////////////////////////////////////////////////////////// - + /** * for testing * @param args @@ -173,13 +173,13 @@ public final double getToll() { public static void main(String args[]){ // create temp output dir String tmpOutputLocation = TempDirectoryUtil.createCustomTempDirectory("test"); - + // create network Network network = LeastCostPathTreeExtended.createTriangularNetwork(); // create scenario Config config = ConfigUtils.createConfig(); // set last iteration and output - ControlerConfigGroup controlerCG = (ControlerConfigGroup) config.getModule(ControlerConfigGroup.GROUP_NAME); + ControllerConfigGroup controlerCG = (ControllerConfigGroup) config.getModule(ControllerConfigGroup.GROUP_NAME); controlerCG.setLastIteration( 1 ); controlerCG.setOutputDirectory( tmpOutputLocation ); // set scenario @@ -190,7 +190,7 @@ public static void main(String args[]){ controler.run(); // init lcpte LeastCostPathTreeExtended lcpte = new LeastCostPathTreeExtended(controler.getLinkTravelTimes(), controler.createTravelDisutilityCalculator(), (RoadPricingSchemeImpl) controler.getScenario().getScenarioElement(RoadPricingScheme.ELEMENT_NAME)); - + // contains all network nodes Map, ? extends Node> networkNodesMap = network.getNodes(); Id originNodeID = Id.create(1, Node.class); @@ -201,15 +201,15 @@ public static void main(String args[]){ double disutility = lcpte.getTree().get( destinationNodeId ).getCost(); double distance = lcpte.getTreeExtended().get( destinationNodeId ).getDistance(); double toll = lcpte.getTreeExtended().get( destinationNodeId ).getToll(); - + log.info("Time = " + time); - log.info("Disutility = " + disutility); + log.info("Disutility = " + disutility); log.info("Distance = " + distance ); log.info("Toll = " + toll); - + TempDirectoryUtil.cleanUpCustomTempDirectories(); } - + /** * creating a test network * the path 1,2,4 has a total length of 1000m with a free speed travel time of 10m/s @@ -226,12 +226,12 @@ private static Network createTriangularNetwork() { * / \ * / \ *(1)-------(3)-------(4) - *(50m,0.1m/s)(50m,0.1m/s) + *(50m,0.1m/s)(50m,0.1m/s) */ MutableScenario scenario = (MutableScenario) ScenarioUtils.createScenario(ConfigUtils.createConfig()); Network network = (Network) scenario.getNetwork(); - + // add nodes Node node1 = NetworkUtils.createAndAddNode(network, Id.create(1, Node.class), new Coord(0, 0)); Node node2 = NetworkUtils.createAndAddNode(network, Id.create(2, Node.class), new Coord(50, 100)); @@ -251,8 +251,8 @@ private static Network createTriangularNetwork() { final Node fromNode3 = node3; final Node toNode3 = node4; NetworkUtils.createAndAddLink(network,Id.create(4, Link.class), fromNode3, toNode3, 50.0, 0.1, 3600.0, (double) 1 ); - + return network; } - + } diff --git a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/AccessibilityIntegrationTest.java b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/AccessibilityIntegrationTest.java index fa3e94febc8..b843e1ae3b5 100644 --- a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/AccessibilityIntegrationTest.java +++ b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/AccessibilityIntegrationTest.java @@ -49,7 +49,7 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.FacilitiesConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ModeParams; +import org.matsim.core.config.groups.ScoringConfigGroup.ModeParams; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; @@ -132,14 +132,14 @@ public void testWithBoundingBoxConfigFile() { acg.setUseParallelization(false); ModeParams ptParams = new ModeParams(TransportMode.transit_walk); - config.planCalcScore().addModeParams(ptParams); + config.scoring().addModeParams(ptParams); MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class) ; - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setRoutingRandomness(0.); final Scenario sc = ScenarioUtils.loadScenario(config); - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); Controler controler = new Controler(sc); @@ -171,13 +171,13 @@ public void testWithBoundingBox() { acg.setBoundingBoxTop(max); acg.setBoundingBoxLeft(min); acg.setBoundingBoxRight(max); - - config.plansCalcRoute().setRoutingRandomness(0.); + + config.routing().setRoutingRandomness(0.); final Scenario sc = createTestScenario(config); MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class ) ; - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); Controler controler = new Controler(sc); @@ -213,11 +213,11 @@ public void testWithBoundingBoxUsingOpportunityWeights() { acg.setUseOpportunityWeights(true); acg.setWeightExponent(2.); - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setRoutingRandomness(0.); final Scenario sc = createTestScenarioUsingOpportunityWeights(config) ; MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class ) ; - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); Controler controler = new Controler(sc); @@ -240,11 +240,11 @@ public void install() { public void testWithExtentDeterminedByNetwork() { final Config config = createTestConfig() ; - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setRoutingRandomness(0.); final Scenario sc = createTestScenario(config) ; MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class ) ; - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); Controler controler = new Controler(sc); @@ -280,11 +280,11 @@ public void testWithExtentDeterminedShapeFile() { // acg.setShapeFileCellBasedAccessibility(url.getPath()); // yyyyyy todo acg.setShapeFileCellBasedAccessibility(f.getAbsolutePath()); - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setRoutingRandomness(0.); final Scenario sc = createTestScenario(config) ; MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class ) ; - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); Controler controler = new Controler(sc); @@ -329,10 +329,10 @@ public void testWithPredefinedMeasuringPoints() { final Scenario sc = createTestScenario(config) ; MatrixBasedPtRouterConfigGroup mbConfig = ConfigUtils.addOrGetModule(config, MatrixBasedPtRouterConfigGroup.class ) ; - final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.plansCalcRoute(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; + final PtMatrix ptMatrix = PtMatrix.createPtMatrix(config.routing(), BoundingBox.createBoundingBox(sc.getNetwork()), mbConfig) ; sc.addScenarioElement(PtMatrix.NAME, ptMatrix); - - config.plansCalcRoute().setRoutingRandomness(0.); + + config.routing().setRoutingRandomness(0.); Controler controler = new Controler(sc); @@ -409,7 +409,7 @@ private Config createTestConfig() { config.transit().setVehiclesFile(utils.getClassInputDirectory() + "vehicles.xml"); ModeParams ptParams = new ModeParams(TransportMode.transit_walk); - config.planCalcScore().addModeParams(ptParams); + config.scoring().addModeParams(ptParams); MatrixBasedPtRouterConfigGroup mbConfig = new MatrixBasedPtRouterConfigGroup(); mbConfig.setPtStopsInputFile(utils.getClassInputDirectory() + "ptStops.csv"); @@ -419,9 +419,9 @@ private Config createTestConfig() { mbConfig.setUsingTravelTimesAndDistances(true); config.addModule(mbConfig); - config.controler().setLastIteration(0); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); return config; } diff --git a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyAccessibilityTest.java b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyAccessibilityTest.java index bb5bcc2d5e7..29283b26ede 100644 --- a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyAccessibilityTest.java +++ b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyAccessibilityTest.java @@ -24,7 +24,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.Coord; @@ -122,11 +121,11 @@ private Config createTestConfig() { acg.setComputingAccessibilityForMode(Modes4Accessibility.freespeed, true); acg.setComputingAccessibilityForMode(Modes4Accessibility.car, true); - config.controler().setLastIteration(0); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setRoutingRandomness(0.); return config; } diff --git a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyMultimodalAccessibilityTest.java b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyMultimodalAccessibilityTest.java index c2abfc8df95..bde5a98c612 100644 --- a/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyMultimodalAccessibilityTest.java +++ b/contribs/accessibility/src/test/java/org/matsim/contrib/accessibility/run/TinyMultimodalAccessibilityTest.java @@ -105,9 +105,9 @@ private Config createTestConfig() { acg.setComputingAccessibilityForMode(Modes4Accessibility.pt, true); acg.setUseParallelization(false); - config.controler().setLastIteration(0); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); return config; } diff --git a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/AccidentWriter.java b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/AccidentWriter.java index 7476fab2e96..8f0b09a8ae2 100644 --- a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/AccidentWriter.java +++ b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/AccidentWriter.java @@ -41,14 +41,14 @@ class AccidentWriter { private static String convertSecondToHHMMSSString(int nSecondTime) { return LocalTime.MIN.plusSeconds(nSecondTime).toString(); } - + public void write(Scenario scenario, IterationEndsEvent event, Map, AccidentLinkInfo> linkId2info, AnalysisEventHandler analzyer) { AccidentsConfigGroup accidentsCfg = (AccidentsConfigGroup) scenario.getConfig().getModules().get(AccidentsConfigGroup.GROUP_NAME); - + double timeBinSize = scenario.getConfig().travelTimeCalculator().getTraveltimeBinSize(); - + //File with Linkinfo for Tests - File linkInfoFile = new File(scenario.getConfig().controler().getOutputDirectory() + "ITERS/it." + event.getIteration() + "/" + scenario.getConfig().controler().getRunId() + "." + event.getIteration() + ".linkInfo.csv"); + File linkInfoFile = new File(scenario.getConfig().controller().getOutputDirectory() + "ITERS/it." + event.getIteration() + "/" + scenario.getConfig().controller().getRunId() + "." + event.getIteration() + ".linkInfo.csv"); BufferedWriter linkInformation = null; try { linkInformation = new BufferedWriter (new FileWriter(linkInfoFile)); @@ -62,7 +62,7 @@ public void write(Scenario scenario, IterationEndsEvent event, Map, Acc } linkInformation.write("demandPerDay ;"); linkInformation.newLine(); - + for (AccidentLinkInfo info : linkId2info.values()) { double demandPerDay = 0.0; linkInformation.write(info.getLinkId().toString()); @@ -77,15 +77,15 @@ public void write(Scenario scenario, IterationEndsEvent event, Map, Acc linkInformation.write(Double.toString(demand)); linkInformation.write(";"); } - linkInformation.write(Double.toString(demandPerDay)); + linkInformation.write(Double.toString(demandPerDay)); linkInformation.newLine(); } linkInformation.close(); } catch (IOException e3) { e3.printStackTrace(); } - - File accidentCostsBVWPFile = new File(scenario.getConfig().controler().getOutputDirectory() + "ITERS/it." + event.getIteration() + "/" + scenario.getConfig().controler().getRunId() + "." + event.getIteration() + ".accidentCosts_BVWP.csv"); + + File accidentCostsBVWPFile = new File(scenario.getConfig().controller().getOutputDirectory() + "ITERS/it." + event.getIteration() + "/" + scenario.getConfig().controller().getRunId() + "." + event.getIteration() + ".accidentCosts_BVWP.csv"); BufferedWriter accidentCostsBVWP = null; try { accidentCostsBVWP = new BufferedWriter (new FileWriter(accidentCostsBVWPFile)); @@ -100,7 +100,7 @@ public void write(Scenario scenario, IterationEndsEvent event, Map, Acc accidentCostsBVWP.write("Costs per Day [EUR] ;"); accidentCostsBVWP.write("Costs per Year [EUR] ;"); accidentCostsBVWP.newLine(); - + } catch (IOException e1) { e1.printStackTrace(); } @@ -109,7 +109,7 @@ public void write(Scenario scenario, IterationEndsEvent event, Map, Acc nf.setMaximumFractionDigits(2); nf.setGroupingUsed(false); - for (AccidentLinkInfo info : linkId2info.values()) { + for (AccidentLinkInfo info : linkId2info.values()) { double accidentCostsPerDay_BVWP = 0.0; double accidentCostsPerYear_BVWP = 0.0; @@ -123,12 +123,12 @@ public void write(Scenario scenario, IterationEndsEvent event, Map, Acc } catch (IOException e1) { e1.printStackTrace(); } - + for (double endTime = timeBinSize ; endTime <= scenario.getConfig().travelTimeCalculator().getMaxTime(); endTime = endTime + timeBinSize ) { - + double time = (endTime - timeBinSize/2.); int timeBinNr = (int) (time / timeBinSize); - + if (linkComputationMethod.toString().equals( AccidentsConfigGroup.AccidentsComputationMethod.BVWP.toString() )){ accidentCostsPerDay_BVWP += info.getTimeSpecificInfo().get(timeBinNr).getAccidentCosts(); try { diff --git a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/RunAccidents.java b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/RunAccidents.java index b9fe07dc88c..a25d3512e71 100644 --- a/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/RunAccidents.java +++ b/contribs/accidents/src/main/java/org/matsim/contrib/accidents/runExample/RunAccidents.java @@ -44,44 +44,44 @@ public class RunAccidents { private static final Logger log = LogManager.getLogger(RunAccidents.class); - - public static void main(String[] args) throws IOException { + + public static void main(String[] args) throws IOException { RunAccidents main = new RunAccidents(); main.run(); } private void run() throws MalformedURLException, IOException { log.info("Loading scenario..."); - + String configFile = "path/to/configFile.xml"; - + Config config = ConfigUtils.loadConfig(configFile ); - + AccidentsConfigGroup accidentsSettings = ConfigUtils.addOrGetModule(config, AccidentsConfigGroup.class); accidentsSettings.setEnableAccidentsModule(true); - + final Scenario scenario = ScenarioUtils.loadScenario(config); - + // Preprocess network AccidentsNetworkModification networkModification = new AccidentsNetworkModification(scenario); - + String[] tunnelLinks = readCSVFile("tunnelLinksCSVfile"); String[] planfreeLinks = readCSVFile("planfreeLinksCSVfile"); - + networkModification.setLinkAttributsBasedOnOSMFile("osmlandUseFile", "EPSG:31468" , tunnelLinks, planfreeLinks ); - + Controler controler = new Controler(scenario); controler.addOverridingModule(new AccidentsModule()); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); controler.run(); } - + private String[] readCSVFile(String csvFile) { ArrayList> links = new ArrayList<>(); BufferedReader br = IOUtils.getBufferedReader(csvFile); - + String line = null; try { line = br.readLine(); @@ -92,7 +92,7 @@ private String[] readCSVFile(String csvFile) { try { int countWarning = 0; while ((line = br.readLine()) != null) { - + String[] columns = line.split(";"); Id linkId = null; for (int column = 0; column < columns.length; column++) { @@ -105,7 +105,7 @@ private String[] readCSVFile(String csvFile) { log.warn("This message is only given once."); } countWarning++; - } + } } log.info("Adding link ID " + linkId); links.add(linkId); @@ -113,7 +113,7 @@ private String[] readCSVFile(String csvFile) { } catch (IOException e) { e.printStackTrace(); } - + String[] linkIDsArray = (String[]) links.toArray(); return linkIDsArray ; } diff --git a/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTest.java b/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTest.java index 3fa83d471d3..b98c144164f 100644 --- a/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTest.java +++ b/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTest.java @@ -19,57 +19,57 @@ /** * @author ikaddoura, mmayobre - * - * + * + * */ public class RunTest { @Rule public MatsimTestUtils utils = new MatsimTestUtils(); - + @Test public void test1() { String configFile = utils.getPackageInputDirectory() + "/trial_scenario/trial_scenario_config.xml"; String outputDirectory = utils.getOutputDirectory(); String runId = "run1"; - + Config config = ConfigUtils.loadConfig(configFile); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - - config.controler().setOutputDirectory(outputDirectory); - config.controler().setRunId(runId); - + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.controller().setOutputDirectory(outputDirectory); + config.controller().setRunId(runId); + AccidentsConfigGroup accidentsSettings = ConfigUtils.addOrGetModule(config, AccidentsConfigGroup.class); accidentsSettings.setEnableAccidentsModule(true); - + final Scenario scenario = ScenarioUtils.loadScenario(config); - + // pre-process network for (Link link : scenario.getNetwork().getLinks().values()) { link.getAttributes().putAttribute(accidentsSettings.getAccidentsComputationMethodAttributeName(), AccidentsComputationMethod.BVWP.toString()); - + int numberOfLanesBVWP; if (link.getNumberOfLanes() > 4){ numberOfLanesBVWP = 4; } else { numberOfLanesBVWP = (int) link.getNumberOfLanes(); } - + if (link.getFreespeed() > 16.) { link.getAttributes().putAttribute( AccidentsConfigGroup.BVWP_ROAD_TYPE_ATTRIBUTE_NAME, "1,0," + numberOfLanesBVWP); } else { link.getAttributes().putAttribute( AccidentsConfigGroup.BVWP_ROAD_TYPE_ATTRIBUTE_NAME, "1,2," + numberOfLanesBVWP); - } + } } - + Controler controler = new Controler(scenario); - + controler.addOverridingModule(new AccidentsModule() ); - + controler.run(); - + BufferedReader br = IOUtils.getBufferedReader(outputDirectory + "ITERS/it.0/run1.0.accidentCosts_BVWP.csv"); - + String line = null; try { line = br.readLine(); @@ -80,27 +80,27 @@ public void test1() { try { int lineCounter = 0; while ((line = br.readLine()) != null) { - + String[] columns = line.split(";"); for (int column = 0; column < columns.length; column++) { - + if (lineCounter == 0 && column == 25) { double accidentCosts = Double.valueOf(columns[column]); Assert.assertEquals("wrong accident costs", 10.38, accidentCosts , MatsimTestUtils.EPSILON); } - + if (lineCounter == 1 && column == 25) { double accidentCosts = Double.valueOf(columns[column]); Assert.assertEquals("wrong accident costs", 16.68, accidentCosts , MatsimTestUtils.EPSILON); } - + } - + lineCounter++; } } catch (IOException e) { e.printStackTrace(); } - + } } diff --git a/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTestEquil.java b/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTestEquil.java index 3cd2bf28fe2..d537b8c40e4 100644 --- a/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTestEquil.java +++ b/contribs/accidents/src/test/java/org/matsim/contrib/accidents/RunTestEquil.java @@ -20,54 +20,54 @@ public class RunTestEquil { @Rule public MatsimTestUtils utils = new MatsimTestUtils(); - - @Test + + @Test public void test1() { String configFile = utils.getPackageInputDirectory() + "/equil_scenario/config.xml"; String outputDirectory = utils.getOutputDirectory(); String runId = "run1"; - + Config config = ConfigUtils.loadConfig( configFile ); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - - config.controler().setOutputDirectory( outputDirectory ); - config.controler().setRunId( runId ); - config.controler().setLastIteration(0); - + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.controller().setOutputDirectory( outputDirectory ); + config.controller().setRunId( runId ); + config.controller().setLastIteration(0); + AccidentsConfigGroup accidentsSettings = ConfigUtils.addOrGetModule(config, AccidentsConfigGroup.class); accidentsSettings.setEnableAccidentsModule(true); - + final Scenario scenario = ScenarioUtils.loadScenario(config); Controler controler = new Controler (scenario); controler.addOverridingModule(new AccidentsModule() ); - - scenario.getNetwork().getLinks().get(Id.createLinkId("6")).setFreespeed(10); + + scenario.getNetwork().getLinks().get(Id.createLinkId("6")).setFreespeed(10); scenario.getNetwork().getLinks().get(Id.createLinkId("6")).setNumberOfLanes(3); - scenario.getNetwork().getLinks().get(Id.createLinkId("15")).setFreespeed(10); + scenario.getNetwork().getLinks().get(Id.createLinkId("15")).setFreespeed(10); scenario.getNetwork().getLinks().get(Id.createLinkId("15")).setNumberOfLanes(2); - + // pre-process network for (Link link : scenario.getNetwork().getLinks().values()) { link.getAttributes().putAttribute(accidentsSettings.getAccidentsComputationMethodAttributeName(), AccidentsComputationMethod.BVWP.toString()); - + int numberOfLanesBVWP; if (link.getNumberOfLanes() > 4){ numberOfLanesBVWP = 4; } else { numberOfLanesBVWP = (int) link.getNumberOfLanes(); } - + if (link.getFreespeed() > 16.) { link.getAttributes().putAttribute( AccidentsConfigGroup.BVWP_ROAD_TYPE_ATTRIBUTE_NAME, "1,0," + numberOfLanesBVWP); } else { link.getAttributes().putAttribute( AccidentsConfigGroup.BVWP_ROAD_TYPE_ATTRIBUTE_NAME, "1,2," + numberOfLanesBVWP); - } + } } - + controler.run(); - + BufferedReader br = IOUtils.getBufferedReader(outputDirectory + "ITERS/it.0/run1.0.accidentCosts_BVWP.csv"); - + String line = null; try { line = br.readLine(); @@ -78,10 +78,10 @@ public void test1() { try { int lineCounter = 0; while ((line = br.readLine()) != null) { - + String[] columns = line.split(";"); for (int column = 0; column < columns.length; column++) { - + // link 22 if (lineCounter == 1 && column == 121) { double accidentCosts = Double.valueOf(columns[column]); @@ -98,7 +98,7 @@ public void test1() { double accidentCostsManualCalculation = (agents * lengthKM * 61.785) / 1000. * 10; Assert.assertEquals("wrong accident costs", accidentCostsManualCalculation, accidentCosts , 0.01); } - + // link 6 if (lineCounter == 16 && column == 121) { double accidentCosts = Double.valueOf(columns[column]); @@ -106,8 +106,8 @@ public void test1() { int lengthKM = 10; double accidentCostsManualCalculation = (agents * lengthKM * 34.735) / 1000. * 10; Assert.assertEquals("wrong accident costs", accidentCostsManualCalculation, accidentCosts , 0.01); - } - + } + // link 15 if (lineCounter == 6 && column == 121) { double accidentCosts = Double.valueOf(columns[column]); @@ -117,7 +117,7 @@ public void test1() { Assert.assertEquals("wrong accident costs", accidentCostsManualCalculation, accidentCosts , 0.01); } } - + lineCounter++; } } catch (IOException e) { diff --git a/contribs/analysis/pom.xml b/contribs/analysis/pom.xml index e164c142592..4cf93dae1a4 100644 --- a/contribs/analysis/pom.xml +++ b/contribs/analysis/pom.xml @@ -32,7 +32,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.2.1 + 5.2.2 diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/ActivitiesAnalyzer.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/ActivitiesAnalyzer.java index 39c5b49040b..bb4239c27c9 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/ActivitiesAnalyzer.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/ActivitiesAnalyzer.java @@ -39,11 +39,11 @@ import org.matsim.core.controler.listener.StartupListener; import org.matsim.core.utils.charts.XYLineChart; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import java.awt.*; import java.io.BufferedWriter; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.*; import java.util.List; @@ -54,21 +54,21 @@ * * @author cdobler */ -public class ActivitiesAnalyzer implements ActivityStartEventHandler, ActivityEndEventHandler, +public class ActivitiesAnalyzer implements ActivityStartEventHandler, ActivityEndEventHandler, StartupListener, BeforeMobsimListener, IterationEndsListener { public static String defaultActivitiesFileName = "activityCounts"; - + private final boolean autoConfig; - + private double endTime = 30*3600; - + private String activitiesFileName = defaultActivitiesFileName; private final Set observedAgents; private boolean createGraphs; private final Map> activityCountData = new TreeMap>(); private final LinkedList overallCount = new LinkedList(); - + /** * This is how most people will probably will use this class. * It has to be created an registered as ControlerListener. @@ -76,56 +76,56 @@ public class ActivitiesAnalyzer implements ActivityStartEventHandler, ActivityEn * get paths to output files, ...). */ public ActivitiesAnalyzer() { - + this.autoConfig = true; this.createGraphs = true; this.observedAgents = null; - + reset(0); } - + public ActivitiesAnalyzer(String activitiesFileName, Set activityTypes, boolean createGraphs) { this(activitiesFileName, activityTypes, null, createGraphs); } - + public ActivitiesAnalyzer(String activitiesFileName, Set activityTypes, Set observedAgents, boolean createGraphs) { - + this.autoConfig = false; - + this.activitiesFileName = activitiesFileName; this.createGraphs = createGraphs; - + // use all activity defined in the set for (String activityType : activityTypes) { this.activityCountData.put(activityType, new LinkedList()); } - + if (observedAgents != null) { // make a copy to prevent people changing the set over the iterations - this.observedAgents = new HashSet(observedAgents); + this.observedAgents = new HashSet(observedAgents); } else this.observedAgents = null; - + reset(0); } - + public void setCreateGraphs(boolean createGraphs) { this.createGraphs = createGraphs; } - + public void setEndTime(double endTime) { this.endTime = endTime; } - + @Override public void handleEvent(ActivityEndEvent event) { - + if (observedAgents != null && !observedAgents.contains(event.getPersonId())) return; - + LinkedList list = this.activityCountData.get(event.getActType()); - + // ignore not observed activity types if (list == null) return; - + changeCount(event.getTime(), list, -1); changeCount(event.getTime(), this.overallCount, -1); } @@ -133,22 +133,22 @@ public void handleEvent(ActivityEndEvent event) { @Override public void handleEvent(ActivityStartEvent event) { - + if (observedAgents != null && !observedAgents.contains(event.getPersonId())) return; - + LinkedList list = this.activityCountData.get(event.getActType()); // ignore not observed activity types if (list == null) return; - + changeCount(event.getTime(), list, 1); changeCount(event.getTime(), this.overallCount, 1); } - + private void changeCount(double time, LinkedList list, int delta) { - + ActivityData activityData = list.getLast(); - + /* * If there is already another entry for the same time step, re-use it. * Otherwise create a new one. @@ -159,7 +159,7 @@ private void changeCount(double time, LinkedList list, int delta) list.add(new ActivityData(time, activityData.activityCount + delta)); } } - + @Override public void reset(final int iter) { for (List list : this.activityCountData.values()) { @@ -169,40 +169,40 @@ public void reset(final int iter) { this.overallCount.clear(); this.overallCount.add(new ActivityData(0.0, 0)); } - + @Override public void notifyStartup(StartupEvent event) { - + MatsimServices controler = event.getServices(); - + if (autoConfig) { // use all activity types defined in the config - Set activityTypes = new TreeSet(event.getServices().getConfig().planCalcScore().getActivityTypes()); + Set activityTypes = new TreeSet(event.getServices().getConfig().scoring().getActivityTypes()); for (String activityType : activityTypes) { this.activityCountData.put(activityType, new LinkedList()); } - + controler.getEvents().addHandler(this); } } - + @Override public void notifyBeforeMobsim(BeforeMobsimEvent event) { - + ActivityData overallActivityData = this.overallCount.getLast(); for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) { - + if (this.observedAgents != null && !this.observedAgents.contains(person.getId())) continue; - + Plan plan = person.getSelectedPlan(); Activity firstActivity = (Activity) plan.getPlanElements().get(0); LinkedList list = activityCountData.get(firstActivity.getType()); - + // ignore not observed activity types if (list == null) continue; - + ActivityData activityData = list.getLast(); activityData.activityCount += 1; overallActivityData.activityCount += 1; @@ -211,19 +211,19 @@ public void notifyBeforeMobsim(BeforeMobsimEvent event) { @Override public void notifyIterationEnds(IterationEndsEvent event) { - + OutputDirectoryHierarchy outputDirectoryHierarchy = event.getServices().getControlerIO(); - + try { for (String activityType : this.activityCountData.keySet()) { String fileName = outputDirectoryHierarchy.getIterationFilename(event.getIteration(), this.activitiesFileName + "_" + activityType + ".txt"); BufferedWriter activitiesWriter = IOUtils.getBufferedWriter(fileName); - + activitiesWriter.write("TIME"); activitiesWriter.write("\t"); activitiesWriter.write(activityType.toUpperCase()); activitiesWriter.write("\n"); - + List list = this.activityCountData.get(activityType); for (ActivityData activityData : list) { activitiesWriter.write(String.valueOf(activityData.time)); @@ -231,18 +231,18 @@ public void notifyIterationEnds(IterationEndsEvent event) { activitiesWriter.write(String.valueOf(activityData.activityCount)); activitiesWriter.write("\n"); } - + activitiesWriter.flush(); activitiesWriter.close(); } } catch (IOException e) { throw new UncheckedIOException(e); } - + if (this.createGraphs) { // create chart when data of more than one iteration is available. XYLineChart chart; - + /* * number of performed activities */ @@ -250,7 +250,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { for (String activityType : this.activityCountData.keySet()) { List list = this.activityCountData.get(activityType); int length = list.size(); - + double[] times = new double[length * 2 - 1]; double[] counts = new double[length * 2 - 1]; Iterator iter = list.iterator(); @@ -287,13 +287,13 @@ public void notifyIterationEnds(IterationEndsEvent event) { i += 2; } chart.addSeries("overall", times, counts); - + NumberAxis domainAxis = (NumberAxis) chart.getChart().getXYPlot().getDomainAxis(); domainAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 11)); domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); domainAxis.setAutoRange(false); domainAxis.setRange(0, endTime / 3600.0); - + chart.addMatsimLogo(); String fileName = outputDirectoryHierarchy.getIterationFilename(event.getIteration(), this.activitiesFileName + ".png"); chart.saveAsPng(fileName, 800, 600); @@ -301,7 +301,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { } private static class ActivityData { - + public final double time; public int activityCount; @@ -311,4 +311,4 @@ public ActivityData(double time, int activityCount) { } } -} \ No newline at end of file +} diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java index eedc53bc91c..21e616f6896 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TravelTimesWriter.java @@ -38,6 +38,7 @@ import org.matsim.core.controler.events.IterationEndsEvent; import org.matsim.core.controler.listener.IterationEndsListener; import org.matsim.core.router.util.TravelTime; +import org.matsim.core.trafficmonitoring.TimeBinUtils; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.PolylineFeatureFactory; import org.matsim.core.utils.gis.ShapeFileWriter; @@ -48,12 +49,12 @@ /** * Analyzes the average link travel times and writes them to files (absolute and * relative values; txt and shp files). - * + * * When added as ControlerListener, car travel times are collected and written * based on the TravelTimeCalculator provided by the services. - * + * * For other modes, first call collectTravelTimes(...) and then the writer methods. - * + * * @author cdobler */ public class TravelTimesWriter implements IterationEndsListener { @@ -62,30 +63,30 @@ public class TravelTimesWriter implements IterationEndsListener { public static String travelTimesRelativeFile = "travelTimesRelative.txt.gz"; public static String travelTimesAbsoluteSHPFile = "travelTimesAbsolute.shp"; public static String travelTimesRelativeSHPFile = "travelTimesRelative.shp"; - + public static final String newLine = "\n"; public static final String delimiter = "\t"; - + private TravelTime travelTime; private Network network; - private int timeSlice; + private double timeSlice; private int numSlots; - + private boolean writeTXTFiles = true; private boolean writeSHPFiles = true; - + private final Map travelTimes = new HashMap(); private String crsString = "EPSG:21781"; - + public TravelTimesWriter() { } - + public TravelTimesWriter(boolean writeTXTFiles, boolean writeSHPFiles) { this.writeTXTFiles = writeTXTFiles; this.writeSHPFiles = writeSHPFiles; } - + public String getCrsString() { return crsString; } @@ -93,13 +94,13 @@ public String getCrsString() { public void setCrsString(String crsString) { this.crsString = crsString; } - + private void initBuffer(Network network) { for (Link link : network.getLinks().values()) { double[] travelTimeArray = new double[numSlots]; - int time = 0; + double time = 0; for (int i = 0; i < numSlots; i++) { // travelTimeArray[i] = link.getLength() / link.getFreespeed(time); travelTimeArray[i] = Double.NaN; @@ -110,14 +111,14 @@ private void initBuffer(Network network) { } } - private void collectTravelTimes(TravelTime travelTime, Network network, int timeSlice, int numSlots) { + private void collectTravelTimes(TravelTime travelTime, Network network, double timeSlice, int numSlots) { this.initBuffer(network); - + for (Entry entry : travelTimes.entrySet()) { double[] travelTimeArray = entry.getValue(); - int time = 0; + double time = 0; for (int i = 0; i < this.numSlots; i++) { travelTimeArray[i] = this.travelTime.getLinkTravelTime(entry.getKey(), time, null, null); time = time + this.timeSlice; @@ -126,53 +127,53 @@ private void collectTravelTimes(TravelTime travelTime, Network network, int time } public void writeAbsoluteTravelTimes(final String file) { - + try { BufferedWriter timesWriter = IOUtils.getBufferedWriter(file); - + writeHeader(timesWriter); writeRows(timesWriter, true); - + timesWriter.flush(); - timesWriter.close(); + timesWriter.close(); } catch (IOException e) { throw new RuntimeException(e); } } public void writeRelativeTravelTimes(final String file) { - + try { BufferedWriter timesWriter = IOUtils.getBufferedWriter(file); - + writeHeader(timesWriter); writeRows(timesWriter, false); - + timesWriter.flush(); - timesWriter.close(); + timesWriter.close(); } catch (IOException e) { throw new RuntimeException(e); } } - + private void writeHeader(BufferedWriter timesWriter) throws IOException { timesWriter.write("linkId"); - - for (int i = 0; i < this.numSlots; i++) { + + for (int i = 0; i < this.numSlots; i++) { timesWriter.write(delimiter); timesWriter.write(String.valueOf(i * this.timeSlice)); - } + } timesWriter.write(newLine); } - + private void writeRows(BufferedWriter timesWriter, boolean absolute) throws IOException { - + for (Link link : this.network.getLinks().values()) { - + timesWriter.write(link.getId().toString()); - + double[] travelTimeArray = this.travelTimes.get(link); - for (int i = 0; i < this.numSlots; i++) { + for (int i = 0; i < this.numSlots; i++) { timesWriter.write(delimiter); if (absolute) { timesWriter.write(String.valueOf(travelTimeArray[i])); @@ -181,15 +182,15 @@ private void writeRows(BufferedWriter timesWriter, boolean absolute) throws IOEx double relativeTravelTime = travelTimeArray[i] / freeSpeedTravelTime; timesWriter.write(String.valueOf(relativeTravelTime)); } - } + } timesWriter.write(newLine); } } - + public void writeAbsoluteSHPTravelTimes(String file, CoordinateReferenceSystem crs, boolean ignoreExitLinks) { try { Collection ft = generateSHPFileData(crs, this.network, true, ignoreExitLinks); - ShapeFileWriter.writeGeometries(ft, file); + ShapeFileWriter.writeGeometries(ft, file); } catch (Exception e) { throw new RuntimeException(e); } @@ -198,16 +199,16 @@ public void writeAbsoluteSHPTravelTimes(String file, CoordinateReferenceSystem c public void writeRelativeSHPTravelTimes(String file, CoordinateReferenceSystem crs, boolean ignoreExitLinks) { try { Collection ft = generateSHPFileData(crs, this.network, false, ignoreExitLinks); - ShapeFileWriter.writeGeometries(ft, file); + ShapeFileWriter.writeGeometries(ft, file); } catch (Exception e) { throw new RuntimeException(e); } } - + private Collection generateSHPFileData(CoordinateReferenceSystem crs, Network network, boolean absolute, boolean ignoreExitLinks) throws Exception { Collection features = new ArrayList(); - + PolylineFeatureFactory.Builder builder = new PolylineFeatureFactory.Builder() .setCrs(crs) .setName("links") @@ -216,19 +217,19 @@ private Collection generateSHPFileData(CoordinateReferenceSystem .addAttribute("toID", String.class) .addAttribute("length", Double.class) .addAttribute("fstt", Double.class); - + for (int i = 0; i < this.numSlots; i++) { builder.addAttribute(String.valueOf(i * this.timeSlice), Double.class); } PolylineFeatureFactory factory = builder.create(); for (Link link : network.getLinks().values()) { - + if (ignoreExitLinks) { String string = link.getId().toString().toLowerCase(); if (string.contains("rescue") || string.contains("exit")) continue; } - + Coordinate[] coordArray = new Coordinate[] {coord2Coordinate(link.getFromNode().getCoord()), coord2Coordinate(link.getCoord()), coord2Coordinate(link.getToNode().getCoord())}; Object[] attributes = new Object[5 + this.numSlots]; @@ -240,7 +241,7 @@ private Collection generateSHPFileData(CoordinateReferenceSystem double[] travelTimeArray = this.travelTimes.get(link); for (int i = 0; i < this.numSlots; i++) { - + if (absolute) { attributes[5 + i] = travelTimeArray[i]; } else { @@ -253,10 +254,10 @@ private Collection generateSHPFileData(CoordinateReferenceSystem SimpleFeature ft = factory.createPolyline(coordArray, attributes, link.getId().toString()); features.add(ft); } - + return features; } - + /** * Converts a MATSim {@link org.matsim.api.core.v01.Coord} into a Geotools Coordinate * @param coord MATSim coordinate @@ -275,13 +276,13 @@ public void notifyIterationEnds(IterationEndsEvent event) { this.network = controler.getScenario().getNetwork(); this.timeSlice = config.travelTimeCalculator().getTraveltimeBinSize(); int maxTime = 30 * 3600; // default value from TravelTimeCalculator - this.numSlots = (maxTime / timeSlice) + 1; - + this.numSlots = TimeBinUtils.getTimeBinCount(maxTime, timeSlice); + this.collectTravelTimes(travelTime, network, timeSlice, numSlots); - + OutputDirectoryHierarchy controlerIO = event.getServices().getControlerIO(); int iteration = event.getIteration(); - + String absoluteFile = TravelTimesWriter.travelTimesAbsoluteFile; String relativeFile = TravelTimesWriter.travelTimesRelativeFile; if (absoluteFile.toLowerCase().endsWith(".gz")) { @@ -290,21 +291,21 @@ public void notifyIterationEnds(IterationEndsEvent event) { if (relativeFile.toLowerCase().endsWith(".gz")) { relativeFile = relativeFile.substring(0, relativeFile.length() - 3); } - + if (writeTXTFiles) { String absoluteTravelTimesFile = controlerIO.getIterationFilename(iteration, absoluteFile); String relativeTravelTimesFile = controlerIO.getIterationFilename(iteration, relativeFile); this.writeAbsoluteTravelTimes(absoluteTravelTimesFile); - this.writeRelativeTravelTimes(relativeTravelTimesFile); + this.writeRelativeTravelTimes(relativeTravelTimesFile); } - + if (writeSHPFiles) { String absoluteSHPTravelTimesFile = controlerIO.getIterationFilename(0, TravelTimesWriter.travelTimesAbsoluteSHPFile); String relativeSHPTravelTimesFile = controlerIO.getIterationFilename(0, TravelTimesWriter.travelTimesRelativeSHPFile); this.writeAbsoluteSHPTravelTimes(absoluteSHPTravelTimesFile, MGC.getCRS(crsString), true); - this.writeRelativeSHPTravelTimes(relativeSHPTravelTimesFile, MGC.getCRS(crsString), true); + this.writeRelativeSHPTravelTimes(relativeSHPTravelTimesFile, MGC.getCRS(crsString), true); } - + this.travelTimes.clear(); } diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TripsAnalyzer.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TripsAnalyzer.java index e5ad1a32205..390adb131b9 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TripsAnalyzer.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/christoph/TripsAnalyzer.java @@ -22,6 +22,7 @@ import java.io.BufferedWriter; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -45,7 +46,6 @@ import org.matsim.core.controler.listener.StartupListener; import org.matsim.core.utils.charts.XYLineChart; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; /** * Calculates: @@ -53,7 +53,7 @@ * - average leg travel time over all modes * - number of trips per mode * - number of trips over all modes - * + * * @author cdobler */ public class TripsAnalyzer implements PersonDepartureEventHandler, PersonArrivalEventHandler, @@ -63,23 +63,23 @@ public class TripsAnalyzer implements PersonDepartureEventHandler, PersonArrival public static String defaultDurationsFileName = "tripDurations"; private final boolean autoConfig; - + private final Set sortedModes = new TreeSet(); private final Set observedAgents; private final Map departureTimes = new HashMap(); private final Map> legTravelTimes = new HashMap>(); - + private String tripsFileName; private String durationsFileName; private boolean createGraphs; - + private BufferedWriter tripsWriter; private BufferedWriter durationWriter; - + private double[][] tripsHistory; private double[][] durationHistory; private int minIteration; - + /** * This is how most people will probably will use this class. * It has to be created an registered as ControlerListener. @@ -87,47 +87,47 @@ public class TripsAnalyzer implements PersonDepartureEventHandler, PersonArrival * get paths to output files, ...). */ public TripsAnalyzer() { - + this.autoConfig = true; this.createGraphs = true; - + // modes which are analyzed by default this.sortedModes.add(TransportMode.bike); this.sortedModes.add(TransportMode.car); this.sortedModes.add(TransportMode.pt); this.sortedModes.add(TransportMode.ride); this.sortedModes.add(TransportMode.walk); - + this.observedAgents = null; } - + public TripsAnalyzer(String tripsFileName, String durationsFileName, Set modes, boolean createGraphs) { this(tripsFileName, durationsFileName, modes, null, createGraphs); } - + public TripsAnalyzer(String tripsFileName, String durationsFileName, Set modes, Set observedAgents, boolean createGraphs) { this.autoConfig = false; - + this.tripsFileName = tripsFileName; this.durationsFileName = durationsFileName; this.sortedModes.addAll(modes); if (observedAgents != null) { // make a copy to prevent people changing the set over the iterations - this.observedAgents = new HashSet(observedAgents); + this.observedAgents = new HashSet(observedAgents); } else this.observedAgents = null; this.createGraphs = createGraphs; } - + public void setCreateGraphs(boolean createGraphs) { this.createGraphs = createGraphs; } - + public Set getModes() { return this.sortedModes; } - + @Override public void reset(int iteration) { for(List modeTravelTime : legTravelTimes.values()) { @@ -137,12 +137,12 @@ public void reset(int iteration) { @Override public void handleEvent(PersonArrivalEvent event) { - + if (observedAgents != null && !observedAgents.contains(event.getPersonId())) return; - + Double departureTime = departureTimes.remove(event.getPersonId()); if (departureTime == null) throw new RuntimeException("No departure time for agent " + event.getPersonId() + " was found!"); - + double travelTime = event.getTime() - departureTime; String mode = event.getLegMode(); List modeTravelTimes = legTravelTimes.get(mode); @@ -159,16 +159,16 @@ public void handleEvent(PersonDepartureEvent event) { public void notifyStartup(final StartupEvent event) { MatsimServices controler = event.getServices(); - this.minIteration = controler.getConfig().controler().getFirstIteration(); - int maxIter = controler.getConfig().controler().getLastIteration(); + this.minIteration = controler.getConfig().controller().getFirstIteration(); + int maxIter = controler.getConfig().controller().getLastIteration(); int iterations = maxIter - this.minIteration; this.tripsHistory = new double[this.sortedModes.size() + 1][iterations + 1]; - this.durationHistory = new double[this.sortedModes.size() + 1][iterations + 1]; - + this.durationHistory = new double[this.sortedModes.size() + 1][iterations + 1]; + if (autoConfig) { this.tripsFileName = event.getServices().getControlerIO().getOutputFilename(defaultTripsFileName); this.durationsFileName = event.getServices().getControlerIO().getOutputFilename(defaultDurationsFileName); - + controler.getEvents().addHandler(this); } @@ -183,7 +183,7 @@ public void notifyStartup(final StartupEvent event) { this.tripsWriter.write(mode.toUpperCase()); this.durationWriter.write("\t"); this.durationWriter.write(mode.toUpperCase()); - + this.legTravelTimes.put(mode, new LinkedList()); } this.tripsWriter.write("\t"); @@ -192,12 +192,12 @@ public void notifyStartup(final StartupEvent event) { this.durationWriter.write("\t"); this.durationWriter.write("OVERALL"); this.durationWriter.write("\n"); - + } catch (IOException e) { throw new UncheckedIOException(e); } } - + @Override public void notifyIterationEnds(IterationEndsEvent event) { try { @@ -205,7 +205,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { this.tripsWriter.write(String.valueOf(iteration)); this.durationWriter.write(String.valueOf(iteration)); int index = iteration - this.minIteration; - + int i = 0; int overallTrips = 0; double overallTravelTime = 0.0; @@ -213,7 +213,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { List modeTravelTimes = legTravelTimes.get(mode); double sumTravelTimes = 0.0; for (double travelTime : modeTravelTimes) sumTravelTimes += travelTime; - + int modeTrips = modeTravelTimes.size(); overallTrips += modeTrips; overallTravelTime += sumTravelTimes; @@ -221,19 +221,19 @@ public void notifyIterationEnds(IterationEndsEvent event) { double averageTravelTime = sumTravelTimes / modeTrips; this.tripsHistory[i][index] = modeTrips; this.durationHistory[i][index] = averageTravelTime; - + this.tripsWriter.write("\t"); this.tripsWriter.write(String.valueOf(modeTrips)); this.durationWriter.write("\t"); this.durationWriter.write(String.valueOf(averageTravelTime)); - + i++; } - + double averageTravelTime = overallTravelTime / overallTrips; this.tripsHistory[sortedModes.size()][index] = overallTrips; this.durationHistory[sortedModes.size()][index] = averageTravelTime; - + this.tripsWriter.write("\t"); this.tripsWriter.write(String.valueOf(overallTrips)); this.tripsWriter.write("\n"); @@ -250,26 +250,26 @@ public void notifyIterationEnds(IterationEndsEvent event) { } catch (IOException e) { throw new RuntimeException(e); } - + if (this.createGraphs && event.getIteration() != this.minIteration) { int index = event.getIteration() - this.minIteration; // create chart when data of more than one iteration is available. XYLineChart chart; - + double[] iterations = new double[index + 1]; for (int i = 0; i <= index; i++) { iterations[i] = i + this.minIteration; } double[] values = new double[index + 1]; - + int i; - + /* * average leg duration */ chart = new XYLineChart("Average Leg Travel Times Statistics", "iteration", "time"); - + i = 0; for (String mode : sortedModes) { System.arraycopy(this.durationHistory[i], 0, values, 0, index + 1); @@ -278,15 +278,15 @@ public void notifyIterationEnds(IterationEndsEvent event) { } System.arraycopy(this.durationHistory[i], 0, values, 0, index + 1); chart.addSeries("overall", iterations, values); - + chart.addMatsimLogo(); chart.saveAsPng(this.durationsFileName + ".png", 800, 600); - + /* * number of trips */ chart = new XYLineChart("Number of Trips per Mode Statistics", "iteration", "number of trips"); - + i = 0; for (String mode : sortedModes) { System.arraycopy(this.tripsHistory[i], 0, values, 0, index + 1); @@ -295,7 +295,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { } System.arraycopy(this.tripsHistory[i], 0, values, 0, index + 1); chart.addSeries("overall", iterations, values); - + chart.addMatsimLogo(); chart.saveAsPng(this.tripsFileName + ".png", 800, 600); } @@ -306,7 +306,7 @@ public void notifyShutdown(ShutdownEvent event) { try { if (this.tripsWriter != null) { this.tripsWriter.flush(); - this.tripsWriter.close(); + this.tripsWriter.close(); } if (this.durationWriter != null) { this.durationWriter.flush(); diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/KNAnalysisEventsHandler.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/KNAnalysisEventsHandler.java index 3bde897e73c..ad1e9cfe938 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/KNAnalysisEventsHandler.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/KNAnalysisEventsHandler.java @@ -43,13 +43,13 @@ import org.matsim.core.router.TripStructureUtils.Trip; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import org.matsim.utils.objectattributes.ObjectAttributes; import org.matsim.utils.objectattributes.ObjectAttributesXmlWriter; import org.matsim.vehicles.Vehicle; import java.io.BufferedWriter; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.*; import java.util.Map.Entry; @@ -100,7 +100,7 @@ enum StatType { private Vehicle2DriverEventHandler delegate = new Vehicle2DriverEventHandler() ; - // general trip counter. Would, in theory, not necessary to do this per StatType, but I find it too brittle + // general trip counter. Would, in theory, not necessary to do this per StatType, but I find it too brittle // to avoid under- or over-counting with respect to loops. // private final Map legCount = new TreeMap() ; diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/RunKNEventsAnalyzer.java b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/RunKNEventsAnalyzer.java index 205cf58ba3b..462943c36f4 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/RunKNEventsAnalyzer.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/analysis/kai/RunKNEventsAnalyzer.java @@ -37,70 +37,70 @@ public class RunKNEventsAnalyzer { public static void main(String[] args) { - + if ( args.length < 3 ) { System.out.println("Usage: cmd eventsFile popFile netFile [popAttrFile] [tollFile] [futureTollFile]. Aborting ..." ) ; System.exit(-1); } - + String eventsFilename = args[0] ; String populationFilename = args[1] ; String networkFilename = args[2] ; - + String popAttrFilename = null ; if ( args.length > 3 && args[3]!=null ) { popAttrFilename = args[3] ; } - + String tollFilename = null ; if ( args.length > 4 && args[4]!=null ) { tollFilename = args[4] ; } - + String otherLinksFilename = null ; if ( args.length > 5 && args[5]!=null ) { otherLinksFilename = args[5] ; } - + // === Config config = ConfigUtils.createConfig() ; - + String[] modes ={"car","commercial"}; config.qsim().setMainModes( Arrays.asList(modes) ); - config.plansCalcRoute().setNetworkModes(Arrays.asList(modes)); - + config.routing().setNetworkModes(Arrays.asList(modes)); + config.network().setInputFile( networkFilename ); config.plans().setInputFile( populationFilename ); config.plans().setInputPersonAttributeFile( popAttrFilename ); ConfigUtils.addOrGetModule(config, RoadPricingConfigGroup.GROUP_NAME, RoadPricingConfigGroup.class).setTollLinksFile(tollFilename); // === - + Scenario scenario = ScenarioUtils.loadScenario(config) ; // ((ScenarioImpl)scenario).createVehicleContainer() ; // GautengControler_subpopulations.createVehiclePerPerson(scenario); - + // === - + EventsManager events = new EventsManagerImpl() ; - + Vehicle2DriverEventHandler vehicle2Driver = new Vehicle2DriverEventHandler(); events.addHandler(vehicle2Driver); - + final KNAnalysisEventsHandler.Builder builder = new KNAnalysisEventsHandler.Builder(scenario) ; builder.setOtherTollLinkFile( otherLinksFilename ); final KNAnalysisEventsHandler calcLegTimes = builder.build(); - + events.addHandler( calcLegTimes ); - + new MatsimEventsReader(events).readFile(eventsFilename) ; - -// String myDate = date.getYear() + "-" + date.getMonthOfYear() + "-" + date.getDayOfMonth() + "-" + + +// String myDate = date.getYear() + "-" + date.getMonthOfYear() + "-" + date.getDayOfMonth() + "-" + // date.getHourOfDay() + "h" + minute ; String myDate = "" ; - + calcLegTimes.writeStats(myDate + "_stats_"); } diff --git a/contribs/analysis/src/main/java/org/matsim/contrib/travelsummary/events2traveldiaries/RunEventsToTravelDiaries.java b/contribs/analysis/src/main/java/org/matsim/contrib/travelsummary/events2traveldiaries/RunEventsToTravelDiaries.java index 403f4d0bdb5..436cb4c800f 100644 --- a/contribs/analysis/src/main/java/org/matsim/contrib/travelsummary/events2traveldiaries/RunEventsToTravelDiaries.java +++ b/contribs/analysis/src/main/java/org/matsim/contrib/travelsummary/events2traveldiaries/RunEventsToTravelDiaries.java @@ -51,7 +51,7 @@ public static void main(String[] args) { printHelp(); try { config = ConfigUtils.loadConfig(args[0]); - outputDirectory = config.controler().getOutputDirectory(); + outputDirectory = config.controller().getOutputDirectory(); eventsFileName = args[1]; diff --git a/contribs/application/pom.xml b/contribs/application/pom.xml index cc12b9bceb7..546d465e7d2 100644 --- a/contribs/application/pom.xml +++ b/contribs/application/pom.xml @@ -22,6 +22,11 @@ + + tech.tablesaw + tablesaw-core + + org.matsim.contrib otfvis @@ -89,7 +94,7 @@ info.picocli picocli - 4.7.3 + 4.7.5 it.unimi.dsi @@ -105,6 +110,10 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.assertj diff --git a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java index 90237fdd3e4..89086cb00d3 100644 --- a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java +++ b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java @@ -4,20 +4,52 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; import org.matsim.application.options.CrsOptions; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; +import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.reflect.Field; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.util.Optional; +import java.util.stream.Stream; public class ApplicationUtils { private static final Logger log = LogManager.getLogger(ApplicationUtils.class); + private ApplicationUtils() { + } + + /** + * Extends a context (usually config location) with an relative filename. + * If the results is a local file, the path will be returned. Otherwise, it will be an url. + * The results can be used as input for command line parameter or {@link IOUtils#resolveFileOrResource(String)}. + * @return string with path or URL + */ + public static String resolve(URL context, String filename) { + + URL refURL = IOUtils.extendUrl(context, filename); + String refData; + try { + refData = new File(refURL.toURI()).getAbsolutePath(); + } catch (URISyntaxException e) { + refData = refURL.toString(); + } + return refData; + + } + /** * Helper function to glob for a required file. * @@ -96,8 +128,8 @@ public static Scenario loadScenario(String runId, Path runDirectory, CrsOptions Config config = ConfigUtils.createConfig(); config.global().setCoordinateSystem(crs.getInputCRS()); - config.controler().setOutputDirectory(runDirectory.toString()); - config.controler().setRunId(resolvedRunId); + config.controller().setOutputDirectory(runDirectory.toString()); + config.controller().setRunId(resolvedRunId); config.plans().setInputFile(populationFile.toString()); config.network().setInputFile(networkFile.toString()); @@ -105,4 +137,117 @@ public static Scenario loadScenario(String runId, Path runDirectory, CrsOptions return ScenarioUtils.loadScenario(config); } + + /** + * Check if a command can be used with {@link CommandRunner}. + * + * @throws IllegalArgumentException if a command is not suitable. + */ + public static void checkCommand(Class command) { + + if (command.getAnnotation(CommandSpec.class) == null) { + throw new IllegalArgumentException(String.format("The command %s has no @CommandSpec annotation.", command)); + } + + Field[] fields = command.getDeclaredFields(); + + boolean input = false; + boolean output = false; + for (Field field : fields) { + if (field.getType().equals(InputOptions.class)) + input = true; + + if (field.getType().equals(OutputOptions.class)) + output = true; + } + + if (!input) { + throw new IllegalArgumentException(String.format("The command %s has no field with InputOptions.", command)); + } + + if (!output) { + throw new IllegalArgumentException(String.format("The command %s has no field with OutputOptions.", command)); + } + } + + + /** + * Whether this command accepts a specific class as options. + */ + public static boolean acceptsOptions(Class command, Class options) { + for (Field field : command.getDeclaredFields()) { + if (field.getType().equals(options) && field.getAnnotation(CommandLine.Mixin.class) != null) + return true; + } + + return false; + } + + /** + * Tries to match input file {@code name} with files from the input directory {@code dir}. + * Please check docs in the code for the conventions. Different naming conventions are tried in a specific order. + * + * @throws IllegalArgumentException if no file could be found. + */ + public static Path matchInput(String name, Path dir) { + + Path possibility = dir.resolve(name); + if (Files.exists(possibility)) + return possibility; + + possibility = dir.resolve(name + ".gz"); + if (Files.exists(possibility)) + return possibility; + + // Match files that could be matsim output files + Optional path = matchSuffix("output_" + name, dir); + if (path.isPresent()) + return path.get(); + + path = matchSuffix("output_" + name + ".gz", dir); + if (path.isPresent()) + return path.get(); + + path = matchSuffix(name, dir); + if (path.isPresent()) + return path.get(); + + // Match more general pattern at last + path = matchPattern( ".+\\.[a-zA-Z0-9]*_" + name + "\\..+", dir); + if (path.isPresent()) + return path.get(); + + throw new IllegalArgumentException("Could not match input file: " + name); + } + + private static Optional matchSuffix(String suffix, Path dir) { + try (Stream stream = Files.list(dir)) { + return stream.filter(p -> p.getFileName().toString().endsWith(suffix)).findFirst(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static Optional matchPattern(String pattern, Path dir) { + try (Stream stream = Files.list(dir)) { + return stream.filter(p -> p.getFileName().toString().matches(pattern)).findFirst(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Get the {@link CommandSpec} of a {@link MATSimAppCommand}. + */ + public static CommandSpec getSpec(Class command) { + return command.getAnnotation(CommandSpec.class); + } + + /** + * Get the {@link CommandLine.Command} of a {@link MATSimAppCommand}. + */ + public static CommandLine.Command getCommand(Class command) { + return command.getAnnotation(CommandLine.Command.class); + } + } diff --git a/contribs/application/src/main/java/org/matsim/application/CommandRunner.java b/contribs/application/src/main/java/org/matsim/application/CommandRunner.java new file mode 100644 index 00000000000..56f4549b678 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/CommandRunner.java @@ -0,0 +1,328 @@ +package org.matsim.application; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jgrapht.Graph; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DirectedAcyclicGraph; +import org.jgrapht.traverse.BreadthFirstIterator; +import org.matsim.application.options.CrsOptions; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.SampleOptions; +import org.matsim.application.options.ShpOptions; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * Automatically runs commands by using the {@link CommandSpec} and various Options classes. + */ +public final class CommandRunner { + + private static final Logger log = LogManager.getLogger(CommandRunner.class); + + /** + * Name of the runner. + */ + private final String name; + private final Map, String> shpFiles = new HashMap<>(); + private final Map, String[]> args = new HashMap<>(); + private Path output; + private String defaultShp = null; + private String defaultCrs = null; + private Double defaultSampleSize = null; + + + /** + * Construct a new runner. + */ + public CommandRunner() { + this(""); + } + + /** + * Construct a new runner with a given name. + */ + public CommandRunner(String name) { + this(name, Path.of(".").toAbsolutePath()); + } + + /** + * Construct a runner with specific name and path. + */ + private CommandRunner(String name, Path output) { + this.name = name; + this.output = output; + } + + /** + * Run the specified command. Required input files are searched on {@code input} path. + * + * @param input search path for input files not defined as output by any command. + */ + public void run(Path input) { + + if (!Files.exists(input)) + throw new IllegalArgumentException("Input path does not exists:" + input); + + // Run graph with dependencies + Graph, DefaultEdge> graph = new DirectedAcyclicGraph<>(DefaultEdge.class); + + Set> start = new HashSet<>(); + + for (Map.Entry, String[]> e : args.entrySet()) { + Class clazz = e.getKey(); + graph.addVertex(clazz); + Class[] depends = ApplicationUtils.getSpec(clazz).dependsOn(); + for (Class d : depends) { + graph.addVertex(d); + graph.addEdge(d, clazz); + } + if (depends.length == 0) + start.add(clazz); + } + + BreadthFirstIterator, DefaultEdge> it = new BreadthFirstIterator<>(graph, start); + while (it.hasNext()) { + Class clazz = it.next(); + try { + // Collect garbage between commands, because they might use quite some memory + System.gc(); + runCommand(clazz, input); + + } catch (ReflectiveOperationException ex) { + log.error("Command {} could not be crated.", clazz, ex); + } catch (RuntimeException e) { + log.error("Command {} threw an error.", clazz, e); + } + } + } + + /** + * Execute the command with configured arguments. + */ + private void runCommand(Class clazz, Path input) throws ReflectiveOperationException { + + MATSimAppCommand command = clazz.getDeclaredConstructor().newInstance(); + String[] args = this.args.get(clazz); + args = ArrayUtils.addAll(args, createArgs(clazz, args, input)); + log.info("Running {} with arguments: {}", clazz, Arrays.toString(args)); + + command.execute(args); + } + + /** + * Build the base path. + */ + private Path buildPath(CommandSpec spec, Class command) { + + // use the command name if it is present and no other group name given + String packageName = command.getPackageName(); + if (packageName.contains(".")) { + packageName = packageName.substring(packageName.lastIndexOf(".") + 1); + } + + String context = spec.group().isBlank() ? packageName : spec.group(); + + if (name != null && !name.isBlank()) + context += "-" + name; + + return output.resolve(context); + } + + + private String[] createArgs(Class command, String[] existingArgs, Path input) { + + List args = new ArrayList<>(); + + CommandSpec spec = ApplicationUtils.getSpec(command); + + for (String require : spec.requires()) { + + // Whether this file is produced by a dependency + boolean depFile = false; + String arg = "--input-" + InputOptions.argName(require); + + boolean present = ArrayUtils.contains(existingArgs, arg); + if (present) + continue; + + for (Class depend : spec.dependsOn()) { + CommandSpec dependency = ApplicationUtils.getSpec(depend); + if (ArrayUtils.contains(dependency.produces(), require)) { + + String path = getPath(depend, require); + + args.add(arg); + args.add(path); + + // Add arg for this file + depFile = true; + } + } + + // Look for this file on the input + if (!depFile) { + String path = ApplicationUtils.matchInput(require, input).toString(); + args.add(arg); + args.add(path); + } + } + + if (spec.requireEvents() && !ArrayUtils.contains(existingArgs, "--events")) { + args.add("--events"); + args.add(ApplicationUtils.matchInput("events.xml", input).toString()); + } + if (spec.requirePopulation() && !ArrayUtils.contains(existingArgs, "--population")) { + args.add("--population"); + args.add(ApplicationUtils.matchInput("plans.xml", input).toString()); + } + if (spec.requireNetwork() && !ArrayUtils.contains(existingArgs, "--network")) { + args.add("--network"); + args.add(ApplicationUtils.matchInput("network.xml", input).toString()); + } + if (spec.requireCounts() && !ArrayUtils.contains(existingArgs, "--counts")) { + args.add("--counts"); + args.add(ApplicationUtils.matchInput("counts.xml", input).toString()); + } + if (spec.requireRunDirectory() && !ArrayUtils.contains(existingArgs, "--run-directory")) { + args.add("--run-directory"); + args.add(input.toString()); + } + + if (ApplicationUtils.acceptsOptions(command, ShpOptions.class) && !ArrayUtils.contains(existingArgs, "--shp")) { + if (shpFiles.containsKey(command)) { + args.add("--shp"); + args.add(shpFiles.get(command)); + } else if (defaultShp != null) { + args.add("--shp"); + args.add(defaultShp); + } + } + + if (ApplicationUtils.acceptsOptions(command, CrsOptions.class) && !ArrayUtils.contains(existingArgs, "--input-crs")) { + if (defaultCrs != null) { + args.add("--input-crs"); + args.add(defaultCrs); + } + } + + if (ApplicationUtils.acceptsOptions(command, SampleOptions.class) && !ArrayUtils.contains(existingArgs, "--sample-size")) { + if (defaultSampleSize != null) { + args.add("--sample-size"); + args.add(String.valueOf(defaultSampleSize)); + } + } + + // Adds output arguments for this class + for (String produce : spec.produces()) { + String arg = "--output-" + InputOptions.argName(produce); + String path = getPath(command, produce); + args.add(arg); + args.add(path); + } + + return args.toArray(new String[0]); + } + + + /** + * Get the path for a certain file produced by a command. + */ + private String getPath(Class command, String file) { + CommandSpec spec = ApplicationUtils.getSpec(command); + return buildPath(spec, command).resolve(file).toString(); + } + + /** + * Returns the output path of a command. Will throw an exception if this command does not declare it as an output. + */ + public Path getRequiredPath(Class command, String file) { + CommandSpec spec = ApplicationUtils.getSpec(command); + if (!ArrayUtils.contains(spec.produces(), file)) + throw new IllegalArgumentException(String.format("Command %s does not declare output %s", command, file)); + + return buildPath(spec, command).resolve(file); + } + + /** + * Base path for the runner. + */ + public Path getOutput() { + return output; + } + + /** + * Set output folder. + * + * @return same instance + */ + public CommandRunner setOutput(Path path) { + this.output = path; + return this; + } + + /** + * Name of the runner. + */ + public String getName() { + return name; + } + + /** + * Add a command with certain arguments to the runner. + */ + public void add(Class command, String... args) { + + if (args.length != 0) { + String[] existing = this.args.get(command); + if (existing != null && existing.length > 0 && !Arrays.equals(existing, args)) { + throw new IllegalArgumentException(String.format("Command %s already registered with args %s, can not define different args as %s", + command.toString(), Arrays.toString(existing), Arrays.toString(args))); + } + } + + ApplicationUtils.checkCommand(command); + + if (!this.args.containsKey(command) || this.args.get(command).length == 0) + this.args.put(command, args); + + CommandSpec spec = ApplicationUtils.getSpec(command); + + // Add dependent classes + for (Class depends : spec.dependsOn()) { + if (!this.args.containsKey(depends)) + add(depends); + } + } + + /** + * Set specific shape file for certain command. + */ + public void setShp(Class command, String path) { + shpFiles.put(command, path); + } + + /** + * Set the default shp file for all commands. + */ + public void setShp(String path) { + defaultShp = path; + } + + /** + * Set the default CRS passed to input. + */ + public void setCRS(String crs) { + defaultCrs = crs; + } + + /** + * Set the default sample size that is passed as input to commands. + */ + public void setSampleSize(double sampleSize) { + this.defaultSampleSize = sampleSize; + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/CommandSpec.java b/contribs/application/src/main/java/org/matsim/application/CommandSpec.java new file mode 100644 index 00000000000..d7acfc227f4 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/CommandSpec.java @@ -0,0 +1,60 @@ +package org.matsim.application; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines the specification for a command usable by external tools. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface CommandSpec { + + /** + * List of file names that are used as input for this class. + */ + String[] requires() default {}; + + /** + * Whether a network is required as input. + */ + boolean requireNetwork() default false; + + /** + * Whether a population is required as input. + */ + boolean requirePopulation() default false; + + /** + * Whether an events file is required as input. + */ + boolean requireEvents() default false; + + /** + * Whether a count file is required as input. + */ + boolean requireCounts() default false; + + /** + * Whether a run directory is required as input. + */ + boolean requireRunDirectory() default false; + + /** + * List of files names that are produces by this command and accessible by others as input. + */ + String[] produces() default {}; + + /** + * Other commands that produce the input needed by this command. + */ + Class[] dependsOn() default {}; + + /** + * Group name / identifier. Will use the package name if this is not changed here. + */ + String group() default ""; + +} diff --git a/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java b/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java index 85b7c057064..c1951c1b2a4 100644 --- a/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java +++ b/contribs/application/src/main/java/org/matsim/application/MATSimApplication.java @@ -11,9 +11,10 @@ import org.matsim.application.commands.RunScenario; import org.matsim.application.commands.ShowGUI; import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigAliases; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.controler.Controler; @@ -177,13 +178,13 @@ public Integer call() throws Exception { } if (iterations > -1) - config.controler().setLastIteration(iterations); + config.controller().setLastIteration(iterations); if (output != null) - config.controler().setOutputDirectory(output.toString()); + config.controller().setOutputDirectory(output.toString()); if (runId != null) - config.controler().setRunId(runId); + config.controller().setRunId(runId); final Scenario scenario = createScenario(config); @@ -199,7 +200,7 @@ public Integer call() throws Exception { if (post != PostProcessOption.disabled) { - List commands = preparePostProcessing(Path.of(config.controler().getOutputDirectory()), config.controler().getRunId()); + List commands = preparePostProcessing(Path.of(config.controller().getOutputDirectory()), config.controller().getRunId()); for (MATSimAppCommand command : commands) { @@ -230,6 +231,9 @@ private static void applySpecs(Config config, Path specs) { ObjectMapper mapper = new ObjectMapper(new YAMLFactory() .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)); + ConfigAliases aliases = new ConfigAliases(); + Deque emptyStack = new ArrayDeque<>(); + try (BufferedReader reader = Files.newBufferedReader(specs)) { JsonNode node = mapper.readTree(reader); @@ -238,10 +242,10 @@ private static void applySpecs(Config config, Path specs) { while (fields.hasNext()) { Map.Entry field = fields.next(); - - ConfigGroup group = config.getModules().get(field.getKey()); + String configGroupName = aliases.resolveAlias(field.getKey(), emptyStack); + ConfigGroup group = config.getModules().get(configGroupName); if (group == null) { - log.warn("Config group not found: {}", field.getKey()); + log.warn("Config group not found: {}", configGroupName); continue; } @@ -313,7 +317,7 @@ protected List getCustomModules() { */ protected List getConfigurableModules() { return Lists.newArrayList( - new ControlerConfigGroup(), + new ControllerConfigGroup(), new GlobalConfigGroup(), new QSimConfigGroup() ); @@ -370,14 +374,14 @@ protected final void addRunOption(Config config, String option, Object value) { else postfix = "-" + option + "_" + value; - String outputDir = config.controler().getOutputDirectory(); + String outputDir = config.controller().getOutputDirectory(); if (outputDir.endsWith("/")) { - config.controler().setOutputDirectory(outputDir.substring(0, outputDir.length() - 1) + postfix + "/"); + config.controller().setOutputDirectory(outputDir.substring(0, outputDir.length() - 1) + postfix + "/"); } else - config.controler().setOutputDirectory(outputDir + postfix); + config.controller().setOutputDirectory(outputDir + postfix); // dot should not be part of run id - config.controler().setRunId(config.controler().getRunId() + postfix.replace(".", "")); + config.controller().setRunId(config.controller().getRunId() + postfix.replace(".", "")); } /** diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/DefaultAnalysisMainModeIdentifier.java b/contribs/application/src/main/java/org/matsim/application/analysis/DefaultAnalysisMainModeIdentifier.java deleted file mode 100644 index ea9c7abd378..00000000000 --- a/contribs/application/src/main/java/org/matsim/application/analysis/DefaultAnalysisMainModeIdentifier.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.matsim.application.analysis; - -import com.google.inject.Inject; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.core.router.AnalysisMainModeIdentifier; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * @author ikaddoura based on nagel / gleich - */ - -public final class DefaultAnalysisMainModeIdentifier implements AnalysisMainModeIdentifier { - private final List modeHierarchy = new ArrayList<>(); - private final List drtModes; - - @Inject - public DefaultAnalysisMainModeIdentifier() { - drtModes = Arrays.asList(TransportMode.drt, "drt1", "drt2", "drt_teleportation"); - - modeHierarchy.add(TransportMode.transit_walk); // !!! - modeHierarchy.add(TransportMode.walk); - modeHierarchy.add("bike"); - modeHierarchy.add("bicycle"); - modeHierarchy.add(TransportMode.ride); - modeHierarchy.add(TransportMode.car); - for (String drtMode : drtModes) { - modeHierarchy.add(drtMode); - } - modeHierarchy.add(TransportMode.pt); - modeHierarchy.add("freight"); - - // NOTE: This hierarchical stuff is not so great: is park-n-ride a car trip or a pt trip? Could weigh it by distance, or by time spent - // in respective mode. Or have combined modes as separate modes. In any case, can't do it at the leg level, since it does not - // make sense to have the system calibrate towards something where we have counted the car and the pt part of a multimodal - // trip as two separate trips. kai, sep'16 - } - - @Override - public String identifyMainMode(List planElements) { - int mainModeIndex = -1; - for (PlanElement pe : planElements) { - int index; - String mode; - if (pe instanceof Leg) { - Leg leg = (Leg) pe; - mode = leg.getMode(); - } else { - continue; - } - if (mode.equals(TransportMode.non_network_walk) || mode.equals("access_walk") || mode.equals("egress_walk")) { // for backward compatibility - // skip, this is only a helper mode for access, egress and pt transfers - continue; - } - if (mode.equals(TransportMode.transit_walk)) { - mode = TransportMode.walk; // this is considered as 'transit_walk' and not pt!!! - } else { - for (String drtMode : drtModes) { - if (mode.equals(drtMode + "_fallback")) {// transit_walk / drt_walk / ... to be replaced by _fallback soon - mode = TransportMode.walk; - } - } - } - index = modeHierarchy.indexOf(mode); - if (index < 0) { - throw new RuntimeException("unknown mode=" + mode); - } - if (index > mainModeIndex) { - mainModeIndex = index; - } - } - if (mainModeIndex == -1) { - throw new RuntimeException("no main mode found for trip " + planElements); - } - return modeHierarchy.get(mainModeIndex); - } -} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java new file mode 100644 index 00000000000..36345aa2a79 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java @@ -0,0 +1,230 @@ +package org.matsim.application.analysis; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.lang3.time.DurationFormatUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CsvOptions; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@CommandLine.Command(name = "log-file", description = "Analyses MATSim log files to gather run information.") +@CommandSpec( + requires = "logfile.log", + produces = {"run_info.csv", "memory_stats.csv", "runtime_stats.csv", "warnings.csv", "status.md"}, + group = "general" +) +public class LogFileAnalysis implements MATSimAppCommand { + + private static final Logger log = LogManager.getLogger(LogFileAnalysis.class); + + @CommandLine.Mixin + private InputOptions input = InputOptions.ofCommand(LogFileAnalysis.class); + @CommandLine.Mixin + private OutputOptions output = OutputOptions.ofCommand(LogFileAnalysis.class); + + @CommandLine.Mixin + private CsvOptions csv = new CsvOptions(CSVFormat.Predefined.Default); + + public static void main(String[] args) { + new LogFileAnalysis().execute(args); + } + + private static LocalDateTime parseDate(String line) { + // Ignore milliseconds part + int idx = line.indexOf(','); + return LocalDateTime.parse(line.substring(0, idx)); + } + + @Override + public Integer call() throws Exception { + + Pattern gbl = Pattern.compile(".+INFO Gbl:\\d+ (.+?):(.+)"); + Pattern mem = Pattern.compile(".+MemoryObserver:\\d+ used RAM: (\\d+) MB\\s+free: (\\d+) MB\\s+total: (\\d+) MB"); + Pattern warn = Pattern.compile(".+(WARN|ERROR) (\\S+(ConfigGroup|ConsistencyCheck).*):[0-9]+ (.+)"); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + Map info = new LinkedHashMap<>(); + + List memory = new ArrayList<>(); + List iterations = new ArrayList<>(); + Set warnings = new LinkedHashSet<>(); + + String first = null; + String last = null; + + LocalDateTime itBegin = null; + + try (BufferedReader reader = IOUtils.getBufferedReader(input.getPath())) { + String line; + while ((line = reader.readLine()) != null) { + + try { + + Matcher m = gbl.matcher(line); + if (m.find()) { + info.put(m.group(1).strip(), m.group(2).strip()); + } + m = mem.matcher(line); + if (m.find()) { + memory.add(new Memory(parseDate(line), Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3)))); + } + m = warn.matcher(line); + if (m.find()) { + warnings.add(new Warning(m.group(2), m.group(4))); + } + + if (line.contains("### ITERATION")) { + if (line.contains("BEGINS")) { + itBegin = parseDate(line); + } else if (line.contains("ENDS")) { + iterations.add(new Iteration(itBegin, parseDate(line))); + } + } + + } catch (Exception e) { + log.warn("Error processing line {}", line, e); + continue; + } + + if (first == null) + first = line; + + last = line; + } + } + + // Ignored attributes + info.remove("Thread performance"); + info.remove("used RAM"); + info.remove("### round time"); + + if (first != null) { + LocalDateTime start = parseDate(first); + LocalDateTime end = parseDate(last); + + info.put("Start", formatter.format(start)); + info.put("End", formatter.format(end)); + info.put("Duration", DurationFormatUtils.formatDurationWords(Duration.between(start, end).toMillis(), true, true)); + } + + + try (CSVPrinter printer = csv.createPrinter(output.getPath("run_info.csv"))) { + printer.printRecord("info", "value"); + for (Map.Entry e : info.entrySet()) { + printer.printRecord(e.getKey(), e.getValue()); + } + } + + try (CSVPrinter printer = csv.createPrinter(output.getPath("memory_stats.csv"))) { + printer.printRecord("time", "used", "free"); + for (Memory m : memory) { + printer.printRecord(formatter.format(m.date), m.used, m.free); + } + } + + try (CSVPrinter printer = csv.createPrinter(output.getPath("runtime_stats.csv"))) { + printer.printRecord("Iteration", "seconds"); + for (int i = 0; i < iterations.size(); i++) { + Iteration it = iterations.get(i); + printer.printRecord(i, ChronoUnit.SECONDS.between(it.begin, it.end)); + } + } + + try (CSVPrinter printer = csv.createPrinter(output.getPath("warnings.csv"))) { + printer.printRecord("Module", "Message"); + for (Warning warning : warnings) { + printer.printRecord(warning.module, warning.msg); + } + } + + try (BufferedWriter writer = Files.newBufferedWriter(output.getPath("status.md"))) { + renderWarnings(writer, warnings); + } + + return 0; + } + + private void renderWarnings(BufferedWriter writer, Set warnings) throws IOException { + + if (warnings.isEmpty()) { + writer.write("

No warnings found ✅

\n\n"); + } else { + + Map> grouped = warnings.stream().collect(Collectors.groupingBy(w -> w.module, Collectors.toList())); + writer.write(String.format("

Warnings found in %d module%s ❌

\n\n", grouped.size(), grouped.size() > 1 ? "s" : "")); + + for (Map.Entry> e : grouped.entrySet()) { + + writer.write("#### " + e.getKey() + "\n\n"); + writer.write("```\n"); + for (Warning w : e.getValue()) { + writer.write(w.msg + "\n"); + } + + writer.write("```\n"); + } + } + + writer.write(""" + """); + } + + private record Memory(LocalDateTime date, int used, int free, int total) { + } + + private record Iteration(LocalDateTime begin, LocalDateTime end) { + } + + private record Warning(String module, String msg) { + + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionAnalysis.java new file mode 100644 index 00000000000..eca4eee1e0b --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionAnalysis.java @@ -0,0 +1,354 @@ +package org.matsim.application.analysis.emissions; + +import it.unimi.dsi.fastutil.objects.Object2DoubleLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.application.ApplicationUtils; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.SampleOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.contrib.analysis.time.TimeBinMap; +import org.matsim.contrib.emissions.EmissionModule; +import org.matsim.contrib.emissions.Pollutant; +import org.matsim.contrib.emissions.analysis.EmissionsOnLinkEventHandler; +import org.matsim.contrib.emissions.analysis.FastEmissionGridAnalyzer; +import org.matsim.contrib.emissions.analysis.Raster; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Injector; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.core.network.filter.NetworkFilterManager; +import org.matsim.core.scenario.ProjectionUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@CommandLine.Command( + name = "air-pollution", description = "General air pollution analysis.", + mixinStandardHelpOptions = true, showDefaultValues = true +) +@CommandSpec(requireRunDirectory = true, + produces = { + "emissions_total.csv", "emissions_grid_per_day.xyt.csv", "emissions_per_link.csv", + "emissions_per_link_per_m.csv", + "emissions_grid_per_hour.xyt.csv", + "emissions_vehicle_info.csv", + } +) +public class AirPollutionAnalysis implements MATSimAppCommand { + + private static final Logger log = LogManager.getLogger(AirPollutionAnalysis.class); + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(AirPollutionAnalysis.class); + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(AirPollutionAnalysis.class); + @CommandLine.Mixin + private final ShpOptions shp = new ShpOptions(); + @CommandLine.Mixin + private SampleOptions sample; + @CommandLine.Option(names = "--grid-size", description = "Grid size in meter", defaultValue = "100") + private double gridSize; + + public static void main(String[] args) { + new AirPollutionAnalysis().execute(args); + } + + @Override + public Integer call() throws Exception { + + Config config = prepareConfig(); + Scenario scenario = ScenarioUtils.loadScenario(config); + + Network filteredNetwork; + if (shp.isDefined()) { + ShpOptions.Index index = shp.createIndex(ProjectionUtils.getCRS(scenario.getNetwork()), "_"); + + NetworkFilterManager manager = new NetworkFilterManager(scenario.getNetwork(), config.network()); + manager.addLinkFilter(l -> index.contains(l.getCoord())); + + filteredNetwork = manager.applyFilters(); + } else + filteredNetwork = scenario.getNetwork(); + + EventsManager eventsManager = EventsUtils.createEventsManager(); + AbstractModule module = new AbstractModule() { + @Override + public void install() { + bind(Scenario.class).toInstance(scenario); + bind(EventsManager.class).toInstance(eventsManager); + bind(EmissionModule.class); + } + }; + + + com.google.inject.Injector injector = Injector.createInjector(config, module); + + // Emissions module will be installed to the event handler + injector.getInstance(EmissionModule.class); + + String eventsFile = ApplicationUtils.matchInput("events", input.getRunDirectory()).toString(); + + EmissionsOnLinkEventHandler emissionsEventHandler = new EmissionsOnLinkEventHandler(3600); + eventsManager.addHandler(emissionsEventHandler); + eventsManager.initProcessing(); + MatsimEventsReader matsimEventsReader = new MatsimEventsReader(eventsManager); + matsimEventsReader.readFile(eventsFile); + + log.info("Done reading the events file."); + log.info("Finish processing..."); + eventsManager.finishProcessing(); + + writeOutput(filteredNetwork, emissionsEventHandler); + + writeTotal(filteredNetwork, emissionsEventHandler); + + writeRaster(filteredNetwork, config, emissionsEventHandler); + + // writeTimeDependentRaster(filteredNetwork, config, emissionsEventHandler); + + return 0; + } + + private Config prepareConfig() { + Config config = ConfigUtils.loadConfig(ApplicationUtils.matchInput("config.xml", input.getRunDirectory()).toAbsolutePath().toString()); + + config.vehicles().setVehiclesFile(ApplicationUtils.matchInput("vehicles", input.getRunDirectory()).toAbsolutePath().toString()); + config.network().setInputFile(ApplicationUtils.matchInput("network", input.getRunDirectory()).toAbsolutePath().toString()); + config.transit().setTransitScheduleFile(ApplicationUtils.matchInput("transitSchedule", input.getRunDirectory()).toAbsolutePath().toString()); + config.transit().setVehiclesFile(ApplicationUtils.matchInput("transitVehicles", input.getRunDirectory()).toAbsolutePath().toString()); + config.plans().setInputFile(null); + config.eventsManager().setNumberOfThreads(null); + config.eventsManager().setEstimatedNumberOfEvents(null); + config.global().setNumberOfThreads(1); + + return config; + } + + + private void writeOutput(Network network, EmissionsOnLinkEventHandler emissionsEventHandler) throws IOException { + + log.info("Emission analysis completed."); + + log.info("Writing output..."); + + NumberFormat nf = NumberFormat.getInstance(Locale.US); + nf.setMaximumFractionDigits(4); + nf.setGroupingUsed(false); + + CSVPrinter absolute = new CSVPrinter(Files.newBufferedWriter(output.getPath("emissions_per_link.csv")), CSVFormat.DEFAULT); + CSVPrinter perMeter = new CSVPrinter(Files.newBufferedWriter(output.getPath("emissions_per_link_per_m.csv")), CSVFormat.DEFAULT); + + absolute.print("linkId"); + perMeter.print("linkId"); + + for (Pollutant pollutant : Pollutant.values()) { + absolute.print(pollutant); + perMeter.print(pollutant + " [g/m]"); + } + + absolute.println(); + perMeter.println(); + + Map, Map> link2pollutants = emissionsEventHandler.getLink2pollutants(); + + for (Id linkId : link2pollutants.keySet()) { + + // Link might be filtered + if (!network.getLinks().containsKey(linkId)) + continue; + + absolute.print(linkId); + perMeter.print(linkId); + + for (Pollutant pollutant : Pollutant.values()) { + double emissionValue = 0.; + if (link2pollutants.get(linkId).get(pollutant) != null) { + emissionValue = link2pollutants.get(linkId).get(pollutant); + } + absolute.print(nf.format(emissionValue)); + + Link link = network.getLinks().get(linkId); + double emissionPerM = emissionValue / link.getLength(); + perMeter.print(nf.format(emissionPerM)); + } + + absolute.println(); + perMeter.println(); + } + + absolute.close(); + perMeter.close(); + } + + /** + * Total emissions table. + */ + private void writeTotal(Network network, EmissionsOnLinkEventHandler emissionsEventHandler) { + + Object2DoubleMap sum = new Object2DoubleLinkedOpenHashMap<>(); + + DecimalFormat simple = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + simple.setMaximumFractionDigits(2); + simple.setMaximumIntegerDigits(5); + + DecimalFormat scientific = new DecimalFormat("0.###E0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + + for (Map.Entry, Map> e : emissionsEventHandler.getLink2pollutants().entrySet()) { + + if (!network.getLinks().containsKey(e.getKey())) + continue; + for (Map.Entry p : e.getValue().entrySet()) { + sum.mergeDouble(p.getKey(), p.getValue(), Double::sum); + } + } + + try (CSVPrinter total = new CSVPrinter(Files.newBufferedWriter(output.getPath("emissions_total.csv")), CSVFormat.DEFAULT)) { + + total.printRecord("Pollutant", "kg"); + for (Pollutant p : Pollutant.values()) { + double val = (sum.getDouble(p) / sample.getSample()) / 1000; + total.printRecord(p, val < 100_000 && val > 100 ? simple.format(val) : scientific.format(val)); + } + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Creates the data for the XY-Time plot. The time is fixed and the data is summarized over the run. + * Currently only the CO2_Total Values is printed because Simwrapper can handle only one value. + */ + private void writeRaster(Network network, Config config, EmissionsOnLinkEventHandler emissionsEventHandler) { + + Map rasterMap = FastEmissionGridAnalyzer.processHandlerEmissions(emissionsEventHandler.getLink2pollutants(), network, gridSize, 20); + + List xLength = rasterMap.values().stream().map(Raster::getXLength).distinct().toList(); + List yLength = rasterMap.values().stream().map(Raster::getYLength).distinct().toList(); + + Raster raster = rasterMap.values().stream().findFirst().orElseThrow(); + + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath("emissions_grid_per_day.xyt.csv")), + CSVFormat.DEFAULT.builder().setCommentMarker('#').build())) { + + String crs = ProjectionUtils.getCRS(network); + if (crs == null) + crs = config.network().getInputCRS(); + if (crs == null) + crs = config.global().getCoordinateSystem(); + + // print coordinate system + printer.printComment(crs); + + // print header + printer.print("time"); + printer.print("x"); + printer.print("y"); + + printer.print("value"); + + printer.println(); + + for (int xi = 0; xi < xLength.get(0); xi++) { + for (int yi = 0; yi < yLength.get(0); yi++) { + + Coord coord = raster.getCoordForIndex(xi, yi); + + printer.print(0.0); + printer.print(coord.getX()); + printer.print(coord.getY()); + + double value = rasterMap.get(Pollutant.CO2_TOTAL).getValueByIndex(xi, yi); + printer.print(value); + + printer.println(); + } + } + + } catch (IOException e) { + log.error("Error writing results", e); + } + } + + private void writeTimeDependentRaster(Network network, Config config, EmissionsOnLinkEventHandler emissionsEventHandler) { + + TimeBinMap> timeBinMap = FastEmissionGridAnalyzer.processHandlerEmissionsPerTimeBin(emissionsEventHandler.getTimeBins(), network, gridSize, 20); + + Map firstBin = timeBinMap.getTimeBin(timeBinMap.getStartTime()).getValue(); + + List xLength = firstBin.values().stream().map(Raster::getXLength).distinct().toList(); + List yLength = firstBin.values().stream().map(Raster::getYLength).distinct().toList(); + + Raster raster = firstBin.values().stream().findFirst().orElseThrow(); + + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("emissions_grid_per_hour.xyt.csv").toString()), + CSVFormat.DEFAULT.builder().setCommentMarker('#').build())) { + + String crs = ProjectionUtils.getCRS(network); + if (crs == null) + crs = config.network().getInputCRS(); + if (crs == null) + crs = config.global().getCoordinateSystem(); + + // print coordinate system + printer.printComment(crs); + + // print header + printer.print("time"); + printer.print("x"); + printer.print("y"); + + printer.print("value"); + + printer.println(); + + for (int xi = 0; xi < xLength.get(0); xi++) { + for (int yi = 0; yi < yLength.get(0); yi++) { + for (TimeBinMap.TimeBin> timeBin : timeBinMap.getTimeBins()) { + + Coord coord = raster.getCoordForIndex(xi, yi); + + printer.print(timeBin.getStartTime()); + printer.print(coord.getX()); + printer.print(coord.getY()); + + double value = timeBin.getValue().get(Pollutant.CO2_TOTAL).getValueByIndex(xi, yi); + printer.print(value); + + printer.println(); + } + } + } + + } catch (IOException e) { + log.error("Error writing results", e); + } + + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionByVehicleCategory.java b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionByVehicleCategory.java index 8b4b3db6d38..20bf17af5c6 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionByVehicleCategory.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionByVehicleCategory.java @@ -64,12 +64,16 @@ import static org.matsim.application.ApplicationUtils.globFile; +/** + * @deprecated Use {@link AirPollutionAnalysis} + */ @CommandLine.Command( name = "air-pollution-by-vehicle", description = "Run offline air pollution analysis assuming default vehicles", mixinStandardHelpOptions = true, showDefaultValues = true ) +@Deprecated public class AirPollutionByVehicleCategory implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(AirPollutionByVehicleCategory.class); @@ -131,8 +135,8 @@ public Integer call() throws Exception { config.transit().setVehiclesFile(globFile(runDirectory, runId, "transitVehicles")); config.global().setCoordinateSystem(crs.getInputCRS()); config.plans().setInputFile(null); - config.parallelEventHandling().setNumberOfThreads(null); - config.parallelEventHandling().setEstimatedNumberOfEvents(null); + config.eventsManager().setNumberOfThreads(null); + config.eventsManager().setEstimatedNumberOfEvents(null); config.global().setNumberOfThreads(1); EmissionsConfigGroup eConfig = ConfigUtils.addOrGetModule(config, EmissionsConfigGroup.class); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionSpatialAggregation.java b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionSpatialAggregation.java index 40e882db3fb..f0877999269 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionSpatialAggregation.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/emissions/AirPollutionSpatialAggregation.java @@ -40,12 +40,16 @@ import java.util.Set; import java.util.stream.Collectors; +/** + * @deprecated Use {@link AirPollutionAnalysis} + */ @CommandLine.Command( name = "air-pollution-spatial-aggregation", description = "Aggregate emissions on a spatial grid", mixinStandardHelpOptions = true, showDefaultValues = true ) +@Deprecated public class AirPollutionSpatialAggregation implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(AirPollutionSpatialAggregation.class); @@ -138,4 +142,4 @@ public Integer call() throws Exception { return 0; } -} \ No newline at end of file +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java index 964ce79c6a8..3e99b052194 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java @@ -71,7 +71,7 @@ public Integer call() throws Exception { if (!runDirectory.endsWith("/")) runDirectory = runDirectory + "/"; config.global().setCoordinateSystem(crs.getInputCRS()); - config.controler().setRunId(runId); + config.controller().setRunId(runId); if (!runId.equals("")) { config.network().setInputFile(runDirectory + runId + ".output_network.xml.gz"); config.plans().setInputFile(runDirectory + runId + ".output_plans.xml.gz"); @@ -79,7 +79,7 @@ public Integer call() throws Exception { config.network().setInputFile(runDirectory + "output_network.xml.gz"); config.plans().setInputFile(runDirectory + "output_plans.xml.gz"); } - config.controler().setOutputDirectory(runDirectory); + config.controller().setOutputDirectory(runDirectory); // adjust the default noise parameters NoiseConfigGroup noiseParameters = ConfigUtils.addOrGetModule(config, NoiseConfigGroup.class); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/PopulationAttributeAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/PopulationAttributeAnalysis.java new file mode 100644 index 00000000000..931b88b825d --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/PopulationAttributeAnalysis.java @@ -0,0 +1,166 @@ +package org.matsim.application.analysis.population; + +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.vehicles.PersonVehicles; +import picocli.CommandLine; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@CommandLine.Command(name = "population-attribute", description = "Generates statistics of the population attributes.") +@CommandSpec(requirePopulation = true, produces = {"amount_per_age_group.csv", "amount_per_sex_group.csv", "total_agents.csv", "average_income_per_age_group.csv"}) +public class PopulationAttributeAnalysis implements MATSimAppCommand { + private static final Logger log = LogManager.getLogger(PopulationAttributeAnalysis.class); + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(PopulationAttributeAnalysis.class); + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(PopulationAttributeAnalysis.class); + private final Int2IntMap amountPerAgeGroup = new Int2IntOpenHashMap(); + private final HashMap amountPerSexGroup = new HashMap<>(); + private final HashMap> averageIncomeOverAge = new HashMap<>(); // > + private Integer totalAgents = 0; + private final List allIncomes = new ArrayList<>(); + private final List allAges = new ArrayList<>(); + + public static void main(String[] args) { + new PopulationAttributeAnalysis().execute(args); + } + + @Override + public Integer call() throws Exception { + + Population population = input.getPopulation(); + + for (Person person : population.getPersons().values()) { + + // Count all algents + totalAgents++; + +// System.out.println(System.lineSeparator() + "#############################################" + System.lineSeparator()); +// amountPerAgeGroup.computeIfAbsent() + + if (person.getAttributes().getAsMap().containsKey("age") && person.getAttributes().getAsMap().containsKey("income") && person.getAttributes().getAsMap().containsKey("sex")) { + this.splitIncomeOverAgeAndSex((int) person.getAttributes().getAttribute("age"), (double) person.getAttributes().getAttribute("income")); + } + + Map map = person.getAttributes().getAsMap(); + for (Map.Entry entry : map.entrySet()) { + + if (entry.getKey().equals("income")) { + allIncomes.add(Double.valueOf(entry.getValue().toString())); + } + + if (entry.getKey().equals("vehicles")) System.out.println(((PersonVehicles) entry.getValue()).getModeVehicles().toString()); + else System.out.println(entry.getValue().toString()); + + if (entry.getKey().equals("sex")) { + String sex = (String) entry.getValue(); + this.splitAgentsIntoSex(sex); + } + + if (entry.getKey().equals("age")) { + Integer age = (Integer) entry.getValue(); + this.splitAgentsIntoAgeGroup(age); + this.allAges.add(age); + } + } + } + + // Agents per age group + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("amount_per_age_group.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord("Age", "# Number"); + for (Int2IntMap.Entry entry : amountPerAgeGroup.int2IntEntrySet()) { + printer.printRecord(String.valueOf(entry.getIntKey()), entry.getIntValue()); + } + } catch (IOException ex) { + log.error(ex); + } + + // Total Agents + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("average_income_per_age_group.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord("Age", "avg. Income"); + for (Map.Entry> entry : averageIncomeOverAge.entrySet()) { + printer.printRecord(entry.getKey(), this.calculateMeanFromDoubleArray(entry.getValue())); + } + } catch (IOException ex) { + log.error(ex); + } + + // Average Income Per Age + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("total_agents.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord("Total Agents", totalAgents, "user-group"); + printer.printRecord("Average Age", new DecimalFormat("#.0#").format(this.calculateMeanFromIntegerArray(allAges))); + printer.printRecord("Average Income", new DecimalFormat("#.0#").format(this.calculateMeanFromDoubleArray(allIncomes)), "money-check-dollar"); + } catch (IOException ex) { + log.error(ex); + } + + // Agents per sex group + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("amount_per_sex_group.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord(amountPerSexGroup.keySet()); + printer.printRecord(amountPerSexGroup.values()); + } catch (IOException ex) { + log.error(ex); + } + + return 0; + } + + private double calculateMeanFromIntegerArray(List allAges) { + Double sum = 0.; + for (Integer income : allAges) { + sum += income; + } + return Math.round((sum / allAges.size()) * 100.0) / 100.0; + } + + private double calculateMeanFromDoubleArray(List value) { + Double sum = 0.; + for (Double income : value) { + sum += income; + } + return Math.round((sum / value.size()) * 100.0) / 100.0; + } + + private void splitIncomeOverAgeAndSex(int age, double income) { + // Rounded Age: 9 -> 10; 10 -> 10; 11 -> 20 + int roundedAge = Math.round(((age - 1)/10) + 1) * 10; + double roundedIncome = (Math.round(((income - 1) / 100) + 1) * 100); + + if (!averageIncomeOverAge.containsKey(roundedAge)) { + averageIncomeOverAge.put(roundedAge, new ArrayList<>()); + } + averageIncomeOverAge.get(roundedAge).add(roundedIncome); + } + + private void splitAgentsIntoSex(String sex) { + if (sex.equals("m")) sex = "Male"; + if (sex.equals("f")) sex = "Female"; + if (amountPerSexGroup.containsKey(sex)) amountPerSexGroup.put(sex, amountPerSexGroup.get(sex) + 1); + else amountPerSexGroup.put(sex, 1); + } + + private void splitAgentsIntoAgeGroup(Integer age) { + // Rounded Age: 9 -> 10; 10 -> 10; 11 -> 20 + int roundedAge = Math.round(((age - 1)/10) + 1) * 10; + if (amountPerAgeGroup.keySet().contains(roundedAge)) amountPerAgeGroup.put(roundedAge, amountPerAgeGroup.get(roundedAge) + 1); + else amountPerAgeGroup.put(roundedAge, 1); + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/StuckAgentAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/StuckAgentAnalysis.java new file mode 100644 index 00000000000..172523813cb --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/StuckAgentAnalysis.java @@ -0,0 +1,170 @@ +package org.matsim.application.analysis.population; + +import it.unimi.dsi.fastutil.ints.Int2DoubleMap; +import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +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.ActivityStartEvent; +import org.matsim.api.core.v01.events.PersonStuckEvent; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.events.handler.PersonStuckEventHandler; +import org.matsim.api.core.v01.network.Link; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; + +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.*; + +@CommandLine.Command(name = "stuck-agents", description = "Generates statistics for stuck agents.") +@CommandSpec(requireEvents = true, produces = {"stuck_agents_per_hour.csv", "stuck_agents_per_mode.csv", "stuck_agents_per_link.csv", "stuck_agents.csv"}) +public class StuckAgentAnalysis implements MATSimAppCommand, PersonStuckEventHandler, ActivityStartEventHandler { + private static final Logger log = LogManager.getLogger(StuckAgentAnalysis.class); + private final Object2IntMap stuckAgentsPerMode = new Object2IntOpenHashMap<>(); + private final Map stuckAgentsPerHour = new HashMap<>(); + private final Map> stuckAgentsPerLink = new HashMap<>(); + private final Object2DoubleOpenHashMap allStuckLinks = new Object2DoubleOpenHashMap<>(); + private final Set allAgents = new HashSet<>(); + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(StuckAgentAnalysis.class); + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(StuckAgentAnalysis.class); + private int maxHour = 0; + + public static void main(String[] args) { + new StuckAgentAnalysis().execute(args); + } + + @Override + public Integer call() throws Exception { + + EventsManager manager = EventsUtils.createEventsManager(); + manager.addHandler(this); + + manager.initProcessing(); + + EventsUtils.readEvents(manager, input.getEventsPath()); + + manager.finishProcessing(); + + // Total stats + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("stuck_agents.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord("Total mobile Agents", allAgents.size(), "user-group"); + printer.printRecord("Stuck Agents", allStuckLinks.keySet().size(), "person-circle-xmark"); + printer.printRecord("Proportion of stuck agents", new DecimalFormat("#.0#", DecimalFormatSymbols.getInstance(Locale.US)).format(((Math.round((100.0 / allAgents.size() * allStuckLinks.keySet().size()) * 100)) / 100.0)) + '%', "chart-pie"); + } catch (IOException ex) { + log.error(ex); + } + + // Per hour + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("stuck_agents_per_hour.csv").toString()), CSVFormat.DEFAULT)) { + List header = new ArrayList<>(stuckAgentsPerHour.keySet()); + header.add(0, "hour"); + header.add(1, "Total"); + // Write to .csv + printer.printRecord(header); + for (int i = 0; i <= maxHour; i++) { + String[] result = new String[header.size()]; + result[0] = String.valueOf(i); + result[1] = String.valueOf(this.summarizeStuckAgentsPerHour(i)); + for (int j = 2; j < header.size(); j++) { + result[j] = String.valueOf(stuckAgentsPerHour.get(header.get(j)).get(i)); + } + printer.printRecord((Object[]) result); + } + } catch (IOException ex) { + log.error(ex); + } + + // Per link + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("stuck_agents_per_link.csv").toString()), CSVFormat.DEFAULT)) { + + // Sort Map + List sorted = new ArrayList<>(allStuckLinks.keySet()); + sorted.sort((o1, o2) -> -Double.compare(allStuckLinks.getDouble(o1), allStuckLinks.getDouble(o2))); + List header = new ArrayList<>(stuckAgentsPerLink.keySet()); + header.add(0, "link"); + header.add(1, "Agents"); + // Write to .csv + printer.printRecord(header); + for (int i = 0; i < 20; i++) { + String[] result = new String[header.size()]; + result[0] = sorted.get(i); + result[1] = String.valueOf(this.summarizeStuckAgentsPerLink(sorted.get(i))); + for (int j = 2; j < header.size(); j++) { + result[j] = String.valueOf(stuckAgentsPerLink.get(header.get(j)).getDouble(sorted.get(i))); + } + printer.printRecord((Object[]) result); + } + } catch (IOException ex) { + log.error(ex); + } + + // Stuck agents per mode + try (CSVPrinter printer = new CSVPrinter(IOUtils.getBufferedWriter(output.getPath("stuck_agents_per_mode.csv").toString()), CSVFormat.DEFAULT)) { + printer.printRecord("Mode", "Agents"); + for (Object2IntMap.Entry entry : stuckAgentsPerMode.object2IntEntrySet()) { + printer.printRecord(entry.getKey(), entry.getIntValue()); + } + } catch (IOException ex) { + log.error(ex); + } + + return 0; + } + + private double summarizeStuckAgentsPerLink(String stuckedLink) { + double sum = 0.; + for (Map.Entry> entry : stuckAgentsPerLink.entrySet()) { + sum += entry.getValue().getDouble(stuckedLink); + } + return sum; + } + + private double summarizeStuckAgentsPerHour(int hour) { + double sum = 0.; + for (Map.Entry entry : stuckAgentsPerHour.entrySet()) { + sum += entry.getValue().get(hour); + } + return sum; + } + + @Override + public void handleEvent(PersonStuckEvent event) { + // Pie chart + if (!stuckAgentsPerMode.containsKey(event.getLegMode())) stuckAgentsPerMode.put(event.getLegMode(), 1); + else stuckAgentsPerMode.put(event.getLegMode(), stuckAgentsPerMode.getInt(event.getLegMode()) + 1); + + // Stuck Agents per Hour + Int2DoubleMap perHour = stuckAgentsPerHour.computeIfAbsent(event.getLegMode(), (k) -> new Int2DoubleOpenHashMap()); + int hour = (int) event.getTime() / 3600; + if (hour > maxHour) maxHour = hour; + perHour.mergeDouble(hour, 1, Double::sum); + + // Stuck Agents per Link + Object2DoubleMap perLink = stuckAgentsPerLink.computeIfAbsent(event.getLegMode(), (k) -> new Object2DoubleOpenHashMap<>()); + + Id link = Objects.requireNonNullElseGet(event.getLinkId(), () -> Id.createLinkId("unknown")); + allStuckLinks.merge(link.toString(), 1., Double::sum); + perLink.mergeDouble(link.toString(), 1, Double::sum); + } + + @Override + public void handleEvent(ActivityStartEvent event) { + allAgents.add(event.getPersonId().toString()); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/SubTourAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/SubTourAnalysis.java index a3cf10def59..37f719b6c17 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/population/SubTourAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/SubTourAnalysis.java @@ -10,10 +10,10 @@ import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; -import org.matsim.application.analysis.DefaultAnalysisMainModeIdentifier; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.ChooseRandomLegModeForSubtour; import org.matsim.core.replanning.modules.SubtourModeChoice; +import org.matsim.core.router.DefaultAnalysisMainModeIdentifier; import org.matsim.core.router.TripStructureUtils; import picocli.CommandLine; diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java new file mode 100644 index 00000000000..35f11ab09d6 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java @@ -0,0 +1,409 @@ +package org.matsim.application.analysis.population; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; +import tech.tablesaw.api.*; +import tech.tablesaw.io.csv.CsvReadOptions; +import tech.tablesaw.joining.DataFrameJoiner; +import tech.tablesaw.selection.Selection; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.util.*; + +import static tech.tablesaw.aggregate.AggregateFunctions.count; + +@CommandLine.Command(name = "trips", description = "Calculates various trip related metrics.") +@CommandSpec( + requires = {"trips.csv", "persons.csv"}, + produces = {"mode_share.csv", "mode_share_per_dist.csv", "mode_users.csv", "trip_stats.csv", "population_trip_stats.csv", "trip_purposes_by_hour.csv"} +) +public class TripAnalysis implements MATSimAppCommand { + + private static final Logger log = LogManager.getLogger(TripAnalysis.class); + + @CommandLine.Mixin + private InputOptions input = InputOptions.ofCommand(TripAnalysis.class); + @CommandLine.Mixin + private OutputOptions output = OutputOptions.ofCommand(TripAnalysis.class); + + @CommandLine.Option(names = "--match-id", description = "Pattern to filter agents by id") + private String matchId; + + @CommandLine.Option(names = "--dist-groups", split = ",", description = "List of distances for binning", defaultValue = "0,1000,2000,5000,10000,20000") + private List distGroups; + + @CommandLine.Option(names = "--modes", split = ",", description = "List of considered modes, if not set all will be used") + private List modeOrder; + + @CommandLine.Option(names = "--shp-filter", description = "Define how the shp file filtering should work", defaultValue = "home") + private LocationFilter filter; + + @CommandLine.Mixin + private ShpOptions shp; + + public static void main(String[] args) { + new TripAnalysis().execute(args); + } + + private static String cut(long dist, List distGroups, List labels) { + + int idx = Collections.binarySearch(distGroups, dist); + + if (idx >= 0) + return labels.get(idx); + + int ins = -(idx + 1); + return labels.get(ins - 1); + } + + private static int[] durationToHour(String d) { + return Arrays.stream(d.split(":")).mapToInt(Integer::valueOf).toArray(); + } + + private static int durationToSeconds(String d) { + String[] split = d.split(":"); + return (Integer.parseInt(split[0]) * 60 * 60) + (Integer.parseInt(split[1]) * 60) + Integer.parseInt(split[2]); + } + + @Override + public Integer call() throws Exception { + + Table persons = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("persons.csv"))) + .columnTypesPartial(Map.of("person", ColumnType.TEXT)) + .sample(false) + .separator(';').build()); + + int total = persons.rowCount(); + + if (matchId != null) { + log.info("Using id filter {}", matchId); + persons = persons.where(persons.textColumn("person").matchesRegex(matchId)); + } + + // Home filter by standard attribute + if (shp.isDefined() && filter == LocationFilter.home) { + Geometry geometry = shp.getGeometry(); + GeometryFactory f = new GeometryFactory(); + + IntList idx = new IntArrayList(); + + for (int i = 0; i < persons.rowCount(); i++) { + Row row = persons.row(i); + Point p = f.createPoint(new Coordinate(row.getDouble("home_x"), row.getDouble("home_y"))); + if (geometry.contains(p)) { + idx.add(i); + } + } + + persons = persons.where(Selection.with(idx.toIntArray())); + } + + log.info("Filtered {} out of {} persons", persons.rowCount(), total); + + Map columnTypes = new HashMap<>(Map.of("person", ColumnType.TEXT, + "trav_time", ColumnType.STRING, "wait_time", ColumnType.STRING, "dep_time", ColumnType.STRING, + "longest_distance_mode", ColumnType.STRING, "main_mode", ColumnType.STRING, + "start_activity_type", ColumnType.TEXT, "end_activity_type", ColumnType.TEXT, + "first_pt_boarding_stop", ColumnType.TEXT, "last_pt_egress_stop", ColumnType.TEXT)); + + // Map.of only has 10 argument max + columnTypes.put("traveled_distance", ColumnType.LONG); + + Table trips = Table.read().csv(CsvReadOptions.builder(IOUtils.getBufferedReader(input.getPath("trips.csv"))) + .columnTypesPartial(columnTypes) + .sample(false) + .separator(';').build()); + + + // Trip filter with start and end + if (shp.isDefined() && filter == LocationFilter.trip_start_and_end) { + Geometry geometry = shp.getGeometry(); + GeometryFactory f = new GeometryFactory(); + IntList idx = new IntArrayList(); + + for (int i = 0; i < trips.rowCount(); i++) { + Row row = trips.row(i); + Point start = f.createPoint(new Coordinate(row.getDouble("start_x"), row.getDouble("start_y"))); + Point end = f.createPoint(new Coordinate(row.getDouble("end_x"), row.getDouble("end_y"))); + if (geometry.contains(start) && geometry.contains(end)) { + idx.add(i); + } + } + + trips = trips.where(Selection.with(idx.toIntArray())); + } + + // Use longest_distance_mode where main_mode is not present + trips.stringColumn("main_mode") + .set(trips.stringColumn("main_mode").isMissing(), + trips.stringColumn("longest_distance_mode")); + + + Table joined = new DataFrameJoiner(trips, "person").inner(persons); + + log.info("Filtered {} out of {} trips", joined.rowCount(), trips.rowCount()); + + List labels = new ArrayList<>(); + for (int i = 0; i < distGroups.size() - 1; i++) { + labels.add(String.format("%d - %d", distGroups.get(i), distGroups.get(i + 1))); + } + labels.add(distGroups.get(distGroups.size() - 1) + "+"); + distGroups.add(Long.MAX_VALUE); + + StringColumn dist_group = joined.longColumn("traveled_distance") + .map(dist -> cut(dist, distGroups, labels), ColumnType.STRING::create).setName("dist_group"); + + joined.addColumns(dist_group); + + writeModeShare(joined, labels); + + writePopulationStats(persons, joined); + + writeTripStats(joined); + + writeTripPurposes(joined); + + return 0; + } + + private void writeModeShare(Table trips, List labels) { + + Table aggr = trips.summarize("trip_id", count).by("dist_group", "main_mode"); + + DoubleColumn share = aggr.numberColumn(2).divide(aggr.numberColumn(2).sum()).setName("share"); + aggr.replaceColumn(2, share); + + // Sort by dist_group and mode + Comparator cmp = Comparator.comparingInt(row -> labels.indexOf(row.getString("dist_group"))); + aggr = aggr.sortOn(cmp.thenComparing(row -> row.getString("main_mode"))); + + aggr.write().csv(output.getPath("mode_share.csv").toFile()); + + // Norm each dist_group to 1 + for (String label : labels) { + DoubleColumn dist_group = aggr.doubleColumn("share"); + Selection sel = aggr.stringColumn("dist_group").isEqualTo(label); + + double total = dist_group.where(sel).sum(); + if (total > 0) + dist_group.set(sel, dist_group.divide(total)); + } + + aggr.write().csv(output.getPath("mode_share_per_dist.csv").toFile()); + + // Derive mode order if not given + if (modeOrder == null) { + modeOrder = new ArrayList<>(); + for (Row row : aggr) { + String mainMode = row.getString("main_mode"); + if (!modeOrder.contains(mainMode)) { + modeOrder.add(mainMode); + } + } + } + } + + private void writeTripStats(Table trips) throws IOException { + + // Stats per mode + Object2IntMap n = new Object2IntLinkedOpenHashMap<>(); + Object2LongMap travelTime = new Object2LongOpenHashMap<>(); + Object2LongMap travelDistance = new Object2LongOpenHashMap<>(); + + for (Row trip : trips) { + String mainMode = trip.getString("main_mode"); + + n.mergeInt(mainMode, 1, Integer::sum); + travelTime.mergeLong(mainMode, durationToSeconds(trip.getString("trav_time")), Long::sum); + travelDistance.mergeLong(mainMode, trip.getLong("traveled_distance"), Long::sum); + } + + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath("trip_stats.csv")), CSVFormat.DEFAULT)) { + + printer.print("Info"); + for (String m : modeOrder) { + printer.print(m); + } + printer.println(); + + printer.print("Number of trips"); + for (String m : modeOrder) { + printer.print(n.getInt(m)); + } + printer.println(); + + printer.print("Total time traveled [h]"); + for (String m : modeOrder) { + long seconds = travelTime.getLong(m); + printer.print(new BigDecimal(seconds / (60d * 60d)).setScale(0, RoundingMode.HALF_UP)); + } + printer.println(); + + printer.print("Total distance traveled [km]"); + for (String m : modeOrder) { + double meter = travelDistance.getLong(m); + printer.print(new BigDecimal(meter / 1000d).setScale(0, RoundingMode.HALF_UP)); + } + printer.println(); + + printer.print("Avg. speed [km/h]"); + for (String m : modeOrder) { + double speed = (travelDistance.getLong(m) / 1000d) / (travelTime.getLong(m) / (60d * 60d)); + printer.print(new BigDecimal(speed).setScale(2, RoundingMode.HALF_UP)); + + } + printer.println(); + + printer.print("Avg. distance per trip [km]"); + for (String m : modeOrder) { + double avg = (travelDistance.getLong(m) / 1000d) / (n.getInt(m)); + printer.print(new BigDecimal(avg).setScale(2, RoundingMode.HALF_UP)); + + } + printer.println(); + } + } + + private void writePopulationStats(Table persons, Table trips) throws IOException { + + Object2IntMap tripsPerPerson = new Object2IntLinkedOpenHashMap<>(); + Map> modesPerPerson = new LinkedHashMap<>(); + + for (Row trip : trips) { + String id = trip.getString("person"); + tripsPerPerson.mergeInt(id, 1, Integer::sum); + String mode = trip.getString("main_mode"); + modesPerPerson.computeIfAbsent(id, s -> new LinkedHashSet<>()).add(mode); + } + + Object2IntMap usedModes = new Object2IntLinkedOpenHashMap<>(); + for (Map.Entry> e : modesPerPerson.entrySet()) { + for (String mode : e.getValue()) { + usedModes.mergeInt(mode, 1, Integer::sum); + } + } + + double totalMobile = tripsPerPerson.size(); + double avgTripsMobile = tripsPerPerson.values().intStream().average().orElse(0); + + for (Row person : persons) { + String id = person.getString("person"); + if (!tripsPerPerson.containsKey(id)) + tripsPerPerson.put(id, 0); + } + + double avgTrips = tripsPerPerson.values().intStream().average().orElse(0); + + Table table = Table.create(TextColumn.create("main_mode", usedModes.size()), DoubleColumn.create("user", usedModes.size())); + + int i = 0; + for (String m : modeOrder) { + int n = usedModes.getInt(m); + table.textColumn(0).set(i, m); + table.doubleColumn(1).set(i++, n / totalMobile); + } + + table.write().csv(output.getPath("mode_users.csv").toFile()); + + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath("population_trip_stats.csv")), CSVFormat.DEFAULT)) { + + printer.printRecord("Info", "Value"); + printer.printRecord("Persons", tripsPerPerson.size()); + printer.printRecord("Mobile persons [%]", new BigDecimal(100 * totalMobile / tripsPerPerson.size()).setScale(2, RoundingMode.HALF_UP)); + printer.printRecord("Avg. trips", new BigDecimal(avgTrips).setScale(2, RoundingMode.HALF_UP)); + printer.printRecord("Avg. trip per mobile persons", new BigDecimal(avgTripsMobile).setScale(2, RoundingMode.HALF_UP)); + } + } + + private void writeTripPurposes(Table trips) { + + IntList departure = new IntArrayList(trips.rowCount()); + IntList arrival = new IntArrayList(trips.rowCount()); + + for (Row t : trips) { + int[] depTime = durationToHour(t.getString("dep_time")); + departure.add(depTime[0]); + + int[] travTimes = durationToHour(t.getString("trav_time")); + + depTime[2] += travTimes[2]; + if (depTime[2] >= 60) + depTime[1]++; + + depTime[1] += travTimes[1]; + if (depTime[1] >= 60) + depTime[0]++; + + depTime[0] += travTimes[0]; + + arrival.add(depTime[0]); + } + + trips.addColumns( + IntColumn.create("departure_h", departure.intStream().toArray()), + IntColumn.create("arrival_h", arrival.intStream().toArray()) + ); + + TextColumn purpose = trips.textColumn("end_activity_type"); + + // Remove suffix durations like _345 + Selection withDuration = purpose.matchesRegex("^.+_[0-9]+$"); + purpose.set(withDuration, purpose.where(withDuration).replaceAll("_[0-9]+$", "")); + + Table tArrival = trips.summarize("trip_id", count).by("end_activity_type", "arrival_h"); + + tArrival.column(0).setName("purpose"); + tArrival.column(1).setName("h"); + + DoubleColumn share = tArrival.numberColumn(2).divide(tArrival.numberColumn(2).sum()).setName("arrival"); + tArrival.replaceColumn(2, share); + + Table tDeparture = trips.summarize("trip_id", count).by("end_activity_type", "departure_h"); + + tDeparture.column(0).setName("purpose"); + tDeparture.column(1).setName("h"); + + share = tDeparture.numberColumn(2).divide(tDeparture.numberColumn(2).sum()).setName("departure"); + tDeparture.replaceColumn(2, share); + + + Table table = new DataFrameJoiner(tArrival, "purpose", "h").fullOuter(tDeparture).sortOn(0, 1); + + table.doubleColumn("departure").setMissingTo(0.0); + table.doubleColumn("arrival").setMissingTo(0.0); + + table.write().csv(output.getPath("trip_purposes_by_hour.csv").toFile()); + + } + + /** + * How shape file filtering should be applied. + */ + enum LocationFilter { + trip_start_and_end, + home, + none + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java new file mode 100644 index 00000000000..42985e22beb --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/CountComparisonAnalysis.java @@ -0,0 +1,253 @@ +package org.matsim.application.analysis.traffic; + +import org.matsim.analysis.VolumesAnalyzer; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.SampleOptions; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.counts.Count; +import org.matsim.counts.Counts; +import org.matsim.counts.MatsimCountsReader; +import org.matsim.counts.Volume; +import picocli.CommandLine; +import tech.tablesaw.api.*; +import tech.tablesaw.selection.Selection; + +import java.nio.file.Path; +import java.util.*; + +import static tech.tablesaw.aggregate.AggregateFunctions.count; +import static tech.tablesaw.aggregate.AggregateFunctions.mean; + +@CommandLine.Command(name = "count-comparison", description = "Produces comparisons of observed and simulated counts.") +@CommandSpec(requireEvents = true, requireCounts = true, requireNetwork = true, + produces = {"count_comparison_by_hour.csv", "count_comparison_daily.csv", "count_comparison_quality.csv", "count_error_by_hour.csv"}) +public class CountComparisonAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(CountComparisonAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(CountComparisonAnalysis.class); + + @CommandLine.Mixin + private SampleOptions sample; + + @CommandLine.Option(names = "--limits", split = ",", description = "Limits for quality label categories", defaultValue = "0.6,0.8,1.2,1.4") + private List limits; + + @CommandLine.Option(names = "--labels", split = ",", description = "Labels for quality categories", defaultValue = "major under,under,ok,over,major over") + private List labels; + + @CommandLine.Option(names = "--transport-mode", description = "Mode to analyze", split = ",", defaultValue = TransportMode.car) + private Set modes; + + public static void main(String[] args) { + new CountComparisonAnalysis().execute(args); + } + + private static String cut(double relError, List errorGroups, List labels) { + + int idx = Collections.binarySearch(errorGroups, relError); + + if (idx >= 0) + return labels.get(idx); + + int ins = -(idx + 1); + return labels.get(ins); + } + + private static int[] sum(int[] a, int[] b) { + int[] counts = new int[a.length]; + for (int i = 0; i < counts.length; i++) { + counts[i] = a[i] + b[i]; + } + + return counts; + } + + @Override + public Integer call() throws Exception { + + EventsManager eventsManager = EventsUtils.createEventsManager(); + + Network network = input.getNetwork(); + + VolumesAnalyzer volume = new VolumesAnalyzer(3600, 86400, network, true); + + eventsManager.addHandler(volume); + + eventsManager.initProcessing(); + + EventsUtils.readEvents(eventsManager, input.getEventsPath()); + + eventsManager.finishProcessing(); + + Counts counts = new Counts<>(); + MatsimCountsReader reader = new MatsimCountsReader(counts); + reader.readFile(input.getCountsPath()); + + Table byHour = writeOutput(counts, network, volume); + + writeErrorMetrics(byHour, output.getPath("count_error_by_hour.csv")); + + return 0; + } + + private Table writeOutput(Counts counts, Network network, VolumesAnalyzer volumes) { + + Map, ? extends Link> links = network.getLinks(); + + Table byHour = Table.create( + StringColumn.create("link_id"), + StringColumn.create("name"), + StringColumn.create("road_type"), + IntColumn.create("hour"), + DoubleColumn.create("observed_traffic_volume"), + DoubleColumn.create("simulated_traffic_volume") + ); + + Table dailyTrafficVolume = Table.create(StringColumn.create("link_id"), + StringColumn.create("name"), + StringColumn.create("road_type"), + DoubleColumn.create("observed_traffic_volume"), + DoubleColumn.create("simulated_traffic_volume") + ); + + for (Map.Entry, Count> entry : counts.getCounts().entrySet()) { + Id key = entry.getKey(); + Map countVolume = entry.getValue().getVolumes(); + String name = entry.getValue().getCsLabel(); + + Link link = links.get(key); + String type = NetworkUtils.getHighwayType(link); + + if (countVolume.isEmpty()) + continue; + + Optional opt = modes.stream() + .map(mode -> volumes.getVolumesForLink(key, mode)) + .filter(Objects::nonNull) + .reduce(CountComparisonAnalysis::sum); + + int[] volumesForLink; + if (countVolume.isEmpty() || opt.isEmpty()) { + volumesForLink = new int[24]; + } else { + volumesForLink = opt.get(); + } + + double simulatedTrafficVolumeByDay = 0; + double observedTrafficVolumeByDay = 0; + + if (countVolume.size() == 24) { + + for (int hour = 1; hour < 25; hour++) { + + double observedTrafficVolumeAtHour = countVolume.get(hour).getValue(); + double simulatedTrafficVolumeAtHour = (double) volumesForLink[hour - 1] / this.sample.getSample(); + + simulatedTrafficVolumeByDay += simulatedTrafficVolumeAtHour; + observedTrafficVolumeByDay += observedTrafficVolumeAtHour; + + Row row = byHour.appendRow(); + row.setString("link_id", key.toString()); + row.setString("name", name); + row.setString("road_type", type); + row.setInt("hour", hour - 1); + row.setDouble("observed_traffic_volume", observedTrafficVolumeAtHour); + row.setDouble("simulated_traffic_volume", simulatedTrafficVolumeAtHour); + } + } else + observedTrafficVolumeByDay = countVolume.values().stream().mapToDouble(Volume::getValue).sum(); + + Row row = dailyTrafficVolume.appendRow(); + row.setString("link_id", key.toString()); + row.setString("name", name); + row.setString("road_type", type); + row.setDouble("observed_traffic_volume", observedTrafficVolumeByDay); + row.setDouble("simulated_traffic_volume", simulatedTrafficVolumeByDay); + } + + DoubleColumn relError = dailyTrafficVolume.doubleColumn("simulated_traffic_volume") + .divide(dailyTrafficVolume.doubleColumn("observed_traffic_volume")) + .setName("rel_error"); + + StringColumn qualityLabel = relError.copy() + .map(err -> cut(err, limits, labels), ColumnType.STRING::create) + .setName("quality"); + + dailyTrafficVolume.addColumns(relError, qualityLabel); + + + dailyTrafficVolume = dailyTrafficVolume.sortOn("road_type", "link_id"); + dailyTrafficVolume.write().csv(output.getPath("count_comparison_daily.csv").toFile()); + + byHour = byHour.sortOn("name"); + byHour.write().csv(output.getPath("count_comparison_by_hour.csv").toFile()); + + Table byQuality = dailyTrafficVolume.summarize("quality", count).by("quality", "road_type"); + byQuality.column(2).setName("n"); + + // Sort by quality + Comparator cmp = Comparator.comparingInt(row -> labels.indexOf(row.getString("quality"))); + + byQuality = byQuality.sortOn(cmp.thenComparing(row -> row.getNumber("n"))); + byQuality.addColumns(byQuality.doubleColumn("n").copy().setName("share")); + + List roadTypes = byQuality.stringColumn("road_type").unique().asList(); + + // Norm within each road type + for (String roadType : roadTypes) { + DoubleColumn share = byQuality.doubleColumn("share"); + Selection sel = byQuality.stringColumn("road_type").isEqualTo(roadType); + + double total = share.where(sel).sum(); + if (total > 0) + share.set(sel, share.divide(total)); + } + + byQuality.write().csv(output.getPath("count_comparison_quality.csv").toFile()); + + return byHour; + } + + private void writeErrorMetrics(Table byHour, Path path) { + + byHour.addColumns( + byHour.doubleColumn("simulated_traffic_volume").subtract(byHour.doubleColumn("observed_traffic_volume")).setName("error") + ); + + byHour.addColumns( + byHour.doubleColumn("error").abs().setName("abs_error") + ); + + DoubleColumn relError = byHour.doubleColumn("abs_error") + .multiply(100) + .divide(byHour.doubleColumn("observed_traffic_volume")) + .setName("rel_error"); + + // Cut-off at Max error + relError = relError.set(relError.isMissing(), 1000d); + relError = relError.map(d -> Math.min(d, 1000d)); + + byHour.addColumns(relError); + + Table aggr = byHour.summarize("error", "abs_error", "rel_error", mean).by("hour"); + + aggr.column("Mean [error]").setName("mean_bias"); + aggr.column("Mean [rel_error]").setName("mean_rel_error"); + aggr.column("Mean [abs_error]").setName("mean_abs_error"); + + aggr.write().csv(path.toFile()); + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficAnalysis.java new file mode 100644 index 00000000000..a1c9d55edd7 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficAnalysis.java @@ -0,0 +1,289 @@ +package org.matsim.application.analysis.traffic; + +import org.matsim.analysis.VolumesAnalyzer; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.SampleOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.groups.NetworkConfigGroup; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.network.filter.NetworkFilterManager; +import org.matsim.core.scenario.ProjectionUtils; +import org.matsim.core.trafficmonitoring.TravelTimeCalculator; +import picocli.CommandLine; +import tech.tablesaw.api.*; +import tech.tablesaw.columns.Column; +import tech.tablesaw.selection.Selection; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static tech.tablesaw.aggregate.AggregateFunctions.mean; +import static tech.tablesaw.aggregate.AggregateFunctions.sum; + +@CommandLine.Command(name = "congestion", description = "Calculates congestion indices and relative travel times.") +@CommandSpec(requireEvents = true, requireNetwork = true, + produces = {"traffic_stats_by_link_daily.csv", "traffic_stats_by_link_and_hour.csv", "traffic_stats_by_road_type_daily.csv", "traffic_stats_by_road_type_and_hour.csv"} +) +public class TrafficAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(TrafficAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(TrafficAnalysis.class); + + /** + * Sample size is used to compute actual flow capacity. + */ + @CommandLine.Mixin + private SampleOptions sample; + + @CommandLine.Mixin + private ShpOptions shp; + + @CommandLine.Option(names = "--transport-modes", description = "transport modes to analyze", defaultValue = "", split = ",") + private Set modes; + + public static void main(String[] args) { + new TrafficAnalysis().execute(args); + } + + private static Table normalizeColumns(Table table) { + for (Column c : table.columns()) { + int start = c.name().indexOf("["); + int end = c.name().indexOf("]"); + + if (start > -1 && end > -1) { + c.setName(c.name().substring(start + 1, end)); + } + } + + return table; + } + + @Override + public Integer call() throws Exception { + + Network network = filterNetwork(); + + TravelTimeCalculator.Builder builder = new TravelTimeCalculator.Builder(network); + if (modes != null && !modes.isEmpty()) { + builder.setFilterModes(true); + builder.setAnalyzedModes(modes); + } + + builder.setCalculateLinkTravelTimes(true); + builder.setMaxTime(86400); + builder.setTimeslice(900); + + TravelTimeCalculator travelTimes = builder.build(); + VolumesAnalyzer volumes = new VolumesAnalyzer(3600, 86400, network, true); + + EventsManager manager = EventsUtils.createEventsManager(); + + manager.addHandler(travelTimes); + manager.addHandler(volumes); + + manager.initProcessing(); + EventsUtils.readEvents(manager, input.getEventsPath()); + manager.finishProcessing(); + + TrafficStatsCalculator calc = new TrafficStatsCalculator(network, travelTimes.getLinkTravelTimes(), 900); + + Table ds = createDataset(network, calc, volumes); + + List means = List.of("speed_performance_index", "congestion_index", "avg_speed", "road_capacity_utilization", "lane_km"); + Table dailyMean = normalizeColumns(ds.summarize(means, mean).by("link_id")); + + List sums = ds.columnNames().stream().filter(s -> s.startsWith("vol_") || s.endsWith("_volume")).toList(); + Table dailySum = normalizeColumns(ds.summarize(sums, sum).by("link_id")); + + Table daily = dailyMean.joinOn("link_id").inner(dailySum); + + daily.write().csv(output.getPath("traffic_stats_by_link_daily.csv").toFile()); + + // Copy of table with all link links under one road_type + Table copy = ds.copy(); + copy.stringColumn("road_type").set(Selection.withRange(0, ds.rowCount()), "all"); + copy.forEach(ds::append); + + Table perRoadTypeAndHour = Table.create(StringColumn.create("road_type"), IntColumn.create("hour"), DoubleColumn.create("congestion_index")); + Set roadTypes = new HashSet<>(ds.stringColumn("road_type").asList()); + + for (int hour = 0; hour < 24; hour++) { + + for (String roadType : roadTypes) { + + double congestionIndex = calc.getNetworkCongestionIndex(hour * 3600, (hour + 1) * 3600, roadType.equals("all") ? null : roadType); + + Row row = perRoadTypeAndHour.appendRow(); + row.setString("road_type", roadType); + row.setInt("hour", hour); + row.setDouble("congestion_index", congestionIndex); + } + } + + perRoadTypeAndHour + .sortOn("road_type", "hour") + .write().csv(output.getPath("traffic_stats_by_road_type_and_hour.csv").toFile()); + + Table dailyCongestionIndex = Table.create(StringColumn.create("road_type"), DoubleColumn.create("congestion_index")); + + for (String roadType : roadTypes) { + + double congestionIndex = calc.getNetworkCongestionIndex(0, 86400, roadType.equals("all") ? null : roadType); + Row row = dailyCongestionIndex.appendRow(); + row.setString("road_type", roadType); + row.setDouble("congestion_index", congestionIndex); + } + + Table perRoadType = dailyCongestionIndex.joinOn("road_type").leftOuter( + weightedMeanBy(ds, means, "road_type").rejectColumns("speed_performance_index", "congestion_index") + ); + + DoubleColumn meanLaneKm = perRoadType.doubleColumn("lane_km").divide(24).multiply(1000).round().divide(1000).setName("lane_km"); + perRoadType.replaceColumn(meanLaneKm); + + perRoadType.column("lane_km").setName("Total lane km"); + perRoadType.column("road_type").setName("Road Type"); + perRoadType.column("road_capacity_utilization").setName("Cap. Utilization"); + perRoadType.column("avg_speed").setName("Avg. Speed [km/h]"); + perRoadType.column("congestion_index").setName("Congestion Index"); + + roundColumns(perRoadType); + perRoadType + .sortOn("Road Type") + .write().csv(output.getPath("traffic_stats_by_road_type_daily.csv").toFile()); + + return 0; + } + + private Table weightedMeanBy(Table table, List aggr, String... by) { + Table first = multiplyWithLinkLength(table).summarize(aggr, sum).by(by); + return divideByLength(normalizeColumns(first)); + } + + private Table divideByLength(Table table) { + + Table copy = Table.create(); + for (Column column : table.columns()) { + + if (column instanceof DoubleColumn d && !column.name().equals("lane_km")) { + String name = column.name(); + DoubleColumn divided = d.divide(table.doubleColumn("lane_km")).setName(name); + copy.addColumns(divided); + } else + copy.addColumns(column); + } + + return copy; + } + + private Table multiplyWithLinkLength(Table table) { + + Table copy = Table.create(); + + for (Column column : table.columns()) { + + if (column instanceof DoubleColumn d && !column.name().equals("lane_km")) { + DoubleColumn multiplied = d.multiply(table.doubleColumn("lane_km")).setName(column.name()); + copy.addColumns(multiplied); + } else + copy.addColumns(column); + } + return copy; + } + + private void roundColumns(Table table) { + + for (Column column : table.columns()) { + if (column instanceof DoubleColumn d) { + d.set(Selection.withRange(0, d.size()), d.multiply(1000).round().divide(1000)); + } + } + } + + /** + * Create table with all disaggregated data. + */ + private Table createDataset(Network network, TrafficStatsCalculator calc, VolumesAnalyzer volumes) { + + Table all = Table.create( + TextColumn.create("link_id"), + IntColumn.create("hour"), + StringColumn.create("road_type"), + DoubleColumn.create("lane_km"), + DoubleColumn.create("speed_performance_index"), + DoubleColumn.create("congestion_index"), + DoubleColumn.create("avg_speed"), + DoubleColumn.create("road_capacity_utilization"), + DoubleColumn.create("simulated_traffic_volume") + ); + + // Somehow Expensive operation + Set modes = volumes.getModes(); + + for (String mode : modes) { + all.addColumns(DoubleColumn.create("vol_" + mode)); + } + + for (Link link : network.getLinks().values()) { + + double[] vol = volumes.getVolumesPerHourForLink(link.getId()); + + for (int h = 0; h < 24; h += 1) { + Row row = all.appendRow(); + + row.setString("link_id", link.getId().toString()); + row.setInt("hour", h); + row.setString("road_type", NetworkUtils.getHighwayType(link)); + row.setDouble("lane_km", (link.getLength() * link.getNumberOfLanes()) / 1000); + + int startTime = h * 3600; + int endTime = (h + 1) * 3600; + + row.setDouble("speed_performance_index", calc.getSpeedPerformanceIndex(link, startTime, endTime)); + row.setDouble("congestion_index", calc.getLinkCongestionIndex(link, startTime, endTime)); + + // as km/h + row.setDouble("avg_speed", calc.getAvgSpeed(link, startTime, endTime) * 3.6); + + double capacity = link.getCapacity() * sample.getSample(); + row.setDouble("road_capacity_utilization", vol[h] / capacity); + + row.setDouble("simulated_traffic_volume", vol[h] / sample.getSample()); + + for (String mode : modes) { + row.setDouble("vol_" + mode, volumes.getVolumesPerHourForLink(link.getId(), mode)[h] / sample.getSample()); + } + } + } + + return all; + } + + private Network filterNetwork() { + + Network unfiltered = input.getNetwork(); + NetworkFilterManager manager = new NetworkFilterManager(unfiltered, new NetworkConfigGroup()); + + // Must contain one of the analyzed modes + manager.addLinkFilter(l -> l.getAllowedModes().stream().anyMatch(s -> modes.contains(s))); + + if (shp.isDefined()) { + String crs = ProjectionUtils.getCRS(unfiltered); + ShpOptions.Index index = shp.createIndex(crs != null ? crs : shp.getShapeCrs(), "_"); + manager.addLinkFilter(l -> index.contains(l.getCoord())); + } + + return manager.applyFilters(); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficStatsCalculator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficStatsCalculator.java new file mode 100644 index 00000000000..b6d29a5319b --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TrafficStatsCalculator.java @@ -0,0 +1,127 @@ +package org.matsim.application.analysis.traffic; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.router.util.TravelTime; + +import javax.annotation.Nullable; +import java.util.Map; + +/** + * Class to calculate the traffic congestion index based on the paper + * "A Traffic Congestion Assessment Method for Urban Road Networks Based on Speed Performance Index" by Feifei He, Xuedong Yan*, Yang Liu, Lu Ma. + */ +public final class TrafficStatsCalculator { + + private final Network network; + private final TravelTime travelTime; + + private final int timeSlice; + + public TrafficStatsCalculator(Network network, TravelTime travelTime, int timeSlice) { + this.network = network; + this.travelTime = travelTime; + this.timeSlice = timeSlice; + } + + /** + * Calculates the speed performance index, which is the ratio of actual travel time and free speed travel time. + */ + public double getSpeedPerformanceIndex(Link link, double time) { + + double length = link.getLength(); + + double allowedSpeed = NetworkUtils.getAllowedSpeed(link); + + double actualTravelTime = travelTime.getLinkTravelTime(link, time, null, null); + + double actualSpeed = length / actualTravelTime; + + double ratio = actualSpeed / allowedSpeed; + + return ratio > 1 ? 1 : ratio; + } + + public double getSpeedPerformanceIndex(Link link, int startTime, int endTime) { + DoubleList indices = new DoubleArrayList(); + + for (int time = startTime; time < endTime; time += timeSlice) + indices.add( + this.getSpeedPerformanceIndex(link, time) + ); + + return indices.doubleStream().average().orElse(-1); + } + + /** + * Calculates the congestion index based on the ratio of actual travel time and free speed travel time. + */ + public double getLinkCongestionIndex(Link link, int startTime, int endTime) { + + DoubleList speedPerformance = new DoubleArrayList(); + + int congestedPeriodCounter = 0; + int totalObservedPeriods = 0; + + for (int time = startTime; time < endTime; time += timeSlice) { + + double speedPerformanceIndex = this.getSpeedPerformanceIndex(link, time); + speedPerformance.add(speedPerformanceIndex); + + if (speedPerformanceIndex <= 0.5) + congestedPeriodCounter++; + + totalObservedPeriods++; + } + + double averageSpeedPerformance = speedPerformance.doubleStream().average().orElse(-1); + + return averageSpeedPerformance * (1 - (double) congestedPeriodCounter / totalObservedPeriods); + } + + /** + * Calculates the network congestion index for a given time period. Can be done for a certain road type. + */ + public double getNetworkCongestionIndex(int startTime, int endTime, @Nullable String roadType) { + + double sumOfLinkLengthMultipiesLinkCongestionIndex = 0.0; + double sumLinkLength = 0.0; + + for (Map.Entry, ? extends Link> entry : this.network.getLinks().entrySet()) { + Link link = entry.getValue(); + + String type = NetworkUtils.getHighwayType(link); + if (roadType != null && !type.equals(roadType)) + continue; + + double linkCongestionIndex = getLinkCongestionIndex(link, startTime, endTime); + + double length = link.getLength() * link.getNumberOfLanes(); + + sumOfLinkLengthMultipiesLinkCongestionIndex += length * linkCongestionIndex; + sumLinkLength += length; + } + + return sumOfLinkLengthMultipiesLinkCongestionIndex / sumLinkLength; + } + + /** + * Calculates the avg speed for a given link and time interval in meter per seconds. + */ + public double getAvgSpeed(Link link, int startTime, int endTime) { + + DoubleList speeds = new DoubleArrayList(); + + for (int time = startTime; time < endTime; time += timeSlice) { + + double seconds = this.travelTime.getLinkTravelTime(link, time, null, null); + speeds.add(link.getLength() / seconds); + } + + return speeds.doubleStream().average().orElse(-1); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimeAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimeAnalysis.java similarity index 98% rename from contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimeAnalysis.java rename to contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimeAnalysis.java index b8588b5c092..27153af03a0 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimeAnalysis.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimeAnalysis.java @@ -1,4 +1,4 @@ -package org.matsim.application.analysis.travelTimeValidation; +package org.matsim.application.analysis.traffic; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,12 +35,16 @@ import static org.matsim.application.ApplicationUtils.globFile; import static org.matsim.application.ApplicationUtils.loadScenario; +/** + * Based on old api and analysis. Consider using {@link org.matsim.application.analysis.traffic.traveltime.SampleValidationRoutes}. + */ @CommandLine.Command( name = "travel-time", description = "Run travel time analysis on events file. pre-analysis: analyze the empty network to validate the free flow speed of the network." + " post-analysis (default): validate the travel time and distance of executed trips." + " Transit mode of MATSim trips and corresponding mode in API need to be configured separately, due to different naming conventions." ) +@Deprecated public class TravelTimeAnalysis implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(TravelTimeAnalysis.class); diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimePatterns.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimePatterns.java similarity index 98% rename from contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimePatterns.java rename to contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimePatterns.java index 0b2a62dbe13..93db7f11e15 100644 --- a/contribs/application/src/main/java/org/matsim/application/analysis/travelTimeValidation/TravelTimePatterns.java +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/TravelTimePatterns.java @@ -1,12 +1,10 @@ -package org.matsim.application.analysis.travelTimeValidation; +package org.matsim.application.analysis.traffic; import com.google.common.base.Joiner; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVRecord; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; @@ -22,7 +20,6 @@ import org.matsim.application.options.CsvOptions; import org.matsim.application.options.ShpOptions; import org.matsim.contrib.analysis.vsp.traveltimedistance.HereMapsLayer; -import org.matsim.contrib.osm.networkReader.LinkProperties; import org.matsim.core.config.groups.NetworkConfigGroup; import org.matsim.core.network.NetworkChangeEvent; import org.matsim.core.network.NetworkUtils; diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/FetchRoutesTask.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/FetchRoutesTask.java new file mode 100644 index 00000000000..e12a6482968 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/FetchRoutesTask.java @@ -0,0 +1,158 @@ +package org.matsim.application.analysis.traffic.traveltime; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +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.network.Node; +import org.matsim.application.analysis.traffic.traveltime.api.*; + +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Task to fetch routes from api service. + */ +final class FetchRoutesTask implements Runnable { + + private static final Logger log = LogManager.getLogger(FetchRoutesTask.class); + + private final RouteAPI api; + private final String apiKey; + + private final List routes; + private final List hours; + private final Path out; + + FetchRoutesTask(RouteAPI api, String apiKey, List routes, List hours, Path out) { + this.api = api; + this.apiKey = apiKey; + this.routes = routes; + this.hours = hours; + this.out = out; + } + + + private void fetch() throws Exception { + + // Collect existing entries to support resuming + Set entries = new HashSet<>(); + OpenOption open; + if (Files.exists(out)) { + open = StandardOpenOption.APPEND; + try (CSVParser csv = new CSVParser(Files.newBufferedReader(out), CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) { + for (CSVRecord r : csv) { + entries.add(new Entry( + Id.createNodeId(r.get("from_node")), + Id.createNodeId(r.get("to_node")), + r.get("api"), + Integer.parseInt(r.get("hour"))) + ); + } + } + + } else + open = StandardOpenOption.CREATE_NEW; + + try (RouteValidator val = switch (api) { + case google -> new GoogleRouteValidator(apiKey); + case woosmap -> new WoosMapRouteValidator(apiKey); + case mapbox -> new MapboxRouteValidator(apiKey); + case here -> new HereRouteValidator(apiKey); + case tomtom -> new TomTomRouteValidator(apiKey); + }) { + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(out, open), CSVFormat.DEFAULT)) { + + if (open == StandardOpenOption.CREATE_NEW) { + csv.printRecord("from_node", "to_node", "api", "hour", "dist", "travel_time"); + csv.flush(); + } + + int i = 0; + int errors = 0; + for (SampleValidationRoutes.Route route : routes) { + for (int h : hours) { + + // Skip entries already present + Entry e = new Entry(route.fromNode(), route.toNode(), val.name(), h); + if (entries.contains(e)) + continue; + + try { + RouteValidator.Result res = fetchWithBackoff(val, route, h, 0); + csv.printRecord(route.fromNode(), route.toNode(), val.name(), h, res.dist(), res.travelTime()); + + // reset errors when one request was successful + errors = 0; + } catch (RouteValidator.Forbidden ex1) { + csv.flush(); + log.error("{}: stopping because API indicated key is not valid or quota exhausted", api); + return; + + } catch (Exception ex) { + log.warn("Could not retrieve result for route {} {}", api, route, ex); + errors++; + } + + } + + csv.flush(); + + if (i++ % 50 == 0 && i - 1 > 0) { + log.info("{}: processed {} routes", api, i - 1); + } + + if (errors > hours.size()) { + log.error("{}: stopping because of too many errors", api); + break; + } + } + } + } + } + + /** + * Fetch route with increasing delay. + */ + private RouteValidator.Result fetchWithBackoff(RouteValidator val, SampleValidationRoutes.Route route, int h, int i) throws InterruptedException { + try { + return val.retrieve(route.from(), route.to(), h); + } catch (RouteValidator.Forbidden fb) { + // No retry here + throw fb; + } catch (Exception e) { + if (i < 3) { + long backoff = (long) (10000d * Math.pow(2, i)); + log.warn("Failed to fetch result for {} {}: {} (retrying after {}s)", api, route, e.getMessage(), backoff / 1000); + + Thread.sleep(backoff); + return fetchWithBackoff(val, route, h, ++i); + } + + throw e; + } + } + + @Override + public void run() { + try { + fetch(); + log.info("API {} finished.", api); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + record Entry(Id fromNode, Id toNode, String api, int hour) { + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/RouteAPI.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/RouteAPI.java new file mode 100644 index 00000000000..b4e0143d6d5 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/RouteAPI.java @@ -0,0 +1,12 @@ +package org.matsim.application.analysis.traffic.traveltime; + +/** + * Defines different available API services. + */ +public enum RouteAPI { + google, + woosmap, + mapbox, + here, + tomtom +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/SampleValidationRoutes.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/SampleValidationRoutes.java new file mode 100644 index 00000000000..168bf28499e --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/SampleValidationRoutes.java @@ -0,0 +1,310 @@ +package org.matsim.application.analysis.traffic.traveltime; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.math3.stat.descriptive.SummaryStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CrsOptions; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; +import org.matsim.core.router.speedy.SpeedyALTFactory; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.scenario.ProjectionUtils; +import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.matsim.core.utils.geometry.CoordUtils; +import org.matsim.core.utils.geometry.transformations.GeotoolsTransformation; +import picocli.CommandLine; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +@CommandLine.Command( + name = "sample-validation-routes", + description = "Sample routes for travel time validation and fetch information from online API.", + showDefaultValues = true +) +@CommandSpec( + requireNetwork = true, + produces = {"routes-validation.csv"} +) +public class SampleValidationRoutes implements MATSimAppCommand { + + private static final Logger log = LogManager.getLogger(SampleValidationRoutes.class); + + @CommandLine.Mixin + private InputOptions input = InputOptions.ofCommand(SampleValidationRoutes.class); + @CommandLine.Mixin + private OutputOptions output = OutputOptions.ofCommand(SampleValidationRoutes.class); + + @CommandLine.Mixin + private ShpOptions shp; + + @CommandLine.Mixin + private CrsOptions crs; + + @CommandLine.Option(names = "--api", description = "Mapping of [name]=[key] with APIs to retrieve. Available: ${COMPLETION-CANDIDATES}") + private Map apis; + + @CommandLine.Option(names = {"-n", "--num-routes"}, description = "Number of routes (per time bin)", defaultValue = "1000") + private int numRoutes; + + @CommandLine.Option(names = "--hours", description = "Hours to validate", defaultValue = "3,7,8,9,12,13,16,17,18,21", split = ",") + private List hours; + + @CommandLine.Option(names = "--dist-range", description = "Range for the sampled distances.", split = ",", defaultValue = "3000,10000") + private List distRange; + + @CommandLine.Option(names = "--exclude-roads", description = "Regexp pattern of road types to exclude as start- and endpoints.", defaultValue = ".+_link") + private String excludeRoads; + + @CommandLine.Option(names = "--mode", description = "Mode to validate", defaultValue = TransportMode.car) + private String mode; + + /** + * Random coord in the same direction as a link. + */ + static Coord rndCoord(SplittableRandom rnd, double dist, Link link) { + + Coord v = link.getFromNode().getCoord(); + Coord u = link.getToNode().getCoord(); + + var angle = Math.atan2(u.getY() - v.getY(), u.getX() - v.getX()); + + var sample = angle + rnd.nextDouble(-0.2, 0.2) * Math.PI * 2; + + var x = Math.cos(sample) * dist; + var y = Math.sin(sample) * dist; + + return CoordUtils.round(new Coord(v.getX() + x, v.getY() + y)); + } + + public static void main(String[] args) { + new SampleValidationRoutes().execute(args); + } + + /** + * Read the produced API files and collect the speeds by hour. + */ + public static Map> readValidation(List validationFiles) throws IOException { + + // entry to hour and list of speeds + Map> entries = new LinkedHashMap<>(); + + for (String file : validationFiles) { + + log.info("Loading {}", file); + + try (CSVParser parser = new CSVParser(Files.newBufferedReader(Path.of(file)), + CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) { + + for (CSVRecord r : parser) { + FromToNodes e = new FromToNodes(Id.createNodeId(r.get("from_node")), Id.createNodeId(r.get("to_node"))); + double speed = Double.parseDouble(r.get("dist")) / Double.parseDouble(r.get("travel_time")); + + if (!Double.isFinite(speed)) { + log.warn("Invalid entry {}", r); + continue; + } + + Int2ObjectMap perHour = entries.computeIfAbsent(e, (k) -> new Int2ObjectLinkedOpenHashMap<>()); + perHour.computeIfAbsent(Integer.parseInt(r.get("hour")), k -> new DoubleArrayList()).add(speed); + } + } + } + + return entries; + } + + @Override + @SuppressWarnings({"IllegalCatch", "NestedTryDepth"}) + public Integer call() throws Exception { + + if (apis == null || apis.isEmpty()) { + log.warn("No API selected. You can use multiple APIs by giving the name and key --api google=[your key]]. Available: {}", + Arrays.toString(RouteAPI.values())); + return 2; + } + + Network network = input.getNetwork(); + + SplittableRandom rnd = new SplittableRandom(0); + + FreeSpeedTravelTime tt = new FreeSpeedTravelTime(); + OnlyTimeDependentTravelDisutility util = new OnlyTimeDependentTravelDisutility(tt); + LeastCostPathCalculator router = new SpeedyALTFactory().createPathCalculator(network, util, tt); + + List routes = sampleRoutes(network, router, rnd); + + log.info("Sampled {} routes in range {}", routes.size(), distRange); + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(output.getPath()), CSVFormat.DEFAULT)) { + csv.printRecord("from_node", "to_node", "beeline_dist", "dist", "travel_time", "geometry"); + for (Route route : routes) { + csv.printRecord(route.fromNode, route.toNode, + CoordUtils.calcEuclideanDistance(network.getNodes().get(route.fromNode).getCoord(), network.getNodes().get(route.toNode).getCoord()), + route.dist, route.travelTime, + String.format(Locale.US, "MULTIPOINT(%.5f %.5f, %.5f %.5f)", route.from.getX(), route.from.getY(), route.to.getX(), route.to.getY())); + } + } + + List files = new ArrayList<>(); + + log.info("Fetching APIs: {}", apis.keySet()); + + // Run all services in parallel + ExecutorService executor = Executors.newCachedThreadPool(); + List> futures = new ArrayList<>(); + + for (Map.Entry e : apis.entrySet()) { + + Path out = Path.of(output.getPath().toString().replace(".csv", "-api-" + e.getKey() + ".csv")); + futures.add( + CompletableFuture.runAsync(new FetchRoutesTask(e.getKey(), e.getValue(), routes, hours, out), executor) + ); + + files.add(out.toString()); + } + + CompletableFuture all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + all.join(); + executor.shutdown(); + + Map> res = readValidation(files); + + Path ref = Path.of(output.getPath().toString().replace(".csv", "-ref.csv")); + + // Write the reference file + try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(ref), CSVFormat.DEFAULT)) { + printer.printRecord("from_node", "to_node", "hour", "min", "max", "mean", "std"); + + // Target values + for (Map.Entry> e : res.entrySet()) { + + Int2ObjectMap perHour = e.getValue(); + for (Int2ObjectMap.Entry e2 : perHour.int2ObjectEntrySet()) { + + SummaryStatistics stats = new SummaryStatistics(); + // This is as kmh + e2.getValue().forEach(v -> stats.addValue(v * 3.6)); + + printer.printRecord(e.getKey().fromNode, e.getKey().toNode, e2.getIntKey(), + stats.getMin(), stats.getMax(), stats.getMean(), stats.getStandardDeviation()); + } + } + } + + log.info("All done."); + + return 0; + } + + /** + * Samples routes from the network. + */ + private List sampleRoutes(Network network, LeastCostPathCalculator router, SplittableRandom rnd) { + + List result = new ArrayList<>(); + List links = new ArrayList<>(network.getLinks().values()); + String crs = ProjectionUtils.getCRS(network); + + if (this.crs.getInputCRS() != null) + crs = this.crs.getInputCRS(); + + if (crs == null) { + throw new IllegalArgumentException("Input CRS could not be detected. Please specify with --input-crs [EPSG:xxx]"); + } + + GeotoolsTransformation ct = new GeotoolsTransformation(crs, "EPSG:4326"); + + ShpOptions.Index index = shp.isDefined() ? shp.createIndex(crs, "_") : null; + Predicate exclude = excludeRoads != null && !excludeRoads.isBlank() ? new Predicate<>() { + final Pattern p = Pattern.compile(excludeRoads, Pattern.CASE_INSENSITIVE); + @Override + public boolean test(Link link) { + return p.matcher(NetworkUtils.getHighwayType(link)).find(); + } + } : link -> false; + + for (int i = 0; i < numRoutes; i++) { + if (links.isEmpty()) { + log.warn("Not enough route samples could be generated"); + break; + } + + Link link = links.remove(rnd.nextInt(0, links.size())); + + if (index != null && !index.contains(link.getCoord())) { + i--; + continue; + } + + if (!link.getAllowedModes().contains(mode) || exclude.test(link)) { + i--; + continue; + } + + Coord dest = rndCoord(rnd, rnd.nextDouble(distRange.get(0), distRange.get(1)), link); + Link to = NetworkUtils.getNearestLink(network, dest); + + if (!to.getAllowedModes().contains(mode) || exclude.test(to)) { + // add the origin link back + links.add(link); + + i--; + continue; + } + + LeastCostPathCalculator.Path path = router.calcLeastCostPath(link.getFromNode(), to.getToNode(), 0, null, null); + + if (path.nodes.size() < 2) { + i--; + continue; + } + + result.add(new Route( + link.getFromNode().getId(), + to.getToNode().getId(), + ct.transform(link.getFromNode().getCoord()), + ct.transform(to.getToNode().getCoord()), + path.travelTime, + path.links.stream().mapToDouble(Link::getLength).sum() + )); + } + return result; + } + + /** + * Key as pair of from and to node. + */ + public record FromToNodes(Id fromNode, Id toNode) { + } + + record Route(Id fromNode, Id toNode, Coord from, Coord to, double travelTime, double dist) { + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/TravelTimeComparison.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/TravelTimeComparison.java new file mode 100644 index 00000000000..3c22fc678a3 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/TravelTimeComparison.java @@ -0,0 +1,156 @@ +package org.matsim.application.analysis.traffic.traveltime; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; +import org.matsim.core.router.speedy.SpeedyALTFactory; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.TravelTime; +import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.matsim.core.trafficmonitoring.TravelTimeCalculator; +import org.matsim.core.utils.io.IOUtils; +import picocli.CommandLine; +import tech.tablesaw.api.ColumnType; +import tech.tablesaw.api.DoubleColumn; +import tech.tablesaw.api.Row; +import tech.tablesaw.api.Table; +import tech.tablesaw.columns.Column; +import tech.tablesaw.io.csv.CsvReadOptions; + +import java.io.BufferedReader; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static tech.tablesaw.aggregate.AggregateFunctions.mean; + +/** + * See {@link SampleValidationRoutes}, which produces the required --input-ref file. + */ +@CommandLine.Command( + name = "travel-time-comparison", + description = "Compare travel time with routes from API." +) +@CommandSpec( + requireEvents = true, + requireNetwork = true, + produces = {"travel_time_comparison_by_hour.csv", "travel_time_comparison_by_route.csv"} +) +public class TravelTimeComparison implements MATSimAppCommand { + + @CommandLine.Mixin + private InputOptions input = InputOptions.ofCommand(TravelTimeComparison.class); + + @CommandLine.Mixin + private OutputOptions output = OutputOptions.ofCommand(TravelTimeComparison.class); + + @CommandLine.Option(names = "--input-ref", description = "File with reference data", required = true) + private String apiFile; + + @CommandLine.Option(names = "--modes", description = "Network modes to analyze", defaultValue = TransportMode.car, split = ",") + private Set networkModes; + + public static void main(String[] args) { + new TravelTimeComparison().execute(args); + } + + @Override + public Integer call() throws Exception { + + Table data; + try (BufferedReader reader = IOUtils.getBufferedReader(apiFile)) { + data = Table.read().csv(CsvReadOptions.builder(reader).columnTypesPartial(Map.of( + "from_node", ColumnType.STRING, + "to_node", ColumnType.STRING + )).build()); + } + + Network network = input.getNetwork(); + TravelTime tt = collectTravelTimes(network).getLinkTravelTimes(); + TravelTime fs = new FreeSpeedTravelTime(); + + OnlyTimeDependentTravelDisutility util = new OnlyTimeDependentTravelDisutility(tt); + + LeastCostPathCalculator congestedRouter = new SpeedyALTFactory().createPathCalculator(network, util, tt); + LeastCostPathCalculator freeflowRouter = new SpeedyALTFactory().createPathCalculator(network, new OnlyTimeDependentTravelDisutility(fs), fs); + + data.addColumns( + DoubleColumn.create("simulated", data.rowCount()), + DoubleColumn.create("free_flow", data.rowCount()) + ); + + for (Row row : data) { + LeastCostPathCalculator.Path congested = computePath(network, congestedRouter, row); + double dist = congested.links.stream().mapToDouble(Link::getLength).sum(); + double speed = 3.6 * dist / congested.travelTime; + + row.setDouble("simulated", speed); + + LeastCostPathCalculator.Path freeflow = computePath(network, freeflowRouter, row); + dist = freeflow.links.stream().mapToDouble(Link::getLength).sum(); + speed = 3.6 * dist / freeflow.travelTime; + + row.setDouble("free_flow", speed); + } + + data.addColumns( + data.doubleColumn("simulated").subtract(data.doubleColumn("mean")).setName("bias") + ); + + data.addColumns(data.doubleColumn("bias").abs().setName("abs_error")); + + data.write().csv(output.getPath("travel_time_comparison_by_route.csv").toFile()); + + List columns = List.of("min", "max", "mean", "std", "simulated", "free_flow", "bias", "abs_error"); + + Table aggr = data.summarize(columns, mean).by("hour"); + + for (Column column : aggr.columns()) { + String name = column.name(); + if (name.startsWith("Mean")) + column.setName(name.substring(6, name.length() - 1)); + } + + aggr.write().csv(output.getPath("travel_time_comparison_by_hour.csv").toFile()); + + return 0; + } + + private LeastCostPathCalculator.Path computePath(Network network, LeastCostPathCalculator router, Row row) { + Node fromNode = network.getNodes().get(Id.createNodeId(row.getString("from_node"))); + Node toNode = network.getNodes().get(Id.createNodeId(row.getString("to_node"))); + + return router.calcLeastCostPath(fromNode, toNode, row.getInt("hour") * 3600, null, null); + } + + private TravelTimeCalculator collectTravelTimes(Network network) { + TravelTimeCalculator.Builder builder = new TravelTimeCalculator.Builder(network); + builder.setCalculateLinkTravelTimes(true); + builder.setMaxTime(86400); + builder.setTimeslice(900); + builder.setAnalyzedModes(networkModes); + builder.setFilterModes(true); + + TravelTimeCalculator travelTimes = builder.build(); + + EventsManager manager = EventsUtils.createEventsManager(); + + manager.addHandler(travelTimes); + + manager.initProcessing(); + EventsUtils.readEvents(manager, input.getEventsPath()); + manager.finishProcessing(); + + return travelTimes; + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/AbstractRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/AbstractRouteValidator.java new file mode 100644 index 00000000000..8ed99bad826 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/AbstractRouteValidator.java @@ -0,0 +1,36 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; + +/** + * Abstract class with http and json functionality. + */ +abstract class AbstractRouteValidator implements RouteValidator { + + protected final String apiKey; + protected final ObjectMapper mapper; + + protected final CloseableHttpClient httpClient; + + AbstractRouteValidator(String apiKey) { + this.apiKey = apiKey; + + mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + httpClient = HttpClients.createDefault(); + } + + @Override + public void close() throws Exception { + httpClient.close(); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/GoogleRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/GoogleRouteValidator.java new file mode 100644 index 00000000000..b6dd6578956 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/GoogleRouteValidator.java @@ -0,0 +1,93 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.matsim.api.core.v01.Coord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.ZonedDateTime; +import java.util.Map; + +/** + * Fetch route information from Google Maps api. + */ +public class GoogleRouteValidator extends AbstractRouteValidator { + + private static final String URL = "https://routes.googleapis.com/directions/v2:computeRoutes"; + + public GoogleRouteValidator(String apiKey) { + super(apiKey); + } + + @Override + public String name() { + return "google"; + } + + @Override + public Result retrieve(Coord from, Coord to, int hour) { + + try { + + ClassicRequestBuilder post = ClassicRequestBuilder.post(URL); + + post.addHeader("Content-Type", "application/json"); + post.addHeader("X-Goog-Api-Key", apiKey); + post.addHeader("X-Goog-FieldMask", "routes.duration,routes.distanceMeters"); + + String request = mapper.writeValueAsString(new Request(from, to, hour)); + + post.setEntity(request, ContentType.APPLICATION_JSON); + + JsonNode data = httpClient.execute(post.build(), response -> { + + if (response.getCode() != 200) { + response.getEntity().writeTo(System.err); + throw new IllegalStateException("Non-success response:"); + } + + return mapper.readValue(response.getEntity().getContent(), JsonNode.class); + }); + + JsonNode route = data.get("routes").get(0); + String duration = route.get("duration").asText().replace("s", ""); + + return new Result(hour, (int) Double.parseDouble(duration), (int) route.get("distanceMeters").asDouble()); + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static final class Request { + + Location origin; + Location destination; + String travelMode = "DRIVE"; + String routingPreference = "TRAFFIC_AWARE_OPTIMAL"; + ZonedDateTime departureTime; + String units = "METRIC"; + + Request(Coord from, Coord to, int hour) { + origin = new Location(from); + destination = new Location(to); + departureTime = RouteValidator.createDateTime(hour); + } + } + + private static final class Location { + final Map location; + + Location(Coord coord) { + + location = Map.of("latLng", Map.of( + "latitude", coord.getY(), + "longitude", coord.getX() + )); + + } + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/HereRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/HereRouteValidator.java new file mode 100644 index 00000000000..73361df2913 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/HereRouteValidator.java @@ -0,0 +1,61 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.matsim.api.core.v01.Coord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * Access data from HERE api. + */ +public class HereRouteValidator extends AbstractRouteValidator { + + private static final String URL = "https://router.hereapi.com/v8/routes"; + + private final DateTimeFormatter rfc3339 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx"); + + public HereRouteValidator(String apiKey) { + super(apiKey); + } + + @Override + public String name() { + return "here"; + } + + @Override + public Result retrieve(Coord from, Coord to, int hour) { + + // Rate limit of 10 request per seconds + try { + Thread.sleep(150); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // https://developer.here.com/documentation/routing-api/dev_guide/topics/use-cases/duration-typical-time-of-day.html + + ClassicHttpRequest req = ClassicRequestBuilder.get(URL) + .addParameter("apikey", apiKey) + .addParameter("origin", String.format(Locale.US, "%.6f,%.6f", from.getY(), from.getX())) + .addParameter("destination", String.format(Locale.US, "%.6f,%.6f", to.getY(), to.getX())) + .addParameter("departureTime", RouteValidator.createDateTime(hour).format(rfc3339)) + .addParameter("return", "summary,typicalDuration") + .addParameter("transportMode", "car") + .build(); + + try { + JsonNode data = httpClient.execute(req, resp -> mapper.readTree(resp.getEntity().getContent())); + JsonNode route = data.get("routes").get(0).get("sections").get(0).get("summary"); + // Dynamic traffic information is not considered. Instead, the duration is calculated using speeds that are typical for the given time of day/day of week, based on historical traffic data. + return new Result(hour, route.get("typicalDuration").asInt(), route.get("length").asInt()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/MapboxRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/MapboxRouteValidator.java new file mode 100644 index 00000000000..dd44eb8dd84 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/MapboxRouteValidator.java @@ -0,0 +1,58 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.matsim.api.core.v01.Coord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Locale; + +/** + * Access route data from mapbox. + */ +public class MapboxRouteValidator extends AbstractRouteValidator { + + private static final String URL = "https://api.mapbox.com/directions/v5/mapbox/driving-traffic/"; + + public MapboxRouteValidator(String apiKey) { + super(apiKey); + } + + @Override + public String name() { + return "mapbox"; + } + + @Override + public Result retrieve(Coord from, Coord to, int hour) { + + // https://docs.mapbox.com/api/overview/#rate-limits + // 300 per minute + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // https://docs.mapbox.com/api/navigation/directions/ + + ClassicHttpRequest req = ClassicRequestBuilder.get(URL + String.format(Locale.US, "%.6f,%.6f;%.6f,%.6f", from.getX(), from.getY(), to.getX(), to.getY())) + .addParameter("access_token", apiKey) + .addParameter("overview", "simplified") + .addParameter("steps", "false") + .addParameter("depart_at", RouteValidator.createLocalDateTime(hour).toString()) + .build(); + + try { + JsonNode data = httpClient.execute(req, resp -> mapper.readTree(resp.getEntity().getContent())); + JsonNode route = data.get("routes").get(0); + + return new Result(hour, route.get("duration_typical").asInt(), route.get("distance").asInt()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/RouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/RouteValidator.java new file mode 100644 index 00000000000..1df0f5682e6 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/RouteValidator.java @@ -0,0 +1,52 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import org.matsim.api.core.v01.Coord; + +import java.time.*; +import java.time.temporal.TemporalAdjusters; + +/** + * Interface to calculate route information. + */ +public interface RouteValidator extends AutoCloseable { + + /** + * Create departure time for a week day in the future. + */ + static ZonedDateTime createDateTime(int hour) { + LocalDate date = LocalDate.now(ZoneId.systemDefault()).with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)); + return date.atStartOfDay(ZoneId.systemDefault()).plusHours(hour).withZoneSameInstant(ZoneOffset.UTC); + } + + /** + * Create departure time for a week day in the future. + */ + static LocalDateTime createLocalDateTime(int hour) { + LocalDate date = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)); + return date.atStartOfDay().plusHours(hour); + } + + /** + * Return the name of the validator. + */ + String name(); + + /** + * Calculate route information between two coordinates. Coordinates are always in WGS84. + */ + Result retrieve(Coord from, Coord to, int hour); + + /** + * Result for one query. + * @param travelTime travel time in seconds + * @param dist distance of route in meter + */ + record Result(int hour, int travelTime, int dist) { + } + + /** + * Exception that can be thrown, if an API key is not valid and processing should stop immediately. + */ + final class Forbidden extends RuntimeException {} + +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/TomTomRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/TomTomRouteValidator.java new file mode 100644 index 00000000000..b61910b3518 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/TomTomRouteValidator.java @@ -0,0 +1,70 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.matsim.api.core.v01.Coord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * Access data from TomTom api. + */ +public class TomTomRouteValidator extends AbstractRouteValidator { + + private static final String URL = "https://api.tomtom.com/routing/1/calculateRoute/"; + + private final DateTimeFormatter rfc3339 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx"); + + public TomTomRouteValidator(String apiKey) { + super(apiKey); + } + + @Override + public String name() { + return "tomtom"; + } + + @Override + public Result retrieve(Coord from, Coord to, int hour) { + + // Rate limit of 5 queries per seconds + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // https://developer.tomtom.com/routing-api/documentation/routing/calculate-route + + ClassicHttpRequest req = ClassicRequestBuilder.get(URL + String.format(Locale.US, "%.6f,%.6f:%.6f,%.6f/json", from.getY(), from.getX(), to.getY(), to.getX())) + .addParameter("key", apiKey) + .addParameter("traffic", "true") + .addParameter("travelMode", "car") + .addParameter("routeRepresentation", "summaryOnly") + .addParameter("routeType", "fastest") + .addParameter("computeTravelTimeFor", "all") + .addParameter("departAt", RouteValidator.createDateTime(hour).format(rfc3339)) + .build(); + + try { + JsonNode data = httpClient.execute(req, resp -> { + // usually quota exhausted + if (resp.getCode() == 403) return null; + + return mapper.readTree(resp.getEntity().getContent()); + }); + + if (data == null) + throw new RouteValidator.Forbidden(); + + JsonNode route = data.get("routes").get(0).get("summary"); + return new Result(hour, route.get("historicTrafficTravelTimeInSeconds").asInt(), route.get("lengthInMeters").asInt()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/WoosMapRouteValidator.java b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/WoosMapRouteValidator.java new file mode 100644 index 00000000000..2bd074bfe82 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/analysis/traffic/traveltime/api/WoosMapRouteValidator.java @@ -0,0 +1,59 @@ +package org.matsim.application.analysis.traffic.traveltime.api; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.matsim.api.core.v01.Coord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Locale; + +/** + * Validator for woos map. Appears to use HERE as datasource. + */ +public class WoosMapRouteValidator extends AbstractRouteValidator { + + private static final String URL = "https://api.woosmap.com/distance/route/json"; + + + + public WoosMapRouteValidator(String apiKey) { + super(apiKey); + } + + @Override + public String name() { + return "woosmap"; + } + + @Override + public Result retrieve(Coord from, Coord to, int hour) { + + // Rate limit of 10 request per seconds + try { + Thread.sleep(150); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // https://developers.woosmap.com/products/distance-api/route-endpoint/ + + ClassicHttpRequest req = ClassicRequestBuilder.get(URL) + .addParameter("key", apiKey) + .addParameter("origin", String.format(Locale.US, "%.6f,%.6f", from.getY(), from.getX())) + .addParameter("destination", String.format(Locale.US, "%.6f,%.6f", to.getY(), to.getX())) + .addParameter("departure_time", String.valueOf(RouteValidator.createDateTime(hour).toEpochSecond())) + .addHeader("Referer", "https://matsim.org") + .build(); + + try { + JsonNode data = httpClient.execute(req, resp -> mapper.readTree(resp.getEntity().getContent())); + JsonNode route = data.get("routes").get(0).get("legs").get(0); + + return new Result(hour, route.get("duration").get("value").asInt(), route.get("distance").get("value").asInt()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/options/InputOptions.java b/contribs/application/src/main/java/org/matsim/application/options/InputOptions.java new file mode 100644 index 00000000000..bd7f268a248 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/options/InputOptions.java @@ -0,0 +1,232 @@ +package org.matsim.application.options; + +import org.apache.commons.lang3.StringUtils; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.PopulationUtils; +import picocli.CommandLine; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Automatically defines input options by reading the {@link CommandSpec} annotation. + */ +public final class InputOptions { + + /** + * Class original used to create this option. + */ + private final Class clazz; + private final CommandSpec spec; + private final Map inputs = new HashMap<>(); + /** + * Needs to be present or the mixin will not be processed at all. + */ + @CommandLine.Option(names = "--unused-input-option", hidden = true) + private String unused; + private Path runDirectory; + private String networkPath; + private String populationPath; + private String countsPath; + private String eventsPath; + + private InputOptions(Class clazz, CommandSpec spec) { + this.clazz = clazz; + this.spec = spec; + } + + /** + * Return the argument names, the first specified argument will also be named with {@code first}. + */ + static String[] argNames(AtomicBoolean flag, String first, String name) { + if (!flag.get()) { + flag.set(true); + return new String[]{first, name}; + } + return new String[]{name}; + } + + /** + * Return the argument name of an input file. + */ + public static String argName(String require) { + + require = require.replace(".gz", ""); + String[] split = require.split("\\."); + + // Remove the suffix + String s = split.length > 1 + ? StringUtils.join(split, "-", 0, split.length - 1) + : require; + + // replace special chars + String result = s.replaceAll("_|/\"|\\\\|:|/", "-"); + + // crs is a reserved name from other options + if (result.equals("crs")) + result = "crs-file"; + + return result; + } + + /** + * Creates a new instance by evaluating the {@link CommandSpec} annotation. + */ + public static InputOptions ofCommand(Class command) { + CommandSpec spec = command.getAnnotation(CommandSpec.class); + if (spec == null) + throw new IllegalArgumentException("CommandSpec annotation must be present on " + command); + + return new InputOptions(command, spec); + } + + /** + * Get the first configured input. + */ + public String getPath() { + if (spec.requires().length == 0) + throw new IllegalArgumentException("Spec input definition is empty. Input can not be accessed."); + + return getPath(spec.requires()[0]); + } + + /** + * Get the input that was configured for a specific name. + */ + public String getPath(String name) { + if (!inputs.containsKey(name)) + throw new IllegalArgumentException(String.format("The input '%s' is not registered as required in @CommandSpec.", name)); + + return inputs.get(name); + } + + public Network getNetwork() { + if (!spec.requireNetwork()) + throw new IllegalArgumentException("Network can not be accessed unless, requireNetwork=true."); + + return NetworkUtils.readNetwork(networkPath); + } + + public Population getPopulation() { + if (!spec.requirePopulation()) + throw new IllegalArgumentException("Population can not be accessed unless, requirePopulation=true."); + + return PopulationUtils.readPopulation(populationPath); + } + + public String getEventsPath() { + if (!spec.requireEvents()) + throw new IllegalArgumentException("Events can not be accessed unless, requireEvents=true."); + + return eventsPath; + } + + public String getCountsPath() { + if (!spec.requireEvents()) + throw new IllegalArgumentException("Counts can not be accessed unless, requireCounts=true."); + + return countsPath; + } + + public Path getRunDirectory() { + if (!spec.requireRunDirectory()) + throw new IllegalArgumentException("Run directory can not be accessed unless, requireRunDirectory=true."); + + return runDirectory; + } + + @CommandLine.Spec(CommandLine.Spec.Target.MIXEE) + void setSpec(CommandLine.Model.CommandSpec command) { + AtomicBoolean flag = new AtomicBoolean(false); + + command.mixinStandardHelpOptions(true); + + if (!command.userObject().getClass().equals(clazz)) { + + throw new IllegalArgumentException(String.format("InputOptions in the implementation of '%s' is using class '%s' as reference. These two must be the same", + command.userObject().getClass(), clazz)); + } + + for (String require : spec.requires()) { + if (require.isBlank()) + throw new IllegalArgumentException("Require argument can not be blank."); + + command.add(createArg(flag, "--input-" + argName(require), "Path to input " + require + ".", + new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + inputs.put(require, (String) value); + return value; + } + })); + } + + if (spec.requireNetwork()) { + command.add(createArg(flag, "--network", "Path to input network.", new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + networkPath = value.toString(); + return value; + } + })); + } + + if (spec.requireEvents()) { + command.add(createArg(flag, "--events", "Path to input events.", new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + eventsPath = value.toString(); + return value; + } + })); + } + + if (spec.requirePopulation()) { + command.add(createArg(flag, "--population", "Path to input plans / population.", new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + populationPath = value.toString(); + return value; + } + })); + } + + if (spec.requireCounts()) { + command.add(createArg(flag, "--counts", "Path to input counts.", new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + countsPath = value.toString(); + return value; + } + })); + } + + if (spec.requireRunDirectory()) { + command.add(createArg(flag, "--run-directory", "Path to input run directory.", new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + runDirectory = Path.of(value.toString()); + return value; + } + })); + } + } + + private CommandLine.Model.OptionSpec createArg(AtomicBoolean flag, String name, String description, CommandLine.Model.ISetter setter) { + CommandLine.Model.OptionSpec.Builder arg = CommandLine.Model.OptionSpec. + builder(argNames(flag, "--input", name)) + .initialValue("") + .type(String.class) + .setter(setter) + .description(description) + .required(true); + + return arg.build(); + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/options/OutputOptions.java b/contribs/application/src/main/java/org/matsim/application/options/OutputOptions.java new file mode 100644 index 00000000000..4ef96b359f5 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/options/OutputOptions.java @@ -0,0 +1,100 @@ +package org.matsim.application.options; + +import org.apache.commons.lang3.ArrayUtils; +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import picocli.CommandLine; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.matsim.application.options.InputOptions.argName; +import static org.matsim.application.options.InputOptions.argNames; + +/** + * Automatically defines output options by reading the {@link CommandSpec} annotation. + */ +public final class OutputOptions { + + private final CommandSpec spec; + private final Map outputs = new HashMap<>(); + /** + * Needs to be present or the mixin will not be processed at all. + */ + @CommandLine.Option(names = "--unused-output-option", hidden = true) + private String unused; + + private OutputOptions(CommandSpec spec) { + this.spec = spec; + } + + /** + * Creates a new instance by evaluating the {@link CommandSpec} annotation. + */ + public static OutputOptions ofCommand(Class command) { + CommandSpec spec = command.getAnnotation(CommandSpec.class); + if (spec == null) + throw new IllegalArgumentException("CommandSpec annotation must be present on " + command); + return new OutputOptions(spec); + } + + /** + * Get the first configured output path. + */ + public Path getPath() { + + if (spec.produces().length == 0) + throw new IllegalArgumentException("There is no output defined for this command"); + + return getPath(spec.produces()[0]); + } + + /** + * Get the output path configured for a specific name. + */ + public Path getPath(String name) { + + if (!ArrayUtils.contains(spec.produces(), name)) + throw new IllegalArgumentException(String.format("The output file '%s' is not defined in the @CommandSpec", name)); + + Path output = outputs.containsKey(name) ? outputs.get(name) : Path.of(name); + try { + Files.createDirectories(output.toAbsolutePath().getParent()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + return output; + } + + @CommandLine.Spec(CommandLine.Spec.Target.MIXEE) + void setSpec(CommandLine.Model.CommandSpec command) { + + command.mixinStandardHelpOptions(true); + + AtomicBoolean flag = new AtomicBoolean(false); + for (String produce : spec.produces()) { + + CommandLine.Model.OptionSpec.Builder arg = CommandLine.Model.OptionSpec. + builder(argNames(flag, "--output", "--output-" + argName(produce))) + .type(Path.class) + .setter(new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + outputs.put(produce, (Path) value); + return value; + } + }) + .defaultValue(produce) + .description("Desired output path for " + produce + ".") + .required(false); + + command.add(arg.build()); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/options/SampleOptions.java b/contribs/application/src/main/java/org/matsim/application/options/SampleOptions.java index a57ba257021..6a1cba85545 100644 --- a/contribs/application/src/main/java/org/matsim/application/options/SampleOptions.java +++ b/contribs/application/src/main/java/org/matsim/application/options/SampleOptions.java @@ -16,7 +16,10 @@ public final class SampleOptions { private static final Pattern PATTERN = Pattern.compile("\\d+pct"); - + /** + * Available sample sizes + */ + private final int[] sizes; /** * Needs to be present or the mixin will not be processed at all. */ @@ -24,14 +27,9 @@ public final class SampleOptions { private String unused; /** - * Available sample sizes - */ - private final int[] sizes; - - /** - * The selected sample size + * The selected sample size in (0, 1) */ - private int sample; + private double sample; /** * Whether sample size was set explicitly. @@ -49,50 +47,88 @@ public SampleOptions(int... sizes) { this.sample = sizes[0]; } - @CommandLine.Spec(CommandLine.Spec.Target.MIXEE) - public void setSpec(CommandLine.Model.CommandSpec spec) { - - CommandLine.Model.ArgGroupSpec.Builder group = CommandLine.Model.ArgGroupSpec.builder() - .exclusive(true) - .heading("\nSample sizes:\n") - .multiplicity("0..1"); - - for (int i = 0; i < sizes.length; i++) { + /** + * Create sample options with arbitrary possible sample size. The sample size option will be indicated as required. + */ + public SampleOptions() { + this.sizes = null; + this.sample = 0; + } - int size = sizes[i]; + @CommandLine.Spec(CommandLine.Spec.Target.MIXEE) + void setSpec(CommandLine.Model.CommandSpec spec) { + // Build simple sample option without predefined sizes + if (sizes == null) { CommandLine.Model.OptionSpec.Builder arg = CommandLine.Model.OptionSpec. - builder(String.format("--%dpct", size)) - .type(Boolean.class) - .order(i) - .description("Run scenario with " + size + " pct sample size") + builder("--sample-size") + .type(Double.class) + .description("Specify sample size fraction in (0, 1).") .setter(new CommandLine.Model.ISetter() { @Override public T set(T value) { - setSize(size); + if (value == null) + return null; + + setSize((double) value); return value; } }) - .defaultValue(i == 0 ? "true" : "false"); + .required(true); - group.addArg(arg.build()); - } + spec.add(arg.build()); - spec.addArgGroup(group.build()); - } + } else { - private void setSize(int sample) { - this.set = true; - this.sample = sample; + CommandLine.Model.ArgGroupSpec.Builder group = CommandLine.Model.ArgGroupSpec.builder() + .exclusive(true) + .heading("\nSample sizes:\n") + .multiplicity("0..1"); + + for (int i = 0; i < sizes.length; i++) { + + int size = sizes[i]; + + CommandLine.Model.OptionSpec.Builder arg = CommandLine.Model.OptionSpec. + builder(String.format("--%dpct", size)) + .type(Boolean.class) + .order(i) + .description("Run scenario with " + size + " pct sample size") + .setter(new CommandLine.Model.ISetter() { + @Override + public T set(T value) { + setSize(size / 100d); + return value; + } + }) + .defaultValue(i == 0 ? "true" : "false"); + + group.addArg(arg.build()); + } + + spec.addArgGroup(group.build()); + } } /** - * Returns the specified sample size. + * Returns the specified sample size as percent between 1 and 100 */ public int getSize() { + return (int) (sample * 100); + } + + /** + * Return sample size as fraction between 0 and 1. + */ + public double getSample() { return sample; } + private void setSize(double sample) { + this.set = true; + this.sample = sample; + } + /** * Check whether the sample size was set explicitly. */ diff --git a/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java b/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java index 5983cdd4fd1..d4c7b45342f 100644 --- a/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java +++ b/contribs/application/src/main/java/org/matsim/application/options/ShpOptions.java @@ -69,7 +69,7 @@ public ShpOptions(Path shp, @Nullable String shpCrs, @Nullable Charset shpCharse * Return whether a shape was set. */ public boolean isDefined() { - return shp != null && !shp.toString().isBlank(); + return shp != null && !shp.toString().isBlank() && !shp.toString().equals("none"); } /** @@ -170,12 +170,12 @@ public Geometry getGeometry() { */ public Index createIndex(String queryCRS, String attr, Set filter) { - if (shp == null) + if (!isDefined()) throw new IllegalStateException("Shape file path not specified"); if (!Files.exists(shp)) throw new IllegalStateException(String.format("Shape file %s does not exists", shp)); if (queryCRS == null) - throw new IllegalArgumentException("Input crs must not be null!"); + throw new IllegalArgumentException("Query crs must not be null!"); CoordinateTransformation ct = TransformationFactory.getCoordinateTransformation(queryCRS, detectCRS()); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/counts/CreateCountsFromBAStData.java b/contribs/application/src/main/java/org/matsim/application/prepare/counts/CreateCountsFromBAStData.java index 567884dbe21..1018c9e0696 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/counts/CreateCountsFromBAStData.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/counts/CreateCountsFromBAStData.java @@ -292,7 +292,7 @@ private void readHourlyTrafficVolume(Path pathToDisaggregatedData, Map index, BAStCountStation station, CountsOption counts) { Id manuallyMatched = counts.isManuallyMatched(station.getId()); Link matched; @@ -357,15 +357,14 @@ private void matchBAStWithNetwork(String pathToNetwork, Map index = new NetworkIndex(filteredNetwork, searchRange, station -> { - BAStCountStation countStation = (BAStCountStation) station; - Coord coord = countStation.getCoord(); + NetworkIndex index = new NetworkIndex<>(filteredNetwork, searchRange, station -> { + Coord coord = station.getCoord(); Coord transform = coordinateTransformation.transform(coord); return MGC.coord2Point(transform); }); index.addLinkFilter((link, station) -> { - String linkDir = BAStCountStation.getLinkDirection(link); + String linkDir = BAStCountStation.getLinkDirection(link.link()); String stationDir = station.getDirection(); return linkDir.contains(stationDir); }); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/counts/NetworkIndex.java b/contribs/application/src/main/java/org/matsim/application/prepare/counts/NetworkIndex.java index 915822ce2e6..da00479b097 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/counts/NetworkIndex.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/counts/NetworkIndex.java @@ -1,12 +1,23 @@ package org.matsim.application.prepare.counts; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.geotools.geometry.jts.JTS; +import org.locationtech.jts.algorithm.distance.DiscreteHausdorffDistance; import org.locationtech.jts.geom.*; import org.locationtech.jts.index.strtree.STRtree; import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.io.IOUtils; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.TransformException; +import javax.annotation.Nullable; +import java.io.IOException; import java.util.*; import java.util.function.BiPredicate; import java.util.stream.Collectors; @@ -14,67 +25,177 @@ /** * Wrapper class for an STRtree. Can be used to match objects, geometries for example, to an MATSim network. */ -public class NetworkIndex { +public final class NetworkIndex { private final STRtree index = new STRtree(); private final double range; private final GeometryFactory factory = new GeometryFactory(); - private final GeometryGetter getter; - private final List> filter = new ArrayList<>(); + private final GeometryGetter getter; + private final List> filter = new ArrayList<>(); + /** + * Stores references to all records in the tree. + */ + private final Map, LinkGeometry> records = new HashMap<>(); + private GeometryDistance distance; + + /** + * Create network index from links in the network. + */ + public NetworkIndex(Network network, double range, GeometryGetter getter) { + this(network, new HashMap<>(), range, getter); + } - public NetworkIndex(Network network, double range, GeometryGetter getter) { + + /** + * Create network index from links in the network with additional geometries. + */ + public NetworkIndex(Network network, Map, Geometry> geometries, double range, GeometryGetter getter) { this.range = range; this.getter = getter; + // Standard geometric distance + this.distance = (geom, toMatch) -> geom.distance(this.getter.getGeometry(toMatch)); for (Link link : network.getLinks().values()) { - Envelope env = getLinkEnvelope(link); - index.insert(env, link); + Geometry geom = geometries.getOrDefault(link.getId(), this.link2LineString(link)); + LinkGeometry r = new LinkGeometry(link, geom); + this.index.insert(r.geometry.getEnvelopeInternal(), r); + this.records.put(link.getId(), r); } - index.build(); + this.index.build(); } /** - * Uses an STRtree to match an Object to a network link. Custom filters are applied to filter the query results. - * The closest link to the object, based on the Geometry distance function is returned. + * Calculate the minimum hausdorff distance between two geometries. + * Unlike the classical distance, this uses the minimum instead of maximum of the two directed distances. + * This makes it more usable for geometries with different extent. + * This function may be used to compute the similarity of two line strings. */ - @SuppressWarnings("unchecked") - public Link query(T toMatch) { + public static double minHausdorffDistance(Geometry g1, Geometry g2) { - Geometry geometry = getter.getGeometry(toMatch); + DiscreteHausdorffDistance d1 = new DiscreteHausdorffDistance(g1, g2); + double u = d1.orientedDistance(); - Envelope searchArea = geometry.buffer(this.range).getEnvelopeInternal(); + DiscreteHausdorffDistance d2 = new DiscreteHausdorffDistance(g2, g1); + double v = d2.orientedDistance(); + + return Math.min(u, v); + } + + /** + * Calculates the angle of vector from v to u. + */ + public static double angle(Coordinate v, Coordinate u) { + return Math.atan2(u.getY() - v.getY(), u.getX() - v.getX()); + } + + /** + * Angle between two line strings in radians -pi to pi. + */ + public static double angle(LineString u, LineString v) { + + double ux = u.getEndPoint().getCoordinate().getX() - u.getStartPoint().getCoordinate().getX(); + double uy = u.getEndPoint().getCoordinate().getY() - u.getStartPoint().getCoordinate().getY(); + + double vx = v.getEndPoint().getCoordinate().getX() - v.getStartPoint().getCoordinate().getX(); + double vy = v.getEndPoint().getCoordinate().getY() - v.getStartPoint().getCoordinate().getY(); + + double cross = ux * vy - uy * vx; + double dot = ux * vx + uy * vy; + + return Math.atan2(cross, dot); + } + + /** + * Read network geometries that have been written with {@link org.matsim.contrib.sumo.SumoNetworkConverter}. + */ + public static Map, Geometry> readGeometriesFromSumo(String path, MathTransform crs) throws IOException, TransformException { + + Map, Geometry> result = new HashMap<>(); + + GeometryFactory factory = new GeometryFactory(); - List result = index.query(searchArea); + try (CSVParser csv = new CSVParser(IOUtils.getBufferedReader(path), CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build())) { - if (result.isEmpty()) return null; + for (CSVRecord r : csv) { + String idAsString = r.get("LinkId"); + String raw = r.get("Geometry"); - Map resultMap = new HashMap<>(); - for (Link link : result) { + LineString link = parseCoordinates(raw, factory); + Id linkId = Id.createLinkId(idAsString); - LineString ls = this.link2LineString(link); - resultMap.put(link, ls); + result.put(linkId, JTS.transform(link, crs)); + } } - return getClosestCandidate(resultMap, toMatch); + return result; + } + + private static LineString parseCoordinates(String coordinateSequence, GeometryFactory factory) { + + String[] split = coordinateSequence.split("\\)"); + + Coordinate[] coordinates = new Coordinate[split.length]; + + for (int i = 0; i < split.length; i++) { + String coord = split[i]; + int toRemove = coord.indexOf("("); + + String cleaned = coord.substring(toRemove + 1); + + String[] split1 = cleaned.split(","); + + Coordinate coordinate = new Coordinate(); + coordinate.setX(Double.parseDouble(split1[0])); + coordinate.setY(Double.parseDouble(split1[1])); + + coordinates[i] = coordinate; + } + + return factory.createLineString(coordinates); } /** - * Returns the envelope of an MATSim network link. + * Change the distance calculation. */ - public Envelope getLinkEnvelope(Link link) { - Coord from = link.getFromNode().getCoord(); - Coord to = link.getToNode().getCoord(); - Coordinate[] coordinates = {MGC.coord2Coordinate(from), MGC.coord2Coordinate(to)}; + public NetworkIndex setDistanceCalculator(GeometryDistance distance) { + this.distance = distance; + return this; + } + + /** + * Uses an STRtree to match an Object to a network link. Custom filters are applied to filter the query results. + * The closest link to the object, based on the distance function is returned. + * + * @param filter optional filter, only for this query. + */ + @SuppressWarnings("unchecked") + public Link query(T toMatch, @Nullable BiPredicate filter) { - return factory.createLineString(coordinates).getEnvelopeInternal(); + Geometry geometry = getter.getGeometry(toMatch); + + Envelope searchArea = geometry.buffer(this.range).getEnvelopeInternal(); + + List result = index.query(searchArea); + + if (result.isEmpty()) + return null; + + return getClosestCandidate(result, toMatch, filter); + } + + /** + * See {@link #query(Object, BiPredicate)}. + */ + public Link query(T toMatch) { + return query(toMatch, null); } /** * Transforms a MATSim network link to a LineString Object. */ - public LineString link2LineString(Link link) { + private LineString link2LineString(Link link) { Coord from = link.getFromNode().getCoord(); Coord to = link.getToNode().getCoord(); @@ -87,53 +208,50 @@ public LineString link2LineString(Link link) { * Removes a Link from the index. */ public void remove(Link link) { - Envelope env = getLinkEnvelope(link); - index.remove(env, link); + LinkGeometry r = records.get(link.getId()); + index.remove(r.geometry.getEnvelopeInternal(), r); } - private Link getClosestCandidate(Map result, T toMatch) { - - if (result.isEmpty()) return null; - if (result.size() == 1) return result.keySet().stream().findFirst().get(); - - applyFilter(result, toMatch); + private Link getClosestCandidate(List result, T toMatch, BiPredicate filter) { if (result.isEmpty()) return null; - Map distances = result.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, r -> r.getValue().distance(getter.getGeometry(toMatch)))); + if (result.size() == 1) + return result.stream().findFirst().get().link; - Double min = Collections.min(distances.values()); + applyFilter(result, toMatch, filter); - for (Map.Entry entry : distances.entrySet()) { - if (entry.getValue().doubleValue() == min.doubleValue()) { - return entry.getKey(); - } - } + if (result.isEmpty()) + return null; - return null; + Map distances = result.stream().collect(Collectors.toMap(r -> r.link, r -> distance.computeDistance(r.geometry, toMatch))); + Map.Entry min = Collections.min(distances.entrySet(), Comparator.comparingDouble(Map.Entry::getValue)); + return min.getKey(); } /** * Add a Predicate to test if query results are a valid candidate. Should return false if element should NOT be returned. */ - public void addLinkFilter(BiPredicate filter) { + public void addLinkFilter(BiPredicate filter) { this.filter.add(filter); } - private void applyFilter(Map result, T toMatch) { - - for (var it = result.entrySet().iterator(); it.hasNext(); ) { + private void applyFilter(List result, T toMatch, BiPredicate filter) { - Map.Entry next = it.next(); - Link link = next.getKey(); - for (BiPredicate predicate : this.filter) { - if (!predicate.test(link, toMatch)) { + outer: + for (var it = result.iterator(); it.hasNext(); ) { + LinkGeometry next = it.next(); + for (BiPredicate predicate : this.filter) { + if (!predicate.test(next, toMatch)) { it.remove(); - break; + // No further filter should be checked + continue outer; } } + + if (filter != null && !filter.test(next, toMatch)) + it.remove(); } } @@ -145,5 +263,21 @@ public interface GeometryGetter { Geometry getGeometry(T toMatch); } + + + /** + * Logic to compute distance or similarity between two objects. + */ + @FunctionalInterface + public interface GeometryDistance { + + double computeDistance(Geometry geom, T toMatch); + + } + + /** + * Holding link and its geometry. + */ + public record LinkGeometry(Link link, Geometry geometry) { } } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java b/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java index 6dd30f93606..8318bef7292 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/freight/optimization/DetermineAverageTruckLoad.java @@ -6,8 +6,6 @@ import org.apache.commons.csv.CSVRecord; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.units.qual.C; -import org.locationtech.jts.geom.Geometry; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Identifiable; @@ -16,7 +14,6 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.CrsOptions; -import org.matsim.application.options.ShpOptions; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; @@ -28,7 +25,6 @@ import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; -import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.geometry.transformations.TransformationFactory; import org.matsim.core.utils.gis.ShapeFileReader; import org.opengis.feature.simple.SimpleFeature; @@ -37,7 +33,6 @@ import java.io.FileWriter; import java.io.IOException; import java.net.URI; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -342,7 +337,7 @@ private boolean checkIfTripIsRelevant(String mode, String from, String to, Strin private LeastCostPathCalculator createRouter(Network network) { Config config = ConfigUtils.createConfig(); - config.plansCalcRoute().setRoutingRandomness(0); + config.routing().setRoutingRandomness(0); TravelTime travelTime = new FreeSpeedTravelTime(); TravelDisutility travelDisutility = new RandomizingTimeDistanceTravelDisutilityFactory (TransportMode.car, config).createTravelDisutility(travelTime); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/freight/tripExtraction/ExtractRelevantFreightTrips.java b/contribs/application/src/main/java/org/matsim/application/prepare/freight/tripExtraction/ExtractRelevantFreightTrips.java index 870cd1be48f..196e114634c 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/freight/tripExtraction/ExtractRelevantFreightTrips.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/freight/tripExtraction/ExtractRelevantFreightTrips.java @@ -82,7 +82,7 @@ public Integer call() throws Exception { config.network().setInputFile(networkPath.toString()); Scenario outputScenario = ScenarioUtils.loadScenario(config); config.plans().setInputFile(freightDataDirectory.toString()); - config.plansCalcRoute().setRoutingRandomness(0); + config.routing().setRoutingRandomness(0); Scenario scenario = ScenarioUtils.loadScenario(config); Network network = scenario.getNetwork(); Population originalPlans = scenario.getPopulation(); diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateGeoJsonNetwork.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateGeoJsonNetwork.java new file mode 100644 index 00000000000..5d4a67851ff --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateGeoJsonNetwork.java @@ -0,0 +1,170 @@ +package org.matsim.application.prepare.network; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.locationtech.jts.geom.Geometry; +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.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.CrsOptions; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.scenario.ProjectionUtils; +import org.matsim.core.utils.geometry.CoordinateTransformation; +import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.geometry.transformations.TransformationFactory; +import picocli.CommandLine; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; + + +@CommandLine.Command(name = "network-geojson", description = "Create geojson representation of a network.") +@CommandSpec(requireNetwork = true, produces = "network.geojson") +public class CreateGeoJsonNetwork implements MATSimAppCommand { + + @CommandLine.Mixin + private InputOptions input = InputOptions.ofCommand(CreateGeoJsonNetwork.class); + @CommandLine.Mixin + private OutputOptions output = OutputOptions.ofCommand(CreateGeoJsonNetwork.class); + @CommandLine.Mixin + private ShpOptions shp; + @CommandLine.Mixin + private CrsOptions crs = new CrsOptions(null, "EPSG:4326"); + + @CommandLine.Option(names = "--match-id", description = "Pattern to filter links by id") + private String matchId; + + @CommandLine.Option(names = "--mode-filter", split = ",", defaultValue = "car,freight,drt", + description = "Only keep links if they have one of the specified modes. Specify 'none' to disable.") + private Set modes; + + @CommandLine.Option(names = "--precision", description = "Number of decimals places", defaultValue = "6") + private int precision; + + @CommandLine.Option(names = "--with-properties", description = "Put network attributes as properties into the geojson.") + private boolean withProperties; + + public static void main(String[] args) { + new CreateGeoJsonNetwork().execute(args); + } + + @Override + public Integer call() throws Exception { + + Network network = input.getNetwork(); + + String networkCrs = ProjectionUtils.getCRS(input.getNetwork()); + if (crs.getInputCRS() != null) + networkCrs = crs.getInputCRS(); + + if (networkCrs == null) { + throw new IllegalArgumentException("Network coordinate system is neither in the xml nor given as option."); + } + + ObjectMapper mapper = new ObjectMapper(); + + ObjectNode json = mapper.createObjectNode(); + + json.put("type", "FeatureCollection"); + + // Default CRS assumed to be 4326 + if (!networkCrs.equalsIgnoreCase("epsg:4326")) { + ObjectNode crs = json.putObject("crs"); + putCrs(crs, networkCrs); + } + + Predicate filter = link -> true; + + if (shp.isDefined()) { + Geometry geom = shp.getGeometry(); + CoordinateTransformation ct = shp.createTransformation(networkCrs); + + filter = link -> geom.contains(MGC.coord2Point(ct.transform(link.getFromNode().getCoord()))) || + geom.contains(MGC.coord2Point(ct.transform(link.getToNode().getCoord()))); + } + + if (matchId != null) { + Pattern p = Pattern.compile(matchId); + filter = filter.and(link -> p.matcher(link.getId().toString()).matches()); + } + + // At least one of the specified modes needs to be contained + if (!modes.isEmpty() && !modes.equals(Set.of("none"))) { + filter = filter.and(link -> modes.stream().anyMatch(m -> link.getAllowedModes().contains(m))); + } + + convert(json, network, TransformationFactory.getCoordinateTransformation(networkCrs, this.crs.getTargetCRS()), filter); + + mapper.writerFor(JsonNode.class).writeValue(output.getPath().toFile(), json); + + return 0; + } + + private void putCrs(ObjectNode crs, String networkCrs) { + crs.put("type", "name"); + ObjectNode prop = crs.putObject("properties"); + prop.put("name", networkCrs); + } + + private void convert(ObjectNode json, Network network, CoordinateTransformation ct, Predicate filter) { + + ArrayNode ft = json.putArray("features"); + + for (Link link : network.getLinks().values()) { + + if (!filter.test(link)) + continue; + + ObjectNode obj = ft.addObject(); + + obj.put("id", link.getId().toString()); + obj.put("type", "Feature"); + + ObjectNode geom = obj.putObject("geometry"); + + geom.put("type", "LineString"); + ArrayNode coords = geom.putArray("coordinates"); + + Coord from = ct.transform(link.getFromNode().getCoord()); + Coord to = ct.transform(link.getToNode().getCoord()); + + ArrayNode f = coords.addArray(); + f.add(round(from.getX())); + f.add(round(from.getY())); + + ArrayNode t = coords.addArray(); + t.add(round(to.getX())); + t.add(round(to.getY())); + + if (withProperties) { + ObjectNode prop = obj.putObject("properties"); + + for (Map.Entry e : link.getAttributes().getAsMap().entrySet()) { + Object value = e.getValue(); + if (value instanceof String || value instanceof Number || value instanceof Boolean) + prop.putPOJO(e.getKey(), value); + } + } + } + } + + /** + * Round to desired precision. + */ + private double round(double x) { + BigDecimal d = BigDecimal.valueOf(x).setScale(precision, RoundingMode.HALF_UP); + return d.doubleValue(); + } + +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java index 759bee6f42f..23672056f9d 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/network/CreateNetworkFromSumo.java @@ -106,6 +106,7 @@ public Integer call() throws Exception { if (crs.getTargetCRS() != null) ProjectionUtils.putCRS(network, crs.getTargetCRS()); + NetworkUtils.writeNetwork(network, output.toAbsolutePath().toString()); new NetworkWriter(network).write(output.toAbsolutePath().toString()); new LanesWriter(lanes).write(output.toAbsolutePath().toString().replace(".xml", "-lanes.xml")); 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/CheckCarAvailability.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/CheckCarAvailability.java index c158e6d1ea4..ec344265554 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/CheckCarAvailability.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/CheckCarAvailability.java @@ -113,7 +113,7 @@ private boolean checkPlan(Plan plan) { for (TripStructureUtils.Trip trip : TripStructureUtils.getTrips(plan)) { for (Leg leg : trip.getLegsOnly()) { - if (leg.getMode().equals(TransportMode.car) || TripStructureUtils.getRoutingMode(leg).equals(TransportMode.car)) { + if (TransportMode.car.equals(leg.getMode()) || TransportMode.car.equals(TripStructureUtils.getRoutingMode(leg))) { correct = false; // replace this whole trip now 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/FixSubtourModes.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/FixSubtourModes.java index b9764aafed7..29364b18445 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/FixSubtourModes.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/FixSubtourModes.java @@ -8,12 +8,12 @@ import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; -import org.matsim.application.analysis.DefaultAnalysisMainModeIdentifier; import org.matsim.core.population.PersonUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.ChooseRandomLegModeForSubtour; import org.matsim.core.population.algorithms.PersonAlgorithm; import org.matsim.core.replanning.modules.SubtourModeChoice; +import org.matsim.core.router.DefaultAnalysisMainModeIdentifier; import org.matsim.core.router.TripStructureUtils; import picocli.CommandLine; @@ -137,8 +137,6 @@ public void run(Person person) { } fixed++; - - throw e; } } diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/RemoveRoutesFromPlans.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/RemoveRoutesFromPlans.java index 0436ddb1447..47d2f338760 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/RemoveRoutesFromPlans.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/RemoveRoutesFromPlans.java @@ -3,9 +3,9 @@ import com.google.common.collect.Lists; import org.matsim.api.core.v01.population.*; import org.matsim.application.MATSimAppCommand; -import org.matsim.application.analysis.DefaultAnalysisMainModeIdentifier; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.TripsToLegsAlgorithm; +import org.matsim.core.router.DefaultAnalysisMainModeIdentifier; import picocli.CommandLine; import java.nio.file.Files; @@ -50,7 +50,7 @@ public Integer call() throws Exception { output = Path.of(plans.toAbsolutePath().toString().replace(".xml", "-no-routes.xml")); Files.createDirectories(output.getParent()); - + // Using the analysis main mode identifier instead of the routing mode based one on purpose // to be able to process older population files without any routing modes! TripsToLegsAlgorithm trips2Legs = new TripsToLegsAlgorithm(new DefaultAnalysisMainModeIdentifier()); 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/application/prepare/population/SetCarAvailabilityByAge.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/SetCarAvailabilityByAge.java new file mode 100644 index 00000000000..2b54152535e --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/SetCarAvailabilityByAge.java @@ -0,0 +1,69 @@ +package org.matsim.application.prepare.population; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.MATSimAppCommand; +import org.matsim.core.population.PersonUtils; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.algorithms.PersonAlgorithm; +import picocli.CommandLine; + +import java.nio.file.Path; + +@CommandLine.Command(name = "set-car-avail", description = "Set car availability to true above a certain age.", showDefaultValues = true) +public class SetCarAvailabilityByAge implements MATSimAppCommand, PersonAlgorithm { + + private static final Logger log = LogManager.getLogger(SetCarAvailabilityByAge.class); + + @CommandLine.Option(names = "--input", description = "Path to input population", required = true) + private Path input; + + @CommandLine.Option(names = "--output", description = "Path for output population") + private Path output; + + @CommandLine.Option(names = "--age", description = "Agents with age greater or equal will have car availability set to always", defaultValue = "18") + private int age; + + @CommandLine.Option(names = "--subpopulation", description = "Subpopulation filter", defaultValue = "person") + private String subpopulation; + + public static void main(String[] args) { + new SetCarAvailabilityByAge().execute(args); + } + + @Override + public Integer call() throws Exception { + + Population population = PopulationUtils.readPopulation(input.toString()); + + for (Person person : population.getPersons().values()) { + run(person); + } + + PopulationUtils.writePopulation(population, output.toString()); + + return 0; + } + + + @Override + public void run(Person person) { + + String subpop = PopulationUtils.getSubpopulation(person); + if (!subpopulation.isEmpty() && !subpop.equals(subpopulation)) return; + + Integer age = PersonUtils.getAge(person); + + if (age == null) + return; + + if (age >= this.age) { + PersonUtils.setCarAvail(person, "always"); + + if ("no".equals(PersonUtils.getLicense(person))) + PersonUtils.setLicence(person, "yes"); + } + } +} diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/population/SplitActivityTypesDuration.java b/contribs/application/src/main/java/org/matsim/application/prepare/population/SplitActivityTypesDuration.java index b159dd3f238..b49a1d5b2c4 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/population/SplitActivityTypesDuration.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/population/SplitActivityTypesDuration.java @@ -1,5 +1,6 @@ package org.matsim.application.prepare.population; +import org.apache.commons.lang3.math.NumberUtils; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; @@ -14,12 +15,13 @@ import java.nio.file.Path; import java.util.List; import java.util.Objects; +import java.util.Set; @CommandLine.Command( - name = "split-activity-types-duration", - description = "Split activity types by duration", - mixinStandardHelpOptions = true, - showDefaultValues = true + name = "split-activity-types-duration", + description = "Split activity types by duration", + mixinStandardHelpOptions = true, + showDefaultValues = true ) public class SplitActivityTypesDuration implements MATSimAppCommand, PersonAlgorithm { @@ -44,9 +46,9 @@ public class SplitActivityTypesDuration implements MATSimAppCommand, PersonAlgor @CommandLine.Option(names = "--subpopulation", description = "Only apply to certain subpopulation") private String subpopulation; - public static void main(String[] args) { - new SplitActivityTypesDuration().execute(args); - } + @CommandLine.Option(names = "--exclude", description = "Activity types that won't be split", split = ",", defaultValue = "") + private Set exclude; + /** * Default Constructor needed for cli usage. @@ -63,6 +65,10 @@ public SplitActivityTypesDuration(int activityBinSize, int maxTypicalDuration, i this.endTimeToDuration = endTimeToDuration; } + public static void main(String[] args) { + new SplitActivityTypesDuration().execute(args); + } + @Override public Integer call() throws Exception { @@ -87,6 +93,9 @@ public void run(Person person) { for (Activity act : activities) { + if (exclude.contains(act.getType())) + continue; + double duration; if (act.getMaximumDuration().isDefined()) duration = act.getMaximumDuration().seconds(); @@ -128,21 +137,36 @@ private int roundDuration(double duration) { private void mergeOvernightActivities(List plan) { - if (plan.size() > 1) { - Activity firstActivity = plan.get(0); - Activity lastActivity = plan.get(plan.size() - 1); + // skipping plans with just one activity + if (plan.size() <= 1) + return; - String firstBaseActivity = firstActivity.getType().split("_")[0]; - String lastBaseActivity = lastActivity.getType().split("_")[0]; + Activity firstActivity = plan.get(0); + Activity lastActivity = plan.get(plan.size() - 1); - if (firstBaseActivity.equals(lastBaseActivity)) { - double mergedDuration = Double.parseDouble(firstActivity.getType().split("_")[1]) + Double.parseDouble(lastActivity.getType().split("_")[1]); + // skip non merge-able + if (!firstActivity.getType().contains("_") || !lastActivity.getType().contains("_")) + return; - int merged = roundDuration(mergedDuration); + int idxFirst = firstActivity.getType().lastIndexOf("_"); + int idxLast = lastActivity.getType().lastIndexOf("_"); - firstActivity.setType(String.format("%s_%d", firstBaseActivity, merged)); - lastActivity.setType(String.format("%s_%d", lastBaseActivity, merged)); - } - } // skipping plans with just one activity + String firstBaseActivity = firstActivity.getType().substring(0, idxFirst); + String lastBaseActivity = lastActivity.getType().substring(0, idxLast); + + String firstDuration = firstActivity.getType().substring(idxFirst + 1); + String lastDuration = lastActivity.getType().substring(idxLast + 1); + + if (!NumberUtils.isParsable(firstDuration) || !NumberUtils.isParsable(lastDuration)) + return; + + if (firstBaseActivity.equals(lastBaseActivity)) { + double mergedDuration = Double.parseDouble(firstDuration) + Double.parseDouble(lastDuration); + + int merged = roundDuration(mergedDuration); + + firstActivity.setType(String.format("%s_%d", firstBaseActivity, merged)); + lastActivity.setType(String.format("%s_%d", lastBaseActivity, merged)); + } } } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java index 591a895aa84..b8dc592921e 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSV.java @@ -27,10 +27,8 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.carrier.CarrierCapabilities.FleetSize; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; @@ -236,7 +234,7 @@ public void setFixedNumberOfVehiclePerTypeAndLocation(int fixedNumberOfVehiclePe * Reads and create the carriers with reading the information from the csv file. * * @param scenario - * @param freightConfigGroup + * @param freightCarriersConfigGroup * @param csvLocationCarrier * @param polygonsInShape * @param defaultJspritIterations @@ -244,14 +242,14 @@ public void setFixedNumberOfVehiclePerTypeAndLocation(int fixedNumberOfVehiclePe * @param shapeCategory * @throws IOException */ - public static void readAndCreateCarrierFromCSV(Scenario scenario, FreightConfigGroup freightConfigGroup, + public static void readAndCreateCarrierFromCSV(Scenario scenario, FreightCarriersConfigGroup freightCarriersConfigGroup, Path csvLocationCarrier, Collection polygonsInShape, int defaultJspritIterations, CoordinateTransformation crsTransformationNetworkAndShape, String shapeCategory) throws IOException { Set allNewCarrierInformation = readCarrierInformation(csvLocationCarrier); - checkNewCarrier(allNewCarrierInformation, freightConfigGroup, scenario, polygonsInShape, shapeCategory); + checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, polygonsInShape, shapeCategory); log.info("The read carrier information from the csv are checked without errors."); - createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightConfigGroup, polygonsInShape, + createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, polygonsInShape, defaultJspritIterations, crsTransformationNetworkAndShape); } @@ -307,23 +305,23 @@ else if (!record.get("fleetSize").isBlank()) * Checks if the read carrier information are consistent. * * @param allNewCarrierInformation - * @param freightConfigGroup + * @param freightCarriersConfigGroup * @param scenario * @param polygonsInShape * @param shapeCategory */ static void checkNewCarrier(Set allNewCarrierInformation, - FreightConfigGroup freightConfigGroup, Scenario scenario, Collection polygonsInShape, String shapeCategory) { + FreightCarriersConfigGroup freightCarriersConfigGroup, Scenario scenario, Collection polygonsInShape, String shapeCategory) { - FreightUtils.addOrGetCarriers(scenario); + CarriersUtils.addOrGetCarriers(scenario); for (CarrierInformationElement carrierElement : allNewCarrierInformation) { - if (FreightUtils.getCarriers(scenario).getCarriers() + if (CarriersUtils.getCarriers(scenario).getCarriers() .containsKey(Id.create(carrierElement.getName(), Carrier.class))) throw new RuntimeException("The Carrier " + carrierElement.getName() + " being loaded from the csv is already in the given Carrier file. It is not possible to add to an existing Carrier. Please check!"); CarrierVehicleTypes carrierVehicleTypes = new CarrierVehicleTypes(); new CarrierVehicleTypeReader(carrierVehicleTypes) - .readFile(freightConfigGroup.getCarriersVehicleTypesFile()); + .readFile(freightCarriersConfigGroup.getCarriersVehicleTypesFile()); if (carrierElement.getVehicleTypes() != null) for (String type : carrierElement.getVehicleTypes()) { if (!carrierVehicleTypes.getVehicleTypes().containsKey(Id.create(type, VehicleType.class))) @@ -410,20 +408,20 @@ static void checkNewCarrier(Set allNewCarrierInformat * * @param scenario * @param allNewCarrierInformation - * @param freightConfigGroup + * @param freightCarriersConfigGroup * @param polygonsInShape * @param defaultJspritIterations * @param crsTransformationNetworkAndShape */ static void createNewCarrierAndAddVehicleTypes(Scenario scenario, - Set allNewCarrierInformation, FreightConfigGroup freightConfigGroup, + Set allNewCarrierInformation, FreightCarriersConfigGroup freightCarriersConfigGroup, Collection polygonsInShape, int defaultJspritIterations, CoordinateTransformation crsTransformationNetworkAndShape) { - Carriers carriers = FreightUtils.addOrGetCarriers(scenario); + Carriers carriers = CarriersUtils.addOrGetCarriers(scenario); CarrierVehicleTypes carrierVehicleTypes = new CarrierVehicleTypes(); - CarrierVehicleTypes usedCarrierVehicleTypes = FreightUtils.getCarrierVehicleTypes(scenario); - new CarrierVehicleTypeReader(carrierVehicleTypes).readFile(freightConfigGroup.getCarriersVehicleTypesFile()); + CarrierVehicleTypes usedCarrierVehicleTypes = CarriersUtils.getCarrierVehicleTypes(scenario); + new CarrierVehicleTypeReader(carrierVehicleTypes).readFile(freightCarriersConfigGroup.getCarriersVehicleTypesFile()); for (CarrierInformationElement singleNewCarrier : allNewCarrierInformation) { if (singleNewCarrier.getVehicleTypes() == null) { @@ -437,11 +435,11 @@ static void createNewCarrierAndAddVehicleTypes(Scenario scenario, if (carrierCapabilities.getFleetSize() == null && singleNewCarrier.getFleetSize() != null) carrierCapabilities.setFleetSize(singleNewCarrier.getFleetSize()); if (singleNewCarrier.getJspritIterations() > 0) - CarrierUtils.setJspritIterations(thisCarrier, singleNewCarrier.getJspritIterations()); + CarriersUtils.setJspritIterations(thisCarrier, singleNewCarrier.getJspritIterations()); } else { - thisCarrier = CarrierUtils.createCarrier(Id.create(singleNewCarrier.getName(), Carrier.class)); + thisCarrier = CarriersUtils.createCarrier(Id.create(singleNewCarrier.getName(), Carrier.class)); if (singleNewCarrier.getJspritIterations() > 0) - CarrierUtils.setJspritIterations(thisCarrier, singleNewCarrier.getJspritIterations()); + CarriersUtils.setJspritIterations(thisCarrier, singleNewCarrier.getJspritIterations()); carrierCapabilities = CarrierCapabilities.Builder.newInstance() .setFleetSize(singleNewCarrier.getFleetSize()).build(); carriers.addCarrier(thisCarrier); @@ -497,8 +495,8 @@ static void createNewCarrierAndAddVehicleTypes(Scenario scenario, thisCarrier.setCarrierCapabilities(carrierCapabilities); } for (Carrier carrier : carriers.getCarriers().values()) { - if (CarrierUtils.getJspritIterations(carrier) == Integer.MIN_VALUE) { - CarrierUtils.setJspritIterations(carrier, defaultJspritIterations); + if (CarriersUtils.getJspritIterations(carrier) == Integer.MIN_VALUE) { + CarriersUtils.setJspritIterations(carrier, defaultJspritIterations); log.warn("The jspritIterations are now set to the default value of " + defaultJspritIterations + " in this simulation!"); } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index fa3357d473c..8c1f113a6d0 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -31,8 +31,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.*; import org.matsim.core.network.NetworkUtils; import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; @@ -648,7 +647,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { .setCapacityDemand(demandForThisLink).setServiceDuration(serviceTime) .setServiceStartTimeWindow(newDemandInformationElement.getFirstJobElementTimeWindow()) .build(); - FreightUtils.getCarriers(scenario).getCarriers() + CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getServices() .put(thisService.getId(), thisService); } @@ -688,7 +687,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { .setCapacityDemand(demandForThisLink).setServiceDuration(serviceTime) .setServiceStartTimeWindow(newDemandInformationElement.getFirstJobElementTimeWindow()) .build(); - FreightUtils.getCarriers(scenario).getCarriers().values().iterator().next().getServices() + CarriersUtils.getCarriers(scenario).getCarriers().values().iterator().next().getServices() .put(thisService.getId(), thisService); } distributedDemand = distributedDemand + demandForThisLink; @@ -740,7 +739,7 @@ else if (samplingOption.equals("changeDemandOnLocation")) { .setCapacityDemand(demandForThisLink).setServiceDuration(serviceTime) .setServiceStartTimeWindow(newDemandInformationElement.getFirstJobElementTimeWindow()) .build(); - FreightUtils.getCarriers(scenario).getCarriers() + CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getServices() .put(thisService.getId(), thisService); } @@ -919,7 +918,7 @@ else if (population == null) .setPickupServiceTime(serviceTimePickup).setPickupTimeWindow(timeWindowPickup) .setDeliveryServiceTime(serviceTimeDelivery).setDeliveryTimeWindow(timeWindowDelivery) .build(); - FreightUtils.getCarriers(scenario).getCarriers() + CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getShipments() .put(thisShipment.getId(), thisShipment); } @@ -1021,7 +1020,7 @@ else if (numberOfPickupLocations != null) { .setPickupServiceTime(serviceTimePickup).setPickupTimeWindow(timeWindowPickup) .setDeliveryServiceTime(serviceTimeDelivery).setDeliveryTimeWindow(timeWindowDelivery) .build(); - FreightUtils.getCarriers(scenario).getCarriers() + CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)) .getShipments().put(thisShipment.getId(), thisShipment); } @@ -1078,7 +1077,7 @@ else if (numberOfPickupLocations != null) { .newInstance(idNewShipment, linkPickup.getId(), linkDelivery.getId(), demandForThisLink) .setPickupServiceTime(serviceTimePickup).setPickupTimeWindow(timeWindowPickup) .setDeliveryServiceTime(serviceTimeDelivery).setDeliveryTimeWindow(timeWindowDelivery).build(); - FreightUtils.getCarriers(scenario).getCarriers() + CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getShipments() .put(thisShipment.getId(), thisShipment); distributedDemand = distributedDemand + demandForThisLink; @@ -1103,10 +1102,10 @@ private static String createJobId(Scenario scenario, DemandInformationElement ne String newJobId; if (linkDelivery != null) { newJobId = "Shipment_" + linkPickup + "_" + linkDelivery; - if (FreightUtils.getCarriers(scenario).getCarriers() + if (CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getShipments() .containsKey(Id.create(newJobId, CarrierShipment.class))) { - for (int x = 1; FreightUtils.getCarriers(scenario).getCarriers() + for (int x = 1; CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getShipments() .containsKey(Id.create(newJobId, CarrierShipment.class)); x++) { newJobId = "Shipment_" + linkPickup + "_" + linkDelivery + "_" + x; @@ -1114,10 +1113,10 @@ private static String createJobId(Scenario scenario, DemandInformationElement ne } } else { newJobId = "Service_" + linkPickup; - if (FreightUtils.getCarriers(scenario).getCarriers() + if (CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getServices() .containsKey(Id.create(newJobId, CarrierShipment.class))) { - for (int x = 1; FreightUtils.getCarriers(scenario).getCarriers() + for (int x = 1; CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getServices() .containsKey(Id.create(newJobId, CarrierShipment.class)); x++) { newJobId = "Service_" + linkPickup + "_" + x; @@ -1143,7 +1142,7 @@ private static void reduceNumberOfJobsIfSameCharacteristics(Scenario scenario, if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) { HashMap, CarrierShipment> shipmentsToRemove = new HashMap, CarrierShipment>(); ArrayList shipmentsToAdd = new ArrayList(); - Carrier thisCarrier = FreightUtils.getCarriers(scenario).getCarriers() + Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); for (Id baseShipmentId : thisCarrier.getShipments().keySet()) { if (!shipmentsToRemove.containsKey(baseShipmentId)) { @@ -1194,7 +1193,7 @@ private static void reduceNumberOfJobsIfSameCharacteristics(Scenario scenario, if (newDemandInformationElement.getTypeOfDemand().equals("service")) { HashMap, CarrierService> servicesToRemove = new HashMap, CarrierService>(); ArrayList servicesToAdd = new ArrayList(); - Carrier thisCarrier = FreightUtils.getCarriers(scenario).getCarriers() + Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); for (Id baseServiceId : thisCarrier.getServices().keySet()) { if (!servicesToRemove.containsKey(baseServiceId)) { diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java index 8eabf5ddb01..2f52ac86d17 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java @@ -26,17 +26,17 @@ import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierPlanWriter; -import org.matsim.contrib.freight.carrier.Carriers; -import org.matsim.contrib.freight.controler.CarrierModule; -import org.matsim.contrib.freight.controler.CarrierScoringFunctionFactory; -import org.matsim.contrib.freight.controler.FreightUtils; -import org.matsim.contrib.freight.usecases.chessboard.CarrierScoringFunctionFactoryImpl; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierPlanWriter; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.Carriers; +import org.matsim.freight.carriers.controler.CarrierModule; +import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory; +import org.matsim.freight.carriers.usecases.chessboard.CarrierScoringFunctionFactoryImpl; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -232,17 +232,17 @@ public Integer call() throws IOException, InvalidAttributeValueException, Execut private Config prepareConfig(int lastMATSimIteration, String coordinateSystem) { Config config = ConfigUtils.createConfig(); // ScenarioUtils.loadScenario(config); - config.controler().setOutputDirectory(outputLocation.toString()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - new OutputDirectoryHierarchy(config.controler().getOutputDirectory(), config.controler().getRunId(), - config.controler().getOverwriteFileSetting(), ControlerConfigGroup.CompressionType.gzip); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles); - config.controler().setLastIteration(lastMATSimIteration); + config.controller().setOutputDirectory(outputLocation.toString()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + new OutputDirectoryHierarchy(config.controller().getOutputDirectory(), config.controller().getRunId(), + config.controller().getOverwriteFileSetting(), ControllerConfigGroup.CompressionType.gzip); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles); + config.controller().setLastIteration(lastMATSimIteration); config.global().setRandomSeed(4177); config.global().setCoordinateSystem(coordinateSystem); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setTravelTimeSliceWidth(1800); - freightConfigGroup.setTimeWindowHandling(FreightConfigGroup.TimeWindowHandling.enforceBeginnings); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setTravelTimeSliceWidth(1800); + freightCarriersConfigGroup.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.enforceBeginnings); return config; } @@ -281,11 +281,11 @@ private static void setNetworkAndNetworkChangeEvents(Config config, String netwo */ private static void prepareVehicles(Config config, String vehicleTypesFileLocation) { - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); if (Objects.equals(vehicleTypesFileLocation, "")) throw new RuntimeException("No path to the vehicleTypes selected"); else { - freightConfigGroup.setCarriersVehicleTypesFile(vehicleTypesFileLocation); + freightCarriersConfigGroup.setCarriersVehicleTypesFile(vehicleTypesFileLocation); log.info("Get vehicleTypes from: " + vehicleTypesFileLocation); } } @@ -306,8 +306,8 @@ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrie String carriersFileLocation, Path csvLocationCarrier, Collection polygonsInShape, int defaultJspritIterations, CoordinateTransformation crsTransformationNetworkAndShape) throws IOException { - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), - FreightConfigGroup.class); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), + FreightCarriersConfigGroup.class); switch (selectedCarrierInputOption) { case addCSVDataToExistingCarrierFileData -> { // reads an existing carrier file and adds the information based on the read csv @@ -315,10 +315,10 @@ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrie if (Objects.equals(carriersFileLocation, "")) throw new RuntimeException("No path to the carrier file selected"); else { - freightConfigGroup.setCarriersFile(carriersFileLocation); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); + freightCarriersConfigGroup.setCarriersFile(carriersFileLocation); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); log.info("Load carriers from: " + carriersFileLocation); - CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightConfigGroup, csvLocationCarrier, + CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightCarriersConfigGroup, csvLocationCarrier, polygonsInShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); } } @@ -327,14 +327,14 @@ private void createCarrier(Scenario scenario, CarrierInputOptions selectedCarrie if (Objects.equals(carriersFileLocation, "")) throw new RuntimeException("No path to the carrier file selected"); else { - freightConfigGroup.setCarriersFile(carriersFileLocation); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); + freightCarriersConfigGroup.setCarriersFile(carriersFileLocation); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); log.info("Load carriers from: " + carriersFileLocation); } } case createCarriersFromCSV -> // creates all carriers based on the given information in the read carrier csv - CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightConfigGroup, csvLocationCarrier, + CarrierReaderFromCSV.readAndCreateCarrierFromCSV(scenario, freightCarriersConfigGroup, csvLocationCarrier, polygonsInShape, defaultJspritIterations, crsTransformationNetworkAndShape, shapeCategory); default -> throw new RuntimeException("no method to create or read carrier selected."); } @@ -415,7 +415,7 @@ private void createDemand(DemandGenerationOptions selectedDemandGenerationOption case useDemandFromCarrierFile -> { // use only the given demand of the read carrier file boolean oneCarrierHasJobs = false; - for (Carrier carrier : FreightUtils.getCarriers(scenario).getCarriers().values()) + for (Carrier carrier : CarriersUtils.getCarriers(scenario).getCarriers().values()) if (carrier.getServices().isEmpty() && carrier.getShipments().isEmpty()) log.warn(carrier.getId().toString() + " has no jobs which can be used"); else { @@ -462,29 +462,29 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution case runJspritAndMATSim -> { // solves the VRP with jsprit and runs MATSim afterwards new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersNoPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, false); controler.run(); new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersWithPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersWithPlans.xml"); } case runJspritAndMATSimWithDistanceConstraint -> { // solves the VRP with jsprit by using the distance constraint and runs MATSim // afterwards new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersNoPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, true); controler.run(); new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersWithPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersWithPlans.xml"); } case runJsprit -> { // solves only the VRP with jsprit new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersNoPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, false); new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersWithPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersWithPlans.xml"); log.warn( "##Finished with the jsprit solution. If you also want to run MATSim, please change case of optionsOfVRPSolutions"); System.exit(0); @@ -492,10 +492,10 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution case runJspritWithDistanceConstraint -> { // solves only the VRP with jsprit by using the distance constraint new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersNoPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); runJsprit(controler, true); new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersWithPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersWithPlans.xml"); log.warn( "##Finished with the jsprit solution. If you also want to run MATSim, please change case of optionsOfVRPSolutions"); System.exit(0); @@ -504,7 +504,7 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution // creates no solution of the VRP and only writes the carrier file with the // generated carriers and demands new CarrierPlanWriter((Carriers) controler.getScenario().getScenarioElement("carriers")) - .write(config.controler().getOutputDirectory() + "/output_carriersNoPlans.xml"); + .write(config.controller().getOutputDirectory() + "/output_carriersNoPlans.xml"); log.warn( "##Finished without solution of the VRP. If you also want to run jsprit and/or MATSim, please change case of optionsOfVRPSolutions"); System.exit(0); @@ -524,11 +524,11 @@ private static void solveSelectedSolution(OptionsOfVRPSolutions selectedSolution */ private static void runJsprit(Controler controler, boolean usingRangeRestriction) throws ExecutionException, InterruptedException { - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(controler.getConfig(), - FreightConfigGroup.class); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(controler.getConfig(), + FreightCarriersConfigGroup.class); if (usingRangeRestriction) - freightConfigGroup.setUseDistanceConstraintForTourPlanning( - FreightConfigGroup.UseDistanceConstraintForTourPlanning.basedOnEnergyConsumption); - FreightUtils.runJsprit(controler.getScenario()); + freightCarriersConfigGroup.setUseDistanceConstraintForTourPlanning( + FreightCarriersConfigGroup.UseDistanceConstraintForTourPlanning.basedOnEnergyConsumption); + CarriersUtils.runJsprit(controler.getScenario()); } } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java index 6c4a09703c4..b2d70951be8 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java @@ -29,10 +29,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.*; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierService; -import org.matsim.contrib.freight.carrier.CarrierShipment; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierService; +import org.matsim.freight.carriers.CarrierShipment; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.controler.Controler; import org.matsim.core.utils.geometry.CoordinateTransformation; import org.matsim.core.utils.geometry.geotools.MGC; @@ -99,11 +99,11 @@ static void preparePopulation(Population population, double sampleSizeInputPopul static void createDemandLocationsFile(Controler controler) { Network network = controler.getScenario().getNetwork(); - File file = new File(controler.getConfig().controler().getOutputDirectory() + "/outputFacilitiesFile.tsv"); + File file = new File(controler.getConfig().controller().getOutputDirectory() + "/outputFacilitiesFile.tsv"); try (FileWriter writer = new FileWriter(file, true)) { writer.write("id x y type ServiceLocation pickupLocation deliveryLocation\n"); - for (Carrier thisCarrier : FreightUtils.getCarriers(controler.getScenario()).getCarriers().values()) { + for (Carrier thisCarrier : CarriersUtils.getCarriers(controler.getScenario()).getCarriers().values()) { for (CarrierService thisService : thisCarrier.getServices().values()) { Coord coord = FreightDemandGenerationUtils .getCoordOfMiddlePointOfLink(network.getLinks().get(thisService.getLocationLinkId())); diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java index 5153a3b930c..e0f71babf5b 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateDifferentPlansForFreightPopulation.java @@ -1,28 +1,25 @@ package org.matsim.smallScaleCommercialTrafficGeneration; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.Population; -import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.api.core.v01.population.*; import org.matsim.application.MATSimAppCommand; import org.matsim.core.population.PopulationUtils; import picocli.CommandLine; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.SplittableRandom; +import java.util.Random; @CommandLine.Command(name = "generate-plan-variants-for-freight", description = "Generates variants of plans for a population of freight agents", showDefaultValues = true) public class CreateDifferentPlansForFreightPopulation implements MATSimAppCommand { - private enum PlanVariantStrategy{changeStartingTimes} + private enum PlanVariantStrategy {changeStartingTimes, activityOrderVariation} - @CommandLine.Parameters(arity = "1", paramLabel = "INPUT", description = "Path to the population", defaultValue = "output/testOutput/testPopulation_new.xml.gz") + @CommandLine.Parameters(arity = "1", paramLabel = "INPUT", description = "Path to the population") private Path populationPath; - @CommandLine.Option(names = "--outputPopulationPath", description = "Path to the outputPopulation", defaultValue = "output/testOutput/testPopulationVariant.xml.gz", required = true) + @CommandLine.Option(names = "--outputPopulationPath", description = "Path to the outputPopulation", required = true) private Path outputPopulationPath; @CommandLine.Option(names = "--numberOfPlanVariants", description = "Set the number of plan variants", required = true, defaultValue = "5") private static int numberOfPlanVariants; @@ -34,7 +31,8 @@ private enum PlanVariantStrategy{changeStartingTimes} private static int typicalTourDuration; @CommandLine.Option(names = "--seed", description = "Set seed", defaultValue = "4411") private static int seed; - private static final SplittableRandom rnd = new SplittableRandom(seed); + private static final Random rnd = new Random(seed); + public static void main(String[] args) { System.exit(new CommandLine(new CreateDifferentPlansForFreightPopulation()).execute(args)); } @@ -42,30 +40,49 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { - PlanVariantStrategy selectedPlanVariantStrategy = PlanVariantStrategy.changeStartingTimes; + PlanVariantStrategy selectedPlanVariantStrategy = PlanVariantStrategy.activityOrderVariation; Population population = PopulationUtils.readPopulation(populationPath.toString()); createPlanVariants(selectedPlanVariantStrategy, population); PopulationUtils.writePopulation(population, outputPopulationPath.toString()); return null; } - public static void createPlanVariantsForPopulations(String planVariantStrategy, Population population, int selectedNumberOfPlanVariants, int selectedEarliestTourStartTime, int selectedLatestTourStartTime, int selectedTypicalTourDuration){ - PlanVariantStrategy selectedPlanVariantStrategy; - switch (planVariantStrategy) { - case ("changeStartingTimes") -> { - selectedPlanVariantStrategy = PlanVariantStrategy.changeStartingTimes; - } - default -> throw new RuntimeException("No possible PlanVariantStrategy selected. Possible strategies are: " + Arrays.toString(PlanVariantStrategy.values())); - } + + /** + * Creates alternative plans (n = selectedNumberOfPlanVariants) by changing the start time (and end time) of the tour. + * + * @param population + * @param selectedNumberOfPlanVariants + * @param selectedEarliestTourStartTime + * @param selectedLatestTourStartTime + * @param selectedTypicalTourDuration + */ + public static void createMorePlansWithDifferentStartTimes(Population population, int selectedNumberOfPlanVariants, + int selectedEarliestTourStartTime, int selectedLatestTourStartTime, + int selectedTypicalTourDuration) { + PlanVariantStrategy selectedPlanVariantStrategy = PlanVariantStrategy.changeStartingTimes; numberOfPlanVariants = selectedNumberOfPlanVariants; earliestTourStartTime = selectedEarliestTourStartTime; latestTourStartTime = selectedLatestTourStartTime; typicalTourDuration = selectedTypicalTourDuration; createPlanVariants(selectedPlanVariantStrategy, population); } + + /** + * Creates alternative plans (n = selectedNumberOfPlanVariants) by changing the order of the activities. + * + * @param population + * @param selectedNumberOfPlanVariants + */ + public static void createMorePlansWithDifferentActivityOrder(Population population, int selectedNumberOfPlanVariants) { + PlanVariantStrategy selectedPlanVariantStrategy = PlanVariantStrategy.activityOrderVariation; + numberOfPlanVariants = selectedNumberOfPlanVariants; + createPlanVariants(selectedPlanVariantStrategy, population); + } + private static void createPlanVariants(PlanVariantStrategy selectedPlanVariantStrategy, Population population) { - for (Person person: population.getPersons().values()) { - switch (selectedPlanVariantStrategy){ + for (Person person : population.getPersons().values()) { + switch (selectedPlanVariantStrategy) { case changeStartingTimes -> { double initTourStart = PopulationUtils.getFirstActivity(person.getSelectedPlan()).getEndTime().seconds(); @@ -79,6 +96,35 @@ private static void createPlanVariants(PlanVariantStrategy selectedPlanVariantSt PopulationUtils.getLastActivity(newPLan).setStartTime(variantEndTime); } } + case activityOrderVariation -> { + + List activityIndexList = new ArrayList<>(); + int count = 0; + for (PlanElement planElement : person.getSelectedPlan().getPlanElements()) { + if (planElement instanceof Activity activity) { + if (activity.getType().equals("service")) { + activityIndexList.add(count); + } + } + count++; + } + for (int i = person.getPlans().size(); i < numberOfPlanVariants; i++) { + if (activityIndexList.size() < 2) + continue; + List activityNewIndexList = new ArrayList(activityIndexList); + Collections.shuffle(activityNewIndexList, rnd); + List alreadySwapedActivity = new ArrayList<>(); + Plan newPLan = person.createCopyOfSelectedPlanAndMakeSelected(); + person.setSelectedPlan(person.getPlans().get(0)); + for (int j = 0; j < activityIndexList.size(); j++) { + if (alreadySwapedActivity.contains(activityIndexList.get(j))) + continue; + Collections.swap(newPLan.getPlanElements(), activityIndexList.get(j), activityNewIndexList.get(j)); + alreadySwapedActivity.add(activityNewIndexList.get(j)); + } + } + + } default -> throw new RuntimeException("No possible PlanVariantStrategy selected"); } } @@ -88,21 +134,23 @@ private static double createStartTimeVariante(List timeVariants) { double vehicleStartTime = 0; while (vehicleStartTime == 0) { double possibleVehicleStartTime = rnd.nextInt(earliestTourStartTime, latestTourStartTime); - if(checkPossibleVehicleStartTime(possibleVehicleStartTime, timeVariants)) + if (checkPossibleVehicleStartTime(possibleVehicleStartTime, timeVariants)) vehicleStartTime = possibleVehicleStartTime; } timeVariants.add(vehicleStartTime); return vehicleStartTime; } - /** Checks if the new starting time has always a minimum 30 minutes difference to all other possible startimes. + /** + * Checks if the new starting time has always a minimum 30 minutes difference to all other possible startimes. + * * @param possibleVehicleStartTime * @param timeVariants * @return */ private static boolean checkPossibleVehicleStartTime(double possibleVehicleStartTime, List timeVariants) { - for (double usedTimes: timeVariants) { - if (Math.abs((possibleVehicleStartTime-usedTimes)) > 1800) + for (double usedTimes : timeVariants) { + if (Math.abs((possibleVehicleStartTime - usedTimes)) > 1800) return true; } return false; diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateSmallScaleCommercialTrafficDemand.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateSmallScaleCommercialTrafficDemand.java deleted file mode 100644 index 73b8533ae4e..00000000000 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/CreateSmallScaleCommercialTrafficDemand.java +++ /dev/null @@ -1,1074 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * Controler.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2007 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ -package org.matsim.smallScaleCommercialTrafficGeneration; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import it.unimi.dsi.fastutil.objects.Object2DoubleMap; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; -import org.locationtech.jts.geom.Geometry; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.application.MATSimAppCommand; -import org.matsim.application.options.ShpOptions; -import org.matsim.application.options.ShpOptions.Index; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.carrier.CarrierCapabilities.FleetSize; -import org.matsim.contrib.freight.controler.*; -import org.matsim.contrib.freight.usecases.chessboard.CarrierTravelDisutilities; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; -import org.matsim.core.config.groups.VspExperimentalConfigGroup; -import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.Controler; -import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.gbl.Gbl; -import org.matsim.core.network.NetworkUtils; -import org.matsim.core.population.routes.NetworkRoute; -import org.matsim.core.replanning.GenericPlanStrategyImpl; -import org.matsim.core.replanning.selectors.ExpBetaPlanChanger; -import org.matsim.core.replanning.selectors.KeepSelected; -import org.matsim.core.router.util.LeastCostPathCalculator; -import org.matsim.core.router.util.LeastCostPathCalculatorFactory; -import org.matsim.core.router.util.TravelDisutility; -import org.matsim.core.router.util.TravelTime; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.scoring.ScoringFunction; -import org.matsim.core.scoring.SumScoringFunction; -import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.vehicles.CostInformation; -import org.matsim.vehicles.Vehicle; -import org.matsim.vehicles.VehicleType; -import org.matsim.vehicles.VehicleUtils; -import org.opengis.feature.simple.SimpleFeature; -import picocli.CommandLine; - -import java.io.File; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.stream.Collectors; - -/** Tool to generate small scale commercial traffic for a selected area. The needed input data are: employee information for the area and three shapes files (zones, buildings, landuse). These data should be available with OSM. - * - * @author Ricardo Ewert - * - */ -@CommandLine.Command(name = "generate-small-scale-commercial-traffic", description = "Generates plans for a small scale commercial traffic model", showDefaultValues = true) -public class CreateSmallScaleCommercialTrafficDemand implements MATSimAppCommand { - - private static final Logger log = LogManager.getLogger(CreateSmallScaleCommercialTrafficDemand.class); - private static final HashMap>> buildingsPerZone = new HashMap<>(); - private static final HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); - - private enum CreationOption { - useExistingCarrierFileWithSolution, createNewCarrierFile, useExistingCarrierFileWithoutSolution - } - - private enum LanduseConfiguration { - useOnlyOSMLanduse, useOSMBuildingsAndLanduse, useExistingDataDistribution - } - - private enum TrafficType { - businessTraffic, freightTraffic, commercialTraffic - } - - @CommandLine.Parameters(arity = "1", paramLabel = "INPUT", description = "Path to the freight data directory") - private Path inputDataDirectory; - - @CommandLine.Option(names = "--sample", description = "Scaling factor of the freight traffic (0, 1)", required = true) - private double sample; - - @CommandLine.Option(names = "--jspritIterations", description = "Set number of jsprit iterations", required = true) - private int jspritIterations; - - @CommandLine.Option(names = "--creationOption", description = "Set option of mode differentiation: useExistingCarrierFileWithSolution, createNewCarrierFile, useExistingCarrierFileWithoutSolution") - private CreationOption usedCreationOption; - - @CommandLine.Option(names = "--landuseConfiguration", description = "Set option of used OSM data. Options: useOnlyOSMLanduse, useOSMBuildingsAndLanduse, useExistingDataDistribution") - private LanduseConfiguration usedLanduseConfiguration; - - @CommandLine.Option(names = "--trafficType", description = "Select traffic type. Options: businessTraffic, freightTraffic, commercialTraffic (contains both types)") - private TrafficType usedTrafficType; - - @CommandLine.Option(names = "--includeExistingModels", description = "If models for some segments exist they can be included.") - private boolean includeExistingModels; - - @CommandLine.Option(names = "--zoneShapeFileName", description = "Path of the zone shape file.") - private Path shapeFileZonePath; - - @CommandLine.Option(names = "--buildingsShapeFileName", description = "Path of the buildings shape file") - private Path shapeFileBuildingsPath; - - @CommandLine.Option(names = "--landuseShapeFileName", description = "Path of the landuse shape file") - private Path shapeFileLandusePath; - - @CommandLine.Option(names = "--shapeCRS", description = "CRS of the three input shape files (zones, landuse, buildings") - private String shapeCRS; - - @CommandLine.Option(names = "--resistanceFactor", defaultValue = "0.005", description = "ResistanceFactor for the trip distribution") - private double resistanceFactor; - - @CommandLine.Option(names = "--nameOutputPopulation", description = "Name of the output Population") - private String nameOutputPopulation; - - @CommandLine.Option(names = "--numberOfPlanVariantsPerAgent", description = "If an agent should have variant plans, you should set this paramter.", defaultValue = "1") - private int numberOfPlanVariantsPerAgent; - - @CommandLine.Option(names = "--PathOutput", description = "Path for the output") - private Path output; - - private SplittableRandom rnd; - - public static void main(String[] args) { - System.exit(new CommandLine(new CreateSmallScaleCommercialTrafficDemand()).execute(args)); - } - - @Override - public Integer call() throws Exception { - Configurator.setLevel("org.matsim.core.utils.geometry.geotools.MGC", Level.ERROR); - - String modelName = inputDataDirectory.getFileName().toString(); - - String sampleName = SmallScaleCommercialTrafficUtils.getSampleNameOfOutputFolder(sample); - - Config config = readAndCheckConfig(inputDataDirectory, modelName, sampleName, output); - - output = Path.of(config.controler().getOutputDirectory()); - - Scenario scenario = ScenarioUtils.loadScenario(config); - String carriersFileLocation; - FreightConfigGroup freightConfigGroup; - switch (usedCreationOption) { - case useExistingCarrierFileWithSolution -> { - log.info("Existing carriers (including carrier vehicle types ) should be set in the freight config group"); - if (includeExistingModels) - 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); - log.info("Load carriers from: " + freightConfigGroup.getCarriersFile()); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); - } - case useExistingCarrierFileWithoutSolution -> { - log.info("Existing carriers (including carrier vehicle types ) should be set in the freight config group"); - if (includeExistingModels) - 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); - log.info("Load carriers from: " + freightConfigGroup.getCarriersFile()); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); - solveSeparatedVRPs(scenario, null); - } - default -> { - if (!Files.exists(shapeFileLandusePath)) { - throw new Exception("Required landuse shape file not found:" + shapeFileLandusePath.toString()); - } - if (!Files.exists(shapeFileBuildingsPath)) { - throw new Exception( - "Required OSM buildings shape file {} not found" + shapeFileBuildingsPath.toString()); - } - if (!Files.exists(shapeFileZonePath)) { - throw new Exception("Required districts shape file {} not found" + shapeFileZonePath.toString()); - } - HashMap> resultingDataPerZone = LanduseBuildingAnalysis - .createInputDataDistribution(output, landuseCategoriesAndDataConnection, inputDataDirectory, - usedLanduseConfiguration.toString(), shapeFileLandusePath, shapeFileZonePath, - shapeFileBuildingsPath, shapeCRS, buildingsPerZone); - ShpOptions shpZones = new ShpOptions(shapeFileZonePath, shapeCRS, StandardCharsets.UTF_8); - Map, Link>> regionLinksMap = filterLinksForZones(scenario, shpZones, - SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS)); - switch (usedTrafficType) { - case businessTraffic, freightTraffic -> - createCarriersAndDemand(config, output, scenario, shpZones, resultingDataPerZone, regionLinksMap, usedTrafficType.toString(), - inputDataDirectory, includeExistingModels); - case commercialTraffic -> { - createCarriersAndDemand(config, output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "businessTraffic", - inputDataDirectory, includeExistingModels); - includeExistingModels = false; // because already included in the step before - createCarriersAndDemand(config, output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "freightTraffic", - inputDataDirectory, includeExistingModels); - } - default -> throw new RuntimeException("No traffic type selected."); - } - if (config.controler().getRunId() == null) - new CarrierPlanWriter(FreightUtils.addOrGetCarriers(scenario)) - .write(scenario.getConfig().controler().getOutputDirectory() + "/output_CarrierDemand.xml"); - else - new CarrierPlanWriter(FreightUtils.addOrGetCarriers(scenario)) - .write(scenario.getConfig().controler().getOutputDirectory() + "/" - + scenario.getConfig().controler().getRunId() + ".output_CarrierDemand.xml"); - solveSeparatedVRPs(scenario, regionLinksMap); - } - } - if (config.controler().getRunId() == null) - new CarrierPlanWriter(FreightUtils.addOrGetCarriers(scenario)).write( - scenario.getConfig().controler().getOutputDirectory() + "/output_CarrierDemandWithPlans.xml"); - else - new CarrierPlanWriter(FreightUtils.addOrGetCarriers(scenario)) - .write(scenario.getConfig().controler().getOutputDirectory() + "/"+scenario.getConfig().controler().getRunId() + ".output_CarrierDemandWithPlans.xml"); - Controler controler = prepareControler(scenario); - controler.run(); - SmallScaleCommercialTrafficUtils.createPlansBasedOnCarrierPlans(controler.getScenario(), - usedTrafficType.toString(), output, modelName, sampleName, nameOutputPopulation, numberOfPlanVariantsPerAgent); - return 0; - } - - /** - * @param originalScenario complete Scenario - * @param regionLinksMap list with Links for each region - */ - private void solveSeparatedVRPs(Scenario originalScenario, Map, Link>> regionLinksMap) throws Exception { - - boolean splitCarrier = true; - boolean splitVRPs = false; - int maxServicesPerCarrier = 100; - Map, Carrier> allCarriers = new HashMap<>( - FreightUtils.getCarriers(originalScenario).getCarriers()); - Map, Carrier> solvedCarriers = new HashMap<>(); - List> keyList = new ArrayList<>(allCarriers.keySet()); - FreightUtils.getCarriers(originalScenario).getCarriers().values().forEach(carrier -> { - if (CarrierUtils.getJspritIterations(carrier) == 0) { - allCarriers.remove(carrier.getId()); - solvedCarriers.put(carrier.getId(), carrier); - } - }); - int carrierSteps = 30; - for (int i = 0; i < allCarriers.size(); i++) { - int fromIndex = i * carrierSteps; - int toIndex = (i + 1) * carrierSteps; - if (toIndex >= allCarriers.size()) - toIndex = allCarriers.size(); - - Map, Carrier> subCarriers = new HashMap<>(allCarriers); - List> subList; - if (splitVRPs) { - subList = keyList.subList(fromIndex, toIndex); - subCarriers.keySet().retainAll(subList); - } else { - fromIndex = 0; - toIndex = allCarriers.size(); - } - - if (splitCarrier) { - Map, Carrier> subCarriersToAdd = new HashMap<>(); - List> keyListCarrierToRemove = new ArrayList<>(); - for (Carrier carrier : subCarriers.values()) { - - int countedServices = 0; - int countedVehicles = 0; - if (carrier.getServices().size() > maxServicesPerCarrier) { - - int numberOfNewCarrier = (int) Math - .ceil((double) carrier.getServices().size() / (double) maxServicesPerCarrier); - int numberOfServicesPerNewCarrier = Math - .round(carrier.getServices().size() / numberOfNewCarrier); - - int j = 0; - while (j < numberOfNewCarrier) { - - int numberOfServicesForNewCarrier = numberOfServicesPerNewCarrier; - int numberOfVehiclesForNewCarrier = numberOfServicesPerNewCarrier; - if (j + 1 == numberOfNewCarrier) { - numberOfServicesForNewCarrier = carrier.getServices().size() - countedServices; - numberOfVehiclesForNewCarrier = carrier.getCarrierCapabilities().getCarrierVehicles() - .size() - countedVehicles; - } - Carrier newCarrier = CarrierUtils.createCarrier( - Id.create(carrier.getId().toString() + "_part_" + (j + 1), Carrier.class)); - CarrierCapabilities newCarrierCapabilities = CarrierCapabilities.Builder.newInstance() - .setFleetSize(carrier.getCarrierCapabilities().getFleetSize()).build(); - newCarrierCapabilities.getCarrierVehicles() - .putAll(carrier.getCarrierCapabilities().getCarrierVehicles()); - newCarrier.setCarrierCapabilities(newCarrierCapabilities); - newCarrier.getServices().putAll(carrier.getServices()); - CarrierUtils.setJspritIterations(newCarrier, CarrierUtils.getJspritIterations(carrier)); - carrier.getAttributes().getAsMap().keySet().forEach(attribute -> newCarrier.getAttributes() - .putAttribute(attribute, carrier.getAttributes().getAttribute(attribute))); - - List> vehiclesForNewCarrier = new ArrayList<>( - carrier.getCarrierCapabilities().getCarrierVehicles().keySet()); - List> servicesForNewCarrier = new ArrayList<>( - carrier.getServices().keySet()); - - List> subListVehicles = vehiclesForNewCarrier.subList( - j * numberOfServicesPerNewCarrier, - j * numberOfServicesPerNewCarrier + numberOfVehiclesForNewCarrier); - List> subListServices = servicesForNewCarrier.subList( - j * numberOfServicesPerNewCarrier, - j * numberOfServicesPerNewCarrier + numberOfServicesForNewCarrier); - - newCarrier.getCarrierCapabilities().getCarrierVehicles().keySet() - .retainAll(subListVehicles); - newCarrier.getServices().keySet().retainAll(subListServices); - - countedVehicles += newCarrier.getCarrierCapabilities().getCarrierVehicles().size(); - countedServices += newCarrier.getServices().size(); - - subCarriersToAdd.put(newCarrier.getId(), newCarrier); - j++; - } - keyListCarrierToRemove.add(carrier.getId()); - if (countedVehicles != carrier.getCarrierCapabilities().getCarrierVehicles().size()) - throw new Exception("Split parts of the carrier " + carrier.getId().toString() - + " has a different number of vehicles than the original carrier"); - if (countedServices != carrier.getServices().size()) - throw new Exception("Split parts of the carrier " + carrier.getId().toString() - + " has a different number of services than the original carrier"); - - } - } - subCarriers.putAll(subCarriersToAdd); - for (Id id : keyListCarrierToRemove) { - subCarriers.remove(id); - } - } - FreightUtils.getCarriers(originalScenario).getCarriers().clear(); - FreightUtils.getCarriers(originalScenario).getCarriers().putAll(subCarriers); - log.info("Solving carriers " + (fromIndex + 1) + "-" + (toIndex) + " of all " + allCarriers.size() - + " carriers. This are " + subCarriers.size() + " VRP to solve."); - FreightUtils.runJsprit(originalScenario); - solvedCarriers.putAll(FreightUtils.getCarriers(originalScenario).getCarriers()); - FreightUtils.getCarriers(originalScenario).getCarriers().clear(); - if (!splitVRPs) - break; - } - FreightUtils.getCarriers(originalScenario).getCarriers().putAll(solvedCarriers); - FreightUtils.getCarriers(originalScenario).getCarriers().values().forEach(carrier -> { - if (regionLinksMap != null && !carrier.getAttributes().getAsMap().containsKey("tourStartArea")) { - List startAreas = new ArrayList<>(); - for (ScheduledTour tour : carrier.getSelectedPlan().getScheduledTours()) { - String tourStartZone = SmallScaleCommercialTrafficUtils - .findZoneOfLink(tour.getTour().getStartLinkId(), regionLinksMap); - if (!startAreas.contains(tourStartZone)) - startAreas.add(tourStartZone); - } - carrier.getAttributes().putAttribute("tourStartArea", - String.join(";", startAreas)); - } - }); - } - - private void createCarriersAndDemand(Config config, Path output, Scenario scenario, ShpOptions shpZones, - HashMap> resultingDataPerZone, - Map, Link>> regionLinksMap, String usedTrafficType, Path inputDataDirectory, - boolean includeExistingModels) throws Exception { - - ArrayList modesORvehTypes; - if (usedTrafficType.equals("freightTraffic")) - modesORvehTypes = new ArrayList<>( - Arrays.asList("vehTyp1", "vehTyp2", "vehTyp3", "vehTyp4", "vehTyp5")); - else if (usedTrafficType.equals("businessTraffic")) - modesORvehTypes = new ArrayList<>(List.of("total")); - else - throw new Exception("Invalid traffic type selected!"); - - TrafficVolumeGeneration.setInputParameters(usedTrafficType); - - HashMap> trafficVolumePerTypeAndZone_start = TrafficVolumeGeneration - .createTrafficVolume_start(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); - HashMap> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration - .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); - - if (includeExistingModels) { - SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, inputDataDirectory, regionLinksMap); - TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, usedTrafficType, - trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); - } - final TripDistributionMatrix odMatrix = createTripDistribution(trafficVolumePerTypeAndZone_start, - trafficVolumePerTypeAndZone_stop, shpZones, usedTrafficType, scenario, output, regionLinksMap); - createCarriers(config, scenario, odMatrix, resultingDataPerZone, usedTrafficType, regionLinksMap); - } - - /** Reads and checks config if all necessary parameter are set. - */ - private Config readAndCheckConfig(Path inputDataDirectory, String modelName, String sampleName, Path output) throws Exception { - - Config config = ConfigUtils.loadConfig(inputDataDirectory.resolve("config_demand.xml").toString()); - if (output == null) - config.controler().setOutputDirectory(Path.of(config.controler().getOutputDirectory()).resolve(modelName) - .resolve(usedTrafficType.toString() + "_" + sampleName + "pct" + "_" - + java.time.LocalDate.now() + "_" + java.time.LocalTime.now().toSecondOfDay() + "_" + resistanceFactor) - .toString()); - else - config.controler().setOutputDirectory(output.toString()); - new OutputDirectoryHierarchy(config.controler().getOutputDirectory(), config.controler().getRunId(), - config.controler().getOverwriteFileSetting(), ControlerConfigGroup.CompressionType.gzip); - new File(Path.of(config.controler().getOutputDirectory()).resolve("calculatedData").toString()).mkdir(); - rnd = new SplittableRandom(config.global().getRandomSeed()); - if (config.network().getInputFile() == null) - throw new Exception("No network file in config"); - if (config.network().getInputCRS() == null) - throw new Exception("No network CRS is set in config"); - if (config.global().getCoordinateSystem() == null) - throw new Exception("No global CRS is set in config"); - if (config.controler().getOutputDirectory() == null) - throw new Exception("No output directory was set"); - - return config; - } - - /** - * Prepares the controller. - */ - private Controler prepareControler(Scenario scenario) { - Controler controler = new Controler(scenario); - - controler.addOverridingModule(new CarrierModule()); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - bind(CarrierStrategyManager.class).toProvider( - new MyCarrierPlanStrategyManagerFactory(FreightUtils.getCarrierVehicleTypes(scenario))); - bind(CarrierScoringFunctionFactory.class).toInstance(new MyCarrierScoringFunctionFactory()); - } - }); - controler.getConfig().vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.abort); - return controler; - } - - /** - * Creates the carriers and the related demand, based on the generated - * TripDistributionMatrix. - */ - private void createCarriers(Config config, Scenario scenario, TripDistributionMatrix odMatrix, - HashMap> resultingDataPerZone, String trafficType, - Map, Link>> regionLinksMap) { - int maxNumberOfCarrier = odMatrix.getListOfPurposes().size() * odMatrix.getListOfZones().size() - * odMatrix.getListOfModesOrVehTypes().size(); - int createdCarrier = 0; - - CarrierVehicleTypes carrierVehicleTypes = FreightUtils.getCarrierVehicleTypes(scenario); - Map, VehicleType> additionalCarrierVehicleTypes = scenario.getVehicles().getVehicleTypes(); - additionalCarrierVehicleTypes.values().forEach( - vehicleType -> carrierVehicleTypes.getVehicleTypes().putIfAbsent(vehicleType.getId(), vehicleType)); - - for (VehicleType vehicleType : carrierVehicleTypes.getVehicleTypes().values()) { - CostInformation costInformation = vehicleType.getCostInformation(); - VehicleUtils.setCostsPerSecondInService(costInformation, costInformation.getCostsPerSecond()); - VehicleUtils.setCostsPerSecondWaiting(costInformation, costInformation.getCostsPerSecond()); - } - - for (Integer purpose : odMatrix.getListOfPurposes()) { - for (String startZone : odMatrix.getListOfZones()) { - for (String modeORvehType : odMatrix.getListOfModesOrVehTypes()) { - boolean isStartingLocation = false; - checkIfIsStartingPosition: { - for (String possibleStopZone : odMatrix.getListOfZones()) { - if (!modeORvehType.equals("pt") && !modeORvehType.equals("op")) - if (odMatrix.getTripDistributionValue(startZone, possibleStopZone, modeORvehType, - purpose, trafficType) != 0) { - isStartingLocation = true; - break checkIfIsStartingPosition; - } - } - } - if (isStartingLocation) { - double occupancyRate = 0; - String[] possibleVehicleTypes = null; - Integer serviceTimePerStop = null; - ArrayList startCategory = new ArrayList<>(); - ArrayList stopCategory = new ArrayList<>(); - if (purpose == 1) { - if (trafficType.equals("freightTraffic")) { - occupancyRate = 1.; - } else if (trafficType.equals("businessTraffic")) { - possibleVehicleTypes = new String[] { "vwCaddy", "e_SpaceTourer"}; - serviceTimePerStop = (int) Math.round(71.7 * 60); - occupancyRate = 1.5; - } - startCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Secondary Sector Rest"); - } else if (purpose == 2) { - if (trafficType.equals("freightTraffic")) { - occupancyRate = 1.; - } else if (trafficType.equals("businessTraffic")) { - possibleVehicleTypes = new String[] { "vwCaddy", "e_SpaceTourer"}; - serviceTimePerStop = (int) Math.round(70.4 * 60); // Durschnitt aus Handel,Transp.,Einw. - occupancyRate = 1.6; - } - startCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Primary Sector"); - stopCategory.add("Employee Construction"); - stopCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Retail"); - stopCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Inhabitants"); - } else if (purpose == 3) { - if (trafficType.equals("freightTraffic")) { - occupancyRate = 1.; - } else if (trafficType.equals("businessTraffic")) { - possibleVehicleTypes = new String[] { "golf1.4", "c_zero" }; - serviceTimePerStop = (int) Math.round(70.4 * 60); - occupancyRate = 1.2; - } - startCategory.add("Employee Retail"); - startCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Employee Primary Sector"); - stopCategory.add("Employee Construction"); - stopCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Retail"); - stopCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Inhabitants"); - } else if (purpose == 4) { - if (trafficType.equals("freightTraffic")) { - occupancyRate = 1.; - } else if (trafficType.equals("businessTraffic")) { - possibleVehicleTypes = new String[] { "golf1.4", "c_zero" }; - serviceTimePerStop = (int) Math.round(100.6 * 60); - occupancyRate = 1.2; - } - startCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Primary Sector"); - stopCategory.add("Employee Construction"); - stopCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Retail"); - stopCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Inhabitants"); - } else if (purpose == 5) { - if (trafficType.equals("freightTraffic")) { - occupancyRate = 1.; - } else if (trafficType.equals("businessTraffic")) { - possibleVehicleTypes = new String[] { "mercedes313", "e_SpaceTourer" }; - serviceTimePerStop = (int) Math.round(214.7 * 60); - occupancyRate = 1.7; - } - startCategory.add("Employee Construction"); - stopCategory.add("Employee Primary Sector"); - stopCategory.add("Employee Construction"); - stopCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Retail"); - stopCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Inhabitants"); - } else if (purpose == 6) { - occupancyRate = 1.; - startCategory.add("Inhabitants"); - stopCategory.add("Employee Primary Sector"); - stopCategory.add("Employee Construction"); - stopCategory.add("Employee Secondary Sector Rest"); - stopCategory.add("Employee Retail"); - stopCategory.add("Employee Traffic/Parcels"); - stopCategory.add("Employee Tertiary Sector Rest"); - stopCategory.add("Inhabitants"); - } - if (trafficType.equals("freightTraffic")) { - switch (modeORvehType) { - case "vehTyp1" -> { - possibleVehicleTypes = new String[]{"vwCaddy", "e_SpaceTourer"}; // possible to add more types, see source - serviceTimePerStop = Math.round(120 * 60); - } - case "vehTyp2" -> { - possibleVehicleTypes = new String[]{"mercedes313", "e_SpaceTourer"}; - serviceTimePerStop = Math.round(150 * 60); - } - case "vehTyp3" -> { - possibleVehicleTypes = new String[]{"light8t", "light8t_electro"}; - serviceTimePerStop = Math.round(120 * 60); - } - case "vehTyp4" -> { - possibleVehicleTypes = new String[]{"light8t", "light8t_electro"}; - serviceTimePerStop = Math.round(75 * 60); - } - case "vehTyp5" -> { - possibleVehicleTypes = new String[]{"medium18t", "medium18t_electro", "heavy40t", "heavy40t_electro"}; - serviceTimePerStop = Math.round(65 * 60); - } - } - } - - // use only types of the possibleTypes which are in the given types file - List vehicleTypes = new ArrayList<>(); - - assert possibleVehicleTypes != null; - for (String possibleVehicleType : possibleVehicleTypes) { - if (FreightUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().containsKey(Id.create(possibleVehicleType, VehicleType.class))) - vehicleTypes.add(possibleVehicleType); - } - String selectedStartCategory = startCategory.get(rnd.nextInt(startCategory.size())); - while (resultingDataPerZone.get(startZone).getDouble(selectedStartCategory) == 0) - selectedStartCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); - - String carrierName = null; - if (trafficType.equals("freightTraffic")) { - carrierName = "Carrier_Freight_" + startZone + "_purpose_" + purpose + "_" + modeORvehType; - } else if (trafficType.equals("businessTraffic")) - carrierName = "Carrier_Business_" + startZone + "_purpose_" + purpose; - int numberOfDepots = odMatrix.getSumOfServicesForStartZone(startZone, modeORvehType, purpose, - trafficType); - FleetSize fleetSize = FleetSize.FINITE; - int fixedNumberOfVehiclePerTypeAndLocation = 1; //TODO possible improvement - ArrayList vehicleDepots = new ArrayList<>(); - createdCarrier++; - log.info("Create carrier number " + createdCarrier + " of a maximum Number of " - + maxNumberOfCarrier + " carriers."); - log.info("Carrier: " + carrierName + "; depots: " + numberOfDepots + "; services: " - + (int) Math.ceil(odMatrix.getSumOfServicesForStartZone(startZone, modeORvehType, - purpose, trafficType) / occupancyRate)); - createNewCarrierAndAddVehicleTypes(scenario, purpose, startZone, - selectedStartCategory, carrierName, vehicleTypes, numberOfDepots, fleetSize, - fixedNumberOfVehiclePerTypeAndLocation, vehicleDepots, regionLinksMap, trafficType); - log.info("Create services for carrier: " + carrierName); - for (String stopZone : odMatrix.getListOfZones()) { - int trafficVolumeForOD = Math.round(odMatrix.getTripDistributionValue(startZone, - stopZone, modeORvehType, purpose, trafficType)); - int numberOfJobs = (int) Math.ceil(trafficVolumeForOD / occupancyRate); - if (numberOfJobs == 0) - continue; - String selectedStopCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); - while (resultingDataPerZone.get(stopZone).getDouble(selectedStopCategory) == 0) - selectedStopCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); - String[] serviceArea = new String[] { stopZone }; - TimeWindow serviceTimeWindow = TimeWindow.newInstance(6 * 3600, 20 * 3600); - createServices(scenario, vehicleDepots, selectedStopCategory, carrierName, - numberOfJobs, serviceArea, serviceTimePerStop, serviceTimeWindow, regionLinksMap); - } - } - } - } - } - log.warn("The jspritIterations are now set to " + jspritIterations + " in this simulation!"); - log.info("Finished creating " + createdCarrier + " carriers including related services."); - } - - /** - * Creates the services for one carrier. - */ - private void createServices(Scenario scenario, ArrayList noPossibleLinks, - String selectedStopCategory, String carrierName, int numberOfJobs, String[] serviceArea, - Integer serviceTimePerStop, TimeWindow serviceTimeWindow, - Map, Link>> regionLinksMap) { - - String stopZone = serviceArea[0]; - - for (int i = 0; i < numberOfJobs; i++) { - - Id linkId = findPossibleLink(stopZone, selectedStopCategory, noPossibleLinks, regionLinksMap, shapeCRS); - Id idNewService = Id.create(carrierName + "_" + linkId + "_" + rnd.nextInt(10000), - CarrierService.class); - - CarrierService thisService = CarrierService.Builder.newInstance(idNewService, linkId) - .setServiceDuration(serviceTimePerStop).setServiceStartTimeWindow(serviceTimeWindow).build(); - FreightUtils.getCarriers(scenario).getCarriers().get(Id.create(carrierName, Carrier.class)).getServices() - .put(thisService.getId(), thisService); - } - - } - - /** - * Creates the carrier and the related vehicles. - */ - private void createNewCarrierAndAddVehicleTypes(Scenario scenario, Integer purpose, String startZone, - String selectedStartCategory, String carrierName, - List vehicleTypes, int numberOfDepots, FleetSize fleetSize, int fixedNumberOfVehiclePerTypeAndLocation, - ArrayList vehicleDepots, Map, Link>> regionLinksMap, String trafficType) { - - Carriers carriers = FreightUtils.addOrGetCarriers(scenario); - CarrierVehicleTypes carrierVehicleTypes = FreightUtils.getCarrierVehicleTypes(scenario); - - CarrierCapabilities carrierCapabilities; - - Carrier thisCarrier = CarrierUtils.createCarrier(Id.create(carrierName, Carrier.class)); - if (trafficType.equals("businessTraffic") && purpose == 3) - thisCarrier.getAttributes().putAttribute("subpopulation", trafficType+"_service"); - else - thisCarrier.getAttributes().putAttribute("subpopulation", trafficType); - - thisCarrier.getAttributes().putAttribute("purpose", purpose); - thisCarrier.getAttributes().putAttribute("tourStartArea", startZone); - if (jspritIterations > 0) - CarrierUtils.setJspritIterations(thisCarrier, jspritIterations); - carrierCapabilities = CarrierCapabilities.Builder.newInstance().setFleetSize(fleetSize).build(); - carriers.addCarrier(thisCarrier); - - while (vehicleDepots.size() < numberOfDepots) { - Id link = findPossibleLink(startZone, selectedStartCategory, null, regionLinksMap, shapeCRS); - vehicleDepots.add(link.toString()); - } - for (String singleDepot : vehicleDepots) { - int vehicleStartTime = rnd.nextInt(6 * 3600, 14 * 3600); // TODO Verteilung über den Tag prüfen - int vehicleEndTime = vehicleStartTime + 8 * 3600; - for (String thisVehicleType : vehicleTypes) { - VehicleType thisType = carrierVehicleTypes.getVehicleTypes() - .get(Id.create(thisVehicleType, VehicleType.class)); - if (fixedNumberOfVehiclePerTypeAndLocation == 0) - fixedNumberOfVehiclePerTypeAndLocation = 1; - for (int i = 0; i < fixedNumberOfVehiclePerTypeAndLocation; i++) { - CarrierVehicle newCarrierVehicle = CarrierVehicle.Builder - .newInstance( - Id.create( - thisCarrier.getId().toString() + "_" - + (carrierCapabilities.getCarrierVehicles().size() + 1), - Vehicle.class), - Id.createLinkId(singleDepot), thisType) - .setEarliestStart(vehicleStartTime).setLatestEnd(vehicleEndTime).build(); - carrierCapabilities.getCarrierVehicles().put(newCarrierVehicle.getId(), newCarrierVehicle); - if (!carrierCapabilities.getVehicleTypes().contains(thisType)) - carrierCapabilities.getVehicleTypes().add(thisType); - } - } - - thisCarrier.setCarrierCapabilities(carrierCapabilities); - } - } - - /** - * Finds a possible link for a service or the vehicle location. - */ - private Id findPossibleLink(String zone, String selectedCategory, ArrayList noPossibleLinks, - Map, Link>> regionLinksMap, String shapeCRS) { - - Index indexZones = SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS); - - if (buildingsPerZone.isEmpty()) { - ShpOptions shpBuildings = new ShpOptions(shapeFileBuildingsPath, "EPSG:4326", StandardCharsets.UTF_8); - List buildingsFeatures = shpBuildings.readFeatures(); - LanduseBuildingAnalysis.analyzeBuildingType(buildingsFeatures, buildingsPerZone, - landuseCategoriesAndDataConnection, shapeFileLandusePath, indexZones, shapeCRS); - } - Id newLink = null; - for (int a = 0; newLink == null && a < buildingsPerZone.get(zone).get(selectedCategory).size() * 2; a++) { - - SimpleFeature possibleBuilding = buildingsPerZone.get(zone).get(selectedCategory) - .get(rnd.nextInt(buildingsPerZone.get(zone).get(selectedCategory).size())); - Coord centroidPointOfBuildingPolygon = MGC - .point2Coord(((Geometry) possibleBuilding.getDefaultGeometry()).getCentroid()); - double minDistance = Double.MAX_VALUE; - int numberOfPossibleLinks = regionLinksMap.get(zone).size(); - - // searches and selects the nearest link of the possible links in this zone - searchLink: for (Link possibleLink : regionLinksMap.get(zone).values()) { - if (noPossibleLinks != null && numberOfPossibleLinks > noPossibleLinks.size()) - for (String depotLink : noPossibleLinks) { - if (depotLink.equals(possibleLink.getId().toString()) - || (NetworkUtils.findLinkInOppositeDirection(possibleLink) != null && depotLink.equals( - NetworkUtils.findLinkInOppositeDirection(possibleLink).getId().toString()))) - continue searchLink; - } - double distance = NetworkUtils.getEuclideanDistance(centroidPointOfBuildingPolygon, - (Coord) possibleLink.getAttributes().getAttribute("newCoord")); - if (distance < minDistance) { - newLink = possibleLink.getId(); - minDistance = distance; - } - } - } - if (newLink == null) - throw new RuntimeException("No possible link for buildings with type '" + selectedCategory + "' in zone '" - + zone + "' found. buildings in category: " + buildingsPerZone.get(zone).get(selectedCategory) - + "; possibleLinks in zone: " + regionLinksMap.get(zone).size()); - return newLink; - } - - /** Filters links by used mode "car" and creates Map with all links in each zone - */ - static Map, Link>> filterLinksForZones(Scenario scenario, ShpOptions shpZones, Index indexZones) throws URISyntaxException { - Map, Link>> regionLinksMap = new HashMap<>(); - List links; - log.info("Filtering and assign links to zones. This take some time..."); - - String networkPath; - if (scenario.getConfig().network().getInputFile().startsWith( "https:" )) - networkPath = scenario.getConfig().network().getInputFile(); - else - networkPath = scenario.getConfig().getContext().toURI().resolve(scenario.getConfig().network().getInputFile()).getPath(); - - Network networkToChange = NetworkUtils.readNetwork(networkPath); - links = networkToChange.getLinks().values().stream().filter(l -> l.getAllowedModes().contains("car")) - .collect(Collectors.toList()); - links.forEach(l -> l.getAttributes().putAttribute("newCoord", - shpZones.createTransformation(scenario.getConfig().network().getInputCRS()).transform(l.getCoord()))); - links.forEach(l -> l.getAttributes().putAttribute("zone", - indexZones.query((Coord) l.getAttributes().getAttribute("newCoord")))); - links = links.stream().filter(l -> l.getAttributes().getAttribute("zone") != null).collect(Collectors.toList()); - links.forEach(l -> regionLinksMap - .computeIfAbsent((String) l.getAttributes().getAttribute("zone"), (k) -> new HashMap<>()) - .put(l.getId(), l)); - return regionLinksMap; - } - - /** - * Creates the number of trips between the zones for each mode and purpose. - */ - private TripDistributionMatrix createTripDistribution( - HashMap> trafficVolume_start, - HashMap> trafficVolume_stop, ShpOptions shpZones, - String usedTrafficType, Scenario scenario, Path output, Map, Link>> regionLinksMap) - throws Exception { - - final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder - .newInstance(shpZones, trafficVolume_start, trafficVolume_stop, usedTrafficType).build(); - ArrayList listOfZones = new ArrayList<>(); - trafficVolume_start.forEach((k, v) -> { - if (!listOfZones.contains(k.getZone())) - listOfZones.add(k.getZone()); - }); - Network network = scenario.getNetwork(); - int count = 0; - - for (TrafficVolumeGeneration.TrafficVolumeKey trafficVolumeKey : trafficVolume_start.keySet()) { - count++; - if (count % 50 == 0 || count == 1) - log.info("Create OD pair " + count + " of " + trafficVolume_start.size()); - - String startZone = trafficVolumeKey.getZone(); - String modeORvehType = trafficVolumeKey.getModeORvehType(); - for (Integer purpose : trafficVolume_start.get(trafficVolumeKey).keySet()) { - Collections.shuffle(listOfZones); - for (String stopZone : listOfZones) { - odMatrix.setTripDistributionValue(startZone, stopZone, modeORvehType, purpose, usedTrafficType, - network, regionLinksMap, resistanceFactor); - } - } - } - odMatrix.clearRoundingError(); - odMatrix.writeODMatrices(output, usedTrafficType); - return odMatrix; - } - - private static class MyCarrierScoringFunctionFactory implements CarrierScoringFunctionFactory { - - @Inject - private Network network; - - @Override - public ScoringFunction createScoringFunction(Carrier carrier) { - SumScoringFunction sf = new SumScoringFunction(); - DriversLegScoring driverLegScoring = new DriversLegScoring(carrier, network); - VehicleEmploymentScoring vehicleEmploymentScoring = new VehicleEmploymentScoring(carrier); - DriversActivityScoring actScoring = new DriversActivityScoring(); - sf.addScoringFunction(driverLegScoring); - sf.addScoringFunction(vehicleEmploymentScoring); - sf.addScoringFunction(actScoring); - return sf; - } - - } - - private static class MyCarrierPlanStrategyManagerFactory implements Provider { - - @Inject - private Network network; - - @Inject - private LeastCostPathCalculatorFactory leastCostPathCalculatorFactory; - - @Inject - private Map modeTravelTimes; - - private final CarrierVehicleTypes types; - - public MyCarrierPlanStrategyManagerFactory(CarrierVehicleTypes types) { - this.types = types; - } - - @Override - public CarrierStrategyManager get() { - TravelDisutility travelDisutility = CarrierTravelDisutilities.createBaseDisutility(types, - modeTravelTimes.get(TransportMode.car)); - final LeastCostPathCalculator router = leastCostPathCalculatorFactory.createPathCalculator(network, - travelDisutility, modeTravelTimes.get(TransportMode.car)); - -// final GenericStrategyManager strategyManager = new GenericStrategyManager<>(); - final CarrierStrategyManager strategyManager = FreightUtils.createDefaultCarrierStrategyManager(); - strategyManager.setMaxPlansPerAgent(5); - { - GenericPlanStrategyImpl strategy = new GenericPlanStrategyImpl<>( - new ExpBetaPlanChanger.Factory().setBetaValue(1.0).build()); - - strategyManager.addStrategy(strategy, null, 1.0); - - } - { - GenericPlanStrategyImpl strategy = new GenericPlanStrategyImpl<>( - new KeepSelected<>()); - strategy.addStrategyModule(new CarrierTimeAllocationMutator.Factory().build()); - strategy.addStrategyModule( new - CarrierReRouteVehicles.Factory(router, network, modeTravelTimes.get(TransportMode.car)).build()); - strategyManager.addStrategy(strategy, null, 0.5); - } - return strategyManager; - } - } - - static class DriversActivityScoring implements SumScoringFunction.BasicScoring, SumScoringFunction.ActivityScoring { - - // private static final Logger log = - // Logger.getLogger(DriversActivityScoring.class); - - private double score; - private final double timeParameter = 0.008; - private final double missedTimeWindowPenalty = 0.01; - - public DriversActivityScoring() { - super(); - } - - @Override - public void finish() { - } - - @Override - public double getScore() { - return score; - } - - @Override - public void handleFirstActivity(Activity act) { - handleActivity(act); - } - - @Override - public void handleActivity(Activity act) { - if (act instanceof FreightActivity) { - double actStartTime = act.getStartTime().seconds(); - - // log.info(act + " start: " + Time.writeTime(actStartTime)); - TimeWindow tw = ((FreightActivity) act).getTimeWindow(); - if (actStartTime > tw.getEnd()) { - double penalty_score = (-1) * (actStartTime - tw.getEnd()) * missedTimeWindowPenalty; - if (!(penalty_score <= 0.0)) - throw new AssertionError("penalty score must be negative"); - // log.info("penalty " + penalty_score); - score += penalty_score; - - } - double actTimeCosts = (act.getEndTime().seconds() - actStartTime) * timeParameter; - // log.info("actCosts " + actTimeCosts); - if (!(actTimeCosts >= 0.0)) - throw new AssertionError("actTimeCosts must be positive"); - score += actTimeCosts * (-1); - } - } - - @Override - public void handleLastActivity(Activity act) { - handleActivity(act); - } - - } - - static class DriversLegScoring implements SumScoringFunction.BasicScoring, SumScoringFunction.LegScoring { - - // private static final Logger log = Logger.getLogger(DriversLegScoring.class); - - private double score = 0.0; - private final Network network; - private final Carrier carrier; - private final Set employedVehicles; - - public DriversLegScoring(Carrier carrier, Network network) { - super(); - this.network = network; - this.carrier = carrier; - employedVehicles = new HashSet<>(); - } - - @Override - public void finish() { - - } - - @Override - public double getScore() { - return score; - } - - private double getTimeParameter(CarrierVehicle vehicle) { - return vehicle.getType().getCostInformation().getCostsPerSecond(); - } - - private double getDistanceParameter(CarrierVehicle vehicle) { - return vehicle.getType().getCostInformation().getCostsPerMeter(); - } - - @Override - public void handleLeg(Leg leg) { - if (leg.getRoute() instanceof NetworkRoute nRoute) { - Id vehicleId = nRoute.getVehicleId(); - CarrierVehicle vehicle = CarrierUtils.getCarrierVehicle(carrier, vehicleId); - Gbl.assertNotNull(vehicle); - employedVehicles.add(vehicle); - double distance = 0.0; - if (leg.getRoute() instanceof NetworkRoute) { - Link startLink = network.getLinks().get(leg.getRoute().getStartLinkId()); - distance += startLink.getLength(); - for (Id linkId : ((NetworkRoute) leg.getRoute()).getLinkIds()) { - distance += network.getLinks().get(linkId).getLength(); - } - distance += network.getLinks().get(leg.getRoute().getEndLinkId()).getLength(); - } - double distanceCosts = distance * getDistanceParameter(vehicle); - if (!(distanceCosts >= 0.0)) - throw new AssertionError("distanceCosts must be positive"); - score += (-1) * distanceCosts; - double timeCosts = leg.getTravelTime().seconds() * getTimeParameter(vehicle); - if (!(timeCosts >= 0.0)) - throw new AssertionError("distanceCosts must be positive"); - score += (-1) * timeCosts; - } - } - } - - static class VehicleEmploymentScoring implements SumScoringFunction.BasicScoring { - - private final Carrier carrier; - - public VehicleEmploymentScoring(Carrier carrier) { - super(); - this.carrier = carrier; - } - - @Override - public void finish() { - - } - - @Override - public double getScore() { - double score = 0.; - CarrierPlan selectedPlan = carrier.getSelectedPlan(); - if (selectedPlan == null) - return 0.; - for (ScheduledTour tour : selectedPlan.getScheduledTours()) { - if (!tour.getTour().getTourElements().isEmpty()) { - score += (-1) * tour.getVehicle().getType().getCostInformation().getFixedCosts(); - } - } - return score; - } - - } -} diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java new file mode 100644 index 00000000000..0f788f5f9d6 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/GenerateSmallScaleCommercialTrafficDemand.java @@ -0,0 +1,2048 @@ +/* *********************************************************************** * + * project: org.matsim.* + * Controler.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2007 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ +package org.matsim.smallScaleCommercialTrafficGeneration; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import it.unimi.dsi.fastutil.objects.Object2DoubleMap; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import org.locationtech.jts.geom.Geometry; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.ShpOptions; +import org.matsim.application.options.ShpOptions.Index; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.controler.*; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; +import org.matsim.freight.carriers.usecases.chessboard.CarrierTravelDisutilities; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ControllerConfigGroup; +import org.matsim.core.config.groups.VspExperimentalConfigGroup; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.replanning.GenericPlanStrategyImpl; +import org.matsim.core.replanning.selectors.ExpBetaPlanChanger; +import org.matsim.core.replanning.selectors.KeepSelected; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.LeastCostPathCalculatorFactory; +import org.matsim.core.router.util.TravelDisutility; +import org.matsim.core.router.util.TravelTime; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.scoring.ScoringFunction; +import org.matsim.core.scoring.SumScoringFunction; +import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.vehicles.CostInformation; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; +import org.opengis.feature.simple.SimpleFeature; +import picocli.CommandLine; + +import java.io.File; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Tool to generate small scale commercial traffic for a selected area. The needed input data are: employee information for the area and three shapes files (zones, buildings, landuse). These data should be available with OSM. + * + * @author Ricardo Ewert + */ +@CommandLine.Command(name = "generate-small-scale-commercial-traffic", description = "Generates plans for a small scale commercial traffic model", showDefaultValues = true) +public class GenerateSmallScaleCommercialTrafficDemand implements MATSimAppCommand { + // freight traffic from extern: + + // Option 1: take "as is" from Chengqi code. + + // Option 2: differentiate FTL and LTL by Gütergruppe. FTL as in option 1. LTL per Gütergruppe _ein_ Ziel in Zone, = "Hub". Verteilverkehr + // von dort. Startseite genauso. + + // Option 3: Leerkamp (nur in RVR Modell). + + private static final Logger log = LogManager.getLogger(GenerateSmallScaleCommercialTrafficDemand.class); + private static final HashMap>> buildingsPerZone = new HashMap<>(); + private static final HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); + + private enum CreationOption { + useExistingCarrierFileWithSolution, createNewCarrierFile, useExistingCarrierFileWithoutSolution + } + + private enum LanduseConfiguration { + useOnlyOSMLanduse, useOSMBuildingsAndLanduse, useExistingDataDistribution + } + + private enum SmallScaleCommercialTrafficType { + commercialPersonTraffic, goodsTraffic, completeSmallScaleCommercialTraffic + } + + @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; + + @CommandLine.Option(names = "--jspritIterations", description = "Set number of jsprit iterations", required = true) + private int jspritIterations; + + @CommandLine.Option(names = "--creationOption", description = "Set option of mode differentiation: useExistingCarrierFileWithSolution, createNewCarrierFile, useExistingCarrierFileWithoutSolution") + private CreationOption usedCreationOption; + + @CommandLine.Option(names = "--landuseConfiguration", description = "Set option of used OSM data. Options: useOnlyOSMLanduse, useOSMBuildingsAndLanduse, useExistingDataDistribution") + private LanduseConfiguration usedLanduseConfiguration; + + @CommandLine.Option(names = "--smallScaleCommercialTrafficType", description = "Select traffic type. Options: commercialPersonTraffic, goodsTraffic, completeSmallScaleCommercialTraffic (contains both types)") + private SmallScaleCommercialTrafficType usedSmallScaleCommercialTrafficType; + + @CommandLine.Option(names = "--includeExistingModels", description = "If models for some segments exist they can be included.") + private boolean includeExistingModels; + + @CommandLine.Option(names = "--zoneShapeFileName", description = "Path of the zone shape file.") + private Path shapeFileZonePath; + + @CommandLine.Option(names = "--buildingsShapeFileName", description = "Path of the buildings shape file") + private Path shapeFileBuildingsPath; + + @CommandLine.Option(names = "--landuseShapeFileName", description = "Path of the landuse shape file") + private Path shapeFileLandusePath; + + @CommandLine.Option(names = "--shapeCRS", description = "CRS of the three input shape files (zones, landuse, buildings") + private String shapeCRS; + + @CommandLine.Option(names = "--resistanceFactor", defaultValue = "0.005", description = "ResistanceFactor for the trip distribution") + private double resistanceFactor; + + @CommandLine.Option(names = "--nameOutputPopulation", description = "Name of the output Population") + private String nameOutputPopulation; + + @CommandLine.Option(names = "--numberOfPlanVariantsPerAgent", description = "If an agent should have variant plans, you should set this parameter.", defaultValue = "1") + private int numberOfPlanVariantsPerAgent; + + @CommandLine.Option(names = "--pathOutput", description = "Path for the output") + private Path output; + + private Random rnd; + + public static void main(String[] args) { + System.exit(new CommandLine(new GenerateSmallScaleCommercialTrafficDemand()).execute(args)); + } + + @Override + public Integer call() throws Exception { + Configurator.setLevel("org.matsim.core.utils.geometry.geotools.MGC", Level.ERROR); + + String modelName = configPath.getParent().getFileName().toString(); + + String sampleName = SmallScaleCommercialTrafficUtils.getSampleNameOfOutputFolder(sample); + + Config config = readAndCheckConfig(configPath, modelName, sampleName, output); + + output = Path.of(config.controller().getOutputDirectory()); + + Scenario scenario = ScenarioUtils.loadScenario(config); + NetworkUtils.runNetworkCleaner(scenario.getNetwork()); // e.g. for vulkaneifel network + + FreightCarriersConfigGroup freightCarriersConfigGroup; + switch (usedCreationOption) { + case useExistingCarrierFileWithSolution -> { + log.info("Existing carriers (including carrier vehicle types ) should be set in the freight config group"); + if (includeExistingModels) + 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."); + freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + if (config.vehicles() != null && freightCarriersConfigGroup.getCarriersVehicleTypesFile() == null) + freightCarriersConfigGroup.setCarriersVehicleTypesFile(config.vehicles().getVehiclesFile()); + log.info("Load carriers from: " + freightCarriersConfigGroup.getCarriersFile()); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); + } + case useExistingCarrierFileWithoutSolution -> { + log.info("Existing carriers (including carrier vehicle types ) should be set in the freight config group"); + if (includeExistingModels) + 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."); + freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + if (config.vehicles() != null && freightCarriersConfigGroup.getCarriersVehicleTypesFile() == null) + freightCarriersConfigGroup.setCarriersVehicleTypesFile(config.vehicles().getVehiclesFile()); + log.info("Load carriers from: " + freightCarriersConfigGroup.getCarriersFile()); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); + solveSeparatedVRPs(scenario, null); + } + default -> { + if (!Files.exists(shapeFileLandusePath)) { + throw new Exception("Required landuse shape file not found:" + shapeFileLandusePath.toString()); + } + if (!Files.exists(shapeFileBuildingsPath)) { + throw new Exception( + "Required OSM buildings shape file {} not found" + shapeFileBuildingsPath.toString()); + } + 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, + shapeFileBuildingsPath, shapeCRS, buildingsPerZone); + ShpOptions shpZones = new ShpOptions(shapeFileZonePath, shapeCRS, StandardCharsets.UTF_8); + Map, Link>> regionLinksMap = filterLinksForZones(scenario, shpZones, + SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS), buildingsPerZone); + + switch (usedSmallScaleCommercialTrafficType) { + case commercialPersonTraffic, goodsTraffic -> + createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, + usedSmallScaleCommercialTrafficType.toString(), + includeExistingModels); + case completeSmallScaleCommercialTraffic -> { + createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "commercialPersonTraffic", + includeExistingModels); + includeExistingModels = false; // because already included in the step before + createCarriersAndDemand(output, scenario, shpZones, resultingDataPerZone, regionLinksMap, "goodsTraffic", + includeExistingModels); + } + default -> throw new RuntimeException("No traffic type selected."); + } + if (config.controller().getRunId() == null) + new CarrierPlanWriter(CarriersUtils.addOrGetCarriers(scenario)) + .write(scenario.getConfig().controller().getOutputDirectory() + "/output_CarrierDemand.xml"); + else + new CarrierPlanWriter(CarriersUtils.addOrGetCarriers(scenario)) + .write(scenario.getConfig().controller().getOutputDirectory() + "/" + + scenario.getConfig().controller().getRunId() + ".output_CarrierDemand.xml"); + solveSeparatedVRPs(scenario, regionLinksMap); + } + } + if (config.controller().getRunId() == null) + new CarrierPlanWriter(CarriersUtils.addOrGetCarriers(scenario)).write( + scenario.getConfig().controller().getOutputDirectory() + "/output_CarrierDemandWithPlans.xml"); + else + new CarrierPlanWriter(CarriersUtils.addOrGetCarriers(scenario)) + .write( + scenario.getConfig().controller().getOutputDirectory() + "/" + scenario.getConfig().controller().getRunId() + ".output_CarrierDemandWithPlans.xml"); + Controler controler = prepareControler(scenario); + controler.run(); + SmallScaleCommercialTrafficUtils.createPlansBasedOnCarrierPlans(controler.getScenario(), + usedSmallScaleCommercialTrafficType.toString(), output, modelName, sampleName, nameOutputPopulation, numberOfPlanVariantsPerAgent); + return 0; + } + + /** + * @param originalScenario complete Scenario + * @param regionLinksMap list with Links for each region + */ + private void solveSeparatedVRPs(Scenario originalScenario, Map, Link>> regionLinksMap) throws Exception { + + boolean splitCarrier = true; + boolean splitVRPs = false; + int maxServicesPerCarrier = 100; + Map, Carrier> allCarriers = new HashMap<>( + CarriersUtils.getCarriers(originalScenario).getCarriers()); + Map, Carrier> solvedCarriers = new HashMap<>(); + List> keyList = new ArrayList<>(allCarriers.keySet()); + CarriersUtils.getCarriers(originalScenario).getCarriers().values().forEach(carrier -> { + if (CarriersUtils.getJspritIterations(carrier) == 0) { + allCarriers.remove(carrier.getId()); + solvedCarriers.put(carrier.getId(), carrier); + } + }); + int carrierSteps = 30; + for (int i = 0; i < allCarriers.size(); i++) { + int fromIndex = i * carrierSteps; + int toIndex = (i + 1) * carrierSteps; + if (toIndex >= allCarriers.size()) + toIndex = allCarriers.size(); + + Map, Carrier> subCarriers = new HashMap<>(allCarriers); + List> subList; + if (splitVRPs) { + subList = keyList.subList(fromIndex, toIndex); + subCarriers.keySet().retainAll(subList); + } else { + fromIndex = 0; + toIndex = allCarriers.size(); + } + + if (splitCarrier) { + Map, Carrier> subCarriersToAdd = new HashMap<>(); + List> keyListCarrierToRemove = new ArrayList<>(); + for (Carrier carrier : subCarriers.values()) { + + int countedServices = 0; + int countedVehicles = 0; + if (carrier.getServices().size() > maxServicesPerCarrier) { + + int numberOfNewCarrier = (int) Math + .ceil((double) carrier.getServices().size() / (double) maxServicesPerCarrier); + int numberOfServicesPerNewCarrier = Math + .round((float) carrier.getServices().size() / numberOfNewCarrier); + + int j = 0; + while (j < numberOfNewCarrier) { + + int numberOfServicesForNewCarrier = numberOfServicesPerNewCarrier; + int numberOfVehiclesForNewCarrier = numberOfServicesPerNewCarrier; + if (j + 1 == numberOfNewCarrier) { + numberOfServicesForNewCarrier = carrier.getServices().size() - countedServices; + numberOfVehiclesForNewCarrier = carrier.getCarrierCapabilities().getCarrierVehicles() + .size() - countedVehicles; + } + Carrier newCarrier = CarriersUtils.createCarrier( + Id.create(carrier.getId().toString() + "_part_" + (j + 1), Carrier.class)); + CarrierCapabilities newCarrierCapabilities = CarrierCapabilities.Builder.newInstance() + .setFleetSize(carrier.getCarrierCapabilities().getFleetSize()).build(); + newCarrierCapabilities.getCarrierVehicles() + .putAll(carrier.getCarrierCapabilities().getCarrierVehicles()); + newCarrier.setCarrierCapabilities(newCarrierCapabilities); + newCarrier.getServices().putAll(carrier.getServices()); + CarriersUtils.setJspritIterations(newCarrier, CarriersUtils.getJspritIterations(carrier)); + carrier.getAttributes().getAsMap().keySet().forEach(attribute -> newCarrier.getAttributes() + .putAttribute(attribute, carrier.getAttributes().getAttribute(attribute))); + + List> vehiclesForNewCarrier = new ArrayList<>( + carrier.getCarrierCapabilities().getCarrierVehicles().keySet()); + List> servicesForNewCarrier = new ArrayList<>( + carrier.getServices().keySet()); + + List> subListVehicles = vehiclesForNewCarrier.subList( + j * numberOfServicesPerNewCarrier, + j * numberOfServicesPerNewCarrier + numberOfVehiclesForNewCarrier); + List> subListServices = servicesForNewCarrier.subList( + j * numberOfServicesPerNewCarrier, + j * numberOfServicesPerNewCarrier + numberOfServicesForNewCarrier); + + newCarrier.getCarrierCapabilities().getCarrierVehicles().keySet() + .retainAll(subListVehicles); + newCarrier.getServices().keySet().retainAll(subListServices); + + countedVehicles += newCarrier.getCarrierCapabilities().getCarrierVehicles().size(); + countedServices += newCarrier.getServices().size(); + + subCarriersToAdd.put(newCarrier.getId(), newCarrier); + j++; + } + keyListCarrierToRemove.add(carrier.getId()); + if (countedVehicles != carrier.getCarrierCapabilities().getCarrierVehicles().size()) + throw new Exception("Split parts of the carrier " + carrier.getId().toString() + + " has a different number of vehicles than the original carrier"); + if (countedServices != carrier.getServices().size()) + throw new Exception("Split parts of the carrier " + carrier.getId().toString() + + " has a different number of services than the original carrier"); + + } + } + subCarriers.putAll(subCarriersToAdd); + for (Id id : keyListCarrierToRemove) { + subCarriers.remove(id); + } + } + CarriersUtils.getCarriers(originalScenario).getCarriers().clear(); + CarriersUtils.getCarriers(originalScenario).getCarriers().putAll(subCarriers); + log.info("Solving carriers " + (fromIndex + 1) + "-" + (toIndex) + " of all " + allCarriers.size() + + " carriers. This are " + subCarriers.size() + " VRP to solve."); + CarriersUtils.runJsprit(originalScenario); + solvedCarriers.putAll(CarriersUtils.getCarriers(originalScenario).getCarriers()); + CarriersUtils.getCarriers(originalScenario).getCarriers().clear(); + if (!splitVRPs) + break; + } + CarriersUtils.getCarriers(originalScenario).getCarriers().putAll(solvedCarriers); + CarriersUtils.getCarriers(originalScenario).getCarriers().values().forEach(carrier -> { + if (regionLinksMap != null && !carrier.getAttributes().getAsMap().containsKey("tourStartArea")) { + List startAreas = new ArrayList<>(); + for (ScheduledTour tour : carrier.getSelectedPlan().getScheduledTours()) { + String tourStartZone = SmallScaleCommercialTrafficUtils + .findZoneOfLink(tour.getTour().getStartLinkId(), regionLinksMap); + if (!startAreas.contains(tourStartZone)) + startAreas.add(tourStartZone); + } + carrier.getAttributes().putAttribute("tourStartArea", + String.join(";", startAreas)); + } + }); + } + + private void createCarriersAndDemand(Path output, Scenario scenario, ShpOptions shpZones, + HashMap> resultingDataPerZone, + Map, Link>> regionLinksMap, String smallScaleCommercialTrafficType, + boolean includeExistingModels) throws Exception { + + ArrayList modesORvehTypes; + if (smallScaleCommercialTrafficType.equals("goodsTraffic")) + modesORvehTypes = new ArrayList<>( + Arrays.asList("vehTyp1", "vehTyp2", "vehTyp3", "vehTyp4", "vehTyp5")); + else if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) + modesORvehTypes = new ArrayList<>(List.of("total")); + else + throw new Exception("Invalid traffic type selected!"); + + TrafficVolumeGeneration.setInputParameters(smallScaleCommercialTrafficType); + + HashMap> trafficVolumePerTypeAndZone_start = TrafficVolumeGeneration + .createTrafficVolume_start(resultingDataPerZone, output, sample, modesORvehTypes, smallScaleCommercialTrafficType); + HashMap> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration + .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, smallScaleCommercialTrafficType); + + if (includeExistingModels) { + SmallScaleCommercialTrafficUtils.readExistingModels(scenario, sample, regionLinksMap); + TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, smallScaleCommercialTrafficType, + trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); + } + final TripDistributionMatrix odMatrix = createTripDistribution(trafficVolumePerTypeAndZone_start, + trafficVolumePerTypeAndZone_stop, shpZones, smallScaleCommercialTrafficType, scenario, output, regionLinksMap); + createCarriers(scenario, odMatrix, resultingDataPerZone, smallScaleCommercialTrafficType, regionLinksMap); + } + + /** + * Reads and checks config if all necessary parameter are set. + */ + private Config readAndCheckConfig(Path configPath, String modelName, String sampleName, Path output) throws Exception { + + Config config = ConfigUtils.loadConfig(configPath.toString()); + if (output == null || output.toString().isEmpty()) + config.controller().setOutputDirectory(Path.of(config.controller().getOutputDirectory()).resolve(modelName) + .resolve(usedSmallScaleCommercialTrafficType.toString() + "_" + sampleName + "pct" + "_" + + java.time.LocalDate.now() + "_" + java.time.LocalTime.now().toSecondOfDay() + "_" + resistanceFactor) + .toString()); + else + config.controller().setOutputDirectory(output.toString()); + new OutputDirectoryHierarchy(config.controller().getOutputDirectory(), config.controller().getRunId(), + config.controller().getOverwriteFileSetting(), ControllerConfigGroup.CompressionType.gzip); + new File(Path.of(config.controller().getOutputDirectory()).resolve("calculatedData").toString()).mkdir(); + rnd = new Random(config.global().getRandomSeed()); + if (config.network().getInputFile() == null) + throw new Exception("No network file in config"); + if (config.network().getInputCRS() == null) + throw new Exception("No network CRS is set in config"); + if (config.global().getCoordinateSystem() == null) + throw new Exception("No global CRS is set in config"); + if (config.controller().getOutputDirectory() == null) + throw new Exception("No output directory was set"); + + return config; + } + + /** + * Prepares the controller. + */ + private Controler prepareControler(Scenario scenario) { + Controler controler = new Controler(scenario); + + controler.addOverridingModule(new CarrierModule()); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + bind(CarrierStrategyManager.class).toProvider( + new MyCarrierPlanStrategyManagerFactory(CarriersUtils.getCarrierVehicleTypes(scenario))); + bind(CarrierScoringFunctionFactory.class).toInstance(new MyCarrierScoringFunctionFactory()); + } + }); + controler.getConfig().vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.abort); + return controler; + } + + /** + * Creates the carriers and the related demand, based on the generated + * TripDistributionMatrix. + */ + private void createCarriers(Scenario scenario, TripDistributionMatrix odMatrix, + HashMap> resultingDataPerZone, String smallScaleCommercialTrafficType, + Map, Link>> regionLinksMap) { + int maxNumberOfCarrier = odMatrix.getListOfPurposes().size() * odMatrix.getListOfZones().size() + * odMatrix.getListOfModesOrVehTypes().size(); + int createdCarrier = 0; + int fixedNumberOfVehiclePerTypeAndLocation = 1; //TODO possible improvement, perhaps check KiD + ValueSelectorUnderGivenProbability tourStartTimeSelector = createTourStartTimeDistribution(smallScaleCommercialTrafficType); + ValueSelectorUnderGivenProbability tourDurationTimeSelector = createTourDurationTimeDistribution(smallScaleCommercialTrafficType); + Map stopDurationTimeSelector = createStopDurationTimeDistributionPerCategory( + smallScaleCommercialTrafficType); + + CarrierVehicleTypes carrierVehicleTypes = CarriersUtils.getCarrierVehicleTypes(scenario); + Map, VehicleType> additionalCarrierVehicleTypes = scenario.getVehicles().getVehicleTypes(); + additionalCarrierVehicleTypes.values().forEach( + vehicleType -> carrierVehicleTypes.getVehicleTypes().putIfAbsent(vehicleType.getId(), vehicleType)); + + for (VehicleType vehicleType : carrierVehicleTypes.getVehicleTypes().values()) { + CostInformation costInformation = vehicleType.getCostInformation(); + VehicleUtils.setCostsPerSecondInService(costInformation, costInformation.getCostsPerSecond()); + VehicleUtils.setCostsPerSecondWaiting(costInformation, costInformation.getCostsPerSecond()); + } + + for (Integer purpose : odMatrix.getListOfPurposes()) { + for (String startZone : odMatrix.getListOfZones()) { + for (String modeORvehType : odMatrix.getListOfModesOrVehTypes()) { + boolean isStartingLocation = false; + checkIfIsStartingPosition: + { + for (String possibleStopZone : odMatrix.getListOfZones()) { + if (!modeORvehType.equals("pt") && !modeORvehType.equals("op")) + if (odMatrix.getTripDistributionValue(startZone, possibleStopZone, modeORvehType, + purpose, smallScaleCommercialTrafficType) != 0) { + isStartingLocation = true; + break checkIfIsStartingPosition; + } + } + } + if (isStartingLocation) { + double occupancyRate = 0; + String[] possibleVehicleTypes = null; + ArrayList startCategory = new ArrayList<>(); + ArrayList stopCategory = new ArrayList<>(); + stopCategory.add("Employee Primary Sector"); + stopCategory.add("Employee Construction"); + stopCategory.add("Employee Secondary Sector Rest"); + stopCategory.add("Employee Retail"); + stopCategory.add("Employee Traffic/Parcels"); + stopCategory.add("Employee Tertiary Sector Rest"); + stopCategory.add("Inhabitants"); + if (purpose == 1) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { + possibleVehicleTypes = new String[]{"vwCaddy", "e_SpaceTourer"}; + occupancyRate = 1.5; + } + startCategory.add("Employee Secondary Sector Rest"); + stopCategory.clear(); + stopCategory.add("Employee Secondary Sector Rest"); + } else if (purpose == 2) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { + possibleVehicleTypes = new String[]{"vwCaddy", "e_SpaceTourer"}; + occupancyRate = 1.6; + } + startCategory.add("Employee Secondary Sector Rest"); + } else if (purpose == 3) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { + possibleVehicleTypes = new String[]{"golf1.4", "c_zero"}; + occupancyRate = 1.2; + } + startCategory.add("Employee Retail"); + startCategory.add("Employee Tertiary Sector Rest"); + } else if (purpose == 4) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { + possibleVehicleTypes = new String[]{"golf1.4", "c_zero"}; + occupancyRate = 1.2; + } + startCategory.add("Employee Traffic/Parcels"); + } else if (purpose == 5) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { + possibleVehicleTypes = new String[]{"mercedes313", "e_SpaceTourer"}; + occupancyRate = 1.7; + } + startCategory.add("Employee Construction"); + } else if (purpose == 6) { + startCategory.add("Inhabitants"); + } + if (smallScaleCommercialTrafficType.equals("goodsTraffic")) { + occupancyRate = 1.; + switch (modeORvehType) { + case "vehTyp1" -> + possibleVehicleTypes = new String[]{"vwCaddy", "e_SpaceTourer"}; // possible to add more types, see source + case "vehTyp2" -> + possibleVehicleTypes = new String[]{"mercedes313", "e_SpaceTourer"}; + case "vehTyp3", "vehTyp4" -> + possibleVehicleTypes = new String[]{"light8t", "light8t_electro"}; + case "vehTyp5" -> + possibleVehicleTypes = new String[]{"medium18t", "medium18t_electro", "heavy40t", "heavy40t_electro"}; + } + } + + // use only types of the possibleTypes which are in the given types file + List vehicleTypes = new ArrayList<>(); + assert possibleVehicleTypes != null; + + for (String possibleVehicleType : possibleVehicleTypes) { + if (CarriersUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().containsKey( + Id.create(possibleVehicleType, VehicleType.class))) + vehicleTypes.add(possibleVehicleType); + } + // find a start category with existing employees in this zone + Collections.shuffle(startCategory, rnd); + String selectedStartCategory = startCategory.get(0); + for (int count = 1; resultingDataPerZone.get(startZone).getDouble(selectedStartCategory) == 0; count++) { + if (count <= startCategory.size()) + selectedStartCategory = startCategory.get(rnd.nextInt(startCategory.size())); + else + selectedStartCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); + } + String carrierName = null; + if (smallScaleCommercialTrafficType.equals("goodsTraffic")) { + carrierName = "Carrier_Goods_" + startZone + "_purpose_" + purpose + "_" + modeORvehType; + } else if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) + carrierName = "Carrier_Business_" + startZone + "_purpose_" + purpose; + int numberOfDepots = odMatrix.getSumOfServicesForStartZone(startZone, modeORvehType, purpose, + smallScaleCommercialTrafficType); + FleetSize fleetSize = FleetSize.FINITE; + ArrayList vehicleDepots = new ArrayList<>(); + createdCarrier++; + log.info("Create carrier number " + createdCarrier + " of a maximum Number of " + + maxNumberOfCarrier + " carriers."); + log.info("Carrier: " + carrierName + "; depots: " + numberOfDepots + "; services: " + + (int) Math.ceil(odMatrix.getSumOfServicesForStartZone(startZone, modeORvehType, + purpose, smallScaleCommercialTrafficType) / occupancyRate)); + createNewCarrierAndAddVehicleTypes(scenario, purpose, startZone, + selectedStartCategory, carrierName, vehicleTypes, numberOfDepots, fleetSize, + fixedNumberOfVehiclePerTypeAndLocation, vehicleDepots, regionLinksMap, smallScaleCommercialTrafficType, + tourStartTimeSelector, tourDurationTimeSelector); + log.info("Create services for carrier: " + carrierName); + for (String stopZone : odMatrix.getListOfZones()) { + int trafficVolumeForOD = Math.round((float)odMatrix.getTripDistributionValue(startZone, + stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType)); + int numberOfJobs = (int) Math.ceil(trafficVolumeForOD / occupancyRate); + if (numberOfJobs == 0) + continue; + // find a category for the tour stop with existing employees in this zone + String selectedStopCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); + while (resultingDataPerZone.get(stopZone).getDouble(selectedStopCategory) == 0) + selectedStopCategory = stopCategory.get(rnd.nextInt(stopCategory.size())); + String[] serviceArea = new String[]{stopZone}; + int serviceTimePerStop; + if (selectedStartCategory.equals("Inhabitants")) + serviceTimePerStop = getServiceTimePerStop(stopDurationTimeSelector, startCategory.get(0), modeORvehType, smallScaleCommercialTrafficType); + else + serviceTimePerStop = getServiceTimePerStop(stopDurationTimeSelector, selectedStartCategory, modeORvehType, smallScaleCommercialTrafficType); + + TimeWindow serviceTimeWindow = TimeWindow.newInstance(0, + 24 * 3600); //TODO eventuell anpassen wegen veränderter Tourzeiten + createServices(scenario, vehicleDepots, selectedStopCategory, carrierName, + numberOfJobs, serviceArea, serviceTimePerStop, serviceTimeWindow, regionLinksMap); + } + } + } + } + } + System.out.println("Final results for the start time distribution"); + tourStartTimeSelector.writeResults(); + + System.out.println("Final results for the tour duration distribution"); + tourDurationTimeSelector.writeResults(); + + for (StopDurationGoodTrafficKey sector : stopDurationTimeSelector.keySet()) { + System.out.println("Final results for the stop duration distribution in sector " + sector); + stopDurationTimeSelector.get(sector).writeResults(); + } + + log.warn("The jspritIterations are now set to " + jspritIterations + " in this simulation!"); + log.info("Finished creating " + createdCarrier + " carriers including related services."); + } + + /** + * Creates the services for one carrier. + */ + private void createServices(Scenario scenario, ArrayList noPossibleLinks, + String selectedStopCategory, String carrierName, int numberOfJobs, String[] serviceArea, + Integer serviceTimePerStop, TimeWindow serviceTimeWindow, + Map, Link>> regionLinksMap) { + + String stopZone = serviceArea[0]; + + for (int i = 0; i < numberOfJobs; i++) { + + Id linkId = findPossibleLink(stopZone, selectedStopCategory, noPossibleLinks, regionLinksMap, shapeCRS); + Id idNewService = Id.create(carrierName + "_" + linkId + "_" + rnd.nextInt(10000), + CarrierService.class); + + CarrierService thisService = CarrierService.Builder.newInstance(idNewService, linkId) + .setServiceDuration(serviceTimePerStop).setServiceStartTimeWindow(serviceTimeWindow).build(); + CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create(carrierName, Carrier.class)).getServices() + .put(thisService.getId(), thisService); + } + + } + + /** + * Creates the carrier and the related vehicles. + */ + private void createNewCarrierAndAddVehicleTypes(Scenario scenario, Integer purpose, String startZone, + String selectedStartCategory, String carrierName, + List vehicleTypes, int numberOfDepots, FleetSize fleetSize, + int fixedNumberOfVehiclePerTypeAndLocation, + ArrayList vehicleDepots, Map, Link>> regionLinksMap, + String smallScaleCommercialTrafficType, + ValueSelectorUnderGivenProbability tourStartTimeSelector, + ValueSelectorUnderGivenProbability tourDurationTimeSelector) { + + Carriers carriers = CarriersUtils.addOrGetCarriers(scenario); + CarrierVehicleTypes carrierVehicleTypes = CarriersUtils.getCarrierVehicleTypes(scenario); + + CarrierCapabilities carrierCapabilities; + + Carrier thisCarrier = CarriersUtils.createCarrier(Id.create(carrierName, Carrier.class)); + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic") && purpose == 3) + thisCarrier.getAttributes().putAttribute("subpopulation", smallScaleCommercialTrafficType + "_service"); + else + thisCarrier.getAttributes().putAttribute("subpopulation", smallScaleCommercialTrafficType); + + thisCarrier.getAttributes().putAttribute("purpose", purpose); + thisCarrier.getAttributes().putAttribute("tourStartArea", startZone); + if (jspritIterations > 0) + CarriersUtils.setJspritIterations(thisCarrier, jspritIterations); + carrierCapabilities = CarrierCapabilities.Builder.newInstance().setFleetSize(fleetSize).build(); + carriers.addCarrier(thisCarrier); + + while (vehicleDepots.size() < numberOfDepots) { + Id link = findPossibleLink(startZone, selectedStartCategory, null, regionLinksMap, shapeCRS); + vehicleDepots.add(link.toString()); + } + + for (String singleDepot : vehicleDepots) { + int vehicleStartTime = getVehicleStartTime(tourStartTimeSelector); + int tourDuration = getVehicleTourDuration(tourDurationTimeSelector); + int vehicleEndTime = vehicleStartTime + tourDuration; + for (String thisVehicleType : vehicleTypes) { //TODO Flottenzusammensetzung anpassen. Momentan pro Depot alle Fahrzeugtypen 1x erzeugen + VehicleType thisType = carrierVehicleTypes.getVehicleTypes() + .get(Id.create(thisVehicleType, VehicleType.class)); + if (fixedNumberOfVehiclePerTypeAndLocation == 0) + fixedNumberOfVehiclePerTypeAndLocation = 1; + for (int i = 0; i < fixedNumberOfVehiclePerTypeAndLocation; i++) { + CarrierVehicle newCarrierVehicle = CarrierVehicle.Builder + .newInstance( + Id.create( + thisCarrier.getId().toString() + "_" + + (carrierCapabilities.getCarrierVehicles().size() + 1), + Vehicle.class), + Id.createLinkId(singleDepot), thisType) + .setEarliestStart(vehicleStartTime).setLatestEnd(vehicleEndTime).build(); + carrierCapabilities.getCarrierVehicles().put(newCarrierVehicle.getId(), newCarrierVehicle); + if (!carrierCapabilities.getVehicleTypes().contains(thisType)) + carrierCapabilities.getVehicleTypes().add(thisType); + } + } + + thisCarrier.setCarrierCapabilities(carrierCapabilities); + } + } + + /** + * Gives a duration for the created tour under the given probability. + * + * @param tourDurationTimeSelector + * @return + */ + private int getVehicleTourDuration(ValueSelectorUnderGivenProbability tourDurationTimeSelector) { + ValueSelectorUnderGivenProbability.ProbabilityForValue selectedValue = tourDurationTimeSelector.getNextValueUnderGivenProbability(); + int tourDurationLowerBound = Integer.parseInt(selectedValue.getValue()); + int tourDurationUpperBound = Integer.parseInt(selectedValue.getUpperBound()); + return rnd.nextInt(tourDurationLowerBound * 3600, tourDurationUpperBound * 3600); + } + + /** + * Gives a tour start time for the created tour under the given probability. + * + * @param tourStartTimeSelector + * @return + */ + private int getVehicleStartTime(ValueSelectorUnderGivenProbability tourStartTimeSelector) { + ValueSelectorUnderGivenProbability.ProbabilityForValue selectedValue = tourStartTimeSelector.getNextValueUnderGivenProbability(); + int tourStartTimeLowerBound = Integer.parseInt(selectedValue.getValue()); + int tourStartTimeUpperBound = Integer.parseInt(selectedValue.getUpperBound()); + return rnd.nextInt(tourStartTimeLowerBound * 3600, tourStartTimeUpperBound * 3600); + + } + + + /** + * Give a service duration based on the purpose and the trafficType under a given probability + * + * @param serviceDurationTimeSelector + * @param employeeCategory + * @param modeORvehType + * @param smallScaleCommercialTrafficType + * @return + */ + private Integer getServiceTimePerStop(Map serviceDurationTimeSelector, + String employeeCategory, + String modeORvehType, String smallScaleCommercialTrafficType) { + StopDurationGoodTrafficKey key = null; + if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.commercialPersonTraffic.toString())) + key = makeStopDurationGoodTrafficKey(employeeCategory, null); + else if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.goodsTraffic.toString())) { + key = makeStopDurationGoodTrafficKey(employeeCategory, modeORvehType); + } + ValueSelectorUnderGivenProbability.ProbabilityForValue selectedValue = serviceDurationTimeSelector.get( + key).getNextValueUnderGivenProbability(); + int serviceDurationLowerBound = Integer.parseInt(selectedValue.getValue()); + int serviceDurationUpperBound = Integer.parseInt(selectedValue.getUpperBound()); + return rnd.nextInt(serviceDurationLowerBound * 60, serviceDurationUpperBound * 60); + } + + /** + * Finds a possible link for a service or the vehicle location. + */ + private Id findPossibleLink(String zone, String selectedCategory, ArrayList noPossibleLinks, + Map, Link>> regionLinksMap, String shapeCRS) { + + Index indexZones = SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, shapeCRS); + + if (buildingsPerZone.isEmpty()) { + ShpOptions shpBuildings = new ShpOptions(shapeFileBuildingsPath, "EPSG:4326", StandardCharsets.UTF_8); + List buildingsFeatures = shpBuildings.readFeatures(); + LanduseBuildingAnalysis.analyzeBuildingType(buildingsFeatures, buildingsPerZone, + landuseCategoriesAndDataConnection, shapeFileLandusePath, indexZones, shapeCRS); + } + Id newLink = null; + for (int a = 0; newLink == null && a < buildingsPerZone.get(zone).get(selectedCategory).size() * 2; a++) { + + SimpleFeature possibleBuilding = buildingsPerZone.get(zone).get(selectedCategory) + .get(rnd.nextInt(buildingsPerZone.get(zone).get(selectedCategory).size())); + Coord centroidPointOfBuildingPolygon = MGC + .point2Coord(((Geometry) possibleBuilding.getDefaultGeometry()).getCentroid()); + + int numberOfPossibleLinks = regionLinksMap.get(zone).size(); + + // searches and selects the nearest link of the possible links in this zone + newLink = SmallScaleCommercialTrafficUtils.findNearestPossibleLink(zone, noPossibleLinks, regionLinksMap, newLink, + centroidPointOfBuildingPolygon, numberOfPossibleLinks); + } + if (newLink == null) + throw new RuntimeException("No possible link for buildings with type '" + selectedCategory + "' in zone '" + + zone + "' found. buildings in category: " + buildingsPerZone.get(zone).get(selectedCategory) + + "; possibleLinks in zone: " + regionLinksMap.get(zone).size()); + return newLink; + } + + /** + * Filters links by used mode "car" and creates Map with all links in each zone + */ + static Map, Link>> filterLinksForZones(Scenario scenario, ShpOptions shpZones, Index indexZones, + HashMap>> buildingsPerZone) throws URISyntaxException { + Map, Link>> regionLinksMap = new HashMap<>(); + List links; + log.info("Filtering and assign links to zones. This take some time..."); + + String networkPath; + if (scenario.getConfig().network().getInputFile().startsWith("https:")) + networkPath = scenario.getConfig().network().getInputFile(); + else + networkPath = scenario.getConfig().getContext().toURI().resolve(scenario.getConfig().network().getInputFile()).getPath(); + + Network networkToChange = NetworkUtils.readNetwork(networkPath); + NetworkUtils.runNetworkCleaner(networkToChange); + + links = networkToChange.getLinks().values().stream().filter(l -> l.getAllowedModes().contains("car")) + .collect(Collectors.toList()); + links.forEach(l -> l.getAttributes().putAttribute("newCoord", + shpZones.createTransformation(scenario.getConfig().network().getInputCRS()).transform(l.getCoord()))); + links.forEach(l -> l.getAttributes().putAttribute("zone", + indexZones.query((Coord) l.getAttributes().getAttribute("newCoord")))); + links = links.stream().filter(l -> l.getAttributes().getAttribute("zone") != null).collect(Collectors.toList()); + links.forEach(l -> regionLinksMap + .computeIfAbsent((String) l.getAttributes().getAttribute("zone"), (k) -> new HashMap<>()) + .put(l.getId(), l)); + if (regionLinksMap.size() != shpZones.readFeatures().size()) + findNearestLinkForZonesWithoutLinks(networkToChange, regionLinksMap, shpZones, buildingsPerZone); + return regionLinksMap; + } + + /** + * Finds for areas without links the nearest Link, if the area contains any building. + */ + private static void findNearestLinkForZonesWithoutLinks(Network networkToChange, Map, Link>> regionLinksMap, + ShpOptions shpZones, + HashMap>> buildingsPerZone) { + for (SimpleFeature singleArea : shpZones.readFeatures()) { + String zoneID = (String) singleArea.getAttribute("areaID"); + if (!regionLinksMap.containsKey(zoneID) && buildingsPerZone.get(zoneID) != null) { + for (ArrayList buildingList : buildingsPerZone.get(zoneID).values()) { + for (SimpleFeature building : buildingList) { + Link l = NetworkUtils.getNearestLink(networkToChange, + MGC.point2Coord(((Geometry) building.getDefaultGeometry()).getCentroid())); + assert l != null; + regionLinksMap + .computeIfAbsent(zoneID, (k) -> new HashMap<>()) + .put(l.getId(), l); + } + } + } + } + } + + /** + * Creates the number of trips between the zones for each mode and purpose. + */ + private TripDistributionMatrix createTripDistribution( + HashMap> trafficVolume_start, + HashMap> trafficVolume_stop, ShpOptions shpZones, + String smallScaleCommercialTrafficType, Scenario scenario, Path output, Map, Link>> regionLinksMap) + throws Exception { + + final TripDistributionMatrix odMatrix = TripDistributionMatrix.Builder + .newInstance(shpZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType).build(); + ArrayList listOfZones = new ArrayList<>(); + trafficVolume_start.forEach((k, v) -> { + if (!listOfZones.contains(k.getZone())) + listOfZones.add(k.getZone()); + }); + Network network = scenario.getNetwork(); + int count = 0; + + for (TrafficVolumeGeneration.TrafficVolumeKey trafficVolumeKey : trafficVolume_start.keySet()) { + count++; + if (count % 50 == 0 || count == 1) + log.info("Create OD pair " + count + " of " + trafficVolume_start.size()); + + String startZone = trafficVolumeKey.getZone(); + String modeORvehType = trafficVolumeKey.getModeORvehType(); + for (Integer purpose : trafficVolume_start.get(trafficVolumeKey).keySet()) { + Collections.shuffle(listOfZones); + for (String stopZone : listOfZones) { + odMatrix.setTripDistributionValue(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType, + network, regionLinksMap, resistanceFactor); + } + } + } + odMatrix.clearRoundingError(); + odMatrix.writeODMatrices(output, smallScaleCommercialTrafficType); + return odMatrix; + } + + private static class MyCarrierScoringFunctionFactory implements CarrierScoringFunctionFactory { + + @Inject + private Network network; + + @Override + public ScoringFunction createScoringFunction(Carrier carrier) { + SumScoringFunction sf = new SumScoringFunction(); + DriversLegScoring driverLegScoring = new DriversLegScoring(carrier, network); + VehicleEmploymentScoring vehicleEmploymentScoring = new VehicleEmploymentScoring(carrier); + DriversActivityScoring actScoring = new DriversActivityScoring(); + sf.addScoringFunction(driverLegScoring); + sf.addScoringFunction(vehicleEmploymentScoring); + sf.addScoringFunction(actScoring); + return sf; + } + + } + + private static class MyCarrierPlanStrategyManagerFactory implements Provider { + + @Inject + private Network network; + + @Inject + private LeastCostPathCalculatorFactory leastCostPathCalculatorFactory; + + @Inject + private Map modeTravelTimes; + + private final CarrierVehicleTypes types; + + public MyCarrierPlanStrategyManagerFactory(CarrierVehicleTypes types) { + this.types = types; + } + + @Override + public CarrierStrategyManager get() { + TravelDisutility travelDisutility = CarrierTravelDisutilities.createBaseDisutility(types, + modeTravelTimes.get(TransportMode.car)); + final LeastCostPathCalculator router = leastCostPathCalculatorFactory.createPathCalculator(network, + travelDisutility, modeTravelTimes.get(TransportMode.car)); + +// final GenericStrategyManager strategyManager = new GenericStrategyManager<>(); + final CarrierStrategyManager strategyManager = CarrierControlerUtils.createDefaultCarrierStrategyManager(); + strategyManager.setMaxPlansPerAgent(5); + { + GenericPlanStrategyImpl strategy = new GenericPlanStrategyImpl<>( + new ExpBetaPlanChanger.Factory().setBetaValue(1.0).build()); + + strategyManager.addStrategy(strategy, null, 1.0); + + } + { + GenericPlanStrategyImpl strategy = new GenericPlanStrategyImpl<>( + new KeepSelected<>()); + strategy.addStrategyModule(new CarrierTimeAllocationMutator.Factory().build()); + strategy.addStrategyModule(new + CarrierReRouteVehicles.Factory(router, network, modeTravelTimes.get(TransportMode.car)).build()); + strategyManager.addStrategy(strategy, null, 0.5); + } + return strategyManager; + } + } + + static class DriversActivityScoring implements SumScoringFunction.BasicScoring, SumScoringFunction.ActivityScoring { + + + private double score; + + public DriversActivityScoring() { + super(); + } + + @Override + public void finish() { + } + + @Override + public double getScore() { + return score; + } + + @Override + public void handleFirstActivity(Activity act) { + handleActivity(act); + } + + @Override + public void handleActivity(Activity act) { + if (act instanceof FreightActivity) { + double actStartTime = act.getStartTime().seconds(); + + // log.info(act + " start: " + Time.writeTime(actStartTime)); + TimeWindow tw = ((FreightActivity) act).getTimeWindow(); + if (actStartTime > tw.getEnd()) { + double missedTimeWindowPenalty = 0.01; + double penalty_score = (-1) * (actStartTime - tw.getEnd()) * missedTimeWindowPenalty; + if (!(penalty_score <= 0.0)) + throw new AssertionError("penalty score must be negative"); + // log.info("penalty " + penalty_score); + score += penalty_score; + + } + double timeParameter = 0.008; + double actTimeCosts = (act.getEndTime().seconds() - actStartTime) * timeParameter; + // log.info("actCosts " + actTimeCosts); + if (!(actTimeCosts >= 0.0)) + throw new AssertionError("actTimeCosts must be positive"); + score += actTimeCosts * (-1); + } + } + + @Override + public void handleLastActivity(Activity act) { + handleActivity(act); + } + + } + + static class DriversLegScoring implements SumScoringFunction.BasicScoring, SumScoringFunction.LegScoring { + + // private static final Logger log = Logger.getLogger(DriversLegScoring.class); + + private double score = 0.0; + private final Network network; + private final Carrier carrier; + private final Set employedVehicles; + + public DriversLegScoring(Carrier carrier, Network network) { + super(); + this.network = network; + this.carrier = carrier; + employedVehicles = new HashSet<>(); + } + + @Override + public void finish() { + + } + + @Override + public double getScore() { + return score; + } + + private double getTimeParameter(CarrierVehicle vehicle) { + return vehicle.getType().getCostInformation().getCostsPerSecond(); + } + + private double getDistanceParameter(CarrierVehicle vehicle) { + return vehicle.getType().getCostInformation().getCostsPerMeter(); + } + + @Override + public void handleLeg(Leg leg) { + if (leg.getRoute() instanceof NetworkRoute nRoute) { + Id vehicleId = nRoute.getVehicleId(); + CarrierVehicle vehicle = CarriersUtils.getCarrierVehicle(carrier, vehicleId); + Gbl.assertNotNull(vehicle); + employedVehicles.add(vehicle); + double distance = 0.0; + if (leg.getRoute() instanceof NetworkRoute) { + Link startLink = network.getLinks().get(leg.getRoute().getStartLinkId()); + distance += startLink.getLength(); + for (Id linkId : ((NetworkRoute) leg.getRoute()).getLinkIds()) { + distance += network.getLinks().get(linkId).getLength(); + } + distance += network.getLinks().get(leg.getRoute().getEndLinkId()).getLength(); + } + double distanceCosts = distance * getDistanceParameter(vehicle); + if (!(distanceCosts >= 0.0)) + throw new AssertionError("distanceCosts must be positive"); + score += (-1) * distanceCosts; + double timeCosts = leg.getTravelTime().seconds() * getTimeParameter(vehicle); + if (!(timeCosts >= 0.0)) + throw new AssertionError("distanceCosts must be positive"); + score += (-1) * timeCosts; + } + } + } + + static class VehicleEmploymentScoring implements SumScoringFunction.BasicScoring { + + private final Carrier carrier; + + public VehicleEmploymentScoring(Carrier carrier) { + super(); + this.carrier = carrier; + } + + @Override + public void finish() { + + } + + @Override + public double getScore() { + double score = 0.; + CarrierPlan selectedPlan = carrier.getSelectedPlan(); + if (selectedPlan == null) + return 0.; + for (ScheduledTour tour : selectedPlan.getScheduledTours()) { + if (!tour.getTour().getTourElements().isEmpty()) { + score += (-1) * tour.getVehicle().getType().getCostInformation().getFixedCosts(); + } + } + return score; + } + + } + + /** + * Creates the probability distribution for the tour start times for the day. + * The values are given in [h] and have an upperBound. + * Data source: KiD 2002 + * + * @return + */ + + private ValueSelectorUnderGivenProbability createTourStartTimeDistribution(String smallScaleCommercialTrafficType) { + + List tourStartProbabilityDistribution = new ArrayList<>(); + if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.commercialPersonTraffic.toString())) { + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "1", 0.002)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("1", "2", 0.001)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("2", "3", 0.001)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("3", "4", 0.002)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("4", "5", 0.008)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("5", "6", 0.031)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("6", "7", 0.144)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("7", "8", 0.335)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("8", "9", 0.182)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("9", "10", 0.108)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "11", 0.057)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("11", "12", 0.032)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("12", "13", 0.021)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("13", "14", 0.021)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("14", "15", 0.019)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("15", "16", 0.012)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("16", "17", 0.009)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("17", "18", 0.006)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("18", "19", 0.004)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("19", "20", 0.003)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "21", 0.001)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("22", "23", 0.001)); + } else if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.goodsTraffic.toString())) { + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "1", 0.008)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("1", "2", 0.003)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("2", "3", 0.008)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("3", "4", 0.012)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("4", "5", 0.028)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("5", "6", 0.052)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("6", "7", 0.115)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("7", "8", 0.222)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("8", "9", 0.197)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("9", "10", 0.14)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "11", 0.076)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("11", "12", 0.035)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("12", "13", 0.022)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("13", "14", 0.022)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("14", "15", 0.021)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("15", "16", 0.014)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("16", "17", 0.008)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("17", "18", 0.005)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("18", "19", 0.004)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("19", "20", 0.002)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "21", 0.001)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("21", "22", 0.001)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("22", "23", 0.002)); + tourStartProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("23", "24", 0.001)); + + } + return new ValueSelectorUnderGivenProbability(tourStartProbabilityDistribution, rnd); + } + + /** + * Creates the probability distribution for the tour duration for the day. + * The values are given in [h] and have an upperBound. + * Data source: KiD 2002 + * + * @return + */ + private ValueSelectorUnderGivenProbability createTourDurationTimeDistribution(String smallScaleCommercialTrafficType) { + List tourDurationProbabilityDistribution = new ArrayList<>(); + if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.commercialPersonTraffic.toString())) { + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "1", 0.14)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("1", "2", 0.066)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("2", "3", 0.056)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("3", "4", 0.052)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("4", "5", 0.061)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("5", "6", 0.063)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("6", "7", 0.07)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("7", "8", 0.086)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("8", "9", 0.14)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("9", "10", 0.122)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "11", 0.068)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("11", "12", 0.031)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("12", "13", 0.018)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("13", "14", 0.01)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("14", "15", 0.006)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("15", "16", 0.003)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("16", "17", 0.002)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("17", "18", 0.001)); + } else if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.goodsTraffic.toString())) { + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "1", 0.096)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("1", "2", 0.074)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("2", "3", 0.065)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("3", "4", 0.071)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("4", "5", 0.086)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("5", "6", 0.084)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("6", "7", 0.084)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("7", "8", 0.101)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("8", "9", 0.118)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("9", "10", 0.092)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "11", 0.048)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("11", "12", 0.027)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("12", "13", 0.015)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("13", "14", 0.011)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("14", "15", 0.006)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("15", "16", 0.004)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("16", "17", 0.002)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("17", "18", 0.001)); + tourDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("18", "19", 0.001)); + } + + return new ValueSelectorUnderGivenProbability(tourDurationProbabilityDistribution, rnd); + } + + /** + * Creates the probability distribution for the duration of the services. + * The values are given in [min] and have an upperBound. + * Data source: KiD 2002 + * + * @param smallScaleCommercialTrafficType + * @return + */ + private Map createStopDurationTimeDistributionPerCategory(String smallScaleCommercialTrafficType) { + + Map stopDurationProbabilityDistribution = new HashMap<>(); + if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.commercialPersonTraffic.toString())) { + List singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.098)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.17)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.127)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.11)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.17)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.076)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.057)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.045)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.064)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.034)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("720", "840", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.054)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.164)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.153)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.087)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.12)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.055)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.044)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.069)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.132)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.058)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("720", "840", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.13)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.324)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.178)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.108)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.097)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.034)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.027)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.029)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.008)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("720", "840", 0.001)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest",null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.178)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.301)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.192)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.104)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.092)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.043)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.013)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("720", "840", 0.001)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail",null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.144)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.372)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.203)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.069)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.038)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.005)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.005)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels",null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "30", 0.196)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "60", 0.292)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "90", 0.19)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "180", 0.105)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.034)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "360", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("360", "420", 0.013)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "480", 0.019)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("480", "540", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "600", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("600", "720", 0.004)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("720", "840", 0.001)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest",null), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + } else if (smallScaleCommercialTrafficType.equals(SmallScaleCommercialTrafficType.goodsTraffic.toString())) { + List singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.038)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.049)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.052)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.125)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.167)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.113)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.056)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.04)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.05)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.043)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.168)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.149)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.081)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.168)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.068)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.068)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.019)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.036)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.098)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.036)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.042)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.124)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.085)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.144)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.105)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.052)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.072)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.052)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.023)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.033)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("660", "780", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.071)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.143)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.429)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.179)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.107)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.071)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.395)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.158)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.132)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.105)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.079)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.053)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Primary Sector", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.033)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.064)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.109)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.088)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.095)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.105)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.114)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.053)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.088)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.038)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.051)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.015)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.027)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.061)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.045)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.068)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.083)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.114)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.146)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.058)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.114)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.036)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.022)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.065)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.023)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.04)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.074)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.09)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.086)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.069)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.113)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.135)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.071)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.008)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.044)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.041)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.03)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.021)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.075)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.022)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.036)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.055)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.236)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.073)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.164)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.091)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.109)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.055)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.055)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.055)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.018)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.163)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.21)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.165)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.125)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.095)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.04)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.03)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.008)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.002)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.004)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.008)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.004)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Construction", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.072)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.123)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.113)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.137)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.081)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.087)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.079)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.032)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.021)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "780", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.14)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.115)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.133)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.098)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.071)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.067)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.038)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.027)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.011)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.051)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.214)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.146)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.129)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.10)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.072)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.083)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.063)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.054)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.022)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.008)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "900", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.163)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.224)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.153)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.061)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.173)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.082)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.122)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.01)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.003)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.195)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.225)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.16)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.143)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.089)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.075)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.031)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.048)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.003)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "660", 0.009)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Secondary Sector Rest", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.057)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.108)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.133)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.133)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.11)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.064)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.104)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.049)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.015)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.015)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.003)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.005)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.084)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.119)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.183)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.076)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.085)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.124)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.069)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.057)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.041)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.002)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.004)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("780", "900", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.103)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.23)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.193)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.08)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.065)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.071)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.072)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.044)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.054)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.035)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.013)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.003)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.179)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.245)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.123)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.075)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.038)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.019)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.019)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.066)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.063)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.142)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.165)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.135)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.122)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.033)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.086)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.043)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.023)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Retail", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.159)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.173)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.173)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.088)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.115)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.071)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.051)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.041)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.031)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.007)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.292)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.135)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.197)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.051)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.079)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.022)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.045)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.056)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.034)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.022)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.092)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.111)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.224)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.173)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.09)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.103)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.045)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.028)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.056)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.019)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.006)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.146)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.098)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.146)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.195)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.268)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.037)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.012)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.042)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.121)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.133)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.144)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.144)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.104)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.121)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.046)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.005)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "900", 0.008)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Traffic/Parcels", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.061)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.125)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.125)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.124)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.08)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.046)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.013)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.004)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.005)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.081)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.101)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.109)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.124)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.065)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.109)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.124)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.097)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.032)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.022)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.017)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.003)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.008)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.052)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.114)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.155)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.111)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.151)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.125)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.043)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.051)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.026)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.016)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.009)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("660", "780", 0.003)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.082)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.449)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.061)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.163)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.102)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.02)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.02)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.151)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.296)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.156)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.065)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.121)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.05)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.075)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.015)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.005)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.005)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Employee Tertiary Sector Rest", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + + + // because no data für private persons; use average numbers of all employee categories + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.056)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.084)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.095)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.118)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.12)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.096)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.112)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.083)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.095)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.045)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.033)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.022)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.018)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.004)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Inhabitants", "vehTyp1"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.077)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.093)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.103)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.092)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.098)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.091)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.108)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.092)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.095)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.043)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.035)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.011)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.021)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.007)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Inhabitants", "vehTyp2"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.06)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.141)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.152)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.107)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.094)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.087)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.089)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.067)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.06)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.037)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.023)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.025)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.015)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.012)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.006)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("660", "780", 0.001)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Inhabitants", "vehTyp3"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.11)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.12)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.144)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.151)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.129)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.062)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.079)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.041)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.031)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.019)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "540", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("540", "660", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Inhabitants", "vehTyp4"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + + singleStopDurationProbabilityDistribution = new ArrayList<>(); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("0", "10", 0.024)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("10", "20", 0.099)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("20", "30", 0.147)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("30", "40", 0.17)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("40", "50", 0.133)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("50", "60", 0.108)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("60", "75", 0.116)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("75", "90", 0.058)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("90", "120", 0.075)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("120", "150", 0.03)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("150", "180", 0.01)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("180", "240", 0.014)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("240", "300", 0.005)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("300", "420", 0.004)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("420", "660", 0.007)); + singleStopDurationProbabilityDistribution.add(new ValueSelectorUnderGivenProbability.ProbabilityForValue("660", "900", 0.002)); + stopDurationProbabilityDistribution.put(makeStopDurationGoodTrafficKey("Inhabitants", "vehTyp5"), + new ValueSelectorUnderGivenProbability(singleStopDurationProbabilityDistribution, rnd)); + } + return stopDurationProbabilityDistribution; + } + + private record StopDurationGoodTrafficKey(String employeeCategory, String vehicleType) { + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StopDurationGoodTrafficKey other = (StopDurationGoodTrafficKey) obj; + if (employeeCategory == null) { + if (other.employeeCategory != null) + return false; + } else if (!employeeCategory.equals(other.employeeCategory)) + return false; + if (vehicleType == null) { + return other.vehicleType == null; + } else return vehicleType.equals(other.vehicleType); + } + } + private StopDurationGoodTrafficKey makeStopDurationGoodTrafficKey(String employeeCategory, String vehicleType) { + return new StopDurationGoodTrafficKey(employeeCategory, vehicleType); + } +} 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 7e9691cea23..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); } @@ -210,7 +210,8 @@ private static void createResultingDataForLanduseInZones( totalEmployeesInCategoriesPerZone.get(investigationArea).mergeDouble(zoneId, resultingNumberPerCategory, Double::sum); } - resultingDataPerZone.get(zoneId).mergeDouble("Employee", + if (totalEmployeesInCategoriesPerZone.get(investigationArea).getDouble(zoneId) != 0) + resultingDataPerZone.get(zoneId).mergeDouble("Employee", totalEmployeesInCategoriesPerZone.get(investigationArea).getDouble(zoneId), Double::sum); } } @@ -327,13 +328,16 @@ static void analyzeBuildingType(List buildingsFeatures, log.info("Investigate Building " + countOSMObjects + " of " + buildingsFeatures.size() + " buildings: " + Math.round((double) countOSMObjects / buildingsFeatures.size() * 100) + " %"); + if (singleBuildingFeature.getFeatureType().indexOf("levels") == -1) + throw new RuntimeException("The buildings object should contain the attribute 'levels'."); + List categoriesOfBuilding = new ArrayList(); String[] buildingTypes; Coord centroidPointOfBuildingPolygon = MGC .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 c778a14bef7..3d8563424a1 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java @@ -33,24 +33,27 @@ import org.apache.commons.csv.CSVRecord; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.*; import org.matsim.application.options.ShpOptions; import org.matsim.application.options.ShpOptions.Index; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.carrier.CarrierCapabilities.FleetSize; -import org.matsim.contrib.freight.carrier.Tour.Pickup; -import org.matsim.contrib.freight.carrier.Tour.ServiceActivity; -import org.matsim.contrib.freight.carrier.Tour.TourElement; -import org.matsim.contrib.freight.controler.FreightUtils; -import org.matsim.contrib.freight.jsprit.MatsimJspritFactory; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; +import org.matsim.freight.carriers.Tour.Pickup; +import org.matsim.freight.carriers.Tour.ServiceActivity; +import org.matsim.freight.carriers.Tour.TourElement; +import org.matsim.freight.carriers.jsprit.MatsimJspritFactory; import org.matsim.core.gbl.MatsimRandom; +import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.utils.io.IOUtils; +import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.Vehicles; import java.io.BufferedWriter; import java.io.IOException; @@ -65,7 +68,6 @@ * Utils for the SmallScaleFreightTraffic * * @author Ricardo Ewert - * */ public class SmallScaleCommercialTrafficUtils { @@ -74,6 +76,7 @@ public class SmallScaleCommercialTrafficUtils { /** * Creates and return the Index of the zones shape. + * * @return indexZones */ static Index getIndexZones(Path shapeFileZonePath, String shapeCRS) { @@ -84,6 +87,7 @@ static Index getIndexZones(Path shapeFileZonePath, String shapeCRS) { /** * Creates and return the Index of the landuse shape. + * * @return indexLanduse */ static Index getIndexLanduse(Path shapeFileLandusePath, String shapeCRS) { @@ -96,24 +100,60 @@ static Index getIndexLanduse(Path shapeFileLandusePath, String shapeCRS) { * Writes a csv file with result of the distribution per zone of the input data. */ static void writeResultOfDataDistribution(HashMap> resultingDataPerZone, - Path outputFileInOutputFolder, HashMap zoneIdNameConnection) - throws IOException { + Path outputFileInOutputFolder, HashMap zoneIdNameConnection) + throws IOException { writeCSVWithCategoryHeader(resultingDataPerZone, outputFileInOutputFolder, zoneIdNameConnection); log.info("The data distribution is finished and written to: " + outputFileInOutputFolder); } + static Id findNearestPossibleLink(String zone, ArrayList noPossibleLinks, Map, Link>> regionLinksMap, + Id newLink, Coord centroidPointOfBuildingPolygon, int numberOfPossibleLinks) { + double minDistance = Double.MAX_VALUE; + searchLink: + for (Link possibleLink : regionLinksMap.get(zone).values()) { + if (possibleLink.getToNode().getOutLinks() == null) + continue; + if (noPossibleLinks != null && numberOfPossibleLinks > noPossibleLinks.size()) + for (String depotLink : noPossibleLinks) { + if (depotLink.equals(possibleLink.getId().toString()) + || (NetworkUtils.findLinkInOppositeDirection(possibleLink) != null && depotLink.equals( + NetworkUtils.findLinkInOppositeDirection(possibleLink).getId().toString()))) + continue searchLink; + } + double distance = NetworkUtils.getEuclideanDistance(centroidPointOfBuildingPolygon, + (Coord) possibleLink.getAttributes().getAttribute("newCoord")); + if (distance < minDistance) { + newLink = possibleLink.getId(); + minDistance = distance; + } + } + if (newLink == null && numberOfPossibleLinks > 0) { + for (Link possibleLink : regionLinksMap.get(zone).values()) { + double distance = NetworkUtils.getEuclideanDistance(centroidPointOfBuildingPolygon, + (Coord) possibleLink.getAttributes().getAttribute("newCoord")); + if (distance < minDistance) { + newLink = possibleLink.getId(); + minDistance = distance; + } + } + } + + return newLink; + } + /** * Writer of data distribution data. */ private static void writeCSVWithCategoryHeader(HashMap> resultingDataPerZone, - Path outputFileInInputFolder, HashMap zoneIdNameConnection) throws MalformedURLException { + Path outputFileInInputFolder, + HashMap zoneIdNameConnection) throws MalformedURLException { BufferedWriter writer = IOUtils.getBufferedWriter(outputFileInInputFolder.toUri().toURL(), - StandardCharsets.UTF_8, true); + StandardCharsets.UTF_8, true); try { - String[] header = new String[] { "areaID", "areaName", "Inhabitants", "Employee", "Employee Primary Sector", - "Employee Construction", "Employee Secondary Sector Rest", "Employee Retail", - "Employee Traffic/Parcels", "Employee Tertiary Sector Rest" }; + String[] header = new String[]{"areaID", "areaName", "Inhabitants", "Employee", "Employee Primary Sector", + "Employee Construction", "Employee Secondary Sector Rest", "Employee Retail", + "Employee Traffic/Parcels", "Employee Tertiary Sector Rest"}; JOIN.appendTo(writer, header); writer.write("\n"); for (String zone : resultingDataPerZone.keySet()) { @@ -131,7 +171,7 @@ private static void writeCSVWithCategoryHeader(HashMap idCounter = new HashMap<>(); Population populationFromCarrier = (Population) scenario.getScenarioElement("allpersons"); + Vehicles allVehicles = VehicleUtils.getOrCreateAllvehicles(scenario); + for (Person person : populationFromCarrier.getPersons().values()) { Plan plan = popFactory.createPlan(); String carrierName = person.getId().toString().split("freight_")[1].split("_veh_")[0]; - Carrier relatedCarrier = FreightUtils.addOrGetCarriers(scenario).getCarriers() - .get(Id.create(carrierName, Carrier.class)); + Carrier relatedCarrier = CarriersUtils.addOrGetCarriers(scenario).getCarriers() + .get(Id.create(carrierName, Carrier.class)); String subpopulation = relatedCarrier.getAttributes().getAttribute("subpopulation").toString(); final String mode; - if (subpopulation.contains("businessTraffic")) + if (subpopulation.contains("commercialPersonTraffic")) mode = "car"; - else if (subpopulation.contains("freightTraffic")) + else if (subpopulation.contains("goodsTraffic")) mode = "freight"; else mode = relatedCarrier.getAttributes().getAttribute("networkMode").toString(); @@ -168,13 +210,16 @@ else if (subpopulation.contains("freightTraffic")) if (tourElement instanceof Activity activity) { activity.setCoord( - scenario.getNetwork().getLinks().get(activity.getLinkId()).getFromNode().getCoord()); - if (!activity.getType().equals("start")) - activity.setEndTimeUndefined(); - else + scenario.getNetwork().getLinks().get(activity.getLinkId()).getFromNode().getCoord()); + if (activity.getType().equals("start")) { tourStartTime = activity.getEndTime().seconds(); - if (activity.getType().equals("end")) + activity.setType("commercial_start"); + } else + activity.setEndTimeUndefined(); + if (activity.getType().equals("end")) { activity.setStartTime(tourStartTime + 8 * 3600); + activity.setType("commercial_end"); + } plan.addActivity(activity); } if (tourElement instanceof Leg) { @@ -183,40 +228,48 @@ else if (subpopulation.contains("freightTraffic")) } } - String key = String.format("%s_%s_%s", relatedCarrier.getAttributes().getAttribute("tourStartArea"), relatedCarrier.getAttributes().getAttribute("purpose"), subpopulation); + String key = String.format("%s_%s_%s", subpopulation, relatedCarrier.getAttributes().getAttribute("tourStartArea"), + relatedCarrier.getAttributes().getAttribute("purpose")); //TODO long id = idCounter.computeIfAbsent(key, (k) -> new AtomicLong()).getAndIncrement(); - Person newPerson = popFactory.createPerson(Id.createPersonId(key+"_"+id)); + Person newPerson = popFactory.createPerson(Id.createPersonId(key + "_" + id)); newPerson.addPlan(plan); PopulationUtils.putSubpopulation(newPerson, subpopulation); newPerson.getAttributes().putAttribute("purpose", - relatedCarrier.getAttributes().getAttribute("purpose")); + relatedCarrier.getAttributes().getAttribute("purpose")); if (relatedCarrier.getAttributes().getAsMap().containsKey("tourStartArea")) newPerson.getAttributes().putAttribute("tourStartArea", - relatedCarrier.getAttributes().getAttribute("tourStartArea")); - VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, (new HashMap<>() { - { - put(mode, (Id.createVehicleId(person.getId().toString()))); - } - })); + relatedCarrier.getAttributes().getAttribute("tourStartArea")); + + Id vehicleId = Id.createVehicleId(person.getId().toString()); + + VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, Map.of(mode, vehicleId)); + VehicleUtils.insertVehicleTypesIntoAttributes(newPerson, Map.of(mode, allVehicles.getVehicles().get(vehicleId).getType().getId())); + population.addPerson(newPerson); } String outputPopulationFile; if (nameOutputPopulation == null) - outputPopulationFile = output.toString() + "/"+modelName +"_" + usedTrafficType + "_" + sampleName + "pct_plans.xml.gz"; - else { - if (numberOfPlanVariantsPerAgent > 1) - CreateDifferentPlansForFreightPopulation.createPlanVariantsForPopulations("changeStartingTimes", population, numberOfPlanVariantsPerAgent, 6*3600, 14*3600, 8*3600); - else if (numberOfPlanVariantsPerAgent < 1) - log.warn("You selected " + numberOfPlanVariantsPerAgent + " of different plan variants per agent. This is invalid. Please check the input parameter. The default is 1 and is now set for the output."); + if (smallScaleCommercialTrafficType.equals("completeSmallScaleCommercialTraffic")) + outputPopulationFile = output.toString() + "/" + modelName + "_" + "smallScaleCommercialTraffic" + "_" + sampleName + "pct_plans.xml.gz"; + else + outputPopulationFile = output.toString() + "/" + modelName + "_" + smallScaleCommercialTrafficType + "_" + sampleName + "pct_plans.xml.gz"; + else outputPopulationFile = output.toString() + "/" + nameOutputPopulation; - } - PopulationUtils.writePopulation(population,outputPopulationFile); + if (numberOfPlanVariantsPerAgent > 1) +// CreateDifferentPlansForFreightPopulation.createMorePlansWithDifferentStartTimes(population, numberOfPlanVariantsPerAgent, 6*3600, 14*3600, 8*3600); + CreateDifferentPlansForFreightPopulation.createMorePlansWithDifferentActivityOrder(population, numberOfPlanVariantsPerAgent); + else if (numberOfPlanVariantsPerAgent < 1) + log.warn( + "You selected " + numberOfPlanVariantsPerAgent + " of different plan variants per agent. This is invalid. Please check the input parameter. The default is 1 and is now set for the output."); + + PopulationUtils.writePopulation(population, outputPopulationFile); scenario.getPopulation().getPersons().clear(); } + static String getSampleNameOfOutputFolder(double sample) { String sampleName; if ((sample * 100) % 1 == 0) @@ -225,23 +278,24 @@ static String getSampleNameOfOutputFolder(double sample) { sampleName = String.valueOf((sample * 100)); return sampleName; } + /** * Reads existing scenarios and add them to the scenario. If the scenario is - * part of the freightTraffic or businessTraffic the demand of the existing + * part of the goodsTraffic or commercialPersonTraffic the demand of the existing * 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, - Map, Link>> regionLinksMap) throws Exception { + 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)); + .setSkipHeaderRecord(true).build().parse(IOUtils.getBufferedReader(locationOfExistingModels)); for (CSVRecord record : parse) { String modelName = record.get("model"); double sampleSizeExistingScenario = Double.parseDouble(record.get("sampleSize")); - String modelTrafficType = record.get("trafficType"); + String modelTrafficType = record.get("smallScaleCommercialTrafficType"); final Integer modelPurpose; if (!Objects.equals(record.get("purpose"), "")) modelPurpose = Integer.parseInt(record.get("purpose")); @@ -254,29 +308,28 @@ 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'"); + + " no carrierFile exists. The carrierFile should have the name 'output_carriers.xml.gz'"); if (!Files.exists(scenarioLocation.resolve("vehicleTypes.xml.gz"))) throw new Exception("For the existing model " + modelName - + " no vehicleTypesFile exists. The vehicleTypesFile should have the name 'vehicleTypes.xml.gz'"); + + " no vehicleTypesFile exists. The vehicleTypesFile should have the name 'vehicleTypes.xml.gz'"); log.info("Integrating existing scenario: " + modelName); CarrierVehicleTypes readVehicleTypes = new CarrierVehicleTypes(); CarrierVehicleTypes usedVehicleTypes = new CarrierVehicleTypes(); new CarrierVehicleTypeReader(readVehicleTypes) - .readFile(scenarioLocation.resolve("vehicleTypes.xml.gz").toString()); + .readFile(scenarioLocation.resolve("vehicleTypes.xml.gz").toString()); Carriers carriers = new Carriers(); new CarrierPlanXmlReader(carriers, readVehicleTypes) - .readFile(scenarioLocation.resolve("output_carriers.xml.gz").toString()); + .readFile(scenarioLocation.resolve("output_carriers.xml.gz").toString()); if (sampleSizeExistingScenario < sampleScenario) throw new Exception("The sample size of the existing scenario " + modelName - + "is smaller than the sample size of the scenario. No upscaling for existing scenarios implemented."); + + "is smaller than the sample size of the scenario. No upscaling for existing scenarios implemented."); double sampleFactor = sampleScenario / sampleSizeExistingScenario; @@ -284,7 +337,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in for (Carrier carrier : carriers.getCarriers().values()) { if (!carrier.getPlans().isEmpty()) numberOfToursExistingScenario = numberOfToursExistingScenario - + carrier.getSelectedPlan().getScheduledTours().size(); + + carrier.getSelectedPlan().getScheduledTours().size(); } int sampledNumberOfToursExistingScenario = (int) Math.round(numberOfToursExistingScenario * sampleFactor); List carrierToRemove = new ArrayList<>(); @@ -292,10 +345,10 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in double roundingError = 0.; log.info("The existing scenario " + modelName + " is a " + (int) (sampleSizeExistingScenario * 100) - + "% scenario and has " + numberOfToursExistingScenario + " tours"); + + "% scenario and has " + numberOfToursExistingScenario + " tours"); log.info("The existing scenario " + modelName + " will be sampled down to the scenario sample size of " - + (int) (sampleScenario * 100) + "% which results in " + sampledNumberOfToursExistingScenario - + " tours."); + + (int) (sampleScenario * 100) + "% which results in " + sampledNumberOfToursExistingScenario + + " tours."); int numberOfAnalyzedTours = 0; for (Carrier carrier : carriers.getCarriers().values()) { @@ -325,7 +378,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in } // last carrier with scheduled tours if (numberOfAnalyzedTours == numberOfToursExistingScenario - && remainedTours != sampledNumberOfToursExistingScenario) { + && remainedTours != sampledNumberOfToursExistingScenario) { numberOfRemainingTours = sampledNumberOfToursExistingScenario - remainedTours; numberOfToursToRemove = numberOfOriginalTours - numberOfRemainingTours; remainedTours = remainedTours + numberOfRemainingTours; @@ -344,7 +397,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in } // remove services/shipments from removed tours - if (carrier.getServices().size() != 0) { + if (!carrier.getServices().isEmpty()) { for (ScheduledTour removedTour : toursToRemove) { for (TourElement tourElement : removedTour.getTour().getTourElements()) { if (tourElement instanceof ServiceActivity service) { @@ -352,7 +405,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in } } } - } else if (carrier.getShipments().size() != 0) { + } else if (!carrier.getShipments().isEmpty()) { for (ScheduledTour removedTour : toursToRemove) { for (TourElement tourElement : removedTour.getTour().getTourElements()) { if (tourElement instanceof Pickup pickup) { @@ -366,13 +419,13 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in if (carrier.getCarrierCapabilities().getFleetSize().equals(FleetSize.FINITE)) { for (ScheduledTour removedTour : toursToRemove) { carrier.getCarrierCapabilities().getCarrierVehicles() - .remove(removedTour.getVehicle().getId()); + .remove(removedTour.getVehicle().getId()); } } else if (carrier.getCarrierCapabilities().getFleetSize().equals(FleetSize.INFINITE)) { carrier.getCarrierCapabilities().getCarrierVehicles().clear(); for (ScheduledTour tour : carrier.getSelectedPlan().getScheduledTours()) { carrier.getCarrierCapabilities().getCarrierVehicles().put(tour.getVehicle().getId(), - tour.getVehicle()); + tour.getVehicle()); } } List vehicleTypesToRemove = new ArrayList<>(); @@ -382,7 +435,7 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in if (vehicle.getType().equals(existingVehicleType)) { vehicleTypeNeeded = true; usedVehicleTypes.getVehicleTypes().put(existingVehicleType.getId(), - existingVehicleType); + existingVehicleType); } } if (!vehicleTypeNeeded) @@ -392,17 +445,17 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in } // carriers without solutions else { - if (carrier.getServices().size() != 0) { + if (!carrier.getServices().isEmpty()) { int numberOfServicesToRemove = carrier.getServices().size() - - (int) Math.round(carrier.getServices().size() * sampleFactor); + - (int) Math.round(carrier.getServices().size() * sampleFactor); for (int i = 0; i < numberOfServicesToRemove; i++) { Object[] services = carrier.getServices().keySet().toArray(); carrier.getServices().remove(services[MatsimRandom.getRandom().nextInt(services.length)]); } } - if (carrier.getShipments().size() != 0) { + if (!carrier.getShipments().isEmpty()) { int numberOfShipmentsToRemove = carrier.getShipments().size() - - (int) Math.round(carrier.getShipments().size() * sampleFactor); + - (int) Math.round(carrier.getShipments().size() * sampleFactor); for (int i = 0; i < numberOfShipmentsToRemove; i++) { Object[] shipments = carrier.getShipments().keySet().toArray(); carrier.getShipments().remove(shipments[MatsimRandom.getRandom().nextInt(shipments.length)]); @@ -411,11 +464,11 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in } } carrierToRemove.forEach(carrier -> carriers.getCarriers().remove(carrier.getId())); - FreightUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().putAll(usedVehicleTypes.getVehicleTypes()); + CarriersUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().putAll(usedVehicleTypes.getVehicleTypes()); carriers.getCarriers().values().forEach(carrier -> { - Carrier newCarrier = CarrierUtils - .createCarrier(Id.create(modelName + "_" + carrier.getId().toString(), Carrier.class)); + Carrier newCarrier = CarriersUtils + .createCarrier(Id.create(modelName + "_" + carrier.getId().toString(), Carrier.class)); newCarrier.getAttributes().putAttribute("subpopulation", modelTrafficType); if (modelPurpose != null) newCarrier.getAttributes().putAttribute("purpose", modelPurpose); @@ -425,9 +478,9 @@ static void readExistingModels(Scenario scenario, double sampleScenario, Path in newCarrier.getAttributes().putAttribute("vehicleType", vehicleType); newCarrier.setCarrierCapabilities(carrier.getCarrierCapabilities()); - if (carrier.getServices().size() > 0) + if (!carrier.getServices().isEmpty()) newCarrier.getServices().putAll(carrier.getServices()); - else if (carrier.getShipments().size() > 0) + else if (!carrier.getShipments().isEmpty()) newCarrier.getShipments().putAll(carrier.getShipments()); if (carrier.getSelectedPlan() != null) { newCarrier.setSelectedPlan(carrier.getSelectedPlan()); @@ -439,27 +492,28 @@ else if (carrier.getShipments().size() > 0) startAreas.add(tourStartZone); } newCarrier.getAttributes().putAttribute("tourStartArea", - String.join(";", startAreas)); + String.join(";", startAreas)); - CarrierUtils.setJspritIterations(newCarrier, 0); + CarriersUtils.setJspritIterations(newCarrier, 0); // recalculate score for selectedPlan VehicleRoutingProblem vrp = MatsimJspritFactory - .createRoutingProblemBuilder(carrier, scenario.getNetwork()).build(); + .createRoutingProblemBuilder(carrier, scenario.getNetwork()).build(); VehicleRoutingProblemSolution solution = MatsimJspritFactory - .createSolution(newCarrier.getSelectedPlan(), vrp); + .createSolution(newCarrier.getSelectedPlan(), vrp); SolutionCostCalculator solutionCostsCalculator = getObjectiveFunction(vrp, Double.MAX_VALUE); double costs = solutionCostsCalculator.getCosts(solution) * (-1); carrier.getSelectedPlan().setScore(costs); } else { - CarrierUtils.setJspritIterations(newCarrier, CarrierUtils.getJspritIterations(carrier)); + CarriersUtils.setJspritIterations(newCarrier, CarriersUtils.getJspritIterations(carrier)); newCarrier.getCarrierCapabilities().setFleetSize(carrier.getCarrierCapabilities().getFleetSize()); } - FreightUtils.addOrGetCarriers(scenario).getCarriers().put(newCarrier.getId(), newCarrier); + CarriersUtils.addOrGetCarriers(scenario).getCarriers().put(newCarrier.getId(), newCarrier); }); } } - /** Find the zone where the link is located + /** + * Find the zone where the link is located */ static String findZoneOfLink(Id linkId, Map, Link>> regionLinksMap) { for (String area : regionLinksMap.keySet()) { @@ -469,7 +523,8 @@ static String findZoneOfLink(Id linkId, Map, Link return null; } - /** Creates a cost calculator. + /** + * Creates a cost calculator. */ private static SolutionCostCalculator getObjectiveFunction(final VehicleRoutingProblem vrp, final double maxCosts) { @@ -486,19 +541,19 @@ public double getCosts(VehicleRoutingProblemSolution solution) { if (act instanceof BreakActivity) hasBreak = true; costs += vrp.getTransportCosts().getTransportCost(prevAct.getLocation(), act.getLocation(), - prevAct.getEndTime(), route.getDriver(), route.getVehicle()); + prevAct.getEndTime(), route.getDriver(), route.getVehicle()); costs += vrp.getActivityCosts().getActivityCost(act, act.getArrTime(), route.getDriver(), - route.getVehicle()); + route.getVehicle()); prevAct = act; } costs += vrp.getTransportCosts().getTransportCost(prevAct.getLocation(), - route.getEnd().getLocation(), prevAct.getEndTime(), route.getDriver(), route.getVehicle()); + route.getEnd().getLocation(), prevAct.getEndTime(), route.getDriver(), route.getVehicle()); if (route.getVehicle().getBreak() != null) { if (!hasBreak) { // break defined and required but not assigned penalty if (route.getEnd().getArrTime() > route.getVehicle().getBreak().getTimeWindow().getEnd()) { costs += 4 * (maxCosts * 2 + route.getVehicle().getBreak().getServiceDuration() - * route.getVehicle().getType().getVehicleCostParams().perServiceTimeUnit); + * route.getVehicle().getType().getVehicleCostParams().perServiceTimeUnit); } } } diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java index e78265591bb..ada637b06d7 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGeneration.java @@ -27,11 +27,10 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.carrier.Tour.Pickup; -import org.matsim.contrib.freight.carrier.Tour.ServiceActivity; -import org.matsim.contrib.freight.carrier.Tour.TourElement; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.Tour.Pickup; +import org.matsim.freight.carriers.Tour.ServiceActivity; +import org.matsim.freight.carriers.Tour.TourElement; import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.utils.io.IOUtils; @@ -245,23 +244,23 @@ private static void writeCSVTrafficVolume(HashMap, Link>> regionLinksMap, String usedTrafficType, + Map, Link>> regionLinksMap, String smallScaleCommercialTrafficType, HashMap> trafficVolumePerTypeAndZone_start, HashMap> trafficVolumePerTypeAndZone_stop) { - for (Carrier carrier : FreightUtils.addOrGetCarriers(scenario).getCarriers().values()) { + for (Carrier carrier : CarriersUtils.addOrGetCarriers(scenario).getCarriers().values()) { if (!carrier.getAttributes().getAsMap().containsKey("subpopulation") - || !carrier.getAttributes().getAttribute("subpopulation").equals(usedTrafficType)) + || !carrier.getAttributes().getAttribute("subpopulation").equals(smallScaleCommercialTrafficType)) continue; String modeORvehType; - if (usedTrafficType.equals("freightTraffic")) + if (smallScaleCommercialTrafficType.equals("goodsTraffic")) modeORvehType = (String) carrier.getAttributes().getAttribute("vehicleType"); else modeORvehType = "total"; @@ -423,10 +422,10 @@ private static void reduceVolumeForOtherArea( /** * Sets the generation rates based on the IVV 2005 * - * @param trafficType used trafficType (freight or business traffic) + * @param smallScaleCommercialTrafficType used trafficType (freight or business traffic) * @param generationType start or stop rates */ - private static HashMap> setGenerationRates(String trafficType, + private static HashMap> setGenerationRates(String smallScaleCommercialTrafficType, String generationType) { HashMap> generationRates = new HashMap<>(); @@ -436,7 +435,7 @@ private static HashMap> setGenerationRates(Stri HashMap ratesPerPurpose4 = new HashMap<>(); HashMap ratesPerPurpose5 = new HashMap<>(); HashMap ratesPerPurpose6 = new HashMap<>(); - if (trafficType.equals("businessTraffic")) { + if (smallScaleCommercialTrafficType.equals("commercialPersonTraffic")) { if (generationType.equals("start")) { ratesPerPurpose1.put("Inhabitants", 0.0); ratesPerPurpose1.put("Employee", 0.0); @@ -529,7 +528,7 @@ private static HashMap> setGenerationRates(Stri ratesPerPurpose5.put("Employee Tertiary Sector Rest", 0.015); } - } else if (trafficType.equals("freightTraffic")) { + } else if (smallScaleCommercialTrafficType.equals("goodsTraffic")) { if (generationType.equals("start")) { ratesPerPurpose1.put("Inhabitants", 0.0); ratesPerPurpose1.put("Employee", 0.0); @@ -651,18 +650,18 @@ private static HashMap> setGenerationRates(Stri } /** - * Sets the commitment rates based on the IVV 2005 for the freight traffic. The - * commitment rate for the businessTraffic is 1, because mode choice will be + * Sets the commitment rates based on the IVV 2005 for the goodsTraffic. The + * commitment rate for the commercialPersonTraffic is 1, because mode choice will be * done in MATSim. * - * @param trafficType used trafficType (freight or business traffic) + * @param smallScaleCommercialTrafficType used trafficType (freight or business traffic) * @param commitmentType start or stop parameter */ - private static HashMap> setCommitmentRates(String trafficType, + private static HashMap> setCommitmentRates(String smallScaleCommercialTrafficType, String commitmentType) { HashMap> commitmentRates = new HashMap<>(); - if (trafficType.equals("freightTraffic")) { + if (smallScaleCommercialTrafficType.equals("goodsTraffic")) { // the first number is the purpose; second number the vehicle type HashMap ratesPerPurpose1_1 = new HashMap<>(); diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java index a0f47d2bafa..68c03dedb7e 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrix.java @@ -33,9 +33,8 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.jsprit.NetworkBasedTransportCosts; +import org.matsim.freight.carriers.jsprit.NetworkBasedTransportCosts; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; import org.opengis.feature.simple.SimpleFeature; @@ -43,6 +42,7 @@ import java.io.BufferedWriter; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -63,22 +63,22 @@ public class TripDistributionMatrix { private final List zonesFeatures; private final HashMap> trafficVolume_start; private final HashMap> trafficVolume_stop; - private final String trafficType; + private final String smallScaleCommercialTrafficType; private static class TripDistributionMatrixKey { private final String fromZone; private final String toZone; private final String modeORvehType; private final int purpose; - private final String trafficType; + private final String smallScaleCommercialTrafficType; - public TripDistributionMatrixKey(String fromZone, String toZone, String modeORvehType, int purpose, String trafficType) { + public TripDistributionMatrixKey(String fromZone, String toZone, String modeORvehType, int purpose, String smallScaleCommercialTrafficType) { super(); this.fromZone = fromZone; this.toZone = toZone; this.modeORvehType = modeORvehType; this.purpose = purpose; - this.trafficType = trafficType; + this.smallScaleCommercialTrafficType = smallScaleCommercialTrafficType; } public String getFromZone() { @@ -107,7 +107,7 @@ public int hashCode() { result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((toZone == null) ? 0 : toZone.hashCode()); result = prime * result + ((modeORvehType == null) ? 0 : modeORvehType.hashCode()); - result = prime * result + ((trafficType == null) ? 0 : trafficType.hashCode()); + result = prime * result + ((smallScaleCommercialTrafficType == null) ? 0 : smallScaleCommercialTrafficType.hashCode()); return result; } @@ -137,9 +137,9 @@ public boolean equals(Object obj) { return false; } else if (!modeORvehType.equals(other.modeORvehType)) return false; - if (trafficType == null) { - return other.trafficType == null; - } else return trafficType.equals(other.trafficType); + if (smallScaleCommercialTrafficType == null) { + return other.smallScaleCommercialTrafficType == null; + } else return smallScaleCommercialTrafficType.equals(other.smallScaleCommercialTrafficType); } } @@ -234,24 +234,24 @@ public static class Builder { private final List zonesFeatures; private final HashMap> trafficVolume_start; private final HashMap> trafficVolume_stop; - private final String trafficType; + private final String smallScaleCommercialTrafficType; public static Builder newInstance(ShpOptions shpZones, HashMap> trafficVolume_start, HashMap> trafficVolume_stop, - String trafficType) { - return new Builder(shpZones, trafficVolume_start, trafficVolume_stop, trafficType); + String smallScaleCommercialTrafficType) { + return new Builder(shpZones, trafficVolume_start, trafficVolume_stop, smallScaleCommercialTrafficType); } private Builder(ShpOptions shpZones, HashMap> trafficVolume_start, HashMap> trafficVolume_stop, - String trafficType) { + String smallScaleCommercialTrafficType) { super(); this.zonesFeatures = shpZones.readFeatures(); this.trafficVolume_start = trafficVolume_start; this.trafficVolume_stop = trafficVolume_stop; - this.trafficType = trafficType; + this.smallScaleCommercialTrafficType = smallScaleCommercialTrafficType; } public TripDistributionMatrix build() { @@ -263,7 +263,7 @@ private TripDistributionMatrix(Builder builder) { zonesFeatures = builder.zonesFeatures; trafficVolume_start = builder.trafficVolume_start; trafficVolume_stop = builder.trafficVolume_stop; - trafficType = builder.trafficType; + smallScaleCommercialTrafficType = builder.smallScaleCommercialTrafficType; } private final ConcurrentHashMap matrixCache = new ConcurrentHashMap<>(); @@ -281,27 +281,38 @@ private TripDistributionMatrix(Builder builder) { * @param stopZone stop zone * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose - * @param trafficType freight or business traffic + * @param smallScaleCommercialTrafficType goodsTraffic or commercialPersonTraffic * @param regionLinksMap links in each zone */ - void setTripDistributionValue(String startZone, String stopZone, String modeORvehType, Integer purpose, String trafficType, Network network, Map, Link>> regionLinksMap, double resistanceFactor) { + void setTripDistributionValue(String startZone, String stopZone, String modeORvehType, Integer purpose, String smallScaleCommercialTrafficType, Network network, + Map, Link>> regionLinksMap, double resistanceFactor) { double volumeStart = trafficVolume_start.get(TrafficVolumeGeneration.makeTrafficVolumeKey(startZone, modeORvehType)).getDouble(purpose); double volumeStop = trafficVolume_stop.get(TrafficVolumeGeneration.makeTrafficVolumeKey(stopZone, modeORvehType)).getDouble(purpose); - double resistanceValue = getResistanceFunktionValue(startZone, stopZone, network, regionLinksMap, resistanceFactor); - double gravityConstantA = getGravityConstant(stopZone, trafficVolume_start, modeORvehType, purpose, network, regionLinksMap, resistanceFactor); - roundingError.computeIfAbsent(stopZone, (k) -> new Object2DoubleOpenHashMap<>()); - - //Bisher: Gravity model mit fixem Zielverkehr - double volume = gravityConstantA * volumeStart * volumeStop * resistanceValue; - int roundedVolume = (int) Math.floor(volume); - double certainRoundingError = (roundedVolume - volume) * -1; - // roundingError based on stopZone, because gravity model is stopVolume fixed - roundingError.get(stopZone).merge((modeORvehType + "_" + purpose), certainRoundingError, Double::sum); - if (roundingError.get(stopZone).getDouble((modeORvehType + "_" + purpose)) >= 1) { - roundedVolume++; - roundingError.get(stopZone).merge((modeORvehType + "_" + purpose), -1, Double::sum); - } - TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, trafficType); + int roundedVolume; + if (volumeStart != 0 && volumeStop != 0) { + + double resistanceValue = getResistanceFunktionValue(startZone, stopZone, network, regionLinksMap, resistanceFactor); + double gravityConstantA = getGravityConstant(stopZone, trafficVolume_start, modeORvehType, purpose, network, regionLinksMap, + resistanceFactor); + roundingError.computeIfAbsent(stopZone, (k) -> new Object2DoubleOpenHashMap<>()); + + //Bisher: Gravity model mit fixem Zielverkehr + double volume = gravityConstantA * volumeStart * volumeStop * resistanceValue; + roundedVolume = (int) Math.floor(volume); + double certainRoundingError = (roundedVolume - volume) * -1; + // roundingError based on stopZone, because gravity model is stopVolume fixed + roundingError.get(stopZone).merge((modeORvehType + "_" + purpose), certainRoundingError, Double::sum); + if (roundingError.get(stopZone).getDouble((modeORvehType + "_" + purpose)) >= 1) { + roundedVolume++; + roundingError.get(stopZone).merge((modeORvehType + "_" + purpose), -1, Double::sum); + } + if (!listOfZones.contains(startZone)) + listOfZones.add(startZone); + if (!listOfZones.contains(stopZone)) + listOfZones.add(stopZone); + } else + roundedVolume = 0; + TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType); matrixCache.put(matrixKey, roundedVolume); } @@ -312,10 +323,10 @@ void setTripDistributionValue(String startZone, String stopZone, String modeORve * @param stopZone stop zone * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose - * @param trafficType freight or business traffic + * @param smallScaleCommercialTrafficType goodsTraffic or commercialPersonTraffic */ - Integer getTripDistributionValue(String startZone, String stopZone, String modeORvehType, Integer purpose, String trafficType) { - TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, trafficType); + Integer getTripDistributionValue(String startZone, String stopZone, String modeORvehType, Integer purpose, String smallScaleCommercialTrafficType) { + TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType); return matrixCache.get(matrixKey); } @@ -329,7 +340,7 @@ Integer getTripDistributionValue(String startZone, String stopZone, String modeO private Double getResistanceFunktionValue(String startZone, String stopZone, Network network, Map, Link>> regionLinksMap, double resistanceFactor) { //if false the calculation is faster; e.g. for debugging - boolean useNetworkRoutsForResistanceFunction = true; + boolean useNetworkRoutesForResistanceFunction = true; double resistanceFunktionResult; if (netBasedCosts == null) { VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create("vwCaddy", VehicleType.class)); @@ -353,7 +364,7 @@ private Double getResistanceFunktionValue(String startZone, String stopZone, Net distance = 0; } else { - if (useNetworkRoutsForResistanceFunction) { + if (useNetworkRoutesForResistanceFunction) { Location startLocation = Location.newInstance(regionLinksMap.get(startZone).keySet().iterator().next().toString()); Location stopLocation = Location.newInstance(regionLinksMap.get(stopZone).keySet().iterator().next().toString()); Vehicle exampleVehicle = getExampleVehicle(startLocation); @@ -368,7 +379,7 @@ private Double getResistanceFunktionValue(String startZone, String stopZone, Net } } - if (useNetworkRoutsForResistanceFunction) + if (useNetworkRoutesForResistanceFunction) resistanceFunktionResult = Math.exp(-resistanceFactor*travelCosts); else resistanceFunktionResult = Math.exp(-distance); @@ -388,19 +399,20 @@ void clearRoundingError() { for (String modeORvehType : getListOfModesOrVehTypes()) { for (Integer purpose : getListOfPurposes()) { double trafficVolume = trafficVolume_stop.get(TrafficVolumeGeneration.makeTrafficVolumeKey(stopZone, modeORvehType)).getDouble(purpose); - int generatedTrafficVolume = getSumOfServicesForStopZone(stopZone, modeORvehType, purpose, trafficType); + int generatedTrafficVolume = getSumOfServicesForStopZone(stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType); if (trafficVolume > generatedTrafficVolume) { if (generatedTrafficVolume == 0) { TripDistributionMatrixKey matrixKey = makeKey(stopZone, stopZone, modeORvehType, purpose, - trafficType); + smallScaleCommercialTrafficType); matrixCache.replace(matrixKey, matrixCache.get(matrixKey) + 1); - generatedTrafficVolume = getSumOfServicesForStopZone(stopZone, modeORvehType, purpose, trafficType); + generatedTrafficVolume = getSumOfServicesForStopZone(stopZone, modeORvehType, purpose, + smallScaleCommercialTrafficType); } else { ArrayList shuffledZones = new ArrayList<>(getListOfZones()); Collections.shuffle(shuffledZones); for (String startZone : shuffledZones) { TripDistributionMatrixKey matrixKey = makeKey(startZone, stopZone, modeORvehType, - purpose, trafficType); + purpose, smallScaleCommercialTrafficType); if (matrixCache.get(matrixKey) > 0) { matrixCache.replace(matrixKey, matrixCache.get(matrixKey) + 1); break; @@ -429,7 +441,7 @@ private VehicleImpl getExampleVehicle(Location fromId) { * Calculates the gravity constant. * * @param baseZone base zone - * @param trafficVolume freight or business traffic + * @param trafficVolume volume of the traffic * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose * @param regionLinksMap links for each zone @@ -445,9 +457,13 @@ private double getGravityConstant(String baseZone, for (TrafficVolumeKey trafficVolumeKey : trafficVolume.keySet()) { if (trafficVolumeKey.getModeORvehType().equals(modeORvehType)) { double volume = trafficVolume.get(trafficVolumeKey).getDouble(purpose); - double resistanceValue = getResistanceFunktionValue(baseZone, trafficVolumeKey.getZone(), network, + if (volume == 0) + continue; + else { + double resistanceValue = getResistanceFunktionValue(baseZone, trafficVolumeKey.getZone(), network, regionLinksMap, resistanceFactor); - sum = sum + (volume * resistanceValue); + sum = sum + (volume * resistanceValue); + } } } double gravityConstant = 1 / sum; @@ -464,11 +480,11 @@ private double getGravityConstant(String baseZone, * @param toZone to zone * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose - * @param trafficType freight or business traffic + * @param smallScaleCommercialTrafficType goodsTraffic or commercialPersonTraffic * @return TripDistributionMatrixKey */ - private TripDistributionMatrixKey makeKey(String fromZone, String toZone, String modeORvehType, int purpose, String trafficType) { - return new TripDistributionMatrixKey(fromZone, toZone, modeORvehType, purpose, trafficType); + private TripDistributionMatrixKey makeKey(String fromZone, String toZone, String modeORvehType, int purpose, String smallScaleCommercialTrafficType) { + return new TripDistributionMatrixKey(fromZone, toZone, modeORvehType, purpose, smallScaleCommercialTrafficType); } /** @@ -500,13 +516,16 @@ private GravityConstantKey makeGravityKey(String fromZone, String modeOrVehType, * @return listOfZones */ ArrayList getListOfZones() { - if (listOfZones.isEmpty()) - for (TripDistributionMatrixKey key : matrixCache.keySet()) { - if (!listOfZones.contains(key.getFromZone())) - listOfZones.add(key.getFromZone()); - if (!listOfZones.contains(key.getToZone())) - listOfZones.add(key.getToZone()); - } +// int count = 0; +// if (listOfZones.isEmpty()) +// for (TripDistributionMatrixKey key : matrixCache.keySet()) { +// count++; +// System.out.println(count); +// if (!listOfZones.contains(key.getFromZone())) +// listOfZones.add(key.getFromZone()); +// if (!listOfZones.contains(key.getToZone())) +// listOfZones.add(key.getToZone()); +// } return listOfZones; } @@ -544,14 +563,14 @@ ArrayList getListOfPurposes() { * @param startZone start Zone * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose - * @param trafficType freight or business traffic + * @param smallScaleCommercialTrafficType goodsTraffic or commercialPersonTraffic * @return numberOfTrips */ - int getSumOfServicesForStartZone(String startZone, String modeORvehType, int purpose, String trafficType) { + int getSumOfServicesForStartZone(String startZone, String modeORvehType, int purpose, String smallScaleCommercialTrafficType) { int numberOfTrips = 0; ArrayList zones = getListOfZones(); for (String stopZone : zones) - numberOfTrips = numberOfTrips + Math.round(matrixCache.get(makeKey(startZone, stopZone, modeORvehType, purpose, trafficType))); + numberOfTrips = numberOfTrips + Math.round(matrixCache.get(makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType))); return numberOfTrips; } @@ -559,21 +578,21 @@ int getSumOfServicesForStartZone(String startZone, String modeORvehType, int pur * @param stopZone stop zone * @param modeORvehType selected mode or vehicle type * @param purpose selected purpose - * @param trafficType freight or business traffic + * @param smallScaleCommercialTrafficType goodsTraffic or commercialPersonTraffic * @return numberOfTrips */ - int getSumOfServicesForStopZone(String stopZone, String modeORvehType, int purpose, String trafficType) { + int getSumOfServicesForStopZone(String stopZone, String modeORvehType, int purpose, String smallScaleCommercialTrafficType) { int numberOfTrips = 0; ArrayList zones = getListOfZones(); for (String startZone : zones) - numberOfTrips = numberOfTrips + Math.round(matrixCache.get(makeKey(startZone, stopZone, modeORvehType, purpose, trafficType))); + numberOfTrips = numberOfTrips + Math.round(matrixCache.get(makeKey(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType))); return numberOfTrips; } /** * Writes every matrix for each mode and purpose */ - void writeODMatrices(Path output, String trafficType) throws UncheckedIOException, MalformedURLException { + void writeODMatrices(Path output, String smallScaleCommercialTrafficType) throws UncheckedIOException, MalformedURLException { ArrayList usedModesORvehTypes = getListOfModesOrVehTypes(); ArrayList usedZones = getListOfZones(); ArrayList usedPurposes = getListOfPurposes(); @@ -582,7 +601,7 @@ void writeODMatrices(Path output, String trafficType) throws UncheckedIOExceptio for (int purpose : usedPurposes) { Path outputFolder = output.resolve("calculatedData") - .resolve("odMatrix_" + trafficType + "_" + modeORvehType + "_purpose" + purpose + ".csv"); + .resolve("odMatrix_" + smallScaleCommercialTrafficType + "_" + modeORvehType + "_purpose" + purpose + ".csv"); BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.toUri().toURL(), StandardCharsets.UTF_8, true); @@ -598,11 +617,11 @@ void writeODMatrices(Path output, String trafficType) throws UncheckedIOExceptio List row = new ArrayList<>(); row.add(startZone); for (String stopZone : usedZones) { - if (getTripDistributionValue(startZone, stopZone, modeORvehType, purpose, trafficType) == null) + if (getTripDistributionValue(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType) == null) throw new RuntimeException("OD pair is missing; start: " + startZone + "; stop: " - + stopZone + "; modeORvehType: " + modeORvehType + "; purpose: " + purpose + "; trafficType: " + trafficType); + + stopZone + "; modeORvehType: " + modeORvehType + "; purpose: " + purpose + "; smallScaleCommercialTrafficType: " + smallScaleCommercialTrafficType); row.add(String - .valueOf(getTripDistributionValue(startZone, stopZone, modeORvehType, purpose, trafficType))); + .valueOf(getTripDistributionValue(startZone, stopZone, modeORvehType, purpose, smallScaleCommercialTrafficType))); } JOIN.appendTo(writer, row); writer.write("\n"); diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java new file mode 100644 index 00000000000..f0aff20d0e1 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/ValueSelectorUnderGivenProbability.java @@ -0,0 +1,147 @@ +package org.matsim.smallScaleCommercialTrafficGeneration; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** This class creates a distribution under given probabilities for the possible values. + * + * @author: Ricardo Ewert + */ +public class ValueSelectorUnderGivenProbability { + + private int anIntAsSum; + private final List ProbabilityDistribution; + private final Random rnd; + + public ValueSelectorUnderGivenProbability(List ProbabilityDistribution, Random rnd) { + this.anIntAsSum = 0; + this.ProbabilityDistribution = ProbabilityDistribution; + this.rnd = rnd; + } + + + public ProbabilityForValue getNextValueUnderGivenProbability() { + anIntAsSum++; + weightedProbability(); + ProbabilityForValue selectedValue = null; + while (selectedValue == null) { + ProbabilityForValue probabilityForValue = ProbabilityDistribution.get(rnd.nextInt(ProbabilityDistribution.size())); + if (probabilityForValue.getRealizedValues() < probabilityForValue.getExpectedCount()) { + selectedValue = probabilityForValue; + selectedValue.increaseRealizedValue(1); + } + } + return selectedValue; + } + + private void weightedProbability() { + + //List to hold the cumulative probabilities of each value + List cumulativeProbabilities = new ArrayList<>(); + double sum = 0; + //Calculate cumulative probabilities for each value + for (ProbabilityForValue l : ProbabilityDistribution) { + sum += l.getProbability(); + cumulativeProbabilities.add(sum); + } + //Generate a random number between 0 and 1 + double r = Math.random() * sum; + //Select a value based on the cumulative probabilities + String selectedLetter = ProbabilityDistribution.stream() + //Find the first value whose cumulative probability is greater than the random number + .filter(l -> r < cumulativeProbabilities.get(ProbabilityDistribution.indexOf(l))) + .findFirst().get().getValue(); + + //Increment the expectedCount for the selected value + ProbabilityDistribution.stream() + .filter(a -> a.getValue().equals(selectedLetter)) + .findFirst() + .ifPresent(l -> l.setExpectedCount(l.getExpectedCount() + 1)); + + //After 'testCount' loops, print out the number of times each value was selected and the percentage it represents +// for (ProbabilityForValue probabilityForValue : ProbabilityDistribution) { +// System.out.println(probabilityForValue.getValue() +// + " -> expected: " + probabilityForValue.getExpectedCount() +// + "(" + String.format("%.2f", (probabilityForValue.getExpectedCount() * Math.pow(anIntAsSum, +// -1)) * 100) + " %); prob: " + ((double)Math.round(probabilityForValue.getProbability() * 1000)/10) + "%"); +// } + } + public void writeResults(){ + for (ProbabilityForValue probabilityForValue : ProbabilityDistribution) { + System.out.println(probabilityForValue.getValue() + + " -> expected: " + probabilityForValue.getExpectedCount() + + "(" + String.format("%.2f", (probabilityForValue.getExpectedCount() * Math.pow(anIntAsSum, + -1)) * 100) + " %); prob: " + ((double) Math.round(probabilityForValue.getProbability() * 1000) / 10) + "%"); + } + } + + public static class ProbabilityForValue { + + private String value; + private String upperBound; + private int expectedCount; + private double probability; + private int realizedValues; + + public ProbabilityForValue(String value, double probability) { + this.value = value; + this.probability = probability; + this.expectedCount = 0; + this.realizedValues = 0; + } + + + public ProbabilityForValue(String lowerBound, String upperBound, double probability) { + this.value = lowerBound; + this.upperBound = upperBound; + this.probability = probability; + this.expectedCount = 0; + this.realizedValues = 0; + } + + public double getProbability() { + return probability; + } + + public void setProbability(double probability) { + this.probability = probability; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public int getExpectedCount() { + return expectedCount; + } + + public void setExpectedCount(int expectedCount) { + this.expectedCount = expectedCount; + } + + public int getRealizedValues() { + return realizedValues; + } + + public void setRealizedValues(int realizedValues) { + this.realizedValues = realizedValues; + } + + public void increaseRealizedValue(int i) { + setRealizedValues(this.getRealizedValues() + i); + } + + public String getUpperBound() { + return upperBound; + } + + public void setUpperBound(String upperBound) { + this.upperBound = upperBound; + } + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/ApplicationUtilsTest.java b/contribs/application/src/test/java/org/matsim/application/ApplicationUtilsTest.java new file mode 100644 index 00000000000..edb2bcdbba6 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/ApplicationUtilsTest.java @@ -0,0 +1,20 @@ +package org.matsim.application; + +import org.junit.Test; +import org.matsim.application.analysis.TestAnalysis; +import org.matsim.application.analysis.TestDependentAnalysis; +import org.matsim.application.options.ShpOptions; + +import static org.junit.Assert.*; + +public class ApplicationUtilsTest { + + @Test + public void shp() { + + assertTrue(ApplicationUtils.acceptsOptions(TestAnalysis.class, ShpOptions.class)); + + assertFalse(ApplicationUtils.acceptsOptions(TestDependentAnalysis.class, ShpOptions.class)); + + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/CommandRunnerTest.java b/contribs/application/src/test/java/org/matsim/application/CommandRunnerTest.java new file mode 100644 index 00000000000..67f566eb5ae --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/CommandRunnerTest.java @@ -0,0 +1,36 @@ +package org.matsim.application; + +import org.assertj.core.api.Assertions; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.application.analysis.TestDependentAnalysis; +import org.matsim.application.analysis.TestOtherAnalysis; +import org.matsim.application.analysis.TestOtherDependentAnalysis; +import org.matsim.testcases.MatsimTestUtils; + +import java.nio.file.Path; + +public class CommandRunnerTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void runner() { + + Path path = Path.of(utils.getOutputDirectory()); + + CommandRunner runner = new CommandRunner().setOutput(path); + runner.add(TestDependentAnalysis.class); + runner.add(TestOtherAnalysis.class, "--option", "1"); + runner.add(TestOtherDependentAnalysis.class); + + runner.run(Path.of(utils.getInputDirectory())); + + // Results will go into analysis subdirectory because of the commands package names + + Assertions.assertThat(path.resolve("analysis")) + .isDirectoryContaining(p -> p.getFileName().toString().equals("out.xml")) + .isDirectoryContaining(p -> p.getFileName().toString().equals("processed.csv")); + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/MATSimApplicationTest.java b/contribs/application/src/test/java/org/matsim/application/MATSimApplicationTest.java index 457941cb3e2..9be98d76122 100644 --- a/contribs/application/src/test/java/org/matsim/application/MATSimApplicationTest.java +++ b/contribs/application/src/test/java/org/matsim/application/MATSimApplicationTest.java @@ -11,7 +11,7 @@ import org.matsim.application.prepare.population.TrajectoryToPlans; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.utils.io.IOUtils; @@ -47,7 +47,7 @@ public void config() { Config config = controler.getConfig(); - assertThat(config.controler().getRunId()).isEqualTo("Test123"); + assertThat(config.controller().getRunId()).isEqualTo("Test123"); assertThat(config.global().getNumberOfThreads()).isEqualTo(4); assertThat(config.plans().getInputCRS()).isEqualTo("EPSG:1234"); @@ -60,12 +60,12 @@ public void yaml() { Controler controler = MATSimApplication.prepare(TestScenario.class, ConfigUtils.createConfig(), "--yaml", yml.toString()); - assertThat(controler.getConfig().controler().getRunId()) + assertThat(controler.getConfig().controller().getRunId()) .isEqualTo("567"); - PlanCalcScoreConfigGroup score = controler.getConfig().planCalcScore(); + ScoringConfigGroup score = controler.getConfig().scoring(); - PlanCalcScoreConfigGroup.ScoringParameterSet params = score.getScoringParameters(null); + ScoringConfigGroup.ScoringParameterSet params = score.getScoringParameters(null); assertThat(params.getOrCreateModeParams("car").getConstant()) .isEqualTo(-1); @@ -81,12 +81,12 @@ public void sample() { Controler controler = MATSimApplication.prepare(TestScenario.class, ConfigUtils.createConfig(), "--10pct"); - assertThat(controler.getConfig().controler().getRunId()) + assertThat(controler.getConfig().controller().getRunId()) .isEqualTo("run-10pct"); controler = MATSimApplication.prepare(TestScenario.class, ConfigUtils.createConfig()); - assertThat(controler.getConfig().controler().getRunId()) + assertThat(controler.getConfig().controller().getRunId()) .isEqualTo("run-25pct"); } @@ -166,9 +166,9 @@ public void run() { Config config = ConfigUtils.createConfig(); Path out = Path.of(utils.getOutputDirectory()).resolve("out"); - config.controler().setOutputDirectory(out.toString()); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(1); + config.controller().setOutputDirectory(out.toString()); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(1); int ret = MATSimApplication.execute(TestScenario.class, config); @@ -181,7 +181,7 @@ public void run() { @MATSimApplication.Prepare({ TrajectoryToPlans.class, GenerateShortDistanceTrips.class, ExtractRelevantFreightTrips.class, MergePopulations.class }) - private static final class TestScenario extends MATSimApplication { + public static final class TestScenario extends MATSimApplication { @CommandLine.Mixin private SampleOptions sample = new SampleOptions(1, 10, 25); @@ -197,7 +197,7 @@ public TestScenario() { @Override protected Config prepareConfig(Config config) { - config.controler().setRunId(sample.adjustName("run-25pct")); + config.controller().setRunId(sample.adjustName("run-25pct")); return config; } diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/LogFileAnalysisTest.java b/contribs/application/src/test/java/org/matsim/application/analysis/LogFileAnalysisTest.java new file mode 100644 index 00000000000..4b1c121aa72 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/LogFileAnalysisTest.java @@ -0,0 +1,54 @@ +package org.matsim.application.analysis; + +import org.assertj.core.api.Assertions; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.application.ApplicationUtils; +import org.matsim.application.MATSimApplication; +import org.matsim.application.MATSimApplicationTest; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class LogFileAnalysisTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void output() throws IOException { + + Config config = ConfigUtils.createConfig(); + + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(2); + + int execute = MATSimApplication.execute(MATSimApplicationTest.TestScenario.class, config); + + Assertions.assertThat(execute) + .isEqualTo(0); + + Path out = Path.of(config.controller().getOutputDirectory()); + new LogFileAnalysis().execute( + "--input", ApplicationUtils.matchInput("logfile.log", out).toString(), + "--output-memory-stats", out.resolve("mem_stats.csv").toString(), + "--output-run-info", out.resolve("run_info.csv").toString(), + "--output-runtime-stats", out.resolve("runtime_stats.csv").toString() + ); + + + Assertions.assertThat(Files.readAllLines(out.resolve("mem_stats.csv"))) + .hasSizeGreaterThan(1); + + Assertions.assertThat(Files.readAllLines(out.resolve("runtime_stats.csv"))) + .hasSize(4); + + Assertions.assertThat(Files.readAllLines(out.resolve("run_info.csv"))) + .hasSizeGreaterThan(8); + + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/TestAnalysis.java b/contribs/application/src/test/java/org/matsim/application/analysis/TestAnalysis.java new file mode 100644 index 00000000000..e151aeaf2f1 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/TestAnalysis.java @@ -0,0 +1,41 @@ +package org.matsim.application.analysis; + +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import org.matsim.application.options.ShpOptions; +import picocli.CommandLine; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Test command that writes content to file. + */ +@CommandSpec(requires = {"test.csv", "stats.csv"}, requireNetwork = true, produces = "out.xml") +public class TestAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(TestAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(TestAnalysis.class); + + @CommandLine.Mixin + private final ShpOptions shp = new ShpOptions(); + + @Override + public Integer call() throws Exception { + + if (!Files.exists(Path.of(input.getPath("test.csv")))) + return 2; + + if (!Files.exists(Path.of(input.getPath("stats.csv")))) + return 2; + + Files.writeString(output.getPath(), "Fixed Content"); + + return 0; + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/TestDependentAnalysis.java b/contribs/application/src/test/java/org/matsim/application/analysis/TestDependentAnalysis.java new file mode 100644 index 00000000000..68a1139ef7e --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/TestDependentAnalysis.java @@ -0,0 +1,31 @@ +package org.matsim.application.analysis; + +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import picocli.CommandLine; + +import java.nio.file.Files; +import java.nio.file.Path; + +@CommandSpec(requires = {"out.xml"}, produces = "processed.csv", dependsOn = {TestAnalysis.class}) +public class TestDependentAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(TestDependentAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(TestDependentAnalysis.class); + + @Override + public Integer call() throws Exception { + + String s = Files.readString(Path.of(input.getPath("out.xml"))); + + if (!s.equals("Fixed Content")) + throw new IllegalStateException("Content in out.xml not as expected."); + + return 0; + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherAnalysis.java b/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherAnalysis.java new file mode 100644 index 00000000000..cbf81281990 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherAnalysis.java @@ -0,0 +1,28 @@ +package org.matsim.application.analysis; + +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import picocli.CommandLine; + +/** + * Test command that writes content to file. + */ +@CommandSpec(requires = {"test.csv", "stats.csv"}, requireNetwork = true, produces = "out.xml") +public class TestOtherAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(TestOtherAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(TestOtherAnalysis.class); + + @CommandLine.Option(names = "--option", description = "Testing option") + private int option; + + @Override + public Integer call() throws Exception { + return 0; + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherDependentAnalysis.java b/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherDependentAnalysis.java new file mode 100644 index 00000000000..51c5164e077 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/analysis/TestOtherDependentAnalysis.java @@ -0,0 +1,27 @@ +package org.matsim.application.analysis; + +import org.matsim.application.CommandSpec; +import org.matsim.application.MATSimAppCommand; +import org.matsim.application.options.InputOptions; +import org.matsim.application.options.OutputOptions; +import picocli.CommandLine; + +import java.nio.file.Files; + +@CommandSpec(requires = {"out.xml"}, produces = "processed.csv", dependsOn = {TestAnalysis.class}) +public class TestOtherDependentAnalysis implements MATSimAppCommand { + + @CommandLine.Mixin + private final InputOptions input = InputOptions.ofCommand(TestOtherDependentAnalysis.class); + + @CommandLine.Mixin + private final OutputOptions output = OutputOptions.ofCommand(TestOtherDependentAnalysis.class); + + @Override + public Integer call() throws Exception { + + Files.writeString(output.getPath(), "something"); + + return 0; + } +} diff --git a/contribs/application/src/test/java/org/matsim/application/options/SampleOptionsTest.java b/contribs/application/src/test/java/org/matsim/application/options/SampleOptionsTest.java new file mode 100644 index 00000000000..545e30560a2 --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/options/SampleOptionsTest.java @@ -0,0 +1,65 @@ +package org.matsim.application.options; + +import com.google.common.util.concurrent.AtomicDouble; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.matsim.application.MATSimAppCommand; +import picocli.CommandLine; + +import java.util.concurrent.atomic.AtomicInteger; + +public class SampleOptionsTest { + + @Test + public void flexible() { + + AtomicInteger size = new AtomicInteger(); + AtomicDouble dSize = new AtomicDouble(); + + MATSimAppCommand command = new MATSimAppCommand() { + + @CommandLine.Mixin + private SampleOptions sample; + + @Override + public Integer call() throws Exception { + size.set(sample.getSize()); + dSize.set(sample.getSample()); + return 0; + } + }; + + command.execute("--sample-size", "0.5"); + + Assertions.assertThat(size.get()) + .isEqualTo(50); + + Assertions.assertThat(dSize.get()) + .isEqualTo(0.5); + } + + @Test + public void fixed() { + + AtomicInteger size = new AtomicInteger(); + + MATSimAppCommand command = new MATSimAppCommand() { + + @CommandLine.Mixin + private SampleOptions sample = new SampleOptions(10); + + @Override + public Integer call() throws Exception { + size.set(sample.getSize()); + return 0; + } + }; + + command.execute("--10pct"); + + Assertions.assertThat(size.get()) + .isEqualTo(10); + } + + +} diff --git a/contribs/application/src/test/java/org/matsim/application/prepare/counts/NetworkIndexTest.java b/contribs/application/src/test/java/org/matsim/application/prepare/counts/NetworkIndexTest.java new file mode 100644 index 00000000000..a342fbac29c --- /dev/null +++ b/contribs/application/src/test/java/org/matsim/application/prepare/counts/NetworkIndexTest.java @@ -0,0 +1,69 @@ +package org.matsim.application.prepare.counts; + +import org.assertj.core.data.Offset; +import org.geotools.geometry.jts.JTSFactoryFinder; +import org.junit.Test; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NetworkIndexTest { + + @Test + public void angle() { + + GeometryFactory f = JTSFactoryFinder.getGeometryFactory(); + + double angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(1, 1)}), + f.createLineString(new Coordinate[]{new Coordinate(1, 1), new Coordinate(0, 0)}) + ); + + assertThat(angle) + .isEqualTo(Math.PI); + + angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(1, 1), new Coordinate(2, 2)}), + f.createLineString(new Coordinate[]{new Coordinate(1, 1), new Coordinate(2, 2)}) + ); + + assertThat(angle) + .isEqualTo(0); + + angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(1, 1)}), + f.createLineString(new Coordinate[]{new Coordinate(1, 1), new Coordinate(2, 0)}) + ); + + assertThat(angle) + .isEqualTo(-Math.PI / 2); + + angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(1, 0)}), + f.createLineString(new Coordinate[]{new Coordinate(0, 0.1), new Coordinate(1, 0)}) + ); + + assertThat(angle) + .isCloseTo(-0.1, Offset.offset(0.001)); + + angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(1, 0)}), + f.createLineString(new Coordinate[]{new Coordinate(0, -0.1), new Coordinate(1, 0)}) + ); + + assertThat(angle) + .isCloseTo(0.1, Offset.offset(0.001)); + + + angle = NetworkIndex.angle( + f.createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(1, 0)}), + f.createLineString(new Coordinate[]{new Coordinate(1, 0), new Coordinate(0, -0.1)}) + ); + + assertThat(angle) + .isCloseTo(-Math.PI + 0.1, Offset.offset(0.001)); + + + } +} diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java index cca68b4d44b..9d2ec63b41c 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/CarrierReaderFromCSVTest.java @@ -17,12 +17,11 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.controler.FreightUtils; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierCapabilities.FleetSize; -import org.matsim.contrib.freight.carrier.CarrierUtils; -import org.matsim.contrib.freight.carrier.CarrierVehicle; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.CarrierVehicle; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; @@ -49,9 +48,9 @@ public void carrierCreation() throws IOException { config.network().setInputFile( "https://raw.githubusercontent.com/matsim-org/matsim/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); Scenario scenario = ScenarioUtils.loadScenario(config); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), - FreightConfigGroup.class); - freightConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), + FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); Path carrierCSVLocation = Path.of(utils.getPackageInputDirectory() + "testCarrierCSV.csv"); Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); ShpOptions shp = new ShpOptions(shapeFilePath, "WGS84", null); @@ -59,22 +58,22 @@ public void carrierCreation() throws IOException { Set allNewCarrierInformation = CarrierReaderFromCSV .readCarrierInformation(carrierCSVLocation); String shapeCategory = "Ortsteil"; - CarrierReaderFromCSV.checkNewCarrier(allNewCarrierInformation, freightConfigGroup, scenario, polygonsInShape, shapeCategory); - CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightConfigGroup, + CarrierReaderFromCSV.checkNewCarrier(allNewCarrierInformation, freightCarriersConfigGroup, scenario, polygonsInShape, shapeCategory); + CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, polygonsInShape, 1, null); - Assert.assertEquals(3, FreightUtils.getCarriers(scenario).getCarriers().size()); + Assert.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size()); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier2", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier2", Carrier.class))); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier3", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier3", Carrier.class))); // check carrier 1 - Carrier testCarrier1 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier1 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier1", Carrier.class)); Assert.assertEquals(FleetSize.INFINITE, testCarrier1.getCarrierCapabilities().getFleetSize()); - Assert.assertEquals(10, CarrierUtils.getJspritIterations(testCarrier1)); + Assert.assertEquals(10, CarriersUtils.getJspritIterations(testCarrier1)); Assert.assertEquals(4, testCarrier1.getCarrierCapabilities().getCarrierVehicles().size()); Object2IntMap depotSums = new Object2IntOpenHashMap<>(); Map> typesPerDepot = new HashMap<>(); @@ -101,10 +100,10 @@ public void carrierCreation() throws IOException { Assert.assertTrue(typesPerDepot.get("j(2,4)R").contains("testVehicle2")); // check carrier 2 - Carrier testCarrier2 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier2 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier2", Carrier.class)); Assert.assertEquals(FleetSize.FINITE, testCarrier2.getCarrierCapabilities().getFleetSize()); - Assert.assertEquals(15, CarrierUtils.getJspritIterations(testCarrier2)); + Assert.assertEquals(15, CarriersUtils.getJspritIterations(testCarrier2)); Assert.assertEquals(9, testCarrier2.getCarrierCapabilities().getCarrierVehicles().size()); depotSums = new Object2IntOpenHashMap<>(); typesPerDepot = new HashMap<>(); @@ -126,10 +125,10 @@ public void carrierCreation() throws IOException { // check carrier 3 Network network = NetworkUtils.readNetwork( "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); - Carrier testCarrier3 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier3 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier3", Carrier.class)); Assert.assertEquals(FleetSize.INFINITE, testCarrier3.getCarrierCapabilities().getFleetSize()); - Assert.assertEquals(1, CarrierUtils.getJspritIterations(testCarrier3)); + Assert.assertEquals(1, CarriersUtils.getJspritIterations(testCarrier3)); Assert.assertEquals(2, testCarrier3.getCarrierCapabilities().getCarrierVehicles().size()); depotSums = new Object2IntOpenHashMap<>(); typesPerDepot = new HashMap<>(); diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java index fdc2aa30d12..d9ff39a695c 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java @@ -18,12 +18,7 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierService; -import org.matsim.contrib.freight.carrier.CarrierShipment; -import org.matsim.contrib.freight.carrier.TimeWindow; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; @@ -78,9 +73,9 @@ public void demandCreation() throws IOException { config.network().setInputFile( "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); Scenario scenario = ScenarioUtils.loadScenario(config); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), - FreightConfigGroup.class); - freightConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), + FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); Path carrierCSVLocation = Path.of(utils.getPackageInputDirectory() + "testCarrierCSV.csv"); Path demandCSVLocation = Path.of(utils.getPackageInputDirectory() + "testDemandCSV.csv"); String shapeCategory = "Ortsteil"; @@ -94,24 +89,24 @@ public void demandCreation() throws IOException { // run methods Set allNewCarrierInformation = CarrierReaderFromCSV .readCarrierInformation(carrierCSVLocation); - CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightConfigGroup, + CarrierReaderFromCSV.createNewCarrierAndAddVehicleTypes(scenario, allNewCarrierInformation, freightCarriersConfigGroup, polygonsInShape, 1, null); Set demandInformation = DemandReaderFromCSV.readDemandInformation(demandCSVLocation); DemandReaderFromCSV.checkNewDemand(scenario, demandInformation, polygonsInShape, shapeCategory); DemandReaderFromCSV.createDemandForCarriers(scenario, polygonsInShape, demandInformation, population, false, null); - Assert.assertEquals(3, FreightUtils.getCarriers(scenario).getCarriers().size()); + Assert.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size()); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier2", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier2", Carrier.class))); Assert.assertTrue( - FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier3", Carrier.class))); + CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier3", Carrier.class))); // check carrier 1 Network network = NetworkUtils.readNetwork( "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); - Carrier testCarrier1 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier1 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier1", Carrier.class)); Assert.assertEquals(14, testCarrier1.getServices().size()); Assert.assertEquals(0, testCarrier1.getShipments().size()); @@ -157,7 +152,7 @@ public void demandCreation() throws IOException { Assert.assertTrue(locationsPerServiceElement.get("serviceElement2").contains("i(2,0)")); // check carrier 2 - Carrier testCarrier2 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier2 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier2", Carrier.class)); Assert.assertEquals(0, testCarrier2.getServices().size()); Assert.assertEquals(11, testCarrier2.getShipments().size()); @@ -208,7 +203,7 @@ public void demandCreation() throws IOException { Assert.assertEquals(2, locationsPerShipmentElement.get("ShipmenElement2_delivery").size()); // check carrier 3 - Carrier testCarrier3 = FreightUtils.getCarriers(scenario).getCarriers() + Carrier testCarrier3 = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create("testCarrier3", Carrier.class)); Assert.assertEquals(0, testCarrier3.getServices().size()); Assert.assertEquals(4, testCarrier3.getShipments().size()); 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/RunCreateSmallScaleCommercialTrafficTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java similarity index 78% rename from contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunCreateSmallScaleCommercialTrafficTest.java rename to contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java index fe2bb57b776..d0f4620ac71 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunCreateSmallScaleCommercialTrafficTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/RunGenerateSmallScaleCommercialTrafficTest.java @@ -25,9 +25,9 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Population; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.population.PopulationUtils; @@ -41,33 +41,32 @@ * @author Ricardo Ewert * */ -public class RunCreateSmallScaleCommercialTrafficTest { +public class RunGenerateSmallScaleCommercialTrafficTest { @Rule public MatsimTestUtils utils = new MatsimTestUtils(); @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 trafficType = "commercialTraffic"; - String includeExistingModels = "true"; + String smallScaleCommercialTrafficType = "commercialPersonTraffic"; String zoneShapeFileName = utils.getPackageInputDirectory() + "/shp/testZones.shp"; String buildingsShapeFileName = utils.getPackageInputDirectory() + "/shp/testBuildings.shp"; String landuseShapeFileName = utils.getPackageInputDirectory() + "/shp/testLanduse.shp"; String shapeCRS = "EPSG:4326"; - new CreateSmallScaleCommercialTrafficDemand().execute( + new GenerateSmallScaleCommercialTrafficDemand().execute( inputDataDirectory, "--sample", sample, "--jspritIterations", jspritIterations, "--creationOption", creationOption, "--landuseConfiguration", landuseConfiguration, - "--trafficType", trafficType, + "--smallScaleCommercialTrafficType", smallScaleCommercialTrafficType, "--includeExistingModels", "--zoneShapeFileName", zoneShapeFileName, "--buildingsShapeFileName", buildingsShapeFileName, @@ -82,7 +81,7 @@ public void testMainRunAndResults() { Population population = null; String carriersWOSolutionFileLocation = null; String carriersWSolutionFileLocation = null; - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); for (File outputFiles : Objects.requireNonNull(Objects.requireNonNull(outputFolder.listFiles())[0].listFiles())) { @@ -93,13 +92,13 @@ public void testMainRunAndResults() { if (outputFiles.getName().contains("output_CarrierDemandWithPlans.xml")) carriersWSolutionFileLocation = outputFiles.getPath(); if (outputFiles.getName().contains("output_carriersVehicleTypes.xml.gz")) - freightConfigGroup.setCarriersVehicleTypesFile(outputFiles.getPath()); + freightCarriersConfigGroup.setCarriersVehicleTypesFile(outputFiles.getPath()); } - freightConfigGroup.setCarriersFile(carriersWOSolutionFileLocation); - FreightUtils.loadCarriersAccordingToFreightConfig(scenarioWOSolution); - freightConfigGroup.setCarriersFile(carriersWSolutionFileLocation); - FreightUtils.loadCarriersAccordingToFreightConfig(scenarioWSolution); + freightCarriersConfigGroup.setCarriersFile(carriersWOSolutionFileLocation); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenarioWOSolution); + freightCarriersConfigGroup.setCarriersFile(carriersWSolutionFileLocation); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenarioWSolution); assert population != null; for (Person person : population.getPersons().values()) { @@ -110,10 +109,10 @@ public void testMainRunAndResults() { Assert.assertTrue(person.getAttributes().getAsMap().containsKey("purpose")); } - Assert.assertEquals(FreightUtils.addOrGetCarriers(scenarioWSolution).getCarriers().size(), - FreightUtils.addOrGetCarriers(scenarioWOSolution).getCarriers().size(), 0); + Assert.assertEquals(CarriersUtils.addOrGetCarriers(scenarioWSolution).getCarriers().size(), + CarriersUtils.addOrGetCarriers(scenarioWOSolution).getCarriers().size(), 0); int countedTours = 0; - for (Carrier carrier_withSolution : FreightUtils.addOrGetCarriers(scenarioWSolution).getCarriers().values()) { + for (Carrier carrier_withSolution : CarriersUtils.addOrGetCarriers(scenarioWSolution).getCarriers().values()) { countedTours += carrier_withSolution.getSelectedPlan().getScheduledTours().size(); } Assert.assertEquals(population.getPersons().size(), countedTours, 0); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java index 213b2fb4699..84c5e0bd188 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtilsTest.java @@ -30,11 +30,13 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.testcases.MatsimTestUtils; +import org.opengis.feature.simple.SimpleFeature; import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -59,9 +61,11 @@ public void findZoneOfLinksTest() throws IOException, URISyntaxException { config.network().setInputFile(networkPath); config.network().setInputCRS("EPSG:4326"); Scenario scenario = ScenarioUtils.loadScenario(config); + HashMap>> buildingsPerZone = new HashMap<>(); - Map, Link>> regionLinksMap = CreateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem())); + Map, Link>> regionLinksMap = GenerateSmallScaleCommercialTrafficDemand + .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem()), + buildingsPerZone); Assert.assertEquals(3, regionLinksMap.size(), MatsimTestUtils.EPSILON); Assert.assertEquals(60, regionLinksMap.get("testArea1_area1").size(), MatsimTestUtils.EPSILON); 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 4d65381a967..fe522f7462d 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TrafficVolumeGenerationTest.java @@ -27,10 +27,9 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; import org.matsim.application.options.ShpOptions; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierCapabilities.FleetSize; -import org.matsim.contrib.freight.carrier.CarrierUtils; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierCapabilities.FleetSize; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.scenario.ScenarioUtils; @@ -54,7 +53,7 @@ public class TrafficVolumeGenerationTest { public MatsimTestUtils utils = new MatsimTestUtils(); @Test - public void testTrafficVolumeGenerationBusinessTraffic() throws IOException { + public void testTrafficVolumeGenerationCommercialPersonTraffic() throws IOException { HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -74,7 +73,7 @@ public void testTrafficVolumeGenerationBusinessTraffic() throws IOException { shapeFileLandusePath, shapeFileZonePath, shapeFileBuildingsPath, null, buildingsPerZone); - String usedTrafficType = "businessTraffic"; + String usedTrafficType = "commercialPersonTraffic"; double sample = 1.; ArrayList modesORvehTypes = new ArrayList<>( List.of("total")); @@ -184,7 +183,7 @@ public void testTrafficVolumeGenerationBusinessTraffic() throws IOException { } @Test - public void testTrafficVolumeGenerationFreightTraffic() throws IOException { + public void testTrafficVolumeGenerationGoodsTraffic() throws IOException { HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -204,7 +203,7 @@ public void testTrafficVolumeGenerationFreightTraffic() throws IOException { shapeFileLandusePath, shapeFileZonePath, shapeFileBuildingsPath, null, buildingsPerZone); - String usedTrafficType = "freightTraffic"; + String usedTrafficType = "goodsTraffic"; double sample = 1.; ArrayList modesORvehTypes = new ArrayList<>( Arrays.asList("vehTyp1", "vehTyp2", "vehTyp3", "vehTyp4", "vehTyp5")); @@ -398,50 +397,53 @@ 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); - Map, Link>> regionLinksMap = CreateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem())); + 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); - Assert.assertTrue(FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier1", Carrier.class))); - Assert.assertTrue(FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier2", Carrier.class))); - Assert.assertTrue(FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleShipmentCarrier_carrier1", Carrier.class))); + Assert.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size(), MatsimTestUtils.EPSILON); + Assert.assertEquals(1, CarriersUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().size(), MatsimTestUtils.EPSILON); + Assert.assertTrue(CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier1", Carrier.class))); + Assert.assertTrue(CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier2", Carrier.class))); + Assert.assertTrue(CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleShipmentCarrier_carrier1", Carrier.class))); - Carrier addedCarrier1 = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier1", Carrier.class)); + Carrier addedCarrier1 = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier1", Carrier.class)); Assert.assertNotNull(addedCarrier1.getSelectedPlan()); - Assert.assertEquals(0, CarrierUtils.getJspritIterations(addedCarrier1), MatsimTestUtils.EPSILON); + Assert.assertEquals(0, CarriersUtils.getJspritIterations(addedCarrier1), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier1.getCarrierCapabilities().getCarrierVehicles().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier1.getCarrierCapabilities().getVehicleTypes().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(3, addedCarrier1.getSelectedPlan().getScheduledTours().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(30, addedCarrier1.getServices().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(6, addedCarrier1.getAttributes().size(), MatsimTestUtils.EPSILON); - Assert.assertEquals("businessTraffic", addedCarrier1.getAttributes().getAttribute("subpopulation")); + Assert.assertEquals("commercialPersonTraffic", addedCarrier1.getAttributes().getAttribute("subpopulation")); Assert.assertEquals(2, (int) addedCarrier1.getAttributes().getAttribute("purpose")); Assert.assertEquals("exampleServiceCarrier", addedCarrier1.getAttributes().getAttribute("existingModel")); Assert.assertEquals("car", addedCarrier1.getAttributes().getAttribute("networkMode")); Assert.assertNull(addedCarrier1.getAttributes().getAttribute("vehicleType")); Assert.assertEquals("testArea2_area3", addedCarrier1.getAttributes().getAttribute("tourStartArea")); - Carrier addedCarrier2 = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier2", Carrier.class)); + Carrier addedCarrier2 = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier2", Carrier.class)); Assert.assertNotNull(addedCarrier2.getSelectedPlan()); - Assert.assertEquals(0, CarrierUtils.getJspritIterations(addedCarrier2), MatsimTestUtils.EPSILON); + Assert.assertEquals(0, CarriersUtils.getJspritIterations(addedCarrier2), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier2.getCarrierCapabilities().getCarrierVehicles().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier2.getCarrierCapabilities().getVehicleTypes().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(2, addedCarrier2.getSelectedPlan().getScheduledTours().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(20, addedCarrier2.getServices().size(), MatsimTestUtils.EPSILON); - Assert.assertEquals("businessTraffic", addedCarrier2.getAttributes().getAttribute("subpopulation")); + Assert.assertEquals("commercialPersonTraffic", addedCarrier2.getAttributes().getAttribute("subpopulation")); Assert.assertEquals(2, (int) addedCarrier2.getAttributes().getAttribute("purpose")); Assert.assertEquals("exampleServiceCarrier", addedCarrier2.getAttributes().getAttribute("existingModel")); Assert.assertEquals("car", addedCarrier2.getAttributes().getAttribute("networkMode")); Assert.assertNull(addedCarrier2.getAttributes().getAttribute("vehicleType")); Assert.assertEquals("testArea2_area3", addedCarrier2.getAttributes().getAttribute("tourStartArea")); - Carrier addedCarrier3 = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleShipmentCarrier_carrier1", Carrier.class)); + Carrier addedCarrier3 = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleShipmentCarrier_carrier1", Carrier.class)); Assert.assertNull(addedCarrier3.getSelectedPlan()); - Assert.assertEquals(50, CarrierUtils.getJspritIterations(addedCarrier3), MatsimTestUtils.EPSILON); + Assert.assertEquals(50, CarriersUtils.getJspritIterations(addedCarrier3), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier3.getCarrierCapabilities().getCarrierVehicles().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier3.getCarrierCapabilities().getVehicleTypes().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(FleetSize.INFINITE, addedCarrier3.getCarrierCapabilities().getFleetSize()); @@ -461,35 +463,38 @@ 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); - Map, Link>> regionLinksMap = CreateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem())); + 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); - Assert.assertTrue(FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier1", Carrier.class))); - Assert.assertTrue(FreightUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleShipmentCarrier_carrier1", Carrier.class))); + Assert.assertEquals(2, CarriersUtils.getCarriers(scenario).getCarriers().size(), MatsimTestUtils.EPSILON); + Assert.assertEquals(1, CarriersUtils.getCarrierVehicleTypes(scenario).getVehicleTypes().size(), MatsimTestUtils.EPSILON); + Assert.assertTrue(CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleServiceCarrier_carrier1", Carrier.class))); + Assert.assertTrue(CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("exampleShipmentCarrier_carrier1", Carrier.class))); - Carrier addedCarrier1 = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier1", Carrier.class)); + Carrier addedCarrier1 = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleServiceCarrier_carrier1", Carrier.class)); Assert.assertNotNull(addedCarrier1.getSelectedPlan()); - Assert.assertEquals(0, CarrierUtils.getJspritIterations(addedCarrier1), MatsimTestUtils.EPSILON); + Assert.assertEquals(0, CarriersUtils.getJspritIterations(addedCarrier1), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier1.getCarrierCapabilities().getCarrierVehicles().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier1.getCarrierCapabilities().getVehicleTypes().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier1.getSelectedPlan().getScheduledTours().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(10, addedCarrier1.getServices().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(6, addedCarrier1.getAttributes().size(), MatsimTestUtils.EPSILON); - Assert.assertEquals("businessTraffic", addedCarrier1.getAttributes().getAttribute("subpopulation")); + Assert.assertEquals("commercialPersonTraffic", addedCarrier1.getAttributes().getAttribute("subpopulation")); Assert.assertEquals(2, (int) addedCarrier1.getAttributes().getAttribute("purpose")); Assert.assertEquals("exampleServiceCarrier", addedCarrier1.getAttributes().getAttribute("existingModel")); Assert.assertEquals("car", addedCarrier1.getAttributes().getAttribute("networkMode")); Assert.assertNull(addedCarrier1.getAttributes().getAttribute("vehicleType")); Assert.assertEquals("testArea2_area3", addedCarrier1.getAttributes().getAttribute("tourStartArea")); - Carrier addedCarrier3 = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleShipmentCarrier_carrier1", Carrier.class)); + Carrier addedCarrier3 = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("exampleShipmentCarrier_carrier1", Carrier.class)); Assert.assertNull(addedCarrier3.getSelectedPlan()); - Assert.assertEquals(50, CarrierUtils.getJspritIterations(addedCarrier3), MatsimTestUtils.EPSILON); + Assert.assertEquals(50, CarriersUtils.getJspritIterations(addedCarrier3), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier3.getCarrierCapabilities().getCarrierVehicles().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(1, addedCarrier3.getCarrierCapabilities().getVehicleTypes().size(), MatsimTestUtils.EPSILON); Assert.assertEquals(FleetSize.INFINITE, addedCarrier3.getCarrierCapabilities().getFleetSize()); @@ -498,7 +503,7 @@ public void testAddingExistingScenariosWithSample() throws Exception { } @Test - public void testReducingDemandAfterAddingExistingScenarios_freight() throws Exception { + public void testReducingDemandAfterAddingExistingScenarios_goods() throws Exception { HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -511,7 +516,7 @@ public void testReducingDemandAfterAddingExistingScenarios_freight() throws Exce Path shapeFileBuildingsPath = inputDataDirectory.resolve("shp/testBuildings.shp"); ShpOptions shpZones = new ShpOptions(shapeFileZonePath, null, StandardCharsets.UTF_8); String networkPath = "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"; - String usedTrafficType = "freightTraffic"; + String usedTrafficType = "goodsTraffic"; double sample = 1.; ArrayList modesORvehTypes = new ArrayList<>( Arrays.asList("vehTyp1", "vehTyp2", "vehTyp3", "vehTyp4", "vehTyp5")); @@ -519,6 +524,7 @@ public void testReducingDemandAfterAddingExistingScenarios_freight() throws Exce 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); @@ -532,10 +538,11 @@ public void testReducingDemandAfterAddingExistingScenarios_freight() throws Exce HashMap> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); - Map, Link>> regionLinksMap = CreateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem())); + 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); TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, usedTrafficType, trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); @@ -656,7 +663,7 @@ public void testReducingDemandAfterAddingExistingScenarios_freight() throws Exce } @Test - public void testReducingDemandAfterAddingExistingScenarios_business() throws Exception { + public void testReducingDemandAfterAddingExistingScenarios_commercialPersonTraffic() throws Exception { HashMap> landuseCategoriesAndDataConnection = new HashMap<>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -669,7 +676,7 @@ public void testReducingDemandAfterAddingExistingScenarios_business() throws Exc Path shapeFileBuildingsPath = inputDataDirectory.resolve("shp/testBuildings.shp"); ShpOptions shpZones = new ShpOptions(shapeFileZonePath, null, StandardCharsets.UTF_8); String networkPath = "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"; - String usedTrafficType = "businessTraffic"; + String usedTrafficType = "commercialPersonTraffic"; double sample = 1.; ArrayList modesORvehTypes = new ArrayList<>( List.of("total")); @@ -677,6 +684,7 @@ public void testReducingDemandAfterAddingExistingScenarios_business() throws Exc 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); @@ -690,10 +698,11 @@ public void testReducingDemandAfterAddingExistingScenarios_business() throws Exc HashMap> trafficVolumePerTypeAndZone_stop = TrafficVolumeGeneration .createTrafficVolume_stop(resultingDataPerZone, output, sample, modesORvehTypes, usedTrafficType); - Map, Link>> regionLinksMap = CreateSmallScaleCommercialTrafficDemand - .filterLinksForZones(scenario, shpZones, SmallScaleCommercialTrafficUtils.getIndexZones(shapeFileZonePath, config.global().getCoordinateSystem())); + 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); TrafficVolumeGeneration.reduceDemandBasedOnExistingCarriers(scenario, regionLinksMap, usedTrafficType, trafficVolumePerTypeAndZone_start, trafficVolumePerTypeAndZone_stop); diff --git a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java index 1d4b1b33672..4392957d1c9 100644 --- a/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java +++ b/contribs/application/src/test/java/org/matsim/smallScaleCommercialTrafficGeneration/TripDistributionMatrixTest.java @@ -47,7 +47,7 @@ public class TripDistributionMatrixTest { public MatsimTestUtils utils = new MatsimTestUtils(); @Test - public void testTripDistributionBusinessTraffic() throws IOException { + public void testTripDistributionCommercialPersonTrafficTraffic() throws IOException { HashMap> landuseCategoriesAndDataConnection = new HashMap>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -67,7 +67,7 @@ public void testTripDistributionBusinessTraffic() throws IOException { inputDataDirectory, usedLanduseConfiguration, shapeFileLandusePath, shapeFileZonePath, shapeFileBuildingsPath, null, buildingsPerZone); - String usedTrafficType = "businessTraffic"; + String usedTrafficType = "commercialPersonTraffic"; double sample = 1.; double resistanceFactor = 0.005; @@ -137,7 +137,7 @@ public void testTripDistributionBusinessTraffic() throws IOException { } @Test - public void testTripDistributionFreightTraffic() throws IOException { + public void testTripDistributionGoodsTraffic() throws IOException { HashMap> landuseCategoriesAndDataConnection = new HashMap>(); HashMap>> buildingsPerZone = new HashMap<>(); @@ -157,7 +157,7 @@ public void testTripDistributionFreightTraffic() throws IOException { inputDataDirectory, usedLanduseConfiguration, shapeFileLandusePath, shapeFileZonePath, shapeFileBuildingsPath, null, buildingsPerZone); - String usedTrafficType = "freightTraffic"; + String usedTrafficType = "goodsTraffic"; double sample = 1.; double resistanceFactor = 0.005; diff --git a/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/run.output_network.xml.gz b/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/run.output_network.xml.gz new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/stats.csv b/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/stats.csv new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/test.csv b/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/test.csv new file mode 100644 index 00000000000..b532bd8e145 --- /dev/null +++ b/contribs/application/test/input/org/matsim/application/CommandRunnerTest/runner/test.csv @@ -0,0 +1,2 @@ +idx,data +0,Hello \ No newline at end of file diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml b/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml index d90720f92dc..bc64ce1501a 100644 --- a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml +++ b/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/config_demand.xml @@ -44,10 +44,10 @@ Depending on the chosen mobsim, you'll have to add additional config modules to configure the corresponding mobsim. For 'qsim', add a module 'qsim' to the config. --> - + - + @@ -70,15 +70,6 @@ - - - - - - - - - @@ -416,24 +407,6 @@ - - - - - - - - - - - - - - - - - - @@ -560,8 +533,6 @@ - - diff --git a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv b/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv index fa9d3e075d4..bb22655c0cc 100644 --- a/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv +++ b/contribs/application/test/input/org/matsim/smallScaleCommercialTrafficGeneration/existingModels/existingModels.csv @@ -1,3 +1,3 @@ -model sampleSize trafficType purpose vehicleType networkMode -exampleServiceCarrier 1.0 businessTraffic 2 car -exampleShipmentCarrier 1.0 freightTraffic 3 vehTyp3 freight \ No newline at end of file +model sampleSize smallScaleCommercialTrafficType purpose vehicleType networkMode +exampleServiceCarrier 1.0 commercialPersonTraffic 2 car +exampleShipmentCarrier 1.0 goodsTraffic 3 vehTyp3 freight \ No newline at end of file diff --git a/contribs/av/src/main/java/org/matsim/contrib/av/intermodal/RunTaxiPTIntermodalExample.java b/contribs/av/src/main/java/org/matsim/contrib/av/intermodal/RunTaxiPTIntermodalExample.java index 95fc4ca0f07..9aaecbc61b2 100644 --- a/contribs/av/src/main/java/org/matsim/contrib/av/intermodal/RunTaxiPTIntermodalExample.java +++ b/contribs/av/src/main/java/org/matsim/contrib/av/intermodal/RunTaxiPTIntermodalExample.java @@ -36,7 +36,7 @@ import org.matsim.contrib.taxi.run.MultiModeTaxiModule; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; import org.matsim.core.scenario.ScenarioUtils; @@ -54,8 +54,8 @@ public class RunTaxiPTIntermodalExample { public void run(URL configUrl, boolean OTFVis) { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeTaxiConfigGroup(), new DvrpConfigGroup()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.routing().setAccessEgressType(RoutingConfigGroup.AccessEgressType.accessEgressModeToLink); SwissRailRaptorConfigGroup srrConfig = new SwissRailRaptorConfigGroup(); diff --git a/contribs/av/src/main/java/org/matsim/contrib/av/robotaxi/run/RunDrtAndTaxiExample.java b/contribs/av/src/main/java/org/matsim/contrib/av/robotaxi/run/RunDrtAndTaxiExample.java index 5f1646efe1a..5c95006afa8 100644 --- a/contribs/av/src/main/java/org/matsim/contrib/av/robotaxi/run/RunDrtAndTaxiExample.java +++ b/contribs/av/src/main/java/org/matsim/contrib/av/robotaxi/run/RunDrtAndTaxiExample.java @@ -48,7 +48,7 @@ public static void run(URL configUrl, boolean otfvis) { new DvrpConfigGroup(), new OTFVisConfigGroup()); Scenario scenario = DrtControlerCreator.createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); - config.controler() + config.controller() .setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); Controler controler = new Controler(scenario); controler.addOverridingModule(new MultiModeDrtModule()); 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 ff07d5f1286..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, 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/BicycleLinkSpeedCalculatorDefaultImpl.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorDefaultImpl.java index fcef35cce2b..f580b7e77f1 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorDefaultImpl.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorDefaultImpl.java @@ -1,35 +1,42 @@ 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.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.mobsim.qsim.qnetsimengine.QVehicle; import org.matsim.vehicles.Vehicle; import jakarta.inject.Inject; -import java.util.Objects; - -public class BicycleLinkSpeedCalculatorDefaultImpl implements BicycleLinkSpeedCalculator { - - @Inject - private BicycleConfigGroup bicycleConfigGroup; +import org.matsim.vehicles.VehicleType; - @Inject - private BicycleLinkSpeedCalculatorDefaultImpl() { - } +import java.util.Objects; +public final class BicycleLinkSpeedCalculatorDefaultImpl implements BicycleLinkSpeedCalculator { + private static final Logger log = LogManager.getLogger(BicycleLinkSpeedCalculatorDefaultImpl.class ); + @Inject private BicycleConfigGroup bicycleConfigGroup; + @Inject private QSimConfigGroup qSimConfigGroup; + @Inject private BicycleLinkSpeedCalculatorDefaultImpl() { } /** * for unit testing */ - BicycleLinkSpeedCalculatorDefaultImpl( BicycleConfigGroup configGroup ) { - this.bicycleConfigGroup = configGroup; + BicycleLinkSpeedCalculatorDefaultImpl( Config config ) { + this.bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); + this.qSimConfigGroup = config.qsim(); } @Override public double getMaximumVelocity(QVehicle qVehicle, Link link, double time) { + if (isBike(qVehicle)){ + return getMaximumVelocityForLink( link, qVehicle.getVehicle() ); + } else{ + return Double.NaN; + // (this now works because the link speed calculator returns the default for all combinations of (vehicle, link, time) that + // are not answered by a specialized link speed calculator. kai, jun'23) + } - if (isBike(qVehicle)) - return getMaximumVelocityForLink(link, qVehicle.getVehicle()); - else - return getDefaultMaximumVelocity(qVehicle, link, time); } @Override public double getMaximumVelocityForLink(Link link, Vehicle vehicle) { @@ -43,9 +50,9 @@ public double getMaximumVelocityForLink(Link link, Vehicle vehicle) { return Math.min(speed, link.getFreespeed()); } - private double getDefaultMaximumVelocity(QVehicle qVehicle, Link link, double time) { - return Math.min(qVehicle.getMaximumVelocity(), link.getFreespeed(time)); - } +// private double getDefaultMaximumVelocity(QVehicle qVehicle, Link link, double time) { +// return Math.min(qVehicle.getMaximumVelocity(), link.getFreespeed(time)); +// } /** * Based on "Flügel et al. -- Empirical speed models for cycling in the Oslo road network" (not yet published!) @@ -136,6 +143,33 @@ private boolean hasNotAttribute(Link link, String attributeName) { } private boolean isBike(QVehicle qVehicle) { - return qVehicle.getVehicle().getType().getId().toString().equals(bicycleConfigGroup.getBicycleMode()); +// return qVehicle.getVehicle().getType().getId().toString().equals(bicycleConfigGroup.getBicycleMode()); + + // the above is what I found. With mode vehicles, the vehicle ID is indeed abused for the model. But we should not rely on this. + // Unfortunately, backwards compatibility may now fail ... I have seen mode vehicles different from car but having car as network + // mode. kai, jun'23 + + final VehicleType vehicleType = qVehicle.getVehicle().getType(); + + // the below consistentcy check is to broad; need a version that is more narrow ... + +// if ( qSimConfigGroup.getVehiclesSource()== QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData ) { +// if ( !vehicleType.getId().toString().equals( vehicleType.getNetworkMode() ) ) { +// throw new RuntimeException( "You are using mode vehicles but the network mode of the vehicle type is wrong: vehType.id=" + vehicleType.getId() +// + "; vehType.mode=" + vehicleType.getNetworkMode() ); +// } +// } + + // ... more narrow version coming here ... + if ( + qVehicle.getVehicle().getType().getId().toString().equals( bicycleConfigGroup.getBicycleMode() ) + && !vehicleType.getNetworkMode().equals( bicycleConfigGroup.getBicycleMode() ) + ) { + throw new RuntimeException( "You are using mode vehicles but the network mode of the vehicle type is wrong: vehType.id=" + vehicleType.getId() + + "; vehType.mode=" + vehicleType.getNetworkMode() ); + } + + + return vehicleType.getNetworkMode().equals(bicycleConfigGroup.getBicycleMode() ); } } 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 8f036f87874..7bf5a766bf4 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 @@ -24,17 +24,11 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.events.StartupEvent; import org.matsim.core.controler.listener.StartupListener; import org.matsim.core.mobsim.qsim.AbstractQSimModule; -import org.matsim.core.mobsim.qsim.qnetsimengine.ConfigurableQNetworkFactory; -import org.matsim.core.mobsim.qsim.qnetsimengine.DefaultTurnAcceptanceLogic; -import org.matsim.core.mobsim.qsim.qnetsimengine.QNetworkFactory; -import org.matsim.core.mobsim.qsim.qnetsimengine.linkspeedcalculator.DefaultLinkSpeedCalculator; import org.matsim.vehicles.VehicleType; -import org.matsim.withinday.mobsim.WithinDayQSimModule; /** * @author smetzler, dziemke @@ -48,59 +42,37 @@ public final class BicycleModule extends AbstractModule { @Override public void install() { + // The idea here is the following: + // * scores are just added as score events. no scoring function is replaced. + + // * 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); - // TODO: bicycle contrib can not be used with other scoring functions at the moment, as only one can be installed - // TODO: scoring should work via a score event so it can be used together with other scoring functions - - switch ( bicycleConfigGroup.getBicycleScoringType() ) { - case legBased -> { + this.addEventHandlerBinding().to( BicycleScoreEventsCreator.class ); + // (the motorized interaction is in the BicycleScoreEventsCreator) -// yyyyyy the status here is that this seems to work. but because of numerical imprecision the results are _slightly_ -// different. This needs to be documented diligently test by test. kai, dec'22 - // yyyyyy remember that the 10it test needs to be un-ignored. kai, dec'22 - - this.addEventHandlerBinding().to( BicycleScoreEventsCreator.class ); - } - case linkBased -> { - bindScoringFunctionFactory().to(BicycleScoringFunctionFactory.class).in(Singleton.class); - } - default -> throw new IllegalStateException( "Unexpected value: " + bicycleConfigGroup.getBicycleScoringType() ); - } + this.bind( AdditionalBicycleLinkScore.class ).to( AdditionalBicycleLinkScoreDefaultImpl.class ); bind( BicycleLinkSpeedCalculator.class ).to( BicycleLinkSpeedCalculatorDefaultImpl.class ) ; - - if (bicycleConfigGroup.isMotorizedInteraction()) { - addMobsimListenerBinding().to(MotorizedInteractionEngine.class); - } - addControlerListenerBinding().to(ConsistencyCheck.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 this.installOverridingQSimModule( new AbstractQSimModule(){ - @Inject EventsManager events; - @Inject Scenario scenario; - @Inject BicycleLinkSpeedCalculator bicycleLinkSpeedCalculator; @Override protected void configureQSim(){ - final ConfigurableQNetworkFactory factory = new ConfigurableQNetworkFactory(events, scenario); - factory.setLinkSpeedCalculator( bicycleLinkSpeedCalculator ); - bind( QNetworkFactory.class ).toInstance(factory ); - // NOTE: Other than when using a provider, this uses the same factory instance over all iterations, re-configuring - // it in every iteration via the initializeFactory(...) method. kai, mar'16 + this.addLinkSpeedCalculator().to( BicycleLinkSpeedCalculator.class ); } } ); + + addControlerListenerBinding().to(ConsistencyCheck.class); } static class ConsistencyCheck implements StartupListener { + @Inject private BicycleConfigGroup bicycleConfigGroup; + @Inject private Scenario scenario; - @Inject - private BicycleConfigGroup bicycleConfigGroup; - - @Inject - private Scenario scenario; - - @Override - public void notifyStartup(StartupEvent event) { + @Override public void notifyStartup(StartupEvent event) { Id bicycleVehTypeId = Id.create(bicycleConfigGroup.getBicycleMode(), VehicleType.class); if (scenario.getVehicles().getVehicleTypes().get(bicycleVehTypeId) == null) { @@ -114,7 +86,7 @@ public void notifyStartup(StartupEvent event) { LOG.warn("There is an inconsistency in the specified maximum velocity for " + bicycleConfigGroup.getBicycleMode() + ":" + " Maximum speed specified in the 'bicycle' config group (used for routing): " + bicycleConfigGroup.getMaxBicycleSpeedForRouting() + " vs." + " maximum speed specified for the vehicle type (used in mobsim): " + mobsimSpeed); - if (scenario.getConfig().plansCalcRoute().getRoutingRandomness() == 0.) { + if (scenario.getConfig().routing().getRoutingRandomness() == 0.) { throw new RuntimeException("The recommended way to deal with the inconsistency between routing and scoring/mobsim is to have a randomized router. Aborting... "); } } 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..8632a8dcf6d 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 @@ -22,8 +22,8 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.network.NetworkUtils; import org.matsim.core.router.util.TravelDisutility; @@ -65,9 +65,9 @@ class BicycleTravelDisutility implements TravelDisutility { private Person prevPerson; - BicycleTravelDisutility(BicycleConfigGroup bicycleConfigGroup, PlanCalcScoreConfigGroup cnScoringGroup, - PlansCalcRouteConfigGroup plansCalcRouteConfigGroup, TravelTime timeCalculator, double normalization) { - final PlanCalcScoreConfigGroup.ModeParams bicycleParams = cnScoringGroup.getModes().get(bicycleConfigGroup.getBicycleMode()); + BicycleTravelDisutility(BicycleConfigGroup bicycleConfigGroup, ScoringConfigGroup cnScoringGroup, + RoutingConfigGroup routingConfigGroup, TravelTime timeCalculator, double normalization) { + final ScoringConfigGroup.ModeParams bicycleParams = cnScoringGroup.getModes().get(bicycleConfigGroup.getBicycleMode()); if (bicycleParams == null) { throw new NullPointerException("Mode " + bicycleConfigGroup.getBicycleMode() + " is not part of the valid mode parameters " + cnScoringGroup.getModes().keySet()); } @@ -86,7 +86,7 @@ class BicycleTravelDisutility implements TravelDisutility { this.timeCalculator = timeCalculator; this.normalization = normalization; - this.sigma = plansCalcRouteConfigGroup.getRoutingRandomness(); + this.sigma = routingConfigGroup.getRoutingRandomness(); this.random = sigma != 0 ? MatsimRandom.getLocalInstance() : null; } @@ -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/BicycleTravelDisutilityFactory.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityFactory.java index 0bd8d6aef8c..135bc7e011f 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityFactory.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/BicycleTravelDisutilityFactory.java @@ -21,8 +21,8 @@ import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelDisutility; import org.matsim.core.router.util.TravelTime; @@ -39,17 +39,19 @@ public final class BicycleTravelDisutilityFactory implements TravelDisutilityFac private static final Logger LOG = LogManager.getLogger(BicycleTravelDisutilityFactory.class); @Inject BicycleConfigGroup bicycleConfigGroup; - @Inject PlanCalcScoreConfigGroup cnScoringGroup; - @Inject PlansCalcRouteConfigGroup plansCalcRouteConfigGroup; - + @Inject + ScoringConfigGroup cnScoringGroup; + @Inject + RoutingConfigGroup routingConfigGroup; + private static int normalisationWrnCnt = 0; /* package-private */ BicycleTravelDisutilityFactory(){} - + @Override public TravelDisutility createTravelDisutility(TravelTime timeCalculator) { - double sigma = plansCalcRouteConfigGroup.getRoutingRandomness(); - + double sigma = routingConfigGroup.getRoutingRandomness(); + double normalization = 1; if ( sigma != 0. ) { normalization = 1. / Math.exp(sigma * sigma / 2); @@ -58,6 +60,6 @@ public TravelDisutility createTravelDisutility(TravelTime timeCalculator) { LOG.info(" sigma: " + sigma + "; resulting normalization: " + normalization); } } - return new BicycleTravelDisutility(bicycleConfigGroup, cnScoringGroup, plansCalcRouteConfigGroup, timeCalculator, normalization); + return new BicycleTravelDisutility(bicycleConfigGroup, cnScoringGroup, routingConfigGroup, timeCalculator, normalization); } } 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 17206a51d7b..00000000000 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/MotorizedInteractionEngine.java +++ /dev/null @@ -1,69 +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 - */ -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/network/BicycleOsmNetworkReaderV2.java b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/network/BicycleOsmNetworkReaderV2.java index a8d72b2e70e..cd51b40275b 100644 --- a/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/network/BicycleOsmNetworkReaderV2.java +++ b/contribs/bicycle/src/main/java/org/matsim/contrib/bicycle/network/BicycleOsmNetworkReaderV2.java @@ -52,7 +52,7 @@ public final class BicycleOsmNetworkReaderV2 extends OsmNetworkReader { private ElevationDataParser elevationDataParser; - private final static String TAG_HIGHWAY = "highway"; +// private final static String TAG_HIGHWAY = "highway"; private final static String TAG_BICYCLE = "bicycle"; private final static String TAG_ONEWAYBICYCLE = "oneway:bicycle"; @@ -63,8 +63,6 @@ public final class BicycleOsmNetworkReaderV2 extends OsmNetworkReader { private int countBicycle = 0; private final String bicycleAsTransportModeName; - private final List bicycleWayTags = Arrays.asList(BicycleUtils.CYCLEWAY, BicycleUtils.SURFACE, BicycleUtils.SMOOTHNESS, TAG_BICYCLE, TAG_ONEWAYBICYCLE); - private final int maxWarnCount = 5; private int warnCount = 0; // OSM Reader is using hierarchy until 8, why following hierarchies are not starting with 9? Amit Feb'18 @@ -121,7 +119,8 @@ public BicycleOsmNetworkReaderV2(final Network network, final CoordinateTransfor // If "useHighwayDefaults" is set to true, super sets defaults for all "roads" ("motorway" to "residential", except "service") // and all "link roads" and "living_street" (part of "special road types"). Hierachies 1 to 6 are used. super(network, transformation, useHighwayDefaults, useVspAdjustments); - super.addWayTags(bicycleWayTags); + List bicycleWayTags = Arrays.asList( BicycleUtils.CYCLEWAY, BicycleUtils.SURFACE, BicycleUtils.SMOOTHNESS, TAG_BICYCLE, TAG_ONEWAYBICYCLE ); + super.addWayTags( bicycleWayTags ); // double bicyclePCU = 0.2; // Use the same in your run this.bicycleAsTransportModeName = bicycleAsTransportModeName; @@ -236,13 +235,14 @@ protected void setOrModifyLinkAttributes(Link l, OsmWay way, boolean forwardDire l.getAttributes().putAttribute(BicycleUtils.SURFACE, surface); this.countSurfaceDirect++; } else { + int maxWarnCount = 5; if (highwayType != null) { // it used to be '&&' instead of '||' which will always be false. Most likely, it must be '||'. Amit Feb'18 if (defaults.hierarchy == 3 || defaults.hierarchy == 4) { // 3 = primary, 4 = secondary l.getAttributes().putAttribute(BicycleUtils.SURFACE, "asphalt"); this.countSurfaceInferred++; } else { - if (warnCount <= maxWarnCount){ + if (warnCount <= maxWarnCount ){ LOG.warn("Link did not get a surface."); LOG.warn(Gbl.FUTURE_SUPPRESSED); warnCount++; 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 28313126390..af6d9d58927 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.ScoringConfigGroup.ActivityParams; +import org.matsim.core.config.groups.ScoringConfigGroup.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.config.groups.ReplanningConfigGroup.StrategySettings; +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; @@ -67,14 +71,14 @@ public static void main(String[] args) { throw new RuntimeException("More than one argument was provided. There is no procedure for this situation. Thus aborting!" + " Provide either (1) only a suitable config file or (2) no argument at all to run example with given example of resources folder."); } - config.controler().setLastIteration(100); // Modify if motorized interaction is used + config.controller().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) { - config.controler().setWriteEventsInterval(1); + config.controller().setWriteEventsInterval(1); BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); bicycleConfigGroup.setMarginalUtilityOfInfrastructure_m(-0.0002); @@ -86,29 +90,56 @@ static void fillConfigWithBicycleStandardValues(Config config) { bicycleConfigGroup.setMaxBicycleSpeedForRouting(4.16666666); + List mainModeList = new ArrayList<>(); - mainModeList.add("bicycle"); + mainModeList.add( bicycleConfigGroup.getBicycleMode() ); mainModeList.add(TransportMode.car); config.qsim().setMainModes(mainModeList); - config.strategy().setMaxAgentPlanMemorySize(5); - config.strategy().addStrategySettings( new StrategySettings().setStrategyName("ChangeExpBeta" ).setWeight(0.8 ) ); - config.strategy().addStrategySettings( new StrategySettings().setStrategyName("ReRoute" ).setWeight(0.2 ) ); + config.replanning().setMaxAgentPlanMemorySize(5); + config.replanning().addStrategySettings( new StrategySettings().setStrategyName("ChangeExpBeta" ).setWeight(0.8 ) ); + config.replanning().addStrategySettings( new StrategySettings().setStrategyName("ReRoute" ).setWeight(0.2 ) ); - config.planCalcScore().addActivityParams( new ActivityParams("home").setTypicalDuration(12*60*60 ) ); - config.planCalcScore().addActivityParams( new ActivityParams("work").setTypicalDuration(8*60*60 ) ); + config.scoring().addActivityParams( new ActivityParams("home").setTypicalDuration(12*60*60 ) ); + config.scoring().addActivityParams( new ActivityParams("work").setTypicalDuration(8*60*60 ) ); - config.planCalcScore().addModeParams( new ModeParams("bicycle").setConstant(0. ).setMarginalUtilityOfDistance(-0.0004 ).setMarginalUtilityOfTraveling(-6.0 ).setMonetaryDistanceRate(0. ) ); + config.scoring().addModeParams( new ModeParams("bicycle").setConstant(0. ).setMarginalUtilityOfDistance(-0.0004 ).setMarginalUtilityOfTraveling(-6.0 ).setMonetaryDistanceRate(0. ) ); - config.plansCalcRoute().setNetworkModes(mainModeList); + config.routing().setNetworkModes(mainModeList); } - public void run(Config config, boolean considerMotorizedInteraction) { + public void run(Config config ) { config.global().setNumberOfThreads(1); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.routing().setRoutingRandomness(3.); + + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); + + final String bicycle = bicycleConfigGroup.getBicycleMode(); + + 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 ) ) + .setNetworkMode( bicycle ).setMaximumVelocity(4.16666666 ).setPcuEquivalents(0.25 ) ); - config.plansCalcRoute().setRoutingRandomness(3.); + Controler controler = new Controler(scenario); + controler.addOverridingModule(new BicycleModule() ); + + controler.run(); + } + public void runWithOwnScoring(Config config, boolean considerMotorizedInteraction) { + config.global().setNumberOfThreads(1); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.routing().setRoutingRandomness(3.); if (considerMotorizedInteraction) { BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); @@ -127,7 +158,30 @@ public void run(Config config, boolean considerMotorizedInteraction) { 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/BicycleLinkSpeedCalculatorTest.java b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorTest.java index da6a7e22a57..1af7f1aec93 100644 --- a/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorTest.java +++ b/contribs/bicycle/src/test/java/org/matsim/contrib/bicycle/BicycleLinkSpeedCalculatorTest.java @@ -1,15 +1,29 @@ package org.matsim.contrib.bicycle; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; -import org.matsim.core.mobsim.qsim.qnetsimengine.QVehicle; -import org.matsim.core.mobsim.qsim.qnetsimengine.QVehicleImpl; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Injector; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.mobsim.framework.MobsimTimer; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.QSimBuilder; +import org.matsim.core.mobsim.qsim.interfaces.AgentCounter; +import org.matsim.core.mobsim.qsim.qnetsimengine.*; +import org.matsim.core.mobsim.qsim.qnetsimengine.linkspeedcalculator.LinkSpeedCalculator; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.testcases.MatsimTestUtils; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; @@ -20,47 +34,27 @@ import static org.junit.Assert.assertTrue; public class BicycleLinkSpeedCalculatorTest { - + @Rule public MatsimTestUtils utils = new MatsimTestUtils(); private static final double MAX_BICYCLE_SPEED = 15; - private final BicycleConfigGroup configGroup = new BicycleConfigGroup(); + private final Config config = ConfigUtils.createConfig(); + private BicycleConfigGroup configGroup; private final Network unusedNetwork = NetworkUtils.createNetwork(); @Before public void before() { + configGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); configGroup.setMaxBicycleSpeedForRouting(MAX_BICYCLE_SPEED); configGroup.setBicycleMode("bike"); } - @Test - public void getMaximumVelocity_noBike() { - Link link = createLinkWithNoGradientAndNoSpecialSurface(); - VehicleType type = VehicleUtils.createVehicleType(Id.create("no-bike", VehicleType.class ) ); - type.setMaximumVelocity(link.getFreespeed() / 2); // less than the link's freespeed - QVehicle vehicle = new QVehicleImpl(VehicleUtils.createVehicle(Id.createVehicleId(1), type)); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); - - double speed = calculator.getMaximumVelocity(vehicle, link, 1); - - assertEquals(type.getMaximumVelocity(), speed, 0.0); - } - - @Test - public void getMaximumVelocity_noBike_vehicleFasterThanFreespeed() { - Link link = createLinkWithNoGradientAndNoSpecialSurface(); - VehicleType type = VehicleUtils.createVehicleType(Id.create("no-bike", VehicleType.class ) ); - type.setMaximumVelocity(link.getFreespeed() * 10); // more than the link's freespeed - QVehicle vehicle = new QVehicleImpl(VehicleUtils.createVehicle(Id.createVehicleId(1), type)); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); - - double speed = calculator.getMaximumVelocity(vehicle, link, 1); - - assertEquals(link.getFreespeed(), speed, 0.0); - } + // The more general parts of the test (testing different car behavior) were moved to SpeedCalculatorTest in the core, because there it was + // possible to make something package-accessible. kai, jun'23 private static Vehicle createVehicle(double maxVelocity, long id) { VehicleType type = VehicleUtils.createVehicleType(Id.create("bike", VehicleType.class)); type.setMaximumVelocity(maxVelocity); + type.setNetworkMode( "bike" ); return VehicleUtils.createVehicle(Id.createVehicleId(id), type); } @@ -69,7 +63,7 @@ public void getMaximumVelocity_bike() { Link link = createLinkWithNoGradientAndNoSpecialSurface(); QVehicle vehicle = new QVehicleImpl(createVehicle(link.getFreespeed() * 0.5, 1)); // higher speed than the default - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double speed = calculator.getMaximumVelocity(vehicle, link, 1); @@ -81,7 +75,7 @@ public void getMaximumVelocity_bike_fasterThanFreespeed() { Link link = createLinkWithNoGradientAndNoSpecialSurface(); QVehicle vehicle = new QVehicleImpl(createVehicle(link.getFreespeed() * 2, 1)); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double speed = calculator.getMaximumVelocity(vehicle, link, 1); @@ -92,7 +86,7 @@ public void getMaximumVelocity_bike_fasterThanFreespeed() { public void getMaximumVelocityForLink_bikeIsNull() { Link link = createLinkWithNoGradientAndNoSpecialSurface(); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double speed = calculator.getMaximumVelocityForLink(link, null); @@ -106,7 +100,7 @@ public void getMaximumVelocityForLink_withGradient() { Link linkWithGradient = createLink(100, "paved", "not-a-cycle-way", 1.0); Vehicle vehicle = createVehicle(linkForComparison.getFreespeed() * 0.5, 1); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double comparisonSpeed = calculator.getMaximumVelocityForLink(linkForComparison, vehicle); double gradientSpeed = calculator.getMaximumVelocityForLink(linkWithGradient, vehicle); @@ -121,7 +115,7 @@ public void getMaximumVelocityForLink_withReducedSpeedFactor() { Link linkWithReducedSpeed = createLink(0, "paved", "not-a-cycle-way", 0.5); Vehicle vehicle = createVehicle(linkForComparison.getFreespeed() * 0.5, 1); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double comparisonSpeed = calculator.getMaximumVelocityForLink(linkForComparison, vehicle); double gradientSpeed = calculator.getMaximumVelocityForLink(linkWithReducedSpeed, vehicle); @@ -134,7 +128,7 @@ public void getMaximumVelocityForLink_noSpeedFactor() { var link = createLinkWithNoGradientAndNoSpecialSurface(); var vehicle = createVehicle(link.getFreespeed() + 1, 1); - var calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + var calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); var speed = calculator.getMaximumVelocityForLink(link, vehicle); @@ -148,7 +142,7 @@ public void getMaximumVelocityForLink_withRoughSurface() { Link linkWithCobbleStone = createLink(0, "cobblestone", "not-a-cycle-way", 1.0); Vehicle vehicle = createVehicle(linkForComparison.getFreespeed(), 1); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double comparisonSpeed = calculator.getMaximumVelocityForLink(linkForComparison, vehicle); double gradientSpeed = calculator.getMaximumVelocityForLink(linkWithCobbleStone, vehicle); @@ -190,7 +184,7 @@ public void getMaximumVelocityForLink_withCycleWay() { Link linkWithCobbleStone = createLink(0, "some-surface", BicycleUtils.CYCLEWAY, 1.0); Vehicle vehicle = createVehicle(linkForComparison.getFreespeed(), 1); - BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(configGroup); + BicycleLinkSpeedCalculatorDefaultImpl calculator = new BicycleLinkSpeedCalculatorDefaultImpl(config); double comparisonSpeed = calculator.getMaximumVelocityForLink(linkForComparison, vehicle); double gradientSpeed = calculator.getMaximumVelocityForLink(linkWithCobbleStone, vehicle); 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 99164a0903f..0f2dea83a91 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,28 +31,32 @@ 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.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ScoringConfigGroup.ActivityParams; +import org.matsim.core.config.groups.ScoringConfigGroup.ModeParams; +import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource; +import org.matsim.core.config.groups.ReplanningConfigGroup.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; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.VehiclesFactory; import java.util.ArrayList; import java.util.HashMap; @@ -67,27 +70,28 @@ /** * @author dziemke */ -public class -BicycleTest { +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); // 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(0); - config.controler().setCreateGraphs(false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().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"; @@ -109,19 +113,21 @@ 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); // Links 4-8 and 13-17 have cobblestones config.network().setInputFile("network_cobblestone.xml"); + config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); - config.controler().setCreateGraphs(false); - new RunBicycleExample().run(config, false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().setCreateGraphs(false); + + new RunBicycleExample().run(config ); { Scenario scenarioReference = ScenarioUtils.createScenario( ConfigUtils.createConfig() ); Scenario scenarioCurrent = ScenarioUtils.createScenario( ConfigUtils.createConfig() ); @@ -140,19 +146,19 @@ 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); // Links 4-8 and 13-17 are pedestrian zones config.network().setInputFile("network_pedestrian.xml"); config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); - config.controler().setCreateGraphs(false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().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"; @@ -169,19 +175,19 @@ 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); // Links 2-4/8-10 and 11-13/17-19 have cycle lanes (cycleway=lane) config.network().setInputFile("network_lane.xml"); config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); - config.controler().setCreateGraphs(false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().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"; @@ -198,19 +204,19 @@ 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); // Nodes 5-8 have a z-coordinate > 0, i.e. the links leading to those nodes have a slope config.network().setInputFile("network_gradient.xml"); config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); - config.controler().setCreateGraphs(false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().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"; @@ -227,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); @@ -235,12 +241,12 @@ public void testGradientLane() { // and links 4-5 and 13-14 have cycle lanes config.network().setInputFile("network_gradient_lane.xml"); config.plans().setInputFile("population_1200.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); - config.controler().setCreateGraphs(false); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); + config.controller().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"; @@ -257,22 +263,22 @@ 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); // 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.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); // 10 iterations - config.controler().setLastIteration(10); - config.controler().setWriteEventsInterval(10); - config.controler().setWritePlansInterval(10); - config.controler().setCreateGraphs(false); + config.controller().setLastIteration(10); + config.controller().setWriteEventsInterval(10); + config.controller().setWritePlansInterval(10); + config.controller().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"; @@ -287,111 +293,223 @@ 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)); + + 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 ); - // Activate link-based scoring - BicycleConfigGroup bicycleConfigGroup = (BicycleConfigGroup) config.getModules().get("bicycle"); - bicycleConfigGroup.setBicycleScoringType(BicycleScoringType.linkBased); + // the following comes from inlining RunBicycleExample, which we need since we need to modify scenario data: + config.global().setNumberOfThreads(1 ); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists ); - // Interaction with motor vehicles - new RunBicycleExample().run(config, true); + config.routing().setRoutingRandomness(3. ); - 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)); + final String bicycle = bicycleConfigGroup.getBicycleMode(); - Scenario scenarioReference = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + 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/"); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); config.addModule(new BicycleConfigGroup()); - config.controler().setWriteEventsInterval(0); - config.controler().setWritePlansInterval(0); - config.controler().setCreateGraphs(false); - config.controler().setDumpDataAtEnd(false); + config.controller().setWriteEventsInterval(0); + config.controller().setWritePlansInterval(0); + config.controller().setCreateGraphs(false); + config.controller().setDumpDataAtEnd(false); config.qsim().setStartTime(6. * 3600.); config.qsim().setEndTime(10. * 3600.); List mainModeList = new ArrayList<>(); - mainModeList.add("bicycle"); + mainModeList.add( bicycleMode ); mainModeList.add(TransportMode.car); config.qsim().setMainModes(mainModeList); - config.strategy().setMaxAgentPlanMemorySize(5); + config.replanning().setMaxAgentPlanMemorySize(5); { StrategySettings strategySettings = new StrategySettings(); strategySettings.setStrategyName("ChangeExpBeta"); strategySettings.setWeight(1.0); - config.strategy().addStrategySettings(strategySettings); + config.replanning().addStrategySettings(strategySettings); } ActivityParams homeActivity = new ActivityParams("home"); homeActivity.setTypicalDuration(12*60*60); - config.planCalcScore().addActivityParams(homeActivity); + config.scoring().addActivityParams(homeActivity); ActivityParams workActivity = new ActivityParams("work"); workActivity.setTypicalDuration(8*60*60); - config.planCalcScore().addActivityParams(workActivity); + config.scoring().addActivityParams(workActivity); - ModeParams bicycle = new ModeParams("bicycle"); + ModeParams bicycle = new ModeParams( bicycleMode ); bicycle.setConstant(0.); bicycle.setMarginalUtilityOfDistance(-0.0004); // util/m bicycle.setMarginalUtilityOfTraveling(-6.0); // util/h bicycle.setMonetaryDistanceRate(0.); - config.planCalcScore().addModeParams(bicycle); + config.scoring().addModeParams(bicycle); - config.plansCalcRoute().setNetworkModes(mainModeList); + config.routing().setNetworkModes(mainModeList); // link 2 has infrastructure speed factor = 1.0, all other links 0.01 config.network().setInputFile("network_infrastructure-speed-factor.xml"); config.plans().setInputFile("population_4.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); + + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); config.global().setNumberOfThreads(1); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + + config.routing().setRoutingRandomness(3.); - config.plansCalcRoute().setRoutingRandomness(3.); + // --- Scenario scenario = ScenarioUtils.loadScenario(config); + VehiclesFactory vf = scenario.getVehicles().getFactory(); - VehicleType car = VehicleUtils.getFactory().createVehicleType(Id.create(TransportMode.car, VehicleType.class)); - scenario.getVehicles().addVehicleType(car); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create(TransportMode.car, VehicleType.class ) ) ); - VehicleType bicycleVehType = VehicleUtils.getFactory().createVehicleType(Id.create("bicycle", VehicleType.class)); - bicycleVehType.setMaximumVelocity(25.0/3.6); - bicycleVehType.setPcuEquivalents(0.25); - scenario.getVehicles().addVehicleType(bicycleVehType); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create( bicycleMode, VehicleType.class ) ) + .setNetworkMode( bicycleMode ).setMaximumVelocity(25.0/3.6 ).setPcuEquivalents(0.25 ) ); - scenario.getConfig().qsim().setVehiclesSource(QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData); + scenario.getConfig().qsim().setVehiclesSource( VehiclesSource.modeVehicleTypesFromVehiclesData ); + + // --- Controler controler = new Controler(scenario); - ((AllowsConfiguration) controler).addOverridingModule(new BicycleModule() ); + controler.addOverridingModule(new BicycleModule() ); LinkDemandEventHandler linkHandler = new LinkDemandEventHandler(); @@ -408,23 +526,23 @@ public void install() { Assert.assertEquals("All bicycle users should use the longest but fastest route where the bicycle infrastructur speed factor is set to 1.0", 3, linkHandler.getLinkId2demand().get(Id.createLinkId("2")), MatsimTestUtils.EPSILON); Assert.assertEquals("Only the car user should use the shortest route", 1, linkHandler.getLinkId2demand().get(Id.createLinkId("6")), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time (car user)", Math.ceil( 10000 / (13.88) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("6")).get(0), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time (bicycle user)", 1.0 + Math.ceil( 13000 / (25.0 /3.6) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("2")).get(0), MatsimTestUtils.EPSILON); Assert.assertEquals("Wrong travel time (bicycle user)", 1.0 + Math.ceil( 13000 / (25.0 /3.6) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("2")).get(1), MatsimTestUtils.EPSILON); Assert.assertEquals("Wrong travel time (bicycle user)", 1.0 + Math.ceil( 13000 / (25.0 /3.6) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("2")).get(2), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time (car user)", Math.ceil( 10000 / (13.88) ), linkHandler.getLinkId2travelTimes().get(Id.createLinkId("6")).get(0), MatsimTestUtils.EPSILON); + } @Test public void testInfrastructureSpeedFactorDistanceMoreRelevantThanTravelTime() { - Config config = ConfigUtils.createConfig("./src/main/resources/bicycle_example/"); - config.addModule(new BicycleConfigGroup()); + Config config = ConfigUtils.createConfig(utils.getClassInputDirectory() ); + BicycleConfigGroup bicycleConfigGroup = ConfigUtils.addOrGetModule( config, BicycleConfigGroup.class ); - config.controler().setWriteEventsInterval(0); - config.controler().setWritePlansInterval(0); - config.controler().setCreateGraphs(false); - config.controler().setDumpDataAtEnd(false); + config.controller().setWriteEventsInterval(0); + config.controller().setWritePlansInterval(0); + config.controller().setCreateGraphs(false); + config.controller().setDumpDataAtEnd(false); config.qsim().setStartTime(6. * 3600.); config.qsim().setEndTime(14. * 3600.); @@ -433,64 +551,59 @@ public void testInfrastructureSpeedFactorDistanceMoreRelevantThanTravelTime() { mainModeList.add(TransportMode.car); config.qsim().setMainModes(mainModeList); - config.strategy().setMaxAgentPlanMemorySize(5); + config.replanning().setMaxAgentPlanMemorySize(5); { StrategySettings strategySettings = new StrategySettings(); strategySettings.setStrategyName("ChangeExpBeta"); strategySettings.setWeight(1.0); - config.strategy().addStrategySettings(strategySettings); + config.replanning().addStrategySettings(strategySettings); } ActivityParams homeActivity = new ActivityParams("home"); homeActivity.setTypicalDuration(12*60*60); - config.planCalcScore().addActivityParams(homeActivity); + config.scoring().addActivityParams(homeActivity); ActivityParams workActivity = new ActivityParams("work"); workActivity.setTypicalDuration(8*60*60); - config.planCalcScore().addActivityParams(workActivity); + config.scoring().addActivityParams(workActivity); ModeParams bicycle = new ModeParams("bicycle"); bicycle.setConstant(0.); bicycle.setMarginalUtilityOfDistance(-999999); // util/m bicycle.setMarginalUtilityOfTraveling(-6.0); // util/h bicycle.setMonetaryDistanceRate(0.); - config.planCalcScore().addModeParams(bicycle); + config.scoring().addModeParams(bicycle); - config.plansCalcRoute().setNetworkModes(mainModeList); + config.routing().setNetworkModes(mainModeList); // link 2 has infrastructure speed factor = 1.0, all other links 0.01 config.network().setInputFile("network_infrastructure-speed-factor.xml"); config.plans().setInputFile("population_4.xml"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setLastIteration(0); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setLastIteration(0); config.global().setNumberOfThreads(1); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.plansCalcRoute().setRoutingRandomness(3.); + config.routing().setRoutingRandomness(3.); Scenario scenario = ScenarioUtils.loadScenario(config); + var vf = scenario.getVehicles().getFactory(); - VehicleType car = VehicleUtils.getFactory().createVehicleType(Id.create(TransportMode.car, VehicleType.class)); - scenario.getVehicles().addVehicleType(car); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create(TransportMode.car, VehicleType.class ) ) ); - VehicleType bicycleVehType = VehicleUtils.getFactory().createVehicleType(Id.create("bicycle", VehicleType.class)); - bicycleVehType.setMaximumVelocity(25.0/3.6); - bicycleVehType.setPcuEquivalents(0.25); - scenario.getVehicles().addVehicleType(bicycleVehType); + scenario.getVehicles().addVehicleType( vf.createVehicleType(Id.create("bicycle", VehicleType.class ) ) + .setMaximumVelocity(25.0/3.6 ).setPcuEquivalents(0.25 ).setNetworkMode( bicycleConfigGroup.getBicycleMode() ) ); - scenario.getConfig().qsim().setVehiclesSource(QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData); + scenario.getConfig().qsim().setVehiclesSource( VehiclesSource.modeVehicleTypesFromVehiclesData ); Controler controler = new Controler(scenario); - ((AllowsConfiguration) controler).addOverridingModule(new BicycleModule() ); + controler.addOverridingModule(new BicycleModule() ); LinkDemandEventHandler linkHandler = new LinkDemandEventHandler(); controler.addOverridingModule(new AbstractModule() { - - @Override - public void install() { + @Override public void install() { this.addEventHandlerBinding().toInstance(linkHandler); } }); @@ -504,6 +617,26 @@ public void install() { 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.controller().setOverwriteFileSetting( OverwriteFileSetting.deleteDirectoryIfExists ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setLastIteration( lastIteration ); + config.controller().setLastIteration( lastIteration ); + config.controller().setWriteEventsInterval( 10 ); + config.controller().setWritePlansInterval( 10 ); + config.controller().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/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsBuilderImpl.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsBuilderImpl.java index 5ed2c548e58..12a9ae8bfbc 100644 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsBuilderImpl.java +++ b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsBuilderImpl.java @@ -46,7 +46,7 @@ private CadytsBuilderImpl(){} // do not instantiate public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(final Config config, final Counts occupCounts, LookUpItemFromId lookUp, Class idType) { - + if (occupCounts.getCounts().size() == 0) { log.warn("Counts container is empty."); } @@ -54,7 +54,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina CadytsConfigGroup cadytsConfig = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class); AnalyticalCalibrator matsimCalibrator = buildCalibrator(config); - + int multiple = cadytsConfig.getTimeBinSize() / 3600 ; // e.g. "3" when timeBinSize_s = 3*3600 = 10800 // If I remember correctly, the following is trying to get around the fact that the counts time bins are fixed at hourly, but we want to @@ -64,16 +64,16 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina // yyyy However, it seems that some of this did not work: We are using "hourly" counts, and dividing the multi-hour values by // the number of hours. ??????? - + // yyyyyy I am currently of the opinion that the multi-hour version should be decoupled from the counts format. There is a // cadyts file format which allows setting mult-hour measurements, and that seems a lot more direct than trying to use a file // format/data structure which is really not meant for this. kai, dec'13 - + //add counts data into calibrator int numberOfAddedMeasurements = 0 ; for (Map.Entry, Count> entry : occupCounts.getCounts().entrySet()) { // (loop over all counting "items" (usually locations/stations) - + T item = lookUp.getItem(Id.create(entry.getKey(), idType)) ; if ( item==null ) { throw new RuntimeException("item is null; entry=" + entry + " idType=" + idType ) ; @@ -83,7 +83,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina double count = -1 ; for (Volume volume : entry.getValue().getVolumes().values()){ // (loop over the different time slots) - + if ( timeBinIndex%multiple == 0 ) { // (i.e. first timeBinIndex belonging to given bin) @@ -95,7 +95,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina log.warn( " NOT adding measurement: timeBinIndex: " + timeBinIndex + "; multiple: " + multiple ) ; } else { // (i.e. last timeBinIndex belonging to given bin) - + int endTimeOfBin_s = volume.getHourOfDayStartingWithOne()*3600 - 1 ; if ( !( cadytsConfig.getStartTime() <= startTimeOfBin_s && endTimeOfBin_s <= cadytsConfig.getEndTime()) ) { log.warn( " NOT adding measurement: cadytsConfigStartTime: " + cadytsConfig.getStartTime() + "; startTimeOfBin_s: " + startTimeOfBin_s + @@ -105,7 +105,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina // matsimCalibrator.addMeasurement(item, startTimeOfBin_s, endTimeOfBin_s, count/multiple, SingleLinkMeasurement.TYPE.FLOW_VEH_H); matsimCalibrator.addMeasurement(item, startTimeOfBin_s, endTimeOfBin_s, count, SingleLinkMeasurement.TYPE.COUNT_VEH ); - // changed this from FLOW_VEH_H to COUNT_VEH on 30/jul/2012 since this is no longer "hourly". + // changed this from FLOW_VEH_H to COUNT_VEH on 30/jul/2012 since this is no longer "hourly". // kai/manuel, jul'12 // Despite the above comment, I am finding this with FLOW_VEH_H. Why? kai, feb'13 // yyyyyy For the test case, this seems to produce weird results. The expected counts are 1 and 5, the expected result is 0 and 4. @@ -120,7 +120,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina if ( numberOfAddedMeasurements==0 ) { log.warn("No measurements were added."); } - + if ( matsimCalibrator.getProportionalAssignment() ) { throw new RuntimeException("Gunnar says that this may not work so do not set to true. kai, sep'14") ; } @@ -129,7 +129,7 @@ public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(fina public static AnalyticalCalibrator buildCalibrator(final Config config) { CadytsConfigGroup cadytsConfig = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class ) ; - + //get timeBinSize_s and validate it if ((Time.MIDNIGHT % cadytsConfig.getTimeBinSize())!= 0 ){ throw new RuntimeException("Cadyts requires a divisor of 86400 as time bin size value ."); @@ -138,10 +138,10 @@ public static AnalyticalCalibrator buildCalibrator(final Config config) { throw new RuntimeException("At this point, time bin sizes need to be multiples of 3600. This is not a restriction " + "of Cadyts, but of the counts file format, which only allows for hourly inputs") ; } - - + + AnalyticalCalibrator matsimCalibrator = new AnalyticalCalibrator<>( - config.controler().getOutputDirectory() + "/cadyts.log", + config.controller().getOutputDirectory() + "/cadyts.log", MatsimRandom.getLocalInstance().nextLong(),cadytsConfig.getTimeBinSize() ) ; @@ -159,9 +159,9 @@ public static AnalyticalCalibrator buildCalibrator(final Config config) { if ( matsimCalibrator.getBruteForce() ) { log.warn("setting bruteForce==true for calibrator, but this won't do anything in the way the cadyts matsim integration is set up. kai, mar'14") ; } - - matsimCalibrator.setStatisticsFile(config.controler().getOutputDirectory() + "/calibration-stats.txt"); + + matsimCalibrator.setStatisticsFile(config.controller().getOutputDirectory() + "/calibration-stats.txt"); return matsimCalibrator; } - + } diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsPlanChanger.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsPlanChanger.java index 4f0be74b8d4..f3a02a0bf4a 100644 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsPlanChanger.java +++ b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsPlanChanger.java @@ -43,9 +43,9 @@ public class CadytsPlanChanger implements PlanSelector { public CadytsPlanChanger(Scenario scenario, CadytsContextI cadytsContext) { this.cadytsContext = cadytsContext; - this.beta = scenario.getConfig().planCalcScore().getBrainExpBeta() ; + this.beta = scenario.getConfig().scoring().getBrainExpBeta() ; } - + @Override public Plan selectPlan(final HasPlansAndId person) { final Plan currentPlan = person.getSelectedPlan(); @@ -90,4 +90,4 @@ public Plan selectPlan(final HasPlansAndId person) { public void setCadytsWeight(double cadytsWeight) { this.cadytsWeight = cadytsWeight; } -} \ No newline at end of file +} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsScoring.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsScoring.java index a19b191361d..e2841dd19b8 100644 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsScoring.java +++ b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/general/CadytsScoring.java @@ -45,7 +45,7 @@ public CadytsScoring(final Plan plan, Config config, final CadytsContextI con this.plansTranslator = context.getPlansTranslator(); this.matsimCalibrator = context.getCalibrator(); this.plan = plan; - this.beta = config.planCalcScore().getBrainExpBeta(); + this.beta = config.scoring().getBrainExpBeta(); } @Override diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsBuilderImplGT.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsBuilderImplGT.java deleted file mode 100644 index 1165c45aed3..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsBuilderImplGT.java +++ /dev/null @@ -1,87 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.cadyts.pt; - -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Id; -import org.matsim.contrib.cadyts.general.CadytsBuilderImpl; -import org.matsim.contrib.cadyts.general.LookUpItemFromId; -import org.matsim.core.config.Config; -import org.matsim.counts.Count; -import org.matsim.counts.Counts; -import org.matsim.counts.Volume; - -import cadyts.calibrators.analytical.AnalyticalCalibrator; -import cadyts.measurements.SingleLinkMeasurement; - -/** - * @author nagel - * @author mrieser - */ -public final class CadytsBuilderImplGT { - // yyyy why "GT"? Is this a typo and should have been PT (or Pt, to be consistent with other classes)? kai, feb'20 - - // yy I also don't know where and when this is used to I am commenting it out to see if someone complains. kai, feb'20 - -// private static Logger log = LogManager.getLogger( CadytsBuilderImplGT.class ) ; -// -// private CadytsBuilderImplGT(){} // do not instantiate -// -// public static AnalyticalCalibrator buildCalibratorAndAddMeasurements(final Config config, final Counts occupCounts, -// LookUpItemFromId lookUp, Class idType ) { -// -// if (occupCounts.getCounts().size() == 0) { -// log.warn("Counts container is empty."); -// } -// -// AnalyticalCalibrator matsimCalibrator = CadytsBuilderImpl.buildCalibrator(config); -// -// //add counts data into calibrator -// int numberOfAddedMeasurements = 0 ; -// for (Map.Entry, Count> entry : occupCounts.getCounts().entrySet()) { -// // (loop over all counting "items" (usually locations/stations) -// -// T item = lookUp.getItem(Id.create(entry.getKey(), idType)) ; -// if ( item==null ) { -// throw new RuntimeException("item is null; entry=" + entry + " idType=" + idType ) ; -// } -// -// double sum = 0; -// for (Volume volume : entry.getValue().getVolumes().values()){ -// // (loop over the different time slots) -// sum += volume.getValue() ; -// } -// numberOfAddedMeasurements++ ; -// matsimCalibrator.addMeasurement(item, 0, 86400, sum, SingleLinkMeasurement.TYPE.COUNT_VEH ); -// } -// -// if ( numberOfAddedMeasurements==0 ) { -// log.warn("No measurements were added."); -// } -// -// if ( matsimCalibrator.getProportionalAssignment() ) { -// throw new RuntimeException("Gunnar says that this may not work so do not set to true. kai, sep'14") ; -// } -// return matsimCalibrator; -// } -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtContext.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtContext.java deleted file mode 100644 index e1f918c4ac1..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtContext.java +++ /dev/null @@ -1,265 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2013 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.contrib.cadyts.pt; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -import jakarta.inject.Inject; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.analysis.IterationStopWatch; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.cadyts.general.CadytsBuilderImpl; -import org.matsim.contrib.cadyts.general.CadytsConfigGroup; -import org.matsim.contrib.cadyts.general.CadytsContextI; -import org.matsim.contrib.cadyts.general.CadytsCostOffsetsXMLFileIO; -import org.matsim.contrib.cadyts.general.PlansTranslator; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.PtCountsConfigGroup; -import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.controler.events.AfterMobsimEvent; -import org.matsim.core.controler.events.BeforeMobsimEvent; -import org.matsim.core.controler.events.IterationEndsEvent; -import org.matsim.core.controler.events.StartupEvent; -import org.matsim.core.controler.listener.AfterMobsimListener; -import org.matsim.core.controler.listener.BeforeMobsimListener; -import org.matsim.core.controler.listener.IterationEndsListener; -import org.matsim.core.controler.listener.StartupListener; -import org.matsim.core.utils.geometry.CoordinateTransformation; -import org.matsim.core.utils.geometry.transformations.TransformationFactory; -import org.matsim.counts.Counts; -import org.matsim.counts.MatsimCountsReader; -import org.matsim.pt.transitSchedule.api.TransitLine; -import org.matsim.pt.transitSchedule.api.TransitRoute; -import org.matsim.pt.transitSchedule.api.TransitRouteStop; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; - -import cadyts.calibrators.analytical.AnalyticalCalibrator; -import cadyts.measurements.SingleLinkMeasurement.TYPE; -import cadyts.supply.SimResults; - -/** - * @author nagel - * - */ -public class CadytsPtContext implements StartupListener, IterationEndsListener, BeforeMobsimListener, AfterMobsimListener, -CadytsContextI { - // can be/remain public as long as constructor is package-private. kai, feb'20 - - private final static Logger log = LogManager.getLogger(CadytsPtContext.class); - - private final static String LINKOFFSET_FILENAME = "linkCostOffsets.xml"; - private static final String FLOWANALYSIS_FILENAME = "flowAnalysis.txt"; - private static final String OCCUPANCYANALYSIS_FILENAME = "cadytsPtOccupancyAnalysis.txt"; - - private AnalyticalCalibrator calibrator = null; - private final SimResults simResults; - private final Counts occupCounts = new Counts<>(); - // private final Counts boardCounts = new Counts(); - // private final Counts alightCounts = new Counts(); - private final CadytsPtOccupancyAnalyzerI cadytsPtOccupAnalyzer; - private PtPlanToPlanStepBasedOnEvents ptStep ; - - private CadytsConfigGroup cadytsConfig; - private EventsManager events; - private Scenario scenario; - private OutputDirectoryHierarchy controlerIO; - private IterationStopWatch stopWatch; - - @Inject - CadytsPtContext(final Config config, EventsManager events, Scenario scenario, OutputDirectoryHierarchy controlerIO, - IterationStopWatch stopWatch, final CadytsPtOccupancyAnalyzerI cadytsPtOccupancyAnalyzer ) { - this.events = events; - this.scenario = scenario; - this.controlerIO = controlerIO; - this.stopWatch = stopWatch; - cadytsConfig = (CadytsConfigGroup) config.getModule(CadytsConfigGroup.GROUP_NAME); - this.cadytsPtOccupAnalyzer = cadytsPtOccupancyAnalyzer ; - - // === prepare the structure which extracts the measurements from the simulation: - // since there is already some other method, we just need to write a wrapper. - -// this.cadytsPtOccupAnalyzer = new CadytsPtOccupancyAnalyzer(CadytsPtOccupancyAnalyzer.toTransitLineIdSet(cadytsConfig.getCalibratedItems()), cadytsConfig.getTimeBinSize() ); - events.addHandler(this.cadytsPtOccupAnalyzer); - - this.simResults = new SimResults() { - private static final long serialVersionUID = 1L; - @Override - public double getSimValue(TransitStopFacility stop, int startTime_s, int endTime_s, TYPE type) { - final int timeBinSize_s = cadytsConfig.getTimeBinSize() ; - final double countsScaleFactor = config.ptCounts().getCountsScaleFactor() ; - double retval = 0. ; - switch ( type ) { - case COUNT_VEH: - retval = cadytsPtOccupAnalyzer.getOccupancyVolumeForStopAndTime(stop.getId(), startTime_s) * countsScaleFactor ; - break; - case FLOW_VEH_H: - int multiple = timeBinSize_s / 3600 ; // e.g. "3" when timeBinSize_s = 3*3600 = 10800 - retval = cadytsPtOccupAnalyzer.getOccupancyVolumeForStopAndTime(stop.getId(), startTime_s) * countsScaleFactor / multiple ; - break; - default: - throw new RuntimeException("not implemented ...") ; - } -// if ( retval != 0. ) { -// log.warn("retval=" + retval ); -// } - return retval ; - } - @Override - public String toString() { - return cadytsPtOccupAnalyzer.toString() ; - } - } ; - // === end wrapper === - - } - - @Override - public void notifyStartup(StartupEvent event) { - - // === prepare the calibrator by giving measurements to it: - String occupancyCountsFilename = scenario.getConfig().ptCounts().getOccupancyCountsFileName(); - new MatsimCountsReader(this.occupCounts).readFile(occupancyCountsFilename); - - // === build the calibrator: - this.calibrator = CadytsBuilderImpl.buildCalibratorAndAddMeasurements(scenario.getConfig(), this.occupCounts, new TransitStopFacilityLookUp(scenario) , TransitStopFacility.class); - - // === find out which plan is contributing what to each measurement: - this.ptStep = new PtPlanToPlanStepBasedOnEvents<>(scenario, CadytsPtOccupancyAnalyzer.toTransitLineIdSet(cadytsConfig.getCalibratedLines())); - events.addHandler(ptStep); - } - - @Override - public void notifyBeforeMobsim(final BeforeMobsimEvent event) { - this.cadytsPtOccupAnalyzer.reset(event.getIteration()); - for (Person person : scenario.getPopulation().getPersons().values()) { - this.calibrator.addToDemand(ptStep.getCadytsPlan(person.getSelectedPlan())); - } - } - - @Override - public void notifyAfterMobsim(final AfterMobsimEvent event) { - int it = event.getIteration(); - - // Get all stations of all analyzed lines and invoke the method write to get all information of them - Set> stopIds = new HashSet<>(); - for ( String pseudoLineId : this.cadytsConfig.getCalibratedLines()) { - Id lineId = Id.create(pseudoLineId, TransitLine.class); - TransitLine line = scenario.getTransitSchedule().getTransitLines().get(lineId); - for (TransitRoute route : line.getRoutes().values()) { - for (TransitRouteStop stop : route.getStops()) { - stopIds.add(stop.getStopFacility().getId()); - } - } - } - String outFile = controlerIO.getIterationFilename(it, OCCUPANCYANALYSIS_FILENAME); - this.cadytsPtOccupAnalyzer.writeResultsForSelectedStopIds(outFile, this.occupCounts, stopIds); - } - - @Override - public void notifyIterationEnds(final IterationEndsEvent event) { - if (cadytsConfig.isWriteAnalysisFile()) { - String analysisFilepath = controlerIO.getIterationFilename(event.getIteration(), FLOWANALYSIS_FILENAME); - this.calibrator.setFlowAnalysisFile(analysisFilepath); - } - - this.calibrator.afterNetworkLoading(this.simResults); - - // write some output - String filename = controlerIO.getIterationFilename(event.getIteration(), LINKOFFSET_FILENAME); - try { - new CadytsCostOffsetsXMLFileIO<>(new TransitStopFacilityLookUp(scenario), TransitStopFacility.class) - .write(filename, this.calibrator.getLinkCostOffsets()); - } catch (IOException e) { - log.error("Could not write link cost offsets!", e); - } - - generateAndWriteCountsComparisons(event); - } - - // =========================================================================================================================== - // private methods & pure delegate methods only below this line - - private void generateAndWriteCountsComparisons(final IterationEndsEvent event) { - if ( this.cadytsConfig.getTimeBinSize()!=3600 ) { - log.warn("generateAndWriteCountsComparisons() does not work when time bin size != 3600. See comments in code. Skipping the comparison ..." ) ; - return ; - // yyyy there are some conceptual problems behind this which are not resolved: - // () There should reasonably be two methods: one describing what cadyts _thinks_ it is comparing, and one that just - // compares the output. There is one methods writing simCountCompare..., and then this one here - // writing cadytsSimCountCompare... . It is not clarified which one is doing which. - // () The method that just compares the output should not rely on cadyts but compute its own observations. -- - // Unfortunately, this collides with the fact that the time bin size is part of the cadyts configuration. This is, in the end, a - // consequence of the fact that the Counts format assumes hourly counts (other than cadyts, which reasonably allows the - // specify the time span for every observation separately). - // kai, feb'13 - } - - - PtCountsConfigGroup ptCountsConfig = scenario.getConfig().ptCounts(); - if (ptCountsConfig.getOccupancyCountsFileName() == null) { // yyyy this check should reasonably also be done in isActiveInThisIteration. kai,oct'10 - log.warn("generateAndWriteCountsComparisons() does not work since occupancy counts file name not given ") ; - return ; - } - int iter = event.getIteration(); - - stopWatch.beginOperation("compare with pt counts"); - - Network network = scenario.getNetwork(); - CadytsPtCountsComparisonAlgorithm ccaOccupancy = new CadytsPtCountsComparisonAlgorithm(this.cadytsPtOccupAnalyzer, - this.occupCounts, network, scenario.getConfig().ptCounts().getCountsScaleFactor()); - - Double distanceFilter = ptCountsConfig.getDistanceFilter(); - String distanceFilterCenterNodeId = ptCountsConfig.getDistanceFilterCenterNode(); - if ((distanceFilter != null) && (distanceFilterCenterNodeId != null)) { - ccaOccupancy.setDistanceFilter(distanceFilter, distanceFilterCenterNodeId); - } - - ccaOccupancy.calculateComparison(); - - String outputFormat = ptCountsConfig.getOutputFormat(); - if (outputFormat.contains("txt") || outputFormat.contains("all")) { - // As far as I can tell, this file is written twice, the other times without the "cadyts" part. kai, feb'13 - // yyyyyy As far as I can tell, the version here is wrong as soon as the time bin is different from 3600.--?? kai, feb'13 - // See near beginning of method. kai, feb'13 - ccaOccupancy.write(controlerIO.getIterationFilename(iter, "cadytsSimCountCompareOccupancy.txt")); - } - - stopWatch.endOperation("compare with pt counts"); - } - - @Override - public AnalyticalCalibrator getCalibrator() { - return calibrator; - } - - @Override - public PlansTranslator getPlansTranslator() { - return ptStep; - } - -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtCountsComparisonAlgorithm.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtCountsComparisonAlgorithm.java deleted file mode 100644 index 6327a98260f..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtCountsComparisonAlgorithm.java +++ /dev/null @@ -1,185 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.cadyts.pt; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Identifiable; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.network.Node; -import org.matsim.core.utils.geometry.CoordUtils; -import org.matsim.counts.Count; -import org.matsim.counts.CountSimComparison; -import org.matsim.counts.CountSimComparisonImpl; -import org.matsim.counts.Counts; -import org.matsim.counts.Volume; -import org.matsim.pt.counts.SimpleWriter; - -/** - * This is a modified copy of CountsComparisonAlgorithm, in order to realize the same functionality - * for pt counts. - */ -final class CadytsPtCountsComparisonAlgorithm { - /** - * The StopAttributes of the simulation - */ - private final CadytsPtOccupancyAnalyzerI oa; - /** - * The counts object - */ - Counts counts; - // needed in CadytsErrorPlot - - /** - * The result list - */ - private final List countSimComp; - - private Node distanceFilterNode = null; - - private Double distanceFilter = null; - - private final Network network; - - double countsScaleFactor; // needed in CadytsErrorPlot - - final static Logger log = LogManager.getLogger(CadytsPtCountsComparisonAlgorithm.class); - - StringBuffer content = new StringBuffer(); - - CadytsPtCountsComparisonAlgorithm(final CadytsPtOccupancyAnalyzerI oa, final Counts counts, final Network network, final double countsScaleFactor) { - this.oa = oa; - this.counts = counts; - this.countSimComp = new ArrayList(); - this.network = network; - this.countsScaleFactor = countsScaleFactor; - } - - /** - * Creates the List with the counts vs sim values stored in the countAttribute Attribute of this - * class. - */ - final String STR_NOVOLUMES = "No volumes for stop: "; - final String STR_STOPID = "StopId :\t"; - final String STR_HEAD = "\nhour\tsimVal\tscaledSimVal\tcountVal\n"; - final char CHR_HT = '\t'; - final char CHR_NL = '\n'; - - public void calculateComparison() { - double countValue; - for (Count count : this.counts.getCounts().values()) { - Id stopId = count.getId(); - if (!isInRange(count.getCoord())) { - continue; - } - int[] volumes = this.getVolumesForStop(stopId); - if (volumes == null) { - log.warn(this.STR_NOVOLUMES + stopId); - continue; - } else /* volumes!=null */if (volumes.length == 0) { - log.warn(this.STR_NOVOLUMES + stopId); - continue; - } - - this.content.append(this.STR_STOPID); - this.content.append(stopId.toString()); - this.content.append(this.STR_HEAD); - - for (int hour = 1; hour <= volumes.length; hour++) { - // real volumes: - Volume volume = count.getVolume(hour); - if (volume != null) { - - this.content.append(hour); - this.content.append(this.CHR_HT); - - countValue = volume.getValue(); - double simValue = volumes[hour - 1]; - - this.content.append(simValue); - this.content.append(this.CHR_HT); - - simValue *= this.countsScaleFactor; - - this.content.append(simValue); - this.content.append(this.CHR_HT); - this.content.append(countValue); - this.content.append(this.CHR_NL); - - this.countSimComp.add(new CountSimComparisonImpl(stopId, hour, countValue, simValue)); - - } else { - countValue = 0.0; - } - - } - } - } - - int[] getVolumesForStop(final Id stopId) { - return this.oa.getOccupancyVolumesForStop(stopId); - } - - /** - * - * @param stopCoord - * @return true if the Link with the given Id is not farther away than the - * distance specified by the distance filter from the center node of the filter. - */ - boolean isInRange(final Coord stopCoord) { - if ((this.distanceFilterNode == null) || (this.distanceFilter == null)) { - return true; - } - - double dist = CoordUtils.calcEuclideanDistance(stopCoord, this.distanceFilterNode.getCoord()); - return dist < this.distanceFilter.doubleValue(); - } - - /** - * - * @return the result list - */ - public List getComparison() { - return this.countSimComp; - } - - /** - * Set a distance filter, dropping everything out which is not in the distance given in meters - * around the given Node Id. - * - * @param distance - * @param nodeId - */ - public void setDistanceFilter(final Double distance, final String nodeId) { - this.distanceFilter = distance; - this.distanceFilterNode = this.network.getNodes().get(Id.create(nodeId, Node.class)); - } - - public void write(final String outputFilename) { - final SimpleWriter simpleWriter = new SimpleWriter(outputFilename); - simpleWriter.write(this.content.toString()); - simpleWriter.close(); - } -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtModule.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtModule.java deleted file mode 100644 index d9f459047b3..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtModule.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.matsim.contrib.cadyts.pt; - - -import org.matsim.core.controler.AbstractModule; - -import jakarta.inject.Singleton; - -public class CadytsPtModule extends AbstractModule { - @Override - public void install() { - bind(CadytsPtContext.class).in( Singleton.class ); - addControlerListenerBinding().to(CadytsPtContext.class); - bind(CadytsPtOccupancyAnalyzerI.class).to(CadytsPtOccupancyAnalyzer.class); - } -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzer.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzer.java deleted file mode 100644 index c116c652e0c..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzer.java +++ /dev/null @@ -1,299 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.cadyts.pt; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import jakarta.inject.Inject; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.PersonEntersVehicleEvent; -import org.matsim.api.core.v01.events.PersonLeavesVehicleEvent; -import org.matsim.api.core.v01.events.TransitDriverStartsEvent; -import org.matsim.api.core.v01.network.Link; -import org.matsim.contrib.cadyts.general.CadytsConfigGroup; -import org.matsim.core.api.experimental.events.VehicleArrivesAtFacilityEvent; -import org.matsim.core.api.experimental.events.VehicleDepartsAtFacilityEvent; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.utils.misc.Time; -import org.matsim.counts.Count; -import org.matsim.counts.Counts; -import org.matsim.counts.Volume; -import org.matsim.pt.counts.SimpleWriter; -import org.matsim.pt.transitSchedule.api.TransitLine; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; -import org.matsim.vehicles.Vehicle; - -/** - * Collects occupancy data of transit-line stations - *

- * This is probably similar to code elsewhere. However, it makes some sense to keep this here since the correct workings of cadyts - * (obviously) depends on the fact that the counts are actually what it thinks, and so it makes sense to decouple this from the upstream - * counting method and leave it here. kai, sep'13 - */ -final class CadytsPtOccupancyAnalyzer implements CadytsPtOccupancyAnalyzerI { - // can be/remain public as long as constructor is package-private. kai, feb'20 - - private final int timeBinSize, maxSlotIndex; - private final double maxTime; - private Map, int[]> occupancies; // Map< stopFacilityId,value[]> - private final Map, Id> vehStops = new HashMap<>(); // Map< vehId,stopFacilityId> - private final Map, Integer> vehPassengers = new HashMap<>(); // Map - private StringBuffer occupancyRecord = new StringBuffer("time\tvehId\tStopId\tno.ofPassengersInVeh\n"); - private final Set analyzedTransitDrivers = new HashSet<>(); - private final Set analyzedTransitVehicles = new HashSet<>(); - private final Set> calibratedLines; - - @Inject - CadytsPtOccupancyAnalyzer( Config config ) { - CadytsConfigGroup ccc = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.class ) ; - - this.calibratedLines = toTransitLineIdSet( ccc.getCalibratedLines() ) ; - this.timeBinSize = ccc.getTimeBinSize() ; - - this.maxTime = Time.MIDNIGHT-1; //24 * 3600 - 1; - // (yy not completely clear if it might be better to use 24*this.timeBimSize, but it is overall not so great - // to have this hardcoded. kai/manuel, jul'12) - - this.maxSlotIndex = ((int) this.maxTime) / this.timeBinSize + 1; - this.occupancies = new HashMap<>(); - } - - @Override - public void reset(final int iteration) { - this.occupancies.clear(); - this.vehStops.clear(); - this.vehPassengers.clear(); - this.occupancyRecord = new StringBuffer("time\tvehId\tStopId\tno.ofPassengersInVeh\n"); - this.analyzedTransitDrivers.clear(); - this.analyzedTransitVehicles.clear(); - } - - @Override - public void handleEvent(final TransitDriverStartsEvent event) { - if (this.calibratedLines.contains(event.getTransitLineId())) { - this.analyzedTransitDrivers.add(event.getDriverId()); - this.analyzedTransitVehicles.add(event.getVehicleId()); - } - } - - @Override - public void handleEvent(final PersonEntersVehicleEvent event) { - if (this.analyzedTransitDrivers.contains(event.getPersonId()) || !this.analyzedTransitVehicles.contains(event.getVehicleId())) { - return; // ignore transit drivers or persons entering non-(analyzed-)transit vehicles - } - - // ------------------veh_passenger- (for occupancy)----------------- - Id vehId = event.getVehicleId(); - Id stopId = this.vehStops.get(vehId); - double time = event.getTime(); - Integer nPassengers = this.vehPassengers.get(vehId); - this.vehPassengers.put(vehId, (nPassengers != null) ? (nPassengers + 1) : 1); - this.occupancyRecord.append("time :\t").append(time).append(" veh :\t").append(vehId).append(" has Passenger\t").append(this.vehPassengers.get(vehId)).append(" \tat stop :\t").append(stopId).append(" ENTERING PERSON :\t").append(event.getPersonId()).append("\n"); - } - - @Override - public void handleEvent(final PersonLeavesVehicleEvent event) { - if (this.analyzedTransitDrivers.contains(event.getPersonId()) || !this.analyzedTransitVehicles.contains(event.getVehicleId())) { - return; // ignore transit drivers or persons entering non-(analyzed-)transit vehicles - } - - // ----------------veh_passenger-(for occupancy)-------------------------- - Id vehId = event.getVehicleId(); - double time = event.getTime(); - Integer nPassengers = this.vehPassengers.get(vehId); - if (nPassengers == null) { - throw new RuntimeException("null passenger-No. in vehicle ?"); - } - this.vehPassengers.put(vehId, nPassengers - 1); - if (this.vehPassengers.get(vehId) == 0) { - this.vehPassengers.remove(vehId); - } - Integer passengers = this.vehPassengers.get(vehId); - this.occupancyRecord.append("time :\t").append(time).append(" veh :\t").append(vehId).append(" has Passenger\t").append((passengers != null) ? passengers : 0).append("\n"); - } - - @Override - public void handleEvent(final VehicleDepartsAtFacilityEvent event) { - Id vehId = event.getVehicleId(); - Id facId = event.getFacilityId(); - - // -----------------------occupancy-------------------------------- - this.vehStops.remove(vehId); - int[] occupancyAtStop = this.occupancies.get(facId); - if (occupancyAtStop == null) { // no previous departure from this stop, therefore no occupancy - // record yet. Create this: - occupancyAtStop = new int[this.maxSlotIndex + 1]; - this.occupancies.put(facId, occupancyAtStop); - } - - Integer noPassengersInVeh = this.vehPassengers.get(vehId); - - if (noPassengersInVeh != null) { - occupancyAtStop[this.getTimeSlotIndex(event.getTime())] += noPassengersInVeh; - this.occupancyRecord.append(event.getTime()); - this.occupancyRecord.append("\t"); - this.occupancyRecord.append(vehId); - this.occupancyRecord.append("\t"); - this.occupancyRecord.append(facId); - this.occupancyRecord.append("\t"); - this.occupancyRecord.append(noPassengersInVeh); - this.occupancyRecord.append("\n"); - } - } - - @Override - public void handleEvent(final VehicleArrivesAtFacilityEvent event) { - Id stopId = event.getFacilityId(); - - this.vehStops.put(event.getVehicleId(), stopId); - // (constructing a table with vehId as key, and stopId as value; constructed when veh arrives at - // stop; necessary - // since personEnters/LeavesVehicle does not carry stop id) - } - - private int getTimeSlotIndex(final double time) { - if (time > this.maxTime) { - return this.maxSlotIndex; - } - return ((int) time / this.timeBinSize); - } - - /** - * @param stopId - * @return Array containing the number of passengers in bus after the transfer at the stop - * {@code stopId} per time bin, starting with time bin 0 from 0 seconds to - * (timeBinSize-1)seconds. - */ - @Override - public int[] getOccupancyVolumesForStop(final Id stopId) { - return this.occupancies.get(stopId); - } - /* (non-Javadoc) - * @see org.matsim.contrib.cadyts.pt.CadytsPtOccupancyAnalyzerI#getOccupancyVolumeForStopAndTime(org.matsim.api.core.v01.Id, int) - */ - @Override - public int getOccupancyVolumeForStopAndTime(final Id stopId, final int time_s ) { - if ( this.occupancies.get(stopId) != null ) { - int timeBinIndex = getTimeSlotIndex( time_s ) ; - return this.occupancies.get(stopId)[timeBinIndex] ; - } else { - return 0 ; - } - } - - public Set> getOccupancyStopIds() { - return this.occupancies.keySet(); - } - - @Override - public void writeResultsForSelectedStopIds(final String filename, final Counts occupCounts, final Collection> stopIds) { - SimpleWriter writer = new SimpleWriter(filename); - - final String TAB = "\t"; - final String NL = "\n"; - - // write header - writer.write("stopId\t"); - for (int i = 0; i < 24; i++) { - writer.write("oc" + i + "-" + (i + 1) + TAB); - } - for (int i = 0; i < 24; i++) { - writer.write("scalSim" + i + "-" + (i + 1) + TAB); - } - writer.write("coordinate\tcsId\n"); - - // write content - for (Id stopId : stopIds) { - // get count data - Count count = occupCounts.getCounts().get(Id.create(stopId, TransitStopFacility.class)); - if (!occupCounts.getCounts().containsKey(Id.create(stopId, TransitStopFacility.class))) { - continue; - } - - // get sim-Values - int[] ocuppancy = this.occupancies.get(stopId); - writer.write(stopId.toString() + TAB); - for (int i = 0; i < ocuppancy.length; i++) { - Volume v = count.getVolume(i + 1); - if (v != null) { - writer.write(v.getValue() + TAB); - } else { - writer.write("n/a" + TAB); - } - } - for (int anOcuppancy : ocuppancy) { - writer.write((anOcuppancy) + TAB); - } - writer.write(count.getCoord().toString() + TAB + count.getCsLabel() + NL); - } - writer.write(this.occupancyRecord.toString()); - writer.close(); - } - - @Override - public String toString() { - final StringBuilder stringBuffer2 = new StringBuilder(); - final String STOPID = "stopId: "; - final String VALUES = "; values:"; - final char TAB = '\t'; - final char RETURN = '\n'; - - for (Id stopId : this.getOccupancyStopIds()) { // Only occupancy! - StringBuilder stringBuffer = new StringBuilder(); - stringBuffer.append(STOPID); - stringBuffer.append(stopId); - stringBuffer.append(VALUES); - - boolean hasValues = false; // only prints stops with volumes > 0 - int[] values = this.getOccupancyVolumesForStop(stopId); - - for (int value : values) { - hasValues = hasValues || (value > 0); - - stringBuffer.append(TAB); - stringBuffer.append(value); - } - stringBuffer.append(RETURN); - if (hasValues) - stringBuffer2.append(stringBuffer.toString()); - - } - return stringBuffer2.toString(); - } - - public static Set> toTransitLineIdSet(Set list) { - Set> converted = new LinkedHashSet<>(); - - for ( String id : list) { - converted.add(Id.create(id, TransitLine.class)); - } - - return converted; - } - - -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzerI.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzerI.java deleted file mode 100644 index 069fe358fd9..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/CadytsPtOccupancyAnalyzerI.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.matsim.contrib.cadyts.pt; - -import java.util.Collection; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler; -import org.matsim.api.core.v01.events.handler.PersonLeavesVehicleEventHandler; -import org.matsim.api.core.v01.events.handler.TransitDriverStartsEventHandler; -import org.matsim.api.core.v01.network.Link; -import org.matsim.core.api.experimental.events.handler.VehicleArrivesAtFacilityEventHandler; -import org.matsim.core.api.experimental.events.handler.VehicleDepartsAtFacilityEventHandler; -import org.matsim.counts.Counts; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; - -public interface CadytsPtOccupancyAnalyzerI extends TransitDriverStartsEventHandler, PersonEntersVehicleEventHandler, -PersonLeavesVehicleEventHandler, VehicleArrivesAtFacilityEventHandler, VehicleDepartsAtFacilityEventHandler { - - int getOccupancyVolumeForStopAndTime(Id stopId, int time_s); - - void writeResultsForSelectedStopIds(String filename, Counts occupCounts, Collection> stopIds); - - int[] getOccupancyVolumesForStop(Id stopId); - -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/PtPlanToPlanStepBasedOnEvents.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/PtPlanToPlanStepBasedOnEvents.java deleted file mode 100644 index f9ab329ad5a..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/PtPlanToPlanStepBasedOnEvents.java +++ /dev/null @@ -1,238 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.cadyts.pt; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -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.events.PersonEntersVehicleEvent; -import org.matsim.api.core.v01.events.PersonLeavesVehicleEvent; -import org.matsim.api.core.v01.events.TransitDriverStartsEvent; -import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler; -import org.matsim.api.core.v01.events.handler.PersonLeavesVehicleEventHandler; -import org.matsim.api.core.v01.events.handler.TransitDriverStartsEventHandler; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.contrib.cadyts.general.PlansTranslator; -import org.matsim.core.api.experimental.events.VehicleDepartsAtFacilityEvent; -import org.matsim.core.api.experimental.events.handler.VehicleDepartsAtFacilityEventHandler; -import org.matsim.core.scenario.MutableScenario; -import org.matsim.pt.transitSchedule.api.TransitLine; -import org.matsim.pt.transitSchedule.api.TransitSchedule; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; - -import cadyts.demand.PlanBuilder; - -/*package*/ class PtPlanToPlanStepBasedOnEvents implements TransitDriverStartsEventHandler, PersonEntersVehicleEventHandler, - PersonLeavesVehicleEventHandler, VehicleDepartsAtFacilityEventHandler, PlansTranslator { - private static final Logger log = LogManager.getLogger(PtPlanToPlanStepBasedOnEvents.class); - - private final Scenario sc; - private final TransitSchedule schedule; - - private final Map> personsFromVehId = new HashMap<>(); - - private int iteration = -1; - - // this is _only_ there for output: - Set plansEverSeen = new HashSet<>(); - - private static final String STR_PLANSTEPFACTORY = "planStepFactory"; - private static final String STR_ITERATION = "iteration"; - // (better to do it this way since when plans are removed, this additional info is removed as well) - - private final Set transitDrivers = new HashSet<>(); - private final Set transitVehicles = new HashSet<>(); - private final Set> calibratedLines; - - PtPlanToPlanStepBasedOnEvents(final Scenario sc, final Set> calibratedLines) { - this.sc = sc; - this.schedule = ((MutableScenario) sc).getTransitSchedule(); - this.calibratedLines = calibratedLines; - } - - private long plansFound = 0; - private long plansNotFound = 0; - - @Override - public final cadyts.demand.Plan getCadytsPlan(final Plan plan) { - @SuppressWarnings("unchecked") // getting stuff from custom attributes has to be untyped. - PlanBuilder planStepFactory = (PlanBuilder) plan.getCustomAttributes().get(STR_PLANSTEPFACTORY); - if (planStepFactory == null) { - this.plansNotFound++; - return null; - } - this.plansFound++; - final cadyts.demand.Plan planSteps = planStepFactory.getResult(); - return planSteps; - } - - @Override - public void reset(final int it) { - this.iteration = it; - - log.warn("found " + this.plansFound + " out of " + (this.plansFound + this.plansNotFound) + " (" - + (100. * this.plansFound / (this.plansFound + this.plansNotFound)) + "%)"); - log.warn("(above values may both be at zero for a couple of iterations if multiple plans per agent all have no score)"); - - long nPlans = 0 ; - long nMemorizedPlans = 0 ; - - for ( Person person : this.sc.getPopulation().getPersons().values() ) { - for ( Plan plan : person.getPlans() ) { - nPlans ++ ; - @SuppressWarnings("unchecked") // getting stuff from custom attributes has to be untyped. - PlanBuilder planStepFactory = (PlanBuilder) plan.getCustomAttributes().get(STR_PLANSTEPFACTORY); - if ( planStepFactory!=null ) { - nMemorizedPlans ++ ; - } - } - } - - log.warn( "nPlans=" + nPlans + ", nMemorizedPlans=" + nMemorizedPlans ); - - this.personsFromVehId.clear(); - this.transitDrivers.clear(); - this.transitVehicles.clear(); - } - - @Override - public void handleEvent(final TransitDriverStartsEvent event) { - if (this.calibratedLines.contains(event.getTransitLineId())) { - this.transitDrivers.add(event.getDriverId()); - this.transitVehicles.add(event.getVehicleId()); - } - } - - @Override - public void handleEvent(final PersonEntersVehicleEvent event) { - if (this.transitDrivers.contains(event.getPersonId()) || !this.transitVehicles.contains(event.getVehicleId())) { - return; // ignore transit drivers or persons entering non-(analyzed-)transit vehicles - } - addPersonToVehicleContainer(event.getPersonId(), event.getVehicleId()); - } - - @Override - public void handleEvent(final PersonLeavesVehicleEvent event) { - if (this.transitDrivers.contains(event.getPersonId()) || !this.transitVehicles.contains(event.getVehicleId())) { - return; // ignore transit drivers or persons entering non-(analyzed-)transit vehicles - } - removePersonFromVehicleContainer(event.getPersonId(), event.getVehicleId()); - } - - @Override - public void handleEvent(final VehicleDepartsAtFacilityEvent event) { - double time = event.getTime(); - Id vehId = event.getVehicleId(); - Id facId = event.getFacilityId(); - if (this.personsFromVehId.get(vehId) == null) { - // (means nobody has entered the vehicle yet) - return; - } - TransitStopFacility fac = this.schedule.getFacilities().get(facId); - - for (Id personId : this.personsFromVehId.get(vehId)) { - // get the "Person" behind the id: - Person person = this.sc.getPopulation().getPersons().get(personId); - - // get the selected plan: - Plan selectedPlan = person.getSelectedPlan(); - - // get the planStepFactory for the plan (or create one): - PlanBuilder tmpPlanStepFactory = getPlanStepFactoryForPlan(selectedPlan); - - if (tmpPlanStepFactory != null) { - // add the "turn" to the planStepfactory - tmpPlanStepFactory.addTurn(fac, (int) time); - } - } - } - - // ################################################################################### - // only private functions below here (low level functionality) - - private void addPersonToVehicleContainer(final Id personId, final Id vehId) { - // get the personsContainer that belongs to the vehicle: - Collection personsInVehicle = this.personsFromVehId.get(vehId); - - if (personsInVehicle == null) { - // means does not exist yet - personsInVehicle = new ArrayList<>(); - this.personsFromVehId.put(vehId, personsInVehicle); - } - - personsInVehicle.add(personId); - } - - private void removePersonFromVehicleContainer(final Id personId, final Id vehId) { - // get the personsContainer that belongs to the vehicle: - Collection personsInVehicle = this.personsFromVehId.get(vehId); - - if (personsInVehicle == null) { - throw new RuntimeException("should not be possible: person should enter before leaving, and then construct the container"); - } - - // remove the person from the personsContainer: - personsInVehicle.remove(personId); // linear time operation; a HashMap might be better. - } - - private PlanBuilder getPlanStepFactoryForPlan(final Plan selectedPlan) { - PlanBuilder planStepFactory = null; - - planStepFactory = (PlanBuilder) selectedPlan.getCustomAttributes().get(STR_PLANSTEPFACTORY); - Integer factoryIteration = (Integer) selectedPlan.getCustomAttributes().get(STR_ITERATION); - if (planStepFactory == null || factoryIteration == null || factoryIteration != this.iteration) { - // attach the iteration number to the plan: - selectedPlan.getCustomAttributes().put(STR_ITERATION, this.iteration); - - // construct a new PlanBulder and attach it to the plan: - planStepFactory = new PlanBuilder<>(); - selectedPlan.getCustomAttributes().put(STR_PLANSTEPFACTORY, planStepFactory); - - // memorize the plan as being seen: - this.plansEverSeen.add(selectedPlan); - } - - return planStepFactory; - } - - static void printCadytsPlan(final cadyts.demand.Plan cadytsPlan) { - // prints Cadyts plan - String sepCadStr = "==printing Cadyts Plan=="; - System.err.println(sepCadStr); - if (cadytsPlan != null) { - for (int ii = 0; ii < cadytsPlan.size(); ii++) { - cadyts.demand.PlanStep cadytsPlanStep = cadytsPlan.getStep(ii); - System.err.println("stopId" + cadytsPlanStep.getLink().getId() + " time: " + cadytsPlanStep.getEntryTime_s()); - } - } else { - System.err.println(" cadyts plan is null "); - } - } - -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/TransitStopFacilityLookUp.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/TransitStopFacilityLookUp.java deleted file mode 100644 index 651b2a99c66..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/TransitStopFacilityLookUp.java +++ /dev/null @@ -1,48 +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.cadyts.pt; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.cadyts.general.LookUpItemFromId; -import org.matsim.pt.transitSchedule.api.TransitSchedule; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; - -/** - * @author nagel - * - */ -class TransitStopFacilityLookUp implements LookUpItemFromId { - - private TransitSchedule schedule; - - public TransitStopFacilityLookUp( Scenario sc ) { - this.schedule = sc.getTransitSchedule() ; - } - - public TransitStopFacilityLookUp( TransitSchedule schedule ) { - this.schedule = schedule ; - } - - @Override - public TransitStopFacility getItem(Id id) { - return this.schedule.getFacilities().get(id); - } - -} diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/package-info.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/package-info.java deleted file mode 100644 index 0b45babafa5..00000000000 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/pt/package-info.java +++ /dev/null @@ -1,74 +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 * - * * - * *********************************************************************** */ -/** - * Integrates automatic calibration upon public transport line occupancies using Cadyts into MATSim. - * - *

Entry point / How to use

- *
    - *
  • Use org.matsim.contrib.cadyts.pt.CadytsPtPlanStrategy as a replanning strategy.
  • - *
  • Add the following configuration parameters (approximately; this has changed since inception; - * check auto-generated config comments; use second config dump for that): - *
    - * <module name="cadytsPt">
    - *   <param name="startTime" value="05:00:00" />
    - *
    - *   <param name="endTime" value="21:00:00" />
    - *
    - *   <!-- Comma-separated list of transit lines to be calibrated. ->
    - *   <param name="calibratedLines" value="M44" />
    - *
    - *   <param name="writeAnalysisFile" value="false" />
    - *
    - *   <!-- see cadyts documentation for the meaning of the following values. -->
    - *   <param name="regressionInertia" value="0.95" />
    - *   <param name="minFlowStddevVehH" value="8.0" />
    - *   <param name="freezeIteration" value="2147483647" />
    - *   <param name="preparatoryIterations" value="1" />
    - *   <param name="varianceScale" value="1.0" />
    - *   <param name="useBruteForce" value="true" />
    - *
    - * </module>
    - * These parameters are defined in {@link org.matsim.contrib.cadyts.general.CadytsConfigGroup} - * - *
  • - *
  • There also needs to be a ptCounts entry, something like: - *
    - *
    - * 	<module name="ptCounts">
    - *		<param name="inputOccupancyCountsFile" value="path-to-counts-file" />
    - *	</module>
    - * And (obviously) a working ptCounts file. - *
  • - *
  • It is a unfortunate that the counts file takes measurements in hourly values, while cadyts takes arbitrary time spans. - * (The cadyts convention seems more powerful, thus we did not want to reduce it to the "Counts" convention.) - * As long as the cadytsPt timeBinSize is set to 3600, things should be straightforward, and there is also (I think) no - * problem if there are measurements for times outside the cadytsPt startTime/endTime interval. yyyy Unfortunately, - * I cannot remember how the counts file is interpreted once the cadytsPt timeBinSize is set to something different: Does the - * Counts file than think in terms of time bins rather than in terms of hours? In fact, I think not; rather, it is probably as - * follows: Counts still refer to hours. If, say, you use timeBinSize of 7200 and start/endTime as 05:00/09:00, then the code - * will aggregate counts from the 6th and 7th hour into one time bin, etc. If things do not correspond, the code will probably - * complain. See CadytsBuilder.buildCalibrator, since there are some consistency checks. (kai, oct'12) - *
  • Typically, {@link org.matsim.contrib.cadyts.pt.CadytsPtPlanStrategy} should be the only - * plan strategy being used. So it is advised to first run the simulation until every - * agent has a few (different) plans, and then do some iterations using only the - * calibration strategy.
  • - *
- * - */ -package org.matsim.contrib.cadyts.pt; diff --git a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/run/RunCadyts4CarExample.java b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/run/RunCadyts4CarExample.java index 4ad3a583ad5..1e1e8d5e95e 100644 --- a/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/run/RunCadyts4CarExample.java +++ b/contribs/cadytsIntegration/src/main/java/org/matsim/contrib/cadyts/run/RunCadyts4CarExample.java @@ -74,7 +74,7 @@ public ScoringFunction createNewScoringFunction(Person person) { scoringFunctionAccumulator.addScoringFunction(new CharyparNagelAgentStuckScoring(params)); final CadytsScoring scoringFunction = new CadytsScoring<>(person.getSelectedPlan(), config, cadytsContext); - scoringFunction.setWeightOfCadytsCorrection(30. * config.planCalcScore().getBrainExpBeta()) ; + scoringFunction.setWeightOfCadytsCorrection(30. * config.scoring().getBrainExpBeta()) ; scoringFunctionAccumulator.addScoringFunction(scoringFunction ); return scoringFunctionAccumulator; diff --git a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarIT.java b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarIT.java index b07564f0a8f..6b72ab1b664 100644 --- a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarIT.java +++ b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarIT.java @@ -38,9 +38,9 @@ import org.matsim.contrib.cadyts.utils.CalibrationStatReader; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup.MobsimType; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ControllerConfigGroup.MobsimType; +import org.matsim.core.config.groups.ScoringConfigGroup.ActivityParams; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.ControlerDefaultsModule; import org.matsim.core.controler.ControlerI; @@ -89,12 +89,12 @@ public final void testInitialization() { String outputDir = this.utils.getOutputDirectory(); Config config = createTestConfig(inputDir, outputDir); - config.controler().setLastIteration(0); + config.controller().setLastIteration(0); StrategySettings strategySettings = new StrategySettings(Id.create(1, StrategySettings.class)); strategySettings.setStrategyName(CADYTS_STRATEGY_NAME) ; strategySettings.setWeight(1.0) ; - config.strategy().addStrategySettings(strategySettings); + config.replanning().addStrategySettings(strategySettings); CadytsConfigGroup cadytsCar = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class); // cadytsCar.addParam("startTime", "04:00:00"); cadytsCar.setStartTime( 4*3600 ); @@ -164,11 +164,11 @@ public final void testCalibrationAsScoring() throws IOException { final Config config = createTestConfig(inputDir, outputDir); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); - config.planCalcScore().setBrainExpBeta(beta); + config.scoring().setBrainExpBeta(beta); - config.strategy().addStrategySettings( new StrategySettings().setStrategyName( DefaultSelector.ChangeExpBeta ).setWeight( 1.0 ) ); + config.replanning().addStrategySettings( new StrategySettings().setStrategyName( DefaultSelector.ChangeExpBeta ).setWeight( 1.0 ) ); // === @@ -320,22 +320,22 @@ private static Config createTestConfig(String inputDir, String outputDir) { config.global().setRandomSeed(4711) ; config.network().setInputFile(inputDir + "network.xml") ; config.plans().setInputFile(inputDir + "plans5.xml") ; - config.controler().setFirstIteration(1) ; - config.controler().setLastIteration(10) ; - config.controler().setOutputDirectory(outputDir) ; - config.controler().setWriteEventsInterval(1) ; - config.controler().setMobsim(MobsimType.qsim.toString()) ; + config.controller().setFirstIteration(1) ; + config.controller().setLastIteration(10) ; + config.controller().setOutputDirectory(outputDir) ; + config.controller().setWriteEventsInterval(1) ; + config.controller().setMobsim(MobsimType.qsim.toString()) ; config.qsim().setFlowCapFactor(1.) ; config.qsim().setStorageCapFactor(1.) ; config.qsim().setStuckTime(10.) ; config.qsim().setRemoveStuckVehicles(false) ; { ActivityParams params = new ActivityParams("h") ; - config.planCalcScore().addActivityParams(params ) ; + config.scoring().addActivityParams(params ) ; params.setTypicalDuration(12*60*60.) ; }{ ActivityParams params = new ActivityParams("w") ; - config.planCalcScore().addActivityParams(params ) ; + config.scoring().addActivityParams(params ) ; params.setTypicalDuration(8*60*60.) ; } config.counts().setInputFile(inputDir + "counts5.xml"); diff --git a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarWithPtScenarioIT.java b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarWithPtScenarioIT.java index 538a47ae917..58f3b81fb2f 100644 --- a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarWithPtScenarioIT.java +++ b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/car/CadytsCarWithPtScenarioIT.java @@ -25,8 +25,8 @@ public class CadytsCarWithPtScenarioIT { @Test @Ignore public void testCadytsWithPtVehicles() { final Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("siouxfalls-2014"), "config_default.xml")); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); - config.controler().setLastIteration(0); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); + config.controller().setLastIteration(0); final Scenario scenario = ScenarioUtils.loadScenario(config); final Counts calibrationCounts = new Counts<>(); final Id testLink = Id.createLinkId("6_1"); diff --git a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CadytsPtIT.java b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CadytsPtIT.java deleted file mode 100644 index 9ea725add52..00000000000 --- a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CadytsPtIT.java +++ /dev/null @@ -1,690 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * CadytsIntegrationTest.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2011 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.contrib.cadyts.pt; - -import cadyts.measurements.SingleLinkMeasurement; -import cadyts.utilities.io.tabularFileParser.TabularFileParser; -import cadyts.utilities.misc.DynamicData; -import com.google.inject.Provider; -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.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.cadyts.general.CadytsConfigGroup; -import org.matsim.contrib.cadyts.general.CadytsCostOffsetsXMLFileIO; -import org.matsim.contrib.cadyts.general.CadytsPlanChanger; -import org.matsim.contrib.cadyts.general.CadytsScoring; -import org.matsim.contrib.cadyts.utils.CalibrationStatReader; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigGroup; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup.MobsimType; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; -import org.matsim.core.config.groups.PlansConfigGroup.HandlingOfPlansWithoutRoutingMode; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; -import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.Controler; -import org.matsim.core.mobsim.framework.Mobsim; -import org.matsim.core.mobsim.framework.MobsimFactory; -import org.matsim.core.replanning.PlanStrategy; -import org.matsim.core.replanning.PlanStrategyImpl; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.scoring.ScoringFunction; -import org.matsim.core.scoring.ScoringFunctionFactory; -import org.matsim.core.scoring.SumScoringFunction; -import org.matsim.core.scoring.functions.CharyparNagelActivityScoring; -import org.matsim.core.scoring.functions.CharyparNagelAgentStuckScoring; -import org.matsim.core.scoring.functions.CharyparNagelLegScoring; -import org.matsim.core.scoring.functions.ScoringParameters; -import org.matsim.core.scoring.functions.ScoringParametersForPerson; -import org.matsim.counts.Count; -import org.matsim.counts.Counts; -import org.matsim.counts.MatsimCountsReader; -import org.matsim.pt.transitSchedule.api.TransitSchedule; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; -import org.matsim.testcases.MatsimTestUtils; - -import jakarta.inject.Inject; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -public class CadytsPtIT { - - @Rule - public MatsimTestUtils utils = new MatsimTestUtils(); - - @Test - public final void testInitialization() { - String inputDir = this.utils.getClassInputDirectory(); - - Config config = createTestConfig(inputDir, this.utils.getOutputDirectory()); - config.controler().setLastIteration(0); - StrategySettings stratSets = new StrategySettings(); - stratSets.setStrategyName("ccc") ; - stratSets.setWeight(1.) ; - config.strategy().addStrategySettings(stratSets) ; - - final Scenario scenario = ScenarioUtils.loadScenario(config) ; - final Controler controler = new Controler(scenario); - controler.addOverridingModule(new CadytsPtModule()); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - addPlanStrategyBinding("ccc").toProvider(new jakarta.inject.Provider() { - @Inject CadytsPtContext context; - @Override - public PlanStrategy get() { - return new PlanStrategyImpl(new CadytsPlanChanger<>(scenario, context)); - } - }); - } - }); - - controler.getConfig().controler().setCreateGraphs(false); - controler.getConfig().controler().setWriteEventsInterval(0); - controler.getConfig().controler().setDumpDataAtEnd(true); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - bindMobsim().toProvider(new Provider() { - @Override - public Mobsim get() { - return new DummyMobsimFactory().createMobsim(controler.getScenario(), controler.getEvents()); - } - }); - } - }); - controler.run(); - - CadytsPtContext context = controler.getInjector().getInstance(CadytsPtContext.class); - - //test calibration settings - Assert.assertEquals(true, context.getCalibrator().getBruteForce()); - Assert.assertEquals(false, context.getCalibrator().getCenterRegression()); - Assert.assertEquals(Integer.MAX_VALUE, context.getCalibrator().getFreezeIteration()); - Assert.assertEquals(8.0, context.getCalibrator().getMinStddev(SingleLinkMeasurement.TYPE.FLOW_VEH_H), MatsimTestUtils.EPSILON); - Assert.assertEquals(1, context.getCalibrator().getPreparatoryIterations()); - Assert.assertEquals(0.95, context.getCalibrator().getRegressionInertia(), MatsimTestUtils.EPSILON); - Assert.assertEquals(1.0, context.getCalibrator().getVarianceScale(), MatsimTestUtils.EPSILON); - Assert.assertEquals(3600.0, context.getCalibrator().getTimeBinSize_s(), MatsimTestUtils.EPSILON); - } - - - @Test - public final void testCalibrationAsScoring() throws IOException { - final double beta=30. ; - final int lastIteration = 20 ; - - String inputDir = this.utils.getClassInputDirectory(); - String outputDir = this.utils.getOutputDirectory(); - - final Config config = createTestConfig(inputDir, outputDir); - - config.controler().setLastIteration(lastIteration) ; - - config.planCalcScore().setBrainExpBeta(beta) ; - - StrategySettings stratSets = new StrategySettings() ; - stratSets.setStrategyName("ChangeExpBeta") ; - stratSets.setWeight(1.0) ; - config.strategy().addStrategySettings(stratSets) ; - - // === - - final Controler controler = new Controler(config); - controler.getConfig().controler().setCreateGraphs(false); - controler.addOverridingModule(new CadytsPtModule()); - - controler.setScoringFunctionFactory(new ScoringFunctionFactory() { - @Inject ScoringParametersForPerson parameters; - @Inject Network network; - @Inject CadytsPtContext cContext; - @Override - public ScoringFunction createNewScoringFunction(Person person) { - final ScoringParameters params = parameters.getScoringParameters(person); - - SumScoringFunction scoringFunctionAccumulator = new SumScoringFunction(); - scoringFunctionAccumulator.addScoringFunction(new CharyparNagelLegScoring(params, network, config.transit().getTransitModes())); - scoringFunctionAccumulator.addScoringFunction(new CharyparNagelActivityScoring(params)) ; - scoringFunctionAccumulator.addScoringFunction(new CharyparNagelAgentStuckScoring(params)); - - final CadytsScoring scoringFunction = new CadytsScoring(person.getSelectedPlan() ,config, cContext); - scoringFunction.setWeightOfCadytsCorrection(beta*30.) ; - scoringFunctionAccumulator.addScoringFunction(scoringFunction ); - - return scoringFunctionAccumulator; - } - }) ; - - controler.run(); - - //scenario data test - Assert.assertNotNull("Config is null" , controler.getConfig()); - Assert.assertEquals("Different number of links in network.", controler.getScenario().getNetwork().getLinks().size() , 23 ); - Assert.assertEquals("Different number of nodes in network.", controler.getScenario().getNetwork().getNodes().size() , 15 ); - Assert.assertNotNull("Transit schedule is null.", controler.getScenario().getTransitSchedule()); - Assert.assertEquals("Num. of trLines is wrong.", 2, controler.getScenario().getTransitSchedule().getTransitLines().size() ); - Assert.assertEquals("Num of facilities in schedule is wrong.", controler.getScenario().getTransitSchedule().getFacilities().size() , 5); - Assert.assertNotNull("Population is null.", controler.getScenario().getPopulation()); - Assert.assertEquals("Num. of persons in population is wrong.", controler.getScenario().getPopulation().getPersons().size() , 4); - Assert.assertEquals("Scale factor is wrong.", controler.getScenario().getConfig().ptCounts().getCountsScaleFactor(), 1.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("Distance filter is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilter() , 30000.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("DistanceFilterCenterNode is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilterCenterNode(), "7"); - //counts - Assert.assertEquals("Occupancy count file is wrong.", controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName(), inputDir + "counts/counts_occupancy.xml"); - Counts occupCounts = new Counts(); - new MatsimCountsReader(occupCounts).readFile(controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName()); - Count count = occupCounts.getCount(Id.create("stop1", Link.class)); // casting id from stop to link, not nice - Assert.assertEquals("Occupancy counts description is wrong", occupCounts.getDescription(), "counts values for equil net"); - Assert.assertEquals("CsId is wrong.", count.getCsLabel() , "stop1"); - Assert.assertEquals("Volume of hour 4 is wrong", count.getVolume(7).getValue(), 4.0 , MatsimTestUtils.EPSILON); - Assert.assertEquals("Max count volume is wrong.", count.getMaxVolume().getValue(), 4.0 , MatsimTestUtils.EPSILON); - - // test resulting simulation volumes - { - String outCounts = outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".simCountCompareOccupancy.txt"; - CountsReaderPt reader = new CountsReaderPt(outCounts); - double[] simValues; - double[] realValues; - - Id stopId1 = Id.create("stop1", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId1); - realValues= reader.getRealValues(stopId1); - Assert.assertEquals("Volume of hour 6 is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 4.0, realValues[6], MatsimTestUtils.EPSILON); - - Id stopId2 = Id.create("stop2", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId2); - realValues= reader.getRealValues(stopId2); -// Assert.assertEquals("Volume of hour 6 is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 2.0, simValues[6], MatsimTestUtils.EPSILON); // Altered after using ChangeExpBeta instead of "ccc" - Assert.assertEquals("Volume of hour 6 is wrong", 1.0, realValues[6] , MatsimTestUtils.EPSILON); - - Id stopId6 = Id.create("stop6", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId6); - realValues= reader.getRealValues(stopId6); - Assert.assertEquals("Volume of hour 6 is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 2.0, realValues[6], MatsimTestUtils.EPSILON); - - Id stopId10 = Id.create("stop10", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId10); - realValues= reader.getRealValues(stopId10); -// Assert.assertEquals("Volume of hour 6 is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 2.0, simValues[6], MatsimTestUtils.EPSILON); // Altered after using ChangeExpBeta instead of "ccc" - Assert.assertEquals("Volume of hour 6 is wrong", 5.0, realValues[6], MatsimTestUtils.EPSILON); - - // test calibration statistics - String testCalibStatPath = outputDir + "calibration-stats.txt"; - CalibrationStatReader calibrationStatReader = new CalibrationStatReader(); - new TabularFileParser().parse(testCalibStatPath, calibrationStatReader); - - CalibrationStatReader.StatisticsData outStatData= calibrationStatReader.getCalStatMap().get(lastIteration); -// Assert.assertEquals("different Count_ll", "-0.046875", outStatData.getCount_ll() ); - Assert.assertEquals("different Count_ll", "-0.109375", outStatData.getCount_ll() ); // Altered after using ChangeExpBeta instead of "ccc" -// Assert.assertEquals("different Count_ll_pred_err", "0.01836234363152515" , outStatData.getCount_ll_pred_err() ); - Assert.assertEquals("different Count_ll_pred_err", "0.008411478550953913" , outStatData.getCount_ll_pred_err() ); // Altered after using ChangeExpBeta instead of "ccc" - // Assert.assertEquals("different Link_lambda_avg", "-2.2604922388914356E-10", outStatData.getLink_lambda_avg() ); - // Assert.assertEquals("different Link_lambda_max", "0.0" , outStatData.getLink_lambda_max() ); - // Assert.assertEquals("different Link_lambda_min", "-7.233575164452593E-9", outStatData.getLink_lambda_min() ); - // Assert.assertEquals("different Link_lambda_stddev", "1.261054219517188E-9", outStatData.getLink_lambda_stddev()); - // Assert.assertEquals("different P2p_ll", "--" , outStatData.getP2p_ll()); - // Assert.assertEquals("different Plan_lambda_avg", "-7.233575164452594E-9", outStatData.getPlan_lambda_avg() ); - // Assert.assertEquals("different Plan_lambda_max", "-7.233575164452593E-9" , outStatData.getPlan_lambda_max() ); - // Assert.assertEquals("different Plan_lambda_min", "-7.233575164452593E-9" , outStatData.getPlan_lambda_min() ); - // Assert.assertEquals("different Plan_lambda_stddev", "0.0" , outStatData.getPlan_lambda_stddev()); -// Assert.assertEquals("different Total_ll", "-0.046875", outStatData.getTotal_ll() ); - Assert.assertEquals("different Total_ll", "-0.109375", outStatData.getTotal_ll() ); // Altered after using ChangeExpBeta instead of "ccc" - - - //test link offsets - final TransitSchedule schedule = controler.getScenario().getTransitSchedule(); - String linkOffsetFile = outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".linkCostOffsets.xml"; - // CadytsPtLinkCostOffsetsXMLFileIO offsetReader = new CadytsPtLinkCostOffsetsXMLFileIO (schedule); - CadytsCostOffsetsXMLFileIO offsetReader - = new CadytsCostOffsetsXMLFileIO (new TransitStopFacilityLookUp(controler.getScenario()), TransitStopFacility.class); - DynamicData stopOffsets = offsetReader.read(linkOffsetFile); - - TransitStopFacility stop2 = schedule.getFacilities().get(stopId2); - TransitStopFacility stop10 = schedule.getFacilities().get(stopId10); - - //find first offset value different from null to compare. Useful to test with different time bin sizes - int binIndex=-1; - boolean isZero; - do { - binIndex++; - isZero = (Math.abs(stopOffsets.getBinValue(stop2 , binIndex) - 0.0) < MatsimTestUtils.EPSILON); - } while (isZero && binIndex<86400); - - Assert.assertEquals("Wrong bin index for first link offset", 6, binIndex); -// Assert.assertEquals("Wrong link offset of stop 10", 0.03515757824042241, stopOffsets.getBinValue(stop10 , binIndex), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong link offset of stop 10", 0.022383938774904025, stopOffsets.getBinValue(stop10 , binIndex), MatsimTestUtils.EPSILON); // Altered after using ChangeExpBeta instead of "ccc" -// Assert.assertEquals("Wrong link offset of stop 2", -0.011353248321030008, stopOffsets.getBinValue(stop2 , binIndex), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong link offset of stop 2", -0.008477236625252698, stopOffsets.getBinValue(stop2 , binIndex), MatsimTestUtils.EPSILON); // Altered after using ChangeExpBeta instead of "ccc" - } - } - - - @Test - public final void testCalibration() throws IOException { - final double beta = 30. ; - final int lastIteration = 20 ; - - String inputDir = this.utils.getClassInputDirectory(); - String outputDir = this.utils.getOutputDirectory(); - - Config config = createTestConfig(inputDir, outputDir) ; - - config.controler().setWriteEventsInterval(0) ; - config.controler().setLastIteration(lastIteration) ; - // seems to need 15 iterations as "warm-up"; at least the cadyts corrections are much smaller until then. - - config.planCalcScore().setBrainExpBeta(beta) ; - - StrategySettings stratSets = new StrategySettings(); - stratSets.setStrategyName("ccc") ; - stratSets.setWeight(1.) ; - config.strategy().addStrategySettings(stratSets) ; - - final Scenario scenario = ScenarioUtils.loadScenario(config) ; - - final Controler controler = new Controler( scenario ); - controler.getConfig().controler().setCreateGraphs(false); - controler.getConfig().controler().setDumpDataAtEnd(true); - - controler.addOverridingModule(new CadytsPtModule()); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - addPlanStrategyBinding("ccc").toProvider(new jakarta.inject.Provider() { - @Inject CadytsPtContext context; - @Override - public PlanStrategy get() { - final CadytsPlanChanger planSelector = new CadytsPlanChanger(scenario, context); - planSelector.setCadytsWeight(beta * 30.); - return new PlanStrategyImpl(planSelector); - } - }); - } - }); - - controler.run(); - - //scenario data test - Assert.assertNotNull("config is null" , controler.getConfig()); - Assert.assertEquals("Different number of links in network.", controler.getScenario().getNetwork().getLinks().size() , 23 ); - Assert.assertEquals("Different number of nodes in network.", controler.getScenario().getNetwork().getNodes().size() , 15 ); - Assert.assertNotNull("Transit schedule is null.", controler.getScenario().getTransitSchedule()); - Assert.assertEquals("Num. of trLines is wrong.", 2, controler.getScenario().getTransitSchedule().getTransitLines().size() ); - Assert.assertEquals("Num of facilities in schedule is wrong.", controler.getScenario().getTransitSchedule().getFacilities().size() , 5); - Assert.assertNotNull("Population is null.", controler.getScenario().getPopulation()); - Assert.assertEquals("Num. of persons in population is wrong.", controler.getScenario().getPopulation().getPersons().size() , 4); - Assert.assertEquals("Scale factor is wrong.", controler.getScenario().getConfig().ptCounts().getCountsScaleFactor(), 1.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("Distance filter is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilter() , 30000.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("DistanceFilterCenterNode is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilterCenterNode(), "7"); - //counts - Assert.assertEquals("Occupancy count file is wrong.", controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName(), inputDir + "counts/counts_occupancy.xml"); - Counts occupCounts = new Counts(); - new MatsimCountsReader(occupCounts).readFile(controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName()); - Count count = occupCounts.getCount(Id.create("stop1", Link.class)); // casting the id from a stop to a link, not nice.. - Assert.assertEquals("Occupancy counts description is wrong", occupCounts.getDescription(), "counts values for equil net"); - Assert.assertEquals("CsId is wrong.", count.getCsLabel() , "stop1"); - Assert.assertEquals("Volume of hour 4 is wrong", count.getVolume(7).getValue(), 4.0 , MatsimTestUtils.EPSILON); - Assert.assertEquals("Max count volume is wrong.", count.getMaxVolume().getValue(), 4.0 , MatsimTestUtils.EPSILON); - - // test resulting simulation volumes - { - String outCounts = outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".simCountCompareOccupancy.txt"; - CountsReaderPt reader = new CountsReaderPt(outCounts); - double[] simValues; - double[] realValues; - - Id stopId1 = Id.create("stop1", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId1); - realValues= reader.getRealValues(stopId1); - Assert.assertEquals("Volume of hour 6 is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 4.0, realValues[6], MatsimTestUtils.EPSILON); - - Id stopId2 = Id.create("stop2", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId2); - realValues= reader.getRealValues(stopId2); - Assert.assertEquals("Volume of hour 6 is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 1.0, realValues[6] , MatsimTestUtils.EPSILON); - - Id stopId6 = Id.create("stop6", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId6); - realValues= reader.getRealValues(stopId6); - Assert.assertEquals("Volume of hour 6 is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 2.0, realValues[6], MatsimTestUtils.EPSILON); - - Id stopId10 = Id.create("stop10", TransitStopFacility.class); - simValues = reader.getSimulatedValues(stopId10); - realValues= reader.getRealValues(stopId10); - Assert.assertEquals("Volume of hour 6 is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 is wrong", 5.0, realValues[6], MatsimTestUtils.EPSILON); - - // test calibration statistics - String testCalibStatPath = outputDir + "calibration-stats.txt"; - CalibrationStatReader calibrationStatReader = new CalibrationStatReader(); - new TabularFileParser().parse(testCalibStatPath, calibrationStatReader); - - CalibrationStatReader.StatisticsData outStatData= calibrationStatReader.getCalStatMap().get(lastIteration); - Assert.assertEquals("different Count_ll", "-0.046875", outStatData.getCount_ll() ); - Assert.assertEquals("different Count_ll_pred_err", "0.008670972399424905" , outStatData.getCount_ll_pred_err() ); - Assert.assertEquals("different Link_lambda_avg", "3.642292018550638E-4", outStatData.getLink_lambda_avg() ); - Assert.assertEquals("different Link_lambda_max", "0.032081715026130615" , outStatData.getLink_lambda_max() ); - Assert.assertEquals("different Link_lambda_min", "-0.008771046107406533", outStatData.getLink_lambda_min() ); - Assert.assertEquals("different Link_lambda_stddev", "0.0041495140513996154", outStatData.getLink_lambda_stddev()); - Assert.assertEquals("different P2p_ll", "--" , outStatData.getP2p_ll()); - Assert.assertEquals("different Plan_lambda_avg", "0.011655334459362041", outStatData.getPlan_lambda_avg() ); - Assert.assertEquals("different Plan_lambda_max", "0.032081715026130615" , outStatData.getPlan_lambda_max() ); - Assert.assertEquals("different Plan_lambda_min", "-0.008771046107406533" , outStatData.getPlan_lambda_min() ); - Assert.assertEquals("different Plan_lambda_stddev", "0.02183671935220152" , outStatData.getPlan_lambda_stddev()); - Assert.assertEquals("different Total_ll", "-0.046875", outStatData.getTotal_ll() ); - - //test link offsets - final TransitSchedule schedule = controler.getScenario().getTransitSchedule(); - String linkOffsetFile = outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".linkCostOffsets.xml"; - // CadytsPtLinkCostOffsetsXMLFileIO offsetReader = new CadytsPtLinkCostOffsetsXMLFileIO (schedule); - CadytsCostOffsetsXMLFileIO offsetReader - = new CadytsCostOffsetsXMLFileIO (new TransitStopFacilityLookUp(controler.getScenario()), TransitStopFacility.class); - DynamicData stopOffsets = offsetReader.read(linkOffsetFile); - - TransitStopFacility stop2 = schedule.getFacilities().get(stopId2); - TransitStopFacility stop10 = schedule.getFacilities().get(stopId10); - - //find first offset value different from null to compare. Useful to test with different time bin sizes - int binIndex=-1; - boolean isZero; - do { - binIndex++; - isZero = (Math.abs(stopOffsets.getBinValue(stop2 , binIndex) - 0.0) < MatsimTestUtils.EPSILON); - }while (isZero && binIndex<86400); - - Assert.assertEquals("Wrong bin index for first link offset", 6, binIndex); - Assert.assertEquals("Wrong link offset of stop 10", 0.031842616249416734, stopOffsets.getBinValue(stop10 , binIndex), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong link offset of stop 2", -0.0079478186053482, stopOffsets.getBinValue(stop2 , binIndex), MatsimTestUtils.EPSILON); - } - } - - - /** - * @author mmoyo - */ - @Test - public final void testCalibrationTwo() throws IOException { - // yyyy I cannot fully certify that this test is doing something reasonable, since simCountComparisonOccupancy.txt and - // cadytsSimCountComparisonOccupancy.txt are returning different results. kai, feb'13 - // There is a comment in CadytsContext that the "cadyts" version may be wrong for time bins different from one hour. kai, dec'13 - - final double beta = 30. ; - final int lastIteration = 20 ; - - String inputDir = this.utils.getClassInputDirectory(); - String outputDir = this.utils.getOutputDirectory(); - - Config config = createTestConfig(inputDir, this.utils.getOutputDirectory()); - - config.controler().setLastIteration(lastIteration) ; - config.controler().setWritePlansInterval(1) ; - config.controler().setWriteEventsInterval(1) ; - - config.ptCounts().setPtCountsInterval(1) ; - - StrategySettings stratSets = new StrategySettings(); - stratSets.setStrategyName("ccc") ; - stratSets.setWeight(1.) ; - config.strategy().addStrategySettings(stratSets) ; - - CadytsConfigGroup cConfig = (CadytsConfigGroup) config.getModule(CadytsConfigGroup.GROUP_NAME) ; - cConfig.setTimeBinSize(7200) ; - - // --- - - final Controler controler = new Controler(config); - controler.addOverridingModule(new CadytsPtModule()); -// controler.setOverwriteFiles(true); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - addPlanStrategyBinding("ccc").toProvider(new jakarta.inject.Provider() { - @Inject Scenario scenario; - @Inject CadytsPtContext context; - @Override - public PlanStrategy get() { - final CadytsPlanChanger planSelector = new CadytsPlanChanger(scenario, context); - planSelector.setCadytsWeight(beta * 30.); - return new PlanStrategyImpl(planSelector); - } - }); - } - }); - - controler.getConfig().controler().setCreateGraphs(false); - controler.getConfig().controler().setWriteEventsInterval(0); - controler.getConfig().controler().setDumpDataAtEnd(true); - controler.run(); - - // ==================================== - - //scenario data test - Assert.assertNotNull("config is null" , controler.getConfig()); - Assert.assertEquals("Different number of links in network.", controler.getScenario().getNetwork().getLinks().size() , 23 ); - Assert.assertEquals("Different number of nodes in network.", controler.getScenario().getNetwork().getNodes().size() , 15 ); - Assert.assertNotNull("Transit schedule is null.", controler.getScenario().getTransitSchedule()); - Assert.assertEquals("Num. of trLines is wrong.", 2, controler.getScenario().getTransitSchedule().getTransitLines().size() ); - Assert.assertEquals("Num of facilities in schedule is wrong.", controler.getScenario().getTransitSchedule().getFacilities().size() , 5); - Assert.assertNotNull("Population is null.", controler.getScenario().getPopulation()); - Assert.assertEquals("Num. of persons in population is wrong.", controler.getScenario().getPopulation().getPersons().size() , 4); - Assert.assertEquals("Scale factor is wrong.", controler.getScenario().getConfig().ptCounts().getCountsScaleFactor(), 1.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("Distance filter is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilter() , 30000.0, MatsimTestUtils.EPSILON); - // Assert.assertEquals("DistanceFilterCenterNode is wrong.", controler.getTestScenarioURL().getConfig().ptCounts().getDistanceFilterCenterNode(), "7"); - //counts - Assert.assertEquals("Occupancy count file is wrong.", controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName(), inputDir + "counts/counts_occupancy.xml"); - Counts occupCounts = new Counts(); - new MatsimCountsReader(occupCounts).readFile(controler.getScenario().getConfig().ptCounts().getOccupancyCountsFileName()); - Count count = occupCounts.getCount(Id.create("stop1", Link.class)); - Assert.assertEquals("Occupancy counts description is wrong", occupCounts.getDescription(), "counts values for equil net"); - Assert.assertEquals("CsId is wrong.", count.getCsLabel() , "stop1"); - - Assert.assertEquals("Volume of hour 4 is wrong", count.getVolume(7).getValue(), 4.0 , MatsimTestUtils.EPSILON); - // yy I don't know why it says "hour 4" but "getVolume(7)". kai, sep'14 - - Assert.assertEquals("Max count volume is wrong.", count.getMaxVolume().getValue(), 4.0 , MatsimTestUtils.EPSILON); - - // test resulting simulation volumes - String outCounts = outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".simCountCompareOccupancy.txt"; - CountsReaderPt reader = new CountsReaderPt(outCounts); - Id stopId1 = Id.create("stop1", TransitStopFacility.class); - { - double[] simValues = reader.getSimulatedValues(stopId1); - double[] realValues= reader.getRealValues(stopId1); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 4.0, realValues[6], MatsimTestUtils.EPSILON); - } - Id stopId2 = Id.create("stop2", TransitStopFacility.class); - { - double[] simValues = reader.getSimulatedValues(stopId2); - double[] realValues= reader.getRealValues(stopId2); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 1.0, realValues[6] , MatsimTestUtils.EPSILON); - } - Id stopId6 = Id.create("stop6", TransitStopFacility.class); - { - double[] simValues = reader.getSimulatedValues(stopId6); - double[] realValues= reader.getRealValues(stopId6); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 0.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 2.0, realValues[6], MatsimTestUtils.EPSILON); - } - Id stopId10 = Id.create("stop10", TransitStopFacility.class); - { - double[] simValues = reader.getSimulatedValues(stopId10); - double[] realValues= reader.getRealValues(stopId10); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 4.0, simValues[6], MatsimTestUtils.EPSILON); - Assert.assertEquals("Volume of hour 6 (probably: 7) is wrong", 5.0, realValues[6], MatsimTestUtils.EPSILON); - } - // 2 bus lines, one using stops 1, 2, 12, the other using 1, 10, 12. Counts say: - // stop1: 4 (should always be satisfied) - // stop2: 1 (should be possible to satisfy bus see below) - // stop6: 2 (not possible to satisfy (no line going there)) - // stop10: 5 (not possible to satisfy (we have not enough plans) but see below) - // and they say this at h=7 (which may, or may not, be the entry "6" in the counts array, but then the error messages are wrong). - // - // stop2 and stop10 are pulling in different directions; would need 7 plans to satisfy but have only 4. Satisfying the higher - // volume station better is the cadyts-conforming solution. - - // test calibration statistics - String testCalibStatPath = outputDir + "calibration-stats.txt"; - CalibrationStatReader calibrationStatReader = new CalibrationStatReader(); - new TabularFileParser().parse(testCalibStatPath, calibrationStatReader); - - CalibrationStatReader.StatisticsData outStatData= calibrationStatReader.getCalStatMap().get(lastIteration); - // Assert.assertEquals("different Count_ll", "-0.01171875", outStatData.getCount_ll() ); - // Assert.assertEquals("different Count_ll_pred_err", "0.004590585907794875" , outStatData.getCount_ll_pred_err() ); - // Assert.assertEquals("different Link_lambda_avg", "-1.8081427328702926E-9", outStatData.getLink_lambda_avg() ); - // Assert.assertEquals("different Link_lambda_max", "0.0" , outStatData.getLink_lambda_max() ); - // Assert.assertEquals("different Link_lambda_min", "-1.4465142715757458E-8", outStatData.getLink_lambda_min() ); - // Assert.assertEquals("different Link_lambda_stddev", "4.501584893410135E-9" , outStatData.getLink_lambda_stddev()); - // Assert.assertEquals("different P2p_ll", "--" , outStatData.getP2p_ll()); - // Assert.assertEquals("different Plan_lambda_avg", "-2.5313998260184097E-8", outStatData.getPlan_lambda_avg() ); - // Assert.assertEquals("different Plan_lambda_max", "-2.5313998260184097E-8" , outStatData.getPlan_lambda_max() ); - // Assert.assertEquals("different Plan_lambda_min", "-2.5313998260184097E-8" , outStatData.getPlan_lambda_min() ); - // Assert.assertEquals("different Plan_lambda_stddev", "NaN" , outStatData.getPlan_lambda_stddev()); - // Assert.assertEquals("different Total_ll", "-0.01171875", outStatData.getTotal_ll() ); - // - // I think that the above quantities change when changing between FLOW_VEH_H and COUNT_VEH. The calibration result, though, - // seems the same. - - - //test link offsets - final TransitSchedule schedule = controler.getScenario().getTransitSchedule(); - CadytsCostOffsetsXMLFileIO offsetReader = - new CadytsCostOffsetsXMLFileIO (new TransitStopFacilityLookUp(controler.getScenario()), TransitStopFacility.class); - DynamicData stopOffsets = - offsetReader.read(outputDir + "ITERS/it." + lastIteration + "/" + lastIteration + ".linkCostOffsets.xml"); - - TransitStopFacility stop1 = schedule.getFacilities().get(stopId1); - TransitStopFacility stop2 = schedule.getFacilities().get(stopId2); - //TransitStopFacility stop6 = schedule.getFacilities().get(stopId6); - TransitStopFacility stop10 = schedule.getFacilities().get(stopId10); - - //find first offset value different from zero to compare. Useful to test with different time bin sizes - int binIndex=-1; - boolean isZero; - do { - binIndex++; - isZero = (Math.abs(stopOffsets.getBinValue(stop2 , binIndex) - 0.0) < MatsimTestUtils.EPSILON); - }while (isZero && binIndex<86400); - - Assert.assertEquals("Wrong Bin index for first link offset", 3, binIndex); // bin size = 3600; fix! //done manuel jul.2012 - Assert.assertEquals("Wrong link offset of stop 1", 0.0, stopOffsets.getBinValue(stop1 , binIndex), MatsimTestUtils.EPSILON); -// Assert.assertEquals("Wrong link offset of stop 2", -0.0028383120802772956, stopOffsets.getBinValue(stop2 , binIndex), MatsimTestUtils.EPSILON); -// Assert.assertEquals("Wrong link offset of stop 10", 0.00878939456017082, stopOffsets.getBinValue(stop10 , binIndex), MatsimTestUtils.EPSILON); - - Assert.assertTrue("Offset at stop 2 has wrong sign.", stopOffsets.getBinValue(stop2, binIndex) < 0. ) ; - Assert.assertTrue("Offset at stop 10 has wrong sign.", stopOffsets.getBinValue(stop10, binIndex) > 0. ) ; - } - - - private static Config createTestConfig(String inputDir, String outputDir) { - Config config = ConfigUtils.createConfig() ; - // --- - config.global().setRandomSeed(4711) ; - // --- - config.network().setInputFile(inputDir + "network.xml") ; - // --- - config.plans().setInputFile(inputDir + "4plans.xml") ; - config.plans().setHandlingOfPlansWithoutRoutingMode(HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier); - // --- - config.transit().setUseTransit(true) ; - // --- - config.controler().setFirstIteration(1) ; - config.controler().setLastIteration(10) ; - config.controler().setOutputDirectory(outputDir) ; - config.controler().setWriteEventsInterval(1) ; - config.controler().setMobsim(MobsimType.qsim.toString()) ; - // --- - - config.qsim().setFlowCapFactor(0.02) ; - config.qsim().setStorageCapFactor(0.06) ; - config.qsim().setStuckTime(10.) ; - config.qsim().setRemoveStuckVehicles(false) ; // ?? - // --- - config.transit().setTransitScheduleFile(inputDir + "transitSchedule1bus.xml") ; - config.transit().setVehiclesFile(inputDir + "vehicles.xml") ; - Set modes = new HashSet() ; - modes.add("pt") ; - config.transit().setTransitModes(modes) ; - // --- - { - ActivityParams params = new ActivityParams("h") ; - config.planCalcScore().addActivityParams(params ) ; - params.setTypicalDuration(12*60*60.) ; - }{ - ActivityParams params = new ActivityParams("w") ; - config.planCalcScore().addActivityParams(params ) ; - params.setTypicalDuration(8*60*60.) ; - } - // --- -// ConfigGroup cadytsPtConfig = config.createModule(CadytsConfigGroup.GROUP_NAME ) ; - CadytsConfigGroup cadytsPtConfig = ConfigUtils.addOrGetModule( config, CadytsConfigGroup.class ); - -// cadytsPtConfig.addParam(CadytsConfigGroup.START_TIME, "04:00:00") ; - cadytsPtConfig.setStartTime( 4*3600 ); -// cadytsPtConfig.addParam(CadytsConfigGroup.END_TIME, "20:00:00" ) ; - cadytsPtConfig.setEndTime( 20*3600 ); - cadytsPtConfig.addParam(CadytsConfigGroup.REGRESSION_INERTIA, "0.95") ; - cadytsPtConfig.addParam(CadytsConfigGroup.USE_BRUTE_FORCE, "true") ; - cadytsPtConfig.addParam(CadytsConfigGroup.MIN_FLOW_STDDEV, "8") ; - cadytsPtConfig.addParam(CadytsConfigGroup.PREPARATORY_ITERATIONS, "1") ; - cadytsPtConfig.addParam(CadytsConfigGroup.TIME_BIN_SIZE, "3600") ; - cadytsPtConfig.addParam(CadytsConfigGroup.CALIBRATED_LINES, "M44,M43") ; - -// CadytsConfigGroup ccc = new CadytsConfigGroup() ; -// config.addModule(ccc) ; - - - // --- - config.ptCounts().setOccupancyCountsFileName(inputDir + "counts/counts_occupancy.xml") ; - config.ptCounts().setBoardCountsFileName(inputDir + "counts/counts_boarding.xml") ; - config.ptCounts().setAlightCountsFileName(inputDir + "counts/counts_alighting.xml") ; - // config.ptCounts().setDistanceFilter(30000.) ; // why? - // config.ptCounts().setDistanceFilterCenterNode("7") ; // why? - config.ptCounts().setOutputFormat("txt"); - config.ptCounts().setCountsScaleFactor(1.) ; - // --- - return config; - } - - private static class DummyMobsimFactory implements MobsimFactory { - @Override - public Mobsim createMobsim(final Scenario sc, final EventsManager eventsManager) { - return new Mobsim() { - @Override public void run() { } - } ; - } - } -} diff --git a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CountsReaderPt.java b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CountsReaderPt.java deleted file mode 100644 index a312c472c48..00000000000 --- a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/pt/CountsReaderPt.java +++ /dev/null @@ -1,130 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.cadyts.pt; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Id; -import org.matsim.core.utils.misc.StringUtils; -import org.matsim.pt.transitSchedule.api.TransitStopFacility; - -/** - * parses a output text file containing counts comparisons - * - * this class is only there in order to read the column-oriented output back in for testing. - * It should not be used elsewhere without further thinking. kai, sep'14 - * */ -final class CountsReaderPt { - - private final static Logger log = LogManager.getLogger(CountsReaderPt.class); - - final String STOP_ID_STRING_0 = "StopId :"; - final String HEAD_STRING_0 = "hour"; - final String ZERO = "0.0"; - - String countsTextFile; - Map, Map> count = new TreeMap<>(); - - CountsReaderPt(final String countsTextFile){ - this.countsTextFile = countsTextFile; - readValues(); - } - - private void readValues() { - try { - FileReader fileReader = new FileReader(this.countsTextFile); - // ->:correct this : reads first row - BufferedReader bufferedReader = new BufferedReader(fileReader); - String row = bufferedReader.readLine(); // TODO : include the first row inside the iteration - String[] values = StringUtils.explode(row, '\t'); - - Id id = Id.create(values[1], TransitStopFacility.class); - while (row != null) { - row = bufferedReader.readLine(); - if (row != null && row != "") { - values = StringUtils.explode(row, '\t'); - if (values[0].equals(this.STOP_ID_STRING_0)) { - id = Id.create(values[1], TransitStopFacility.class); - } else if (values[0].equals(this.HEAD_STRING_0)) { - // it does nothing, correct this condition - } else { - if (!this.count.containsKey(id)) { - this.count.put(id, new TreeMap()); - } - this.count.get(id).put(values[0], - new double[] { Double.parseDouble(values[1]), Double.parseDouble(values[2]), Double.parseDouble(values[3]) }); - } - } - } - bufferedReader.close(); - fileReader.close(); - } catch (Exception e) { - log.error(e); - } - } - - /** - * I am reasonably sure that the first entry (hour 1) is at array-position 0. kai, sep'14 - */ - double[]getSimulatedValues(final Id stopId) { - return this.getCountValues(stopId, 0); - } - - /** - * I am reasonably sure that the first entry (hour 1) is at array-position 0. kai, sep'14 - */ - double[]getSimulatedScaled(final Id stopId) { - return this.getCountValues(stopId, 1); - } - - /** - * I am reasonably sure that the first entry (hour 1) is at array-position 0. kai, sep'14 - */ - double[]getRealValues(final Id stopId) { - return this.getCountValues(stopId, 2); - } - - /** - * I am reasonably sure that the first entry (hour 1) is at array-position 0. kai, sep'14 - */ - double[]getCountValues(final Id stopId, final int col) { - double[] valueArray = new double[24]; - for (byte i= 0; i<24 ; i++) { - String hour = String.valueOf(i+1); - if (this.count.keySet().contains(stopId)) { - double[] value = this.count.get(stopId).get(hour); - if (value == null){ - valueArray[i] = 0.0; - } else { - valueArray[i] = value[col] ; //0 = simulated; 1= simulatedEscaled ; 2=realValues - } - } else { - valueArray = null; - } - } - return valueArray; - } - -} diff --git a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/utils/CalibrationStatReaderTest.java b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/utils/CalibrationStatReaderTest.java index 5733a037c50..cf1c31b9126 100644 --- a/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/utils/CalibrationStatReaderTest.java +++ b/contribs/cadytsIntegration/src/test/java/org/matsim/contrib/cadyts/utils/CalibrationStatReaderTest.java @@ -20,12 +20,10 @@ package org.matsim.contrib.cadyts.utils; import java.io.IOException; -import java.net.URL; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; -import org.matsim.contrib.cadyts.pt.CadytsPtIT; import org.matsim.testcases.MatsimTestUtils; import cadyts.utilities.io.tabularFileParser.TabularFileParser; @@ -42,17 +40,17 @@ public void testReader() throws IOException { CalibrationStatReader calibrationStatReader = new CalibrationStatReader(); tabularFileParser.parse(calibStatFile, calibrationStatReader); CalibrationStatReader.StatisticsData statData6= calibrationStatReader.getCalStatMap().get(Integer.valueOf(6)); - Assert.assertEquals("differrent Count_ll", "-1.546875", statData6.getCount_ll() ); - Assert.assertEquals("differrent Count_ll_pred_err", "9.917082938182276E-8" , statData6.getCount_ll_pred_err() ); - Assert.assertEquals("differrent Link_lambda_avg", "0.0013507168476099964", statData6.getLink_lambda_avg() ); - Assert.assertEquals("differrent Link_lambda_max", "0.031434867572002166" , statData6.getLink_lambda_max() ); - Assert.assertEquals("differrent Link_lambda_min", "0.0", statData6.getLink_lambda_min() ); - Assert.assertEquals("differrent Link_lambda_stddev", "0.0058320747961925256" , statData6.getLink_lambda_stddev()); - Assert.assertEquals("differrent P2p_ll", "--" , statData6.getP2p_ll()); - Assert.assertEquals("differrent Plan_lambda_avg", "0.04322293912351989", statData6.getPlan_lambda_avg() ); - Assert.assertEquals("differrent Plan_lambda_max", "0.04715229919344063" , statData6.getPlan_lambda_max() ); - Assert.assertEquals("differrent Plan_lambda_min", "0.03929357905359915" , statData6.getPlan_lambda_min() ); - Assert.assertEquals("differrent Plan_lambda_stddev", "0.004200662608832472" , statData6.getPlan_lambda_stddev()); - Assert.assertEquals("differrent Total_ll", "-1.546875", statData6.getTotal_ll() ); + Assert.assertEquals("different Count_ll", "-1.546875", statData6.getCount_ll() ); + Assert.assertEquals("different Count_ll_pred_err", "9.917082938182276E-8" , statData6.getCount_ll_pred_err() ); + Assert.assertEquals("different Link_lambda_avg", "0.0013507168476099964", statData6.getLink_lambda_avg() ); + Assert.assertEquals("different Link_lambda_max", "0.031434867572002166" , statData6.getLink_lambda_max() ); + Assert.assertEquals("different Link_lambda_min", "0.0", statData6.getLink_lambda_min() ); + Assert.assertEquals("different Link_lambda_stddev", "0.0058320747961925256" , statData6.getLink_lambda_stddev()); + Assert.assertEquals("different P2p_ll", "--" , statData6.getP2p_ll()); + Assert.assertEquals("different Plan_lambda_avg", "0.04322293912351989", statData6.getPlan_lambda_avg() ); + Assert.assertEquals("different Plan_lambda_max", "0.04715229919344063" , statData6.getPlan_lambda_max() ); + Assert.assertEquals("different Plan_lambda_min", "0.03929357905359915" , statData6.getPlan_lambda_min() ); + Assert.assertEquals("different Plan_lambda_stddev", "0.004200662608832472" , statData6.getPlan_lambda_stddev()); + Assert.assertEquals("different Total_ll", "-1.546875", statData6.getTotal_ll() ); } } diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/config/FreefloatingAreasReader.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/config/FreefloatingAreasReader.java index 20f8b7197cb..8230aaf2296 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/config/FreefloatingAreasReader.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/config/FreefloatingAreasReader.java @@ -28,6 +28,7 @@ public class FreefloatingAreasReader extends MatsimXmlParser { private ArrayList coords; public FreefloatingAreasReader() { + super(ValidationType.DTD_ONLY); this.polygonFeatureFactory = new PolygonFeatureFactory.Builder() .setName("freefloating_area") .setCrs(DefaultGeographicCRS.WGS84) diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/control/listeners/CarsharingListener.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/control/listeners/CarsharingListener.java index 3409c505f63..42c2d594c05 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/control/listeners/CarsharingListener.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/control/listeners/CarsharingListener.java @@ -69,7 +69,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { e.printStackTrace(); } - if (event.getIteration() == controler.getConfig().controler().getLastIteration()) { + if (event.getIteration() == controler.getConfig().controller().getLastIteration()) { final BufferedWriter outLinkStats = IOUtils .getBufferedWriter(this.controler.getControlerIO().getOutputFilename("CS.txt")); try { diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/VehicleChoiceAgentImpl.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/VehicleChoiceAgentImpl.java index 9906d235e08..455f57a72f4 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/VehicleChoiceAgentImpl.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/VehicleChoiceAgentImpl.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,7 +14,7 @@ import org.matsim.contrib.carsharing.manager.supply.CarsharingSupplyInterface; import org.matsim.contrib.carsharing.manager.supply.costs.CostsCalculatorContainer; import org.matsim.contrib.carsharing.vehicles.CSVehicle; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.LeastCostPathCalculator.Path; @@ -72,7 +71,7 @@ public CSVehicle chooseVehicleActivityTimeIncluded(List vehicleOption CSVehicle chosenVehicle = null; double maxUtility = Integer.MIN_VALUE; - double marginalUtilityOfMoney = ((PlanCalcScoreConfigGroup) scenario.getConfig().getModule("planCalcScore")) + double marginalUtilityOfMoney = ((ScoringConfigGroup) scenario.getConfig().getModule("planCalcScore")) .getMarginalUtilityOfMoney(); for (CSVehicle vehicle : vehicleOptions) { Link vehicleLocation = this.carsharingSupply.getCompany(vehicle.getCompanyId()) diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/membership/MembershipReader.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/membership/MembershipReader.java index 5824ce34431..76c8cc33e64 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/membership/MembershipReader.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/manager/demand/membership/MembershipReader.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.Stack; import java.util.TreeSet; -/** +/** * @author balac */ public class MembershipReader extends MatsimXmlParser{ @@ -21,44 +21,48 @@ public class MembershipReader extends MatsimXmlParser{ private String companyId; private HashMap stringCache = new HashMap<>(); + public MembershipReader() { + super(ValidationType.DTD_ONLY); + } + @Override public void startTag(String name, Attributes atts, Stack context) { if (name.equals("person")) { - + personId = atts.getValue("id"); memberships = new HashMap<>(); membershipPerCSType = new HashMap<>(); } else if (name.equals("company")) { - + companyId = atts.getValue("id"); companyId = stringCache.computeIfAbsent(companyId, id -> id); carsharingTypes = new TreeSet<>(); } else if (name.equals("carsharing")) { - + String csType = atts.getValue("name"); csType = stringCache.computeIfAbsent(csType, type -> type); if (this.membershipPerCSType.containsKey(csType)) { - + Set companies = this.membershipPerCSType.get(csType); companies.add(companyId); this.membershipPerCSType.put(csType, companies); - + } else { Set companies = new TreeSet<>(); companies.add(companyId); - this.membershipPerCSType.put(csType, companies); + this.membershipPerCSType.put(csType, companies); } carsharingTypes.add(csType); - } + } } @Override public void endTag(String name, String content, Stack context) { - + if (name.equals("person")) { PersonMembership personMembership = new PersonMembership(memberships, membershipPerCSType); membershipContainer.addPerson(personId, personMembership); @@ -66,7 +70,7 @@ public void endTag(String name, String content, Stack context) { else if (name.equals("company")) { memberships.put(companyId, carsharingTypes); - } + } } public MembershipContainer getMembershipContainer() { return membershipContainer; diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/qsim/CarSharingQSimModule.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/qsim/CarSharingQSimModule.java index e8ced1447db..e5d5179194e 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/qsim/CarSharingQSimModule.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/qsim/CarSharingQSimModule.java @@ -3,7 +3,6 @@ import org.matsim.contrib.carsharing.manager.CarsharingManagerInterface; import org.matsim.contrib.carsharing.manager.supply.CarsharingSupplyInterface; import org.matsim.core.mobsim.qsim.AbstractQSimModule; -import org.matsim.core.mobsim.qsim.PopulationModule; import org.matsim.core.mobsim.qsim.QSim; import org.matsim.core.mobsim.qsim.agents.AgentFactory; import org.matsim.core.mobsim.qsim.components.QSimComponentsConfig; @@ -19,7 +18,7 @@ public class CarSharingQSimModule extends AbstractQSimModule { @Override protected void configureQSim() { //addQSimComponentBinding(COMPONENT_NAME).to(ParkCSVehicles.class); - addNamedComponent(ParkCSVehicles.class, COMPONENT_NAME); + addQSimComponentBinding( COMPONENT_NAME ).to( ParkCSVehicles.class ); } @Provides diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/readers/CarsharingXmlReaderNew.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/readers/CarsharingXmlReaderNew.java index 26559dd78c9..b73e7a56f6a 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/readers/CarsharingXmlReaderNew.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/readers/CarsharingXmlReaderNew.java @@ -27,16 +27,16 @@ import org.matsim.core.utils.collections.QuadTree; import org.matsim.core.utils.io.MatsimXmlParser; import org.xml.sax.Attributes; -/** - * +/** + * * @author balac */ public class CarsharingXmlReaderNew extends MatsimXmlParser { private Network network; - private String id; + private String id; private int avaialbleParkingSpots; private ArrayList vehicles; - private Link link; + private Link link; private String csType; private String companyName; private boolean hasFF = false; @@ -59,36 +59,36 @@ public Map getCompanies() { } private Map allVehicles = new ConcurrentHashMap(); - + public Map getAllVehicles() { return allVehicles; } private Map allVehicleLocations = new ConcurrentHashMap(); - + private ArrayList twStations = new ArrayList(); private ArrayList owStations = new ArrayList(); - - private QuadTree ffVehicleLocationQuadTree; - private QuadTree owvehicleLocationQuadTree; + + private QuadTree ffVehicleLocationQuadTree; + private QuadTree owvehicleLocationQuadTree; private QuadTree twvehicleLocationQuadTree; private Map twowaycarsharingstationsMap = new ConcurrentHashMap(); private Map onewaycarsharingstationsMap = new ConcurrentHashMap(); - - private Map ffvehiclesMap = new ConcurrentHashMap(); + + private Map ffvehiclesMap = new ConcurrentHashMap(); private Map ffvehicleIdMap = new ConcurrentHashMap(); - + private Map owvehicleIdMap = new ConcurrentHashMap(); private Map owvehiclesMap = new ConcurrentHashMap(); - - + + private Map twvehicleIdMap = new ConcurrentHashMap(); private Map twvehiclesMap = new ConcurrentHashMap(); private Map owvehicleToStationMap = new ConcurrentHashMap(); - + public Map getTwowaycarsharingstationsMap() { return twowaycarsharingstationsMap; } @@ -97,8 +97,8 @@ public Map getOnewaycarsharingstationsMap() { return onewaycarsharingstationsMap; } - - + + public Map getOwvehicleIdMap() { return owvehicleIdMap; } @@ -108,11 +108,11 @@ public Map getFfvehicleIdMap() { } public CarsharingXmlReaderNew(Network network) { - - this.network = network; + super(ValidationType.DTD_ONLY); + this.network = network; createQuadTrees(); } - + private void createQuadTrees() { double minx = (1.0D / 0.0D); @@ -130,15 +130,15 @@ private void createQuadTrees() { ffVehicleLocationQuadTree = new QuadTree(minx, miny, maxx, maxy); owvehicleLocationQuadTree = new QuadTree(minx, miny, maxx, maxy); - twvehicleLocationQuadTree = new QuadTree(minx, miny, maxx, maxy); + twvehicleLocationQuadTree = new QuadTree(minx, miny, maxx, maxy); } @Override public void startTag(String name, Attributes atts, Stack context) { - - + + if (name.equals("company")) { - + companyName = atts.getValue("name"); this.companyNames.add(companyName); createQuadTrees(); @@ -146,29 +146,29 @@ public void startTag(String name, Attributes atts, Stack context) { twowaycarsharingstationsMap = new ConcurrentHashMap(); onewaycarsharingstationsMap = new ConcurrentHashMap(); - ffvehiclesMap = new ConcurrentHashMap(); + ffvehiclesMap = new ConcurrentHashMap(); ffvehicleIdMap = new ConcurrentHashMap(); - + owvehicleIdMap = new ConcurrentHashMap(); - owvehiclesMap = new ConcurrentHashMap(); - + owvehiclesMap = new ConcurrentHashMap(); + twvehicleIdMap = new ConcurrentHashMap(); twvehiclesMap = new ConcurrentHashMap(); - + owvehicleToStationMap = new ConcurrentHashMap(); - - + + //allVehicles = new ConcurrentHashMap(); //companies = new ConcurrentHashMap(); } - + else if (name.equals("twoway") || name.equals("oneway")) { csType = name; id = atts.getValue("id"); String xCoord = atts.getValue("x"); String yCoord = atts.getValue("y"); Coord coordStation = new Coord(Double.parseDouble(xCoord), Double.parseDouble(yCoord)); - + link = (Link)NetworkUtils.getNearestLinkExactly(network, coordStation); vehicles = new ArrayList(); if (name.equals("oneway")) { @@ -177,7 +177,7 @@ else if (name.equals("twoway") || name.equals("oneway")) { } else hasTW = true; - + } else if (name.equals("freefloating")) { hasFF= true; @@ -185,7 +185,7 @@ else if (name.equals("freefloating")) { String yCoord = atts.getValue("y"); String type = atts.getValue("type"); Coord coordStation = new Coord(Double.parseDouble(xCoord), Double.parseDouble(yCoord)); - + link = (Link)NetworkUtils.getNearestLinkExactly(network, coordStation); FFVehicleImpl ffcsvehicle = new FFVehicleImpl(type, atts.getValue("id"), companyName); ffVehicleLocationQuadTree.put(link.getCoord().getX(), link.getCoord().getY(), ffcsvehicle); @@ -195,12 +195,12 @@ else if (name.equals("freefloating")) { allVehicleLocations.put(ffcsvehicle, link); } else if (name.equals("vehicle")) { - + StationBasedVehicle vehicle = new StationBasedVehicle(atts.getValue("type"), atts.getValue("vehicleID"), id, csType, companyName); vehicles.add(vehicle); this.allVehicles.put(atts.getValue("vehicleID"), vehicle); this.allVehicleLocations.put(vehicle, link); - + } } @@ -227,22 +227,22 @@ public void endTag(String name, String content, Stack context) { companyContainer.addCarsharingType("twoway", twvehiclesContainer); hasTW = false; } - + if (hasOW) { VehiclesContainer owvehiclesContainer = new OneWayContainer(owvehicleLocationQuadTree, onewaycarsharingstationsMap, owvehiclesMap, owvehicleToStationMap); companyContainer.addCarsharingType("oneway", owvehiclesContainer); hasOW = false; - + } - + companies.put(companyName, companyContainer); - + } else if (name.equals("twoway") || name.equals("oneway")) { Map numberOfVehiclesPerType = new ConcurrentHashMap(); Map> vehiclesPerType = new ConcurrentHashMap>(); - + for (CSVehicle vehicle : vehicles) { if (name.equals("oneway")) { this.owvehicleIdMap.put(vehicle.getVehicleId(), vehicle); @@ -254,17 +254,17 @@ else if (name.equals("twoway")) { this.twvehiclesMap.put(vehicle, link); } if (numberOfVehiclesPerType.containsKey(vehicle.getType())) { - + int number = numberOfVehiclesPerType.get(vehicle.getType()); ArrayList oldArray = vehiclesPerType.get(vehicle.getType()); number++; oldArray.add(vehicle); numberOfVehiclesPerType.put(vehicle.getType(), number); vehiclesPerType.put(vehicle.getType(), oldArray); - + } else { - + numberOfVehiclesPerType.put(vehicle.getType(), 1); ArrayList newArray = new ArrayList(); newArray.add(vehicle); @@ -273,21 +273,21 @@ else if (name.equals("twoway")) { } if (name.equals("twoway")) { TwoWayCarsharingStation station = new TwoWayCarsharingStation(id, link, numberOfVehiclesPerType, vehiclesPerType); - + //TODO: check if the station already exists on the link - + /*if (twvehicleLocationQuadTree.getDisk(link.getCoord().getX(), link.getCoord().getY(), 0.0).size() != 0) { TwoWayCarsharingStation stationOld = (TwoWayCarsharingStation) twvehicleLocationQuadTree.getClosest(link.getCoord().getX(), link.getCoord().getY()); for (String type : vehiclesPerType.keySet()) { - + if (stationOld.getVehiclesPerType().containsKey(type)) { - - + + } } stationOld. }*/ - + twvehicleLocationQuadTree.put(link.getCoord().getX(), link.getCoord().getY(), station); this.twowaycarsharingstationsMap.put(id, station); twStations.add(station); @@ -295,14 +295,14 @@ else if (name.equals("twoway")) { else { OneWayCarsharingStation station = new OneWayCarsharingStation(id, link, numberOfVehiclesPerType, vehiclesPerType, avaialbleParkingSpots); - + owvehicleLocationQuadTree.put(link.getCoord().getX(), link.getCoord().getY(), station); this.onewaycarsharingstationsMap.put(id, station); owStations.add(station); - + } - } + } } public Map getAllVehicleLocations() { diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationAgentsReader.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationAgentsReader.java index 84b2cbae967..1c459e4c9cf 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationAgentsReader.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationAgentsReader.java @@ -15,6 +15,11 @@ public class RelocationAgentsReader extends MatsimXmlParser { private Counter counter; + + public RelocationAgentsReader() { + super(ValidationType.DTD_ONLY); + } + @Override public void startTag(final String name, final Attributes atts, final Stack context) { if ( name.equals("relocationAgentBases" ) ) { diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationTimesReader.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationTimesReader.java index f9d2de240dd..292eac63da4 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationTimesReader.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationTimesReader.java @@ -18,6 +18,10 @@ public class RelocationTimesReader extends MatsimXmlParser { private Counter counter; + public RelocationTimesReader() { + super(ValidationType.DTD_ONLY); + } + @Override public void startTag(final String name, final Attributes atts, final Stack context) { if ( name.equals("relocationTimes" ) ) { diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationZonesReader.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationZonesReader.java index d98c2cd8424..07df9ed165b 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationZonesReader.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/relocation/utils/RelocationZonesReader.java @@ -32,6 +32,7 @@ public class RelocationZonesReader extends MatsimXmlParser { private ArrayList coords; public RelocationZonesReader() { + super(ValidationType.DTD_ONLY); this.polygonFeatureFactory = new PolygonFeatureFactory.Builder() .setName("carsharing_relocation_zone") .setCrs(DefaultGeographicCRS.WGS84) diff --git a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/scoring/CarsharingLegScoringFunction.java b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/scoring/CarsharingLegScoringFunction.java index 4c41d77c15e..f30d27c9ad3 100644 --- a/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/scoring/CarsharingLegScoringFunction.java +++ b/contribs/carsharing/src/main/java/org/matsim/contrib/carsharing/scoring/CarsharingLegScoringFunction.java @@ -11,15 +11,15 @@ import org.matsim.contrib.carsharing.manager.supply.costs.CostsCalculatorContainer; import org.matsim.contrib.carsharing.vehicles.CSVehicle; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.scoring.functions.ScoringParameters; public class CarsharingLegScoringFunction extends org.matsim.core.scoring.functions.CharyparNagelLegScoring { - - private Config config; - + + private Config config; + private CostsCalculatorContainer costsCalculatorContainer; private DemandHandler demandHandler; private Person person; @@ -27,11 +27,11 @@ public class CarsharingLegScoringFunction extends org.matsim.core.scoring.functi /* private static final Set walkingLegs = ImmutableSet.of("egress_walk_ow", "access_walk_ow", "egress_walk_tw", "access_walk_tw", "egress_walk_ff", "access_walk_ff"); - + private static final Set carsharingLegs = ImmutableSet.of("oneway_vehicle", "twoway_vehicle", "freefloating_vehicle");*/ - - public CarsharingLegScoringFunction(ScoringParameters params, + + public CarsharingLegScoringFunction(ScoringParameters params, Config config, Network network, DemandHandler demandHandler, CostsCalculatorContainer costsCalculatorContainer, CarsharingSupplyInterface carsharingSupplyContainer, Person person) @@ -41,62 +41,62 @@ public CarsharingLegScoringFunction(ScoringParameters params, this.demandHandler = demandHandler; this.carsharingSupplyContainer = carsharingSupplyContainer; this.costsCalculatorContainer = costsCalculatorContainer; - this.person = person; + this.person = person; } @Override public void handleEvent(Event event) { - super.handleEvent(event); - } - + super.handleEvent(event); + } + @Override - public void finish() { - super.finish(); - + public void finish() { + super.finish(); + AgentRentals agentRentals = this.demandHandler.getAgentRentalsMap().get(person.getId()); if (agentRentals != null) { - double marginalUtilityOfMoney = ((PlanCalcScoreConfigGroup)this.config.getModule("planCalcScore")).getMarginalUtilityOfMoney(); + double marginalUtilityOfMoney = this.config.scoring().getMarginalUtilityOfMoney(); for(RentalInfo rentalInfo : agentRentals.getArr()) { CSVehicle vehicle = this.carsharingSupplyContainer.getAllVehicles().get(rentalInfo.getVehId().toString()); if (marginalUtilityOfMoney != 0.0) - score += -1 * this.costsCalculatorContainer.getCost(vehicle.getCompanyId(), + score += -1 * this.costsCalculatorContainer.getCost(vehicle.getCompanyId(), rentalInfo.getCarsharingType(), rentalInfo) * marginalUtilityOfMoney; - } - } - } - + } + } + } + @Override protected double calcLegScore(double departureTime, double arrivalTime, Leg leg) { - - + + double tmpScore = 0.0D; /*double travelTime = arrivalTime - departureTime; String mode = leg.getMode(); if (carsharingLegs.contains(mode)) { - - if (("oneway_vehicle").equals(mode)) { + + if (("oneway_vehicle").equals(mode)) { tmpScore += Double.parseDouble(this.config.getModule("OneWayCarsharing").getParams().get("constantOneWayCarsharing")); tmpScore += travelTime * Double.parseDouble(this.config.getModule("OneWayCarsharing").getParams().get("travelingOneWayCarsharing")) / 3600.0; - } - - else if (("freefloating_vehicle").equals(mode)) { - + } + + else if (("freefloating_vehicle").equals(mode)) { + tmpScore += Double.parseDouble(this.config.getModule("FreeFloating").getParams().get("constantFreeFloating")); tmpScore += travelTime * Double.parseDouble(this.config.getModule("FreeFloating").getParams().get("travelingFreeFloating")) / 3600.0; - } - - else if (("twoway_vehicle").equals(mode)) { - + } + + else if (("twoway_vehicle").equals(mode)) { + tmpScore += Double.parseDouble(this.config.getModule("TwoWayCarsharing").getParams().get("constantTwoWayCarsharing")); tmpScore += travelTime * Double.parseDouble(this.config.getModule("TwoWayCarsharing").getParams().get("travelingTwoWayCarsharing")) / 3600.0; } } - + else if (walkingLegs.contains(mode)) { - + tmpScore += getWalkScore(leg.getRoute().getDistance(), travelTime); - - }*/ + + }*/ return tmpScore; } diff --git a/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingIT.java b/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingIT.java index c3b5b6cd770..9431005a492 100644 --- a/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingIT.java +++ b/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingIT.java @@ -19,8 +19,8 @@ package org.matsim.contrib.carsharing.runExample; -import static org.matsim.core.config.groups.PlansCalcRouteConfigGroup.AccessEgressType; -import static org.matsim.core.config.groups.PlansCalcRouteConfigGroup.ModeRoutingParams; +import static org.matsim.core.config.groups.RoutingConfigGroup.AccessEgressType; +import static org.matsim.core.config.groups.RoutingConfigGroup.TeleportedModeParams; import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; @@ -66,8 +66,8 @@ public final void test() { new CarsharingConfigGroup(), new DvrpConfigGroup()); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles); config.network().setInputFile("network.xml"); @@ -79,8 +79,8 @@ public final void test() { config.facilities().setInputFile("facilities.xml"); config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.fromFile); - config.plansCalcRoute().setAccessEgressType(AccessEgressType.none); // otherwise does not work. kai,feb'16 - config.plansCalcRoute().setRoutingRandomness(0.); + config.routing().setAccessEgressType(AccessEgressType.none); // otherwise does not work. kai,feb'16 + config.routing().setRoutingRandomness(0.); // config.plansCalcRoute().setInsertingAccessEgressWalk(AccessEgressType.directWalk); CarsharingConfigGroup csConfig = (CarsharingConfigGroup) config.getModule(CarsharingConfigGroup.GROUP_NAME); @@ -99,25 +99,25 @@ public final void test() { // config.qsim().setNumberOfThreads(1); { - ModeRoutingParams params = new ModeRoutingParams(TransportMode.non_network_walk); + TeleportedModeParams params = new TeleportedModeParams(TransportMode.non_network_walk); params.setTeleportedModeSpeed(0.83333333333); // params.setTeleportedModeSpeed( 2.0 ); params.setBeelineDistanceFactor(1.3); - config.plansCalcRoute().addModeRoutingParams(params); + config.routing().addModeRoutingParams(params); } { - config.plansCalcRoute().removeModeRoutingParams(TransportMode.walk); - ModeRoutingParams params = new ModeRoutingParams(TransportMode.walk); + config.routing().removeModeRoutingParams(TransportMode.walk); + TeleportedModeParams params = new TeleportedModeParams(TransportMode.walk); params.setTeleportedModeSpeed(0.83333333333); // params.setTeleportedModeSpeed( 2.0 ); params.setBeelineDistanceFactor(1.3); - config.plansCalcRoute().addModeRoutingParams(params); + config.routing().addModeRoutingParams(params); } // --- Scenario scenario = ScenarioUtils.loadScenario(config); - config.plansCalcRoute().setAccessEgressType(AccessEgressType.accessEgressModeToLink); + config.routing().setAccessEgressType(AccessEgressType.accessEgressModeToLink); // --- diff --git a/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingRelocationIT.java b/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingRelocationIT.java deleted file mode 100644 index 64f0d96606b..00000000000 --- a/contribs/carsharing/src/test/java/org/matsim/contrib/carsharing/runExample/RunCarsharingRelocationIT.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.matsim.contrib.carsharing.runExample; - -public class RunCarsharingRelocationIT { - - - - - -} diff --git a/contribs/carsharing/test/input/org/matsim/contrib/carsharing/runExample/RunCarsharingIT/config.xml b/contribs/carsharing/test/input/org/matsim/contrib/carsharing/runExample/RunCarsharingIT/config.xml index 0be55d6d96c..adb6c05c1a0 100644 --- a/contribs/carsharing/test/input/org/matsim/contrib/carsharing/runExample/RunCarsharingIT/config.xml +++ b/contribs/carsharing/test/input/org/matsim/contrib/carsharing/runExample/RunCarsharingIT/config.xml @@ -23,8 +23,6 @@ - - @@ -45,11 +43,11 @@ - - + + - + @@ -254,7 +252,7 @@ - + diff --git a/contribs/commercialTrafficApplications/scenarios/grid/jointDemand_config.xml b/contribs/commercialTrafficApplications/scenarios/grid/jointDemand_config.xml index a50d4e78394..d7c91a56d08 100644 --- a/contribs/commercialTrafficApplications/scenarios/grid/jointDemand_config.xml +++ b/contribs/commercialTrafficApplications/scenarios/grid/jointDemand_config.xml @@ -1,15 +1,6 @@ - - - - - - - - - @@ -47,7 +38,7 @@ - + @@ -392,24 +383,6 @@ - - - - - - - - - - - - - - - - - - @@ -544,8 +517,6 @@ - - diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperator.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperator.java index 29c3c8662e9..f1862ee3842 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperator.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperator.java @@ -23,8 +23,8 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Activity; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.Carriers; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.population.algorithms.PlanAlgorithm; diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialJobGenerator.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialJobGenerator.java index 024ab6e45e6..77b82e6208b 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialJobGenerator.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialJobGenerator.java @@ -21,7 +21,7 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Population; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carriers; import org.matsim.core.controler.listener.AfterMobsimListener; import org.matsim.core.controler.listener.BeforeMobsimListener; diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficAnalysisListener.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficAnalysisListener.java index fb67736cd99..d41196496c1 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficAnalysisListener.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficAnalysisListener.java @@ -25,8 +25,8 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.matsim.api.core.v01.Id; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.Carriers; import org.matsim.core.controler.MatsimServices; import org.matsim.core.controler.events.IterationEndsEvent; import org.matsim.core.controler.listener.IterationEndsListener; @@ -77,7 +77,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { writeIterationCarrierStats(event); writeJobStats(services.getControlerIO().getIterationFilename(event.getIteration(), "commercialJobStats.csv")); analyzeCarrierMarketShares(event.getIteration()); - + firstIteration = false; } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficChecker.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficChecker.java index 88e68e04d36..8c561e9217c 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficChecker.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficChecker.java @@ -27,8 +27,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.Population; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.Carriers; import java.util.Collection; import java.util.Map; diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/DefaultCommercialJobGenerator.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/DefaultCommercialJobGenerator.java index c1d34f7eb65..3327f46292c 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/DefaultCommercialJobGenerator.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/DefaultCommercialJobGenerator.java @@ -36,20 +36,10 @@ import org.matsim.api.core.v01.population.Population; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierPlanWriter; -import org.matsim.contrib.freight.carrier.CarrierService; -import org.matsim.contrib.freight.carrier.CarrierUtils; -import org.matsim.contrib.freight.carrier.CarrierVehicle; -import org.matsim.contrib.freight.carrier.CarrierVehicleTypes; -import org.matsim.contrib.freight.carrier.Carriers; -import org.matsim.contrib.freight.carrier.FreightConstants; -import org.matsim.contrib.freight.carrier.ScheduledTour; -import org.matsim.contrib.freight.carrier.TimeWindow; -import org.matsim.contrib.freight.carrier.Tour; -import org.matsim.contrib.freight.jsprit.VRPTransportCostsFactory; +import org.matsim.freight.carriers.*; +import org.matsim.freight.carriers.jsprit.VRPTransportCostsFactory; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; import org.matsim.core.controler.events.AfterMobsimEvent; import org.matsim.core.controler.events.BeforeMobsimEvent; import org.matsim.core.population.PopulationUtils; @@ -127,7 +117,7 @@ private void buildVehicleAndDriver(Carrier carrier, Person driverPerson, Carrier if (!scenario.getVehicles().getVehicleTypes().containsKey(carrierVehicle.getType().getId())) scenario.getVehicles().addVehicleType(carrierVehicle.getType()); Id vid = Id.createVehicleId(driverPerson.getId()); - VehicleUtils.insertVehicleIdsIntoAttributes(driverPerson, Map.of(CarrierUtils.getCarrierMode(carrier), vid)); + VehicleUtils.insertVehicleIdsIntoAttributes(driverPerson, Map.of(CarriersUtils.getCarrierMode(carrier), vid)); scenario.getVehicles() .addVehicle(scenario.getVehicles().getFactory().createVehicle(vid, carrierVehicle.getType())); freightVehicles.add(vid); @@ -155,7 +145,7 @@ private void manageJspritDepartureTimes(Plan plan) { Activity currentActivity = (Activity) planElement; // Handle all regular services - if(!currentActivity.getType().equals(FreightConstants.START)) + if(!currentActivity.getType().equals(CarrierConstants.START)) { // ExpectedArrivalTime from jsprit needs to be recalculated @@ -165,7 +155,7 @@ private void manageJspritDepartureTimes(Plan plan) { Activity prevAct = (Activity) planElements.get(i - 2); - if (!prevAct.getType().equals(FreightConstants.START)) { + if (!prevAct.getType().equals(CarrierConstants.START)) { //End of plan is reached if (nextLeg == null) { @@ -187,7 +177,7 @@ private void manageJspritDepartureTimes(Plan plan) { } - } else if (currentActivity.getType().equals(FreightConstants.START)) + } else if (currentActivity.getType().equals(CarrierConstants.START)) { double travelTimeToFirstJob = ((Leg) planElements.get(i+1)).getTravelTime().seconds(); @@ -211,19 +201,19 @@ private void manageJspritDepartureTimes(Plan plan) { */ private Plan createPlainPlanFromTour(Carrier carrier, ScheduledTour scheduledTour) { - String carrierMode = CarrierUtils.getCarrierMode(carrier); + String carrierMode = CarriersUtils.getCarrierMode(carrier); // Create empty plan Plan plan = PopulationUtils.createPlan(); // Create start activity - Activity startActivity = PopulationUtils.createActivityFromLinkId(FreightConstants.START, + Activity startActivity = PopulationUtils.createActivityFromLinkId(CarrierConstants.START, scheduledTour.getVehicle().getLinkId() ); plan.addActivity(startActivity); for (Tour.TourElement tourElement : scheduledTour.getTour().getTourElements()) { - if (tourElement instanceof org.matsim.contrib.freight.carrier.Tour.Leg) { + if (tourElement instanceof Tour.Leg) { // Take information from scheduled leg and create a defaultLeg Tour.Leg tourLeg = (Tour.Leg) tourElement; @@ -273,7 +263,7 @@ private Plan createPlainPlanFromTour(Carrier carrier, ScheduledTour scheduledTou } // Create end activity - Activity endActivity = PopulationUtils.createActivityFromLinkId(FreightConstants.END, + Activity endActivity = PopulationUtils.createActivityFromLinkId(CarrierConstants.END, scheduledTour.getVehicle().getLinkId() ); plan.addActivity(endActivity); @@ -301,7 +291,7 @@ public void notifyBeforeMobsim(BeforeMobsimEvent event) { createAndAddFreightAgents(this.carriers, this.scenario.getPopulation()); event.getServices().getInjector().getInstance(ScoreCommercialJobs.class).prepareTourArrivalsForDay(); - String dir = event.getServices().getConfig().controler().getOutputDirectory() + "/ITERS/it." + event.getIteration() + "/"; + String dir = event.getServices().getConfig().controller().getOutputDirectory() + "/ITERS/it." + event.getIteration() + "/"; log.info("writing carrier file of iteration " + event.getIteration() + " to " + dir); CarrierPlanWriter planWriter = new CarrierPlanWriter(carriers); planWriter.write(dir + "carriers_it" + event.getIteration() + ".xml"); @@ -415,7 +405,7 @@ private void toggleChangeCommercialJobOperatorStrategy(int currentIteration) { log.info("Toggle " + ChangeCommercialJobOperator.SELECTOR_NAME); } - Collection allStrategies = this.scenario.getConfig().strategy().getStrategySettings(); + Collection allStrategies = this.scenario.getConfig().replanning().getStrategySettings(); for (StrategySettings strategy : allStrategies) { if (strategy.getStrategyName().equals(ChangeCommercialJobOperator.SELECTOR_NAME)) { diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandModule.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandModule.java index 5860e8a5cd9..a4f182cf17a 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandModule.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandModule.java @@ -27,10 +27,10 @@ import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.drt.run.MultiModeDrtModule; import org.matsim.contrib.dvrp.run.DvrpModule; -import org.matsim.contrib.freight.carrier.Carriers; -import org.matsim.contrib.freight.jsprit.NetworkBasedTransportCostsFactory; -import org.matsim.contrib.freight.jsprit.VRPTransportCostsFactory; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.Carriers; +import org.matsim.freight.carriers.jsprit.NetworkBasedTransportCostsFactory; +import org.matsim.freight.carriers.jsprit.VRPTransportCostsFactory; import org.matsim.core.config.Config; import org.matsim.core.controler.AbstractModule; import org.matsim.core.replanning.PlanStrategy; @@ -92,7 +92,7 @@ private CarrierProvider() { } public Carriers get() { - return FreightUtils.getCarriers(this.scenario); + return CarriersUtils.getCarriers(this.scenario); } } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandUtils.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandUtils.java index 5089e8b9e87..e433a3651e6 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandUtils.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/JointDemandUtils.java @@ -22,9 +22,9 @@ import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierVehicle; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierVehicle; +import org.matsim.freight.carriers.Carriers; import javax.annotation.Nullable; import java.util.*; diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample.java index 9a0df38130b..905102b912c 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample.java @@ -22,12 +22,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -70,17 +70,17 @@ public void run(URL configUrl){ jointDemandConfigGroup.setFirstLegTraveltimeBufferFactor(1.5); jointDemandConfigGroup.setChangeCommercialJobOperatorInterval(2); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setTravelTimeSliceWidth(3600); - freightConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); - freightConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setTravelTimeSliceWidth(3600); + freightCarriersConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); + freightCarriersConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); prepareConfig(config); Scenario scenario = loadScenario(config); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightConfigGroup + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightCarriersConfigGroup //alternatively, one can read in the input Carriers and CarrierVehicleTypes manually and use - //FreightUtils.getCarriers(scenario) and FreightUtils.getCarrierVehicleTypes(scenario) + //CarrierControlerUtils.getCarriers(scenario) and CarrierControlerUtils.getCarrierVehicleTypes(scenario) Controler controler = new Controler(scenario); controler.addOverridingModule(new JointDemandModule() ); @@ -88,28 +88,28 @@ public void run(URL configUrl){ } private static void prepareConfig(Config config) { - StrategyConfigGroup.StrategySettings changeExpBeta = new StrategyConfigGroup.StrategySettings(); + ReplanningConfigGroup.StrategySettings changeExpBeta = new ReplanningConfigGroup.StrategySettings(); changeExpBeta.setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta); changeExpBeta.setWeight(0.5); - config.strategy().addStrategySettings(changeExpBeta); + config.replanning().addStrategySettings(changeExpBeta); - StrategyConfigGroup.StrategySettings changeJobOperator = new StrategyConfigGroup.StrategySettings(); + ReplanningConfigGroup.StrategySettings changeJobOperator = new ReplanningConfigGroup.StrategySettings(); changeJobOperator.setStrategyName(ChangeCommercialJobOperator.SELECTOR_NAME); changeJobOperator.setWeight(0.5); - config.strategy().addStrategySettings(changeJobOperator); + config.replanning().addStrategySettings(changeJobOperator); - config.strategy().setFractionOfIterationsToDisableInnovation(.8); - PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + config.replanning().setFractionOfIterationsToDisableInnovation(.8); + ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(14 * 3600); - config.planCalcScore().addActivityParams(home); - PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + config.scoring().addActivityParams(home); + ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(14 * 3600); work.setOpeningTime(8 * 3600); work.setClosingTime(8 * 3600); - config.planCalcScore().addActivityParams(work); - config.controler().setWriteEventsInterval(5); - config.controler().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample"); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(5); + config.scoring().addActivityParams(work); + config.controller().setWriteEventsInterval(5); + config.controller().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandCarToggleJspritExample"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(5); } } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ScoreCommercialJobs.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ScoreCommercialJobs.java index dfc0ef82bb8..c3a6a068701 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ScoreCommercialJobs.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ScoreCommercialJobs.java @@ -30,10 +30,10 @@ import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.*; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierService; -import org.matsim.contrib.freight.carrier.Carriers; -import org.matsim.contrib.freight.carrier.FreightConstants; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierService; +import org.matsim.freight.carriers.Carriers; +import org.matsim.freight.carriers.CarrierConstants; import org.matsim.core.api.experimental.events.EventsManager; import java.util.*; @@ -93,7 +93,7 @@ void prepareTourArrivalsForDay() { private void handleFreightActivityStart(ActivityStartEvent event) { - if (event.getActType().equals(FreightConstants.END)) { + if (event.getActType().equals(CarrierConstants.END)) { activeDeliveryAgents.remove(event.getPersonId()); } else if (event.getActType().startsWith(CommercialJobGenerator.COMMERCIALJOB_ACTIVITYTYPE_PREFIX)) { @@ -126,7 +126,7 @@ public void reset(int iteration) { @Override public void handleEvent(ActivityEndEvent event) { - if (event.getActType().equals(FreightConstants.START)) { + if (event.getActType().equals(CarrierConstants.START)) { activeDeliveryAgents.add(event.getPersonId()); } } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourLengthAnalyzer.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourLengthAnalyzer.java index 6863d152a7d..c1434c351b9 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourLengthAnalyzer.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourLengthAnalyzer.java @@ -33,7 +33,7 @@ import org.matsim.api.core.v01.events.handler.PersonLeavesVehicleEventHandler; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.freight.carrier.FreightConstants; +import org.matsim.freight.carriers.CarrierConstants; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.vehicles.Vehicle; @@ -59,7 +59,7 @@ private TourLengthAnalyzer(Network network, EventsManager eventsManager) { @Override public void handleEvent(ActivityEndEvent event) { - if (event.getActType().equals(FreightConstants.START)) { + if (event.getActType().equals(CarrierConstants.START)) { deliveryAgentDistances.put(event.getPersonId(), 0.0); } } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourPlanning.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourPlanning.java index 1a3fd7e9713..2c1e9d82e2e 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourPlanning.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TourPlanning.java @@ -31,14 +31,15 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.CarrierPlan; -import org.matsim.contrib.freight.carrier.CarrierUtils; -import org.matsim.contrib.freight.carrier.Carriers; -import org.matsim.contrib.freight.jsprit.MatsimJspritFactory; -import org.matsim.contrib.freight.jsprit.NetworkBasedTransportCosts; -import org.matsim.contrib.freight.jsprit.VRPTransportCosts; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.CarrierPlan; +import org.matsim.freight.carriers.CarriersUtils; +import org.matsim.freight.carriers.Carriers; +import org.matsim.freight.carriers.jsprit.MatsimJspritFactory; +import org.matsim.freight.carriers.jsprit.NetworkBasedTransportCosts; +import org.matsim.freight.carriers.jsprit.VRPTransportCosts; import org.matsim.core.router.util.TravelTime; +import org.matsim.freight.carriers.jsprit.NetworkRouter; import org.matsim.vehicles.VehicleType; import java.util.ArrayList; @@ -91,8 +92,8 @@ static void runTourPlanningForCarriers(Carriers carriers, Scenario scenario, VRP HashMap, Integer> sortedMap = carrierServiceCounterMap.entrySet().stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new)); - - + + ArrayList> tempList = new ArrayList<>(sortedMap.keySet()); ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors()); forkJoinPool.submit(() -> tempList.parallelStream().forEach(carrierId -> { @@ -152,7 +153,7 @@ static void runTourPlanningForCarriers(Carriers carriers, Scenario scenario, VRP log.info("setting maxIterations=1 as carrier has no services"); algorithm.setMaxIterations(1); } else { - algorithm.setMaxIterations(CarrierUtils.getJspritIterations(carrier)); + algorithm.setMaxIterations(CarriersUtils.getJspritIterations(carrier)); } // variationCoefficient = stdDeviation/mean. so i set the threshold rather soft @@ -167,7 +168,7 @@ static void runTourPlanningForCarriers(Carriers carriers, Scenario scenario, VRP CarrierPlan carrierPlan = MatsimJspritFactory.createPlan(carrier, bestSolution); log.info("routing plan for carrier " + carrier.getId()); - org.matsim.contrib.freight.jsprit.NetworkRouter.routePlan(carrierPlan, transportCosts); // we need to route + NetworkRouter.routePlan(carrierPlan, transportCosts); // we need to route // the plans in // order to create // reasonable diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandCarExample.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandCarExample.java index 5a3386fba32..62c6584a1de 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandCarExample.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandCarExample.java @@ -25,12 +25,12 @@ import org.matsim.contrib.commercialTrafficApplications.jointDemand.ChangeCommercialJobOperator; import org.matsim.contrib.commercialTrafficApplications.jointDemand.JointDemandConfigGroup; import org.matsim.contrib.commercialTrafficApplications.jointDemand.JointDemandModule; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -72,17 +72,17 @@ public void run(URL configUrl){ JointDemandConfigGroup jointDemandConfigGroup = ConfigUtils.addOrGetModule(config, JointDemandConfigGroup.class); jointDemandConfigGroup.setFirstLegTraveltimeBufferFactor(1.5); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setTravelTimeSliceWidth(3600); - freightConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); - freightConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setTravelTimeSliceWidth(3600); + freightCarriersConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); + freightCarriersConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); prepareConfig(config); Scenario scenario = loadScenario(config); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightConfigGroup + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightCarriersConfigGroup //alternatively, one can read in the input Carriers and CarrierVehicleTypes manually and use - //FreightUtils.getCarriers(scenario) and FreightUtils.getCarrierVehicleTypes(scenario) + //CarrierControlerUtils.getCarriers(scenario) and CarrierControlerUtils.getCarrierVehicleTypes(scenario) Controler controler = new Controler(scenario); controler.addOverridingModule(new JointDemandModule() ); @@ -90,28 +90,28 @@ public void run(URL configUrl){ } private static void prepareConfig(Config config) { - StrategyConfigGroup.StrategySettings changeExpBeta = new StrategyConfigGroup.StrategySettings(); + ReplanningConfigGroup.StrategySettings changeExpBeta = new ReplanningConfigGroup.StrategySettings(); changeExpBeta.setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta); changeExpBeta.setWeight(0.5); - config.strategy().addStrategySettings(changeExpBeta); + config.replanning().addStrategySettings(changeExpBeta); - StrategyConfigGroup.StrategySettings changeJobOperator = new StrategyConfigGroup.StrategySettings(); + ReplanningConfigGroup.StrategySettings changeJobOperator = new ReplanningConfigGroup.StrategySettings(); changeJobOperator.setStrategyName(ChangeCommercialJobOperator.SELECTOR_NAME); changeJobOperator.setWeight(0.5); - config.strategy().addStrategySettings(changeJobOperator); + config.replanning().addStrategySettings(changeJobOperator); - config.strategy().setFractionOfIterationsToDisableInnovation(.8); - PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + config.replanning().setFractionOfIterationsToDisableInnovation(.8); + ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(14 * 3600); - config.planCalcScore().addActivityParams(home); - PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + config.scoring().addActivityParams(home); + ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(14 * 3600); work.setOpeningTime(8 * 3600); work.setClosingTime(8 * 3600); - config.planCalcScore().addActivityParams(work); - config.controler().setWriteEventsInterval(5); - config.controler().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandCarExample"); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(5); + config.scoring().addActivityParams(work); + config.controller().setWriteEventsInterval(5); + config.controller().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandCarExample"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(5); } } diff --git a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandDRTExample.java b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandDRTExample.java index ee0983bd2df..68addb0a68a 100644 --- a/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandDRTExample.java +++ b/contribs/commercialTrafficApplications/src/main/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/examples/RunJointDemandDRTExample.java @@ -36,13 +36,13 @@ import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.run.DvrpQSimComponents; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -80,14 +80,14 @@ public static void main(String[] args) throws IOException { public void run(URL configURL){ Config config = loadConfig(configURL); prepareConfig(config); - DrtConfigs.adjustMultiModeDrtConfig(MultiModeDrtConfigGroup.get(config), config.planCalcScore(), - config.plansCalcRoute()); + DrtConfigs.adjustMultiModeDrtConfig(MultiModeDrtConfigGroup.get(config), config.scoring(), + config.routing()); Scenario scenario = DrtControlerCreator.createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightConfigGroup + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); //assumes that input file paths are set in FreightCarriersConfigGroup //alternatively, one can read in the input Carriers and CarrierVehicleTypes manually and use - //FreightUtils.getCarriers(scenario) and FreightUtils.getCarrierVehicleTypes(scenario) + //CarrierControlerUtils.getCarriers(scenario) and CarrierControlerUtils.getCarrierVehicleTypes(scenario) Controler controler = new Controler(scenario); controler.addOverridingModule(new JointDemandModule()); @@ -102,33 +102,33 @@ private static void prepareConfig(Config config) { config.qsim().setSimStarttimeInterpretation(QSimConfigGroup.StarttimeInterpretation.onlyUseStarttime); - StrategyConfigGroup.StrategySettings changeExpBeta = new StrategyConfigGroup.StrategySettings(); + ReplanningConfigGroup.StrategySettings changeExpBeta = new ReplanningConfigGroup.StrategySettings(); changeExpBeta.setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta); changeExpBeta.setWeight(0.5); - config.strategy().addStrategySettings(changeExpBeta); - StrategyConfigGroup.StrategySettings changeServiceOperator = new StrategyConfigGroup.StrategySettings(); + config.replanning().addStrategySettings(changeExpBeta); + ReplanningConfigGroup.StrategySettings changeServiceOperator = new ReplanningConfigGroup.StrategySettings(); changeServiceOperator.setStrategyName(ChangeCommercialJobOperator.SELECTOR_NAME); changeServiceOperator.setWeight(0.5); - config.strategy().addStrategySettings(changeServiceOperator); + config.replanning().addStrategySettings(changeServiceOperator); - config.strategy().setFractionOfIterationsToDisableInnovation(.8); - PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + config.replanning().setFractionOfIterationsToDisableInnovation(.8); + ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(14 * 3600); - config.planCalcScore().addActivityParams(home); - PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + config.scoring().addActivityParams(home); + ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(14 * 3600); work.setOpeningTime(8 * 3600); work.setClosingTime(8 * 3600); - config.planCalcScore().addActivityParams(work); - config.controler().setWriteEventsInterval(1); - config.controler().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandUsingDRTExample"); - config.controler() + config.scoring().addActivityParams(work); + config.controller().setWriteEventsInterval(1); + config.controller().setOutputDirectory("output/commercialTrafficApplications/jointDemand/RunJointDemandUsingDRTExample"); + config.controller() .setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); config.qsim().setEndTime(26 * 3600); config.qsim().setSimEndtimeInterpretation(QSimConfigGroup.EndtimeInterpretation.onlyUseEndtime); - config.controler().setLastIteration(5); + config.controller().setLastIteration(5); } private static void loadConfigGroups(Config config) { @@ -147,8 +147,8 @@ private static void loadConfigGroups(Config config) { JointDemandConfigGroup jointDemandConfigGroup = ConfigUtils.addOrGetModule(config, JointDemandConfigGroup.class); jointDemandConfigGroup.setFirstLegTraveltimeBufferFactor(1.5); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setCarriersFile("jointDemand_carriers_drt.xml"); - freightConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersFile("jointDemand_carriers_drt.xml"); + freightCarriersConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); } } diff --git a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperatorTest.java b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperatorTest.java index a66096756ac..18de26d446d 100644 --- a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperatorTest.java +++ b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/ChangeCommercialJobOperatorTest.java @@ -7,11 +7,8 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Plan; -import org.matsim.contrib.commercialTrafficApplications.jointDemand.ChangeCommercialJobOperator; -import org.matsim.contrib.commercialTrafficApplications.jointDemand.JointDemandUtils; -import org.matsim.contrib.commercialTrafficApplications.jointDemand.TestScenarioGeneration; -import org.matsim.contrib.freight.carrier.Carrier; -import org.matsim.contrib.freight.carrier.Carriers; +import org.matsim.freight.carriers.Carrier; +import org.matsim.freight.carriers.Carriers; import org.matsim.core.router.util.TravelTime; import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; @@ -49,4 +46,4 @@ public void getPlanAlgoInstance() { } -} \ No newline at end of file +} diff --git a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficIntegrationTest.java b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficIntegrationTest.java index 2ea7d0a9853..27b59b0d621 100644 --- a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficIntegrationTest.java +++ b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/CommercialTrafficIntegrationTest.java @@ -2,8 +2,8 @@ import org.junit.Test; import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.freight.FreightConfigGroup; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.FreightCarriersConfigGroup; +import org.matsim.freight.carriers.CarriersUtils; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.Controler; @@ -15,13 +15,13 @@ public class CommercialTrafficIntegrationTest { @Test public void runCommercialTrafficIT() { Config config = ConfigUtils.loadConfig("./scenarios/grid/jointDemand_config.xml"); - config.controler().setLastIteration(5); + config.controller().setLastIteration(5); ConfigUtils.addOrGetModule(config, JointDemandConfigGroup.class); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); - freightConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); + freightCarriersConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); Scenario scenario = ScenarioUtils.loadScenario(config); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); Controler controler = new Controler(scenario); controler.addOverridingModule(new JointDemandModule()); controler.run(); diff --git a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/IsTheRightCustomerScoredTest.java b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/IsTheRightCustomerScoredTest.java index 3b250c4f855..b9f40a81c5d 100644 --- a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/IsTheRightCustomerScoredTest.java +++ b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/IsTheRightCustomerScoredTest.java @@ -27,9 +27,7 @@ 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.freight.FreightConfigGroup; -import org.matsim.contrib.freight.carrier.*; -import org.matsim.contrib.freight.controler.FreightUtils; +import org.matsim.freight.carriers.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.Controler; @@ -47,18 +45,18 @@ public class IsTheRightCustomerScoredTest { public void setUp() { Config config = ConfigUtils.loadConfig("./scenarios/grid/jointDemand_config.xml"); - config.controler().setLastIteration(0); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); + config.controller().setLastIteration(0); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles); JointDemandConfigGroup jointDemandConfigGroup = ConfigUtils.addOrGetModule(config, JointDemandConfigGroup.class); jointDemandConfigGroup.setMaxJobScore(MAX_JOB_SCORE); - FreightConfigGroup freightConfigGroup = ConfigUtils.addOrGetModule(config, FreightConfigGroup.class); - freightConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); - freightConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersFile("jointDemand_carriers_car.xml"); + freightCarriersConfigGroup.setCarriersVehicleTypesFile("jointDemand_vehicleTypes.xml"); scenario = ScenarioUtils.loadScenario(config); - FreightUtils.loadCarriersAccordingToFreightConfig(scenario); + CarriersUtils.loadCarriersAccordingToFreightConfig(scenario); //limit the fleet size of carrier pizza_1 so that it can handly only one order/job - FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("salamiPizza", Carrier.class)).getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.FINITE); + CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("salamiPizza", Carrier.class)).getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.FINITE); preparePopulation(scenario); @@ -107,7 +105,7 @@ public void testIfTheRightPersonIsScoredForReceivingAJob() { Plan nonCustomerPlan = scenario.getPopulation().getPersons().get(Id.createPersonId("nonCustomer")).getSelectedPlan(); //derive the service activity from the carrier plan and compare the service id (which should contain the customer id) with the person id of the expected customer - Carrier pizzaCarrier = FreightUtils.getCarriers(scenario).getCarriers().get(Id.create("salamiPizza", Carrier.class)); + Carrier pizzaCarrier = CarriersUtils.getCarriers(scenario).getCarriers().get(Id.create("salamiPizza", Carrier.class)); ScheduledTour tour = (ScheduledTour) pizzaCarrier.getSelectedPlan().getScheduledTours().toArray()[0]; Id serviceActivity = tour.getTour().getTourElements().stream() .filter(tourElement -> tourElement instanceof Tour.ServiceActivity) diff --git a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TestScenarioGeneration.java b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TestScenarioGeneration.java index 629c1bbd6c6..1d6b845a665 100644 --- a/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TestScenarioGeneration.java +++ b/contribs/commercialTrafficApplications/src/test/java/org/matsim/contrib/commercialTrafficApplications/jointDemand/TestScenarioGeneration.java @@ -26,9 +26,9 @@ import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; -import org.matsim.contrib.freight.carrier.*; import org.matsim.core.config.Config; import org.matsim.core.population.PopulationUtils; +import org.matsim.freight.carriers.*; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; @@ -54,18 +54,18 @@ public static Carriers generateCarriers() { Carriers carriers = new Carriers(); Carrier italianPizzaPlace = CarrierImpl.newInstance(Id.create("pizza_italian", Carrier.class)); - CarrierUtils.setCarrierMode(italianPizzaPlace, TransportMode.car); - CarrierUtils.setJspritIterations(italianPizzaPlace, 20); + CarriersUtils.setCarrierMode(italianPizzaPlace, TransportMode.car); + CarriersUtils.setJspritIterations(italianPizzaPlace, 20); italianPizzaPlace.getAttributes().putAttribute(JointDemandUtils.CARRIER_MARKET_ATTRIBUTE_NAME, "pizza"); Carrier americanPizzaPlace = CarrierImpl.newInstance(Id.create("pizza_american", Carrier.class)); - CarrierUtils.setCarrierMode(americanPizzaPlace, TransportMode.car); - CarrierUtils.setJspritIterations(americanPizzaPlace, 20); + CarriersUtils.setCarrierMode(americanPizzaPlace, TransportMode.car); + CarriersUtils.setJspritIterations(americanPizzaPlace, 20); americanPizzaPlace.getAttributes().putAttribute(JointDemandUtils.CARRIER_MARKET_ATTRIBUTE_NAME, "pizza"); Carrier shopping_1 = CarrierImpl.newInstance(Id.create("shopping_1", Carrier.class)); - CarrierUtils.setCarrierMode(shopping_1, TransportMode.car); - CarrierUtils.setJspritIterations(shopping_1, 20); + CarriersUtils.setCarrierMode(shopping_1, TransportMode.car); + CarriersUtils.setJspritIterations(shopping_1, 20); shopping_1.getAttributes().putAttribute(JointDemandUtils.CARRIER_MARKET_ATTRIBUTE_NAME, "shopping"); VehicleType type = createLightType(); diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/csv/CompactCSVWriter.java b/contribs/common/src/main/java/org/matsim/contrib/common/csv/CompactCSVWriter.java index c51911f531a..35d7ec5f660 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/csv/CompactCSVWriter.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/csv/CompactCSVWriter.java @@ -21,10 +21,9 @@ package org.matsim.contrib.common.csv; import java.io.IOException; +import java.io.UncheckedIOException; import java.io.Writer; -import org.matsim.core.utils.io.UncheckedIOException; - import com.opencsv.CSVWriter; public class CompactCSVWriter extends CSVWriter { diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterFactory.java b/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterFactory.java deleted file mode 100644 index 7fba53c4198..00000000000 --- a/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterFactory.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.matsim.contrib.common.randomizedtransitrouter;/* *********************************************************************** * - * project: org.matsim.* - * RandomizedTransitRouterFacotry - * * - * *********************************************************************** * - * * - * copyright : (C) 2013 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -import org.matsim.core.config.Config; -import org.matsim.pt.router.*; -import org.matsim.pt.transitSchedule.api.TransitSchedule; - -import jakarta.inject.Inject; -import jakarta.inject.Provider; - - -/** - * @author dgrether - * - */ -public class RandomizingTransitRouterFactory implements Provider { - - private TransitRouterConfig trConfig; - private TransitSchedule schedule; - private TransitRouterNetwork routerNetwork; - - @Inject - RandomizingTransitRouterFactory(Config config, TransitSchedule schedule) { - this.trConfig = new TransitRouterConfig(config); - this.schedule = schedule; - this.routerNetwork = TransitRouterNetwork.createFromSchedule(schedule, trConfig.getBeelineWalkConnectionDistance()); - } - - @Override - public TransitRouter get() { - RandomizingTransitRouterTravelTimeAndDisutility ttCalculator = new RandomizingTransitRouterTravelTimeAndDisutility(trConfig); - ttCalculator.setDataCollection(RandomizingTransitRouterTravelTimeAndDisutility.DataCollection.randomizedParameters, true) ; - ttCalculator.setDataCollection(RandomizingTransitRouterTravelTimeAndDisutility.DataCollection.additionalInformation, false) ; - return new TransitRouterImpl(trConfig, new PreparedTransitSchedule(schedule), routerNetwork, ttCalculator, ttCalculator); - } - -} diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterTravelTimeAndDisutility.java b/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterTravelTimeAndDisutility.java deleted file mode 100644 index d75e386838e..00000000000 --- a/contribs/common/src/main/java/org/matsim/contrib/common/randomizedtransitrouter/RandomizingTransitRouterTravelTimeAndDisutility.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.matsim.contrib.common.randomizedtransitrouter;/* *********************************************************************** * - * project: org.matsim.* - * RandomizedTransitRouterNetworkTravelTimeAndDisutility2 - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.population.Person; -import org.matsim.core.gbl.MatsimRandom; -import org.matsim.pt.router.CustomDataManager; -import org.matsim.pt.router.TransitRouterConfig; -import org.matsim.pt.router.TransitRouterNetwork.TransitRouterNetworkLink; -import org.matsim.pt.router.TransitRouterNetworkTravelTimeAndDisutility; -import org.matsim.vehicles.Vehicle; - -import java.util.HashMap; -import java.util.Map; - - -/** - * When plugged into the transit router, will switch to a different, randomly generated combination of - * marginal utilities every time it is called for a new agent. - *

- * Comments:
    - *
  • In the literature, they seem to use log-normal distributions for the tastes. (Makes sense - * since it addresse the symmetry problem which I am addressing ad-hoc.) [M. K. Anderson, hEART'13] - * kai, sep'13 - *
  • People seem to use "gamma distributions for the error term" ("double stochastic assignment"). - * Not sure what that does, if we need it, etc. [M. K. Anderson, hEART'13] kai, sep'13 - *
- * @author kai - * @author dgrether - * - */ -public class RandomizingTransitRouterTravelTimeAndDisutility extends TransitRouterNetworkTravelTimeAndDisutility { - - public enum DataCollection {randomizedParameters, additionalInformation} - private Id cachedPersonId = null ; - private final TransitRouterConfig originalTransitRouterConfig ; - - private double localMarginalUtilityOfTravelTimeWalk_utl_s = Double.NaN ; - private double localMarginalUtilityOfWaitingPt_utl_s = Double.NaN ; - private double localUtilityOfLineSwitch_utl = Double.NaN ; - private double localMarginalUtilityOfTravelTimePt_utl_s = Double.NaN ; - private double localMarginalUtilityOfTravelDistancePt_utl_m = Double.NaN ; - - private Map dataCollectionConfig = new HashMap() ; - private Map dataCollectionStrings = new HashMap() ; - - public RandomizingTransitRouterTravelTimeAndDisutility(TransitRouterConfig routerConfig) { - super(routerConfig); - - prepareDataCollection(); - - // make sure that some parameters are not zero since otherwise the randomization will not work: - - // marg utl time wlk should be around -3/h or -(3/3600)/sec. Give warning if not at least 1/3600: - if ( -routerConfig.getMarginalUtilityOfTravelTimeWalk_utl_s() < 1./3600. ) { - LogManager.getLogger(this.getClass()).warn( "marg utl of walk rather close to zero; randomization may not work") ; - } - // utl of line switch should be around -300sec or -0.5u. Give warning if not at least 0.1u: - if ( -routerConfig.getUtilityOfLineSwitch_utl() < 0.1 ) { - LogManager.getLogger(this.getClass()).warn( "utl of line switch rather close to zero; randomization may not work") ; - } - - this.originalTransitRouterConfig = routerConfig ; - - this.localMarginalUtilityOfTravelDistancePt_utl_m = routerConfig.getMarginalUtilityOfTravelDistancePt_utl_m(); - this.localMarginalUtilityOfTravelTimePt_utl_s = routerConfig.getMarginalUtilityOfTravelTimePt_utl_s() ; - this.localMarginalUtilityOfTravelTimeWalk_utl_s = routerConfig.getMarginalUtilityOfTravelTimeWalk_utl_s() ; -// this.localMarginalUtilityOfWaitingPt_utl_s = routerConfig.getMarginalUtilityOfTravelTimePt_utl_s() ; - this.localMarginalUtilityOfWaitingPt_utl_s = routerConfig.getMarginalUtilityOfWaitingPt_utl_s() ; - this.localUtilityOfLineSwitch_utl = routerConfig.getUtilityOfLineSwitch_utl() ; - } - public final String getDataCollectionString( DataCollection item ) { - return dataCollectionStrings.get(item).toString() ; - } - - @Override - public double getLinkTravelDisutility(final Link link, final double time, final Person person, final Vehicle vehicle, - final CustomDataManager dataManager) { - - regenerateUtilityParametersIfPersonHasChanged(person); - - double disutl; - if (((TransitRouterNetworkLink) link).getRoute() == null) { - // (this means that it is a transfer link (walk)) - - double transfertime = getLinkTravelTime(link, time, person, vehicle); - double waittime = this.originalTransitRouterConfig.getAdditionalTransferTime(); - - // say that the effective walk time is the transfer time minus some "buffer" - double walktime = transfertime - waittime; - - disutl = - walktime * localMarginalUtilityOfTravelTimeWalk_utl_s - - waittime * localMarginalUtilityOfWaitingPt_utl_s - - localUtilityOfLineSwitch_utl; - - } else { - - double offVehWaitTime = offVehicleWaitTime(link, time); - - double inVehTime = getLinkTravelTime(link,time, person, vehicle) - offVehWaitTime ; - - disutl = -inVehTime * this.localMarginalUtilityOfTravelTimePt_utl_s - - offVehWaitTime * this.localMarginalUtilityOfWaitingPt_utl_s - - link.getLength() * this.localMarginalUtilityOfTravelDistancePt_utl_m; - } - - if ( this.dataCollectionConfig.get(DataCollection.additionalInformation )) { - StringBuffer strb = this.dataCollectionStrings.get(DataCollection.additionalInformation ) ; - strb.append("also collecting additional information") ; - } - - return disutl; - } - - @Override - public double getWalkTravelDisutility(Person person, Coord coord, Coord toCoord) { - regenerateUtilityParametersIfPersonHasChanged(person); - return - getWalkTravelTime(person, coord, toCoord) * localMarginalUtilityOfTravelTimeWalk_utl_s ; - } - - public final void setDataCollection( DataCollection item, Boolean bbb ) { - LogManager.getLogger(this.getClass()).info( " settin data collection of " + item.toString() + " to " + bbb.toString() ) ; - dataCollectionConfig.put( item, bbb ) ; - } - - private void prepareDataCollection() { - for ( DataCollection dataCollection : DataCollection.values() ) { - switch ( dataCollection ) { - case randomizedParameters: - dataCollectionConfig.put( dataCollection, false ) ; - dataCollectionStrings.put( dataCollection, new StringBuffer() ) ; - break; - case additionalInformation: - dataCollectionConfig.put( dataCollection, false ) ; - dataCollectionStrings.put( dataCollection, new StringBuffer() ) ; - break; - } - } - } - - private void regenerateUtilityParametersIfPersonHasChanged(final Person person) { - if ( !person.getId().equals(this.cachedPersonId)) { - // yyyyyy probably not thread safe (?!?!) - - // person has changed, so ... - - // ... memorize new person id: - this.cachedPersonId = person.getId() ; - - // ... generate new random parameters: - { - double tmp = this.originalTransitRouterConfig.getMarginalUtilityOfTravelTimeWalk_utl_s() ; - tmp *= 5. * MatsimRandom.getRandom().nextDouble() ; - localMarginalUtilityOfTravelTimeWalk_utl_s = tmp ; - // yy if this becomes too small, they may walk the whole way (is it really clear why this can happen?) - } - { - double tmp = this.originalTransitRouterConfig.getUtilityOfLineSwitch_utl() ; - tmp *= 5. * MatsimRandom.getRandom().nextDouble() ; - localUtilityOfLineSwitch_utl = tmp ; - } - { - double tmp = this.originalTransitRouterConfig.getMarginalUtilityOfWaitingPt_utl_s(); - tmp *= 5. * MatsimRandom.getRandom().nextDouble(); - localMarginalUtilityOfWaitingPt_utl_s = tmp; - } - { - // (Conceptually, the following is not necessary, but empirically, it seems to help. kai, jan'13) - double tmp = this.originalTransitRouterConfig.getMarginalUtilityOfTravelTimePt_utl_s() ; - tmp *= 5. * MatsimRandom.getRandom().nextDouble(); - localMarginalUtilityOfTravelTimePt_utl_s = tmp; - } - - if ( this.dataCollectionConfig.get(DataCollection.randomizedParameters) ) { -// StringBuffer strb = this.dataCollectionStrings.get(DataCollection.randomizedParameters) ; -// strb.append - System.out.println("personId: " + person.getId() + - "; margUtlOfTimeWlk_h: " + this.localMarginalUtilityOfTravelTimeWalk_utl_s*3600. + - "; utlOfLineSwitch: " + this.localUtilityOfLineSwitch_utl + - "; margUtlOfWait_h: " + this.localMarginalUtilityOfWaitingPt_utl_s*3600. + - "; margUtlOfTimePt_h: " + this.localMarginalUtilityOfTravelTimePt_utl_s*3600. ) ; - } - } - } - -} diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java index d0d096fd999..768ec3ea404 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java @@ -48,7 +48,7 @@ public class ProfileWriter implements IterationEndsListener { public interface ProfileView { // times at which profile samples were collected - int[] times(); + double[] times(); // map of sampled time profiles ImmutableMap profiles(); @@ -77,7 +77,8 @@ public void notifyIterationEnds(IterationEndsEvent event) { String file = filename(outputFile); String timeFormat = Time.TIMEFORMAT_HHMMSS; - try (CompactCSVWriter writer = new CompactCSVWriter(IOUtils.getBufferedWriter(file + ".txt"))) { + try (CompactCSVWriter writer = new CompactCSVWriter(IOUtils.getBufferedWriter(file + ".txt"), + matsimServices.getConfig().global().getDefaultDelimiter().charAt(0))) { String[] profileHeader = profiles.keySet().toArray(new String[0]); writer.writeNext(new CSVLineBuilder().add("time").addAll(profileHeader)); for (int i = 0; i < times.length; i++) { @@ -86,7 +87,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { } } - if (this.matsimServices.getConfig().controler().isCreateGraphs()) { + if (this.matsimServices.getConfig().controller().isCreateGraphs()) { DefaultTableXYDataset xyDataset = createXYDataset(times, profiles); generateImage(xyDataset, TimeProfileCharts.ChartType.Line); generateImage(xyDataset, TimeProfileCharts.ChartType.StackedArea); @@ -97,7 +98,7 @@ private Stream cells(Map profiles, int idx) { return profiles.values().stream().map(profile -> profile[idx] + ""); } - private DefaultTableXYDataset createXYDataset(int[] times, Map profiles) { + private DefaultTableXYDataset createXYDataset(double[] times, Map profiles) { List seriesList = new ArrayList<>(profiles.size()); profiles.forEach((name, profile) -> { XYSeries series = new XYSeries(name, true, false); @@ -114,7 +115,7 @@ private DefaultTableXYDataset createXYDataset(int[] times, Map private void generateImage(DefaultTableXYDataset xyDataset, TimeProfileCharts.ChartType chartType) { JFreeChart chart = TimeProfileCharts.chartProfile(xyDataset, chartType); - String runID = matsimServices.getConfig().controler().getRunId(); + String runID = matsimServices.getConfig().controller().getRunId(); if (runID != null) { chart.setTitle(runID + " " + chart.getTitle().getText()); } diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeDiscretizer.java b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeDiscretizer.java index e0b6343776b..47337835b97 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeDiscretizer.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeDiscretizer.java @@ -25,35 +25,36 @@ import java.util.stream.IntStream; import org.matsim.core.config.groups.TravelTimeCalculatorConfigGroup; +import org.matsim.core.trafficmonitoring.TimeBinUtils; public class TimeDiscretizer { private final int intervalCount; - private final int timeInterval; + private final double timeInterval; private final int maxTime; public TimeDiscretizer(TravelTimeCalculatorConfigGroup ttcConfig) { this(ttcConfig.getMaxTime(), ttcConfig.getTraveltimeBinSize()); } - public TimeDiscretizer(int maxTime, int timeInterval) { + public TimeDiscretizer(int maxTime, double timeInterval) { checkArgument(timeInterval > 0, "interval size must be positive"); checkArgument(maxTime >= 0, "maxTime must not be negative"); this.timeInterval = timeInterval; this.maxTime = maxTime; - intervalCount = maxTime / timeInterval + 1; + intervalCount = TimeBinUtils.getTimeBinCount(maxTime, timeInterval); } public int getIdx(double time) { checkArgument(time >= 0); checkArgument(time <= maxTime); - return (int)time / timeInterval; + return TimeBinUtils.getTimeBinIndex(time, timeInterval, intervalCount); } - public int discretize(double time) { + public double discretize(double time) { return getIdx(time) * timeInterval; } - public int getTimeInterval() { + public double getTimeInterval() { return timeInterval; } @@ -61,12 +62,12 @@ public int getIntervalCount() { return intervalCount; } - public int[] getTimes() { - return IntStream.range(0, intervalCount).map(i -> i * timeInterval).toArray(); + public double[] getTimes() { + return IntStream.range(0, intervalCount).mapToDouble(i -> i * timeInterval).toArray(); } public interface TimeBinConsumer { - void accept(int bin, int time); + void accept(int bin, double time); } public void forEach(TimeBinConsumer consumer) { diff --git a/contribs/common/src/test/java/org/matsim/contrib/common/randomizingtransitrouter/RandomizingTransitRouterIT.java b/contribs/common/src/test/java/org/matsim/contrib/common/randomizingtransitrouter/RandomizingTransitRouterIT.java deleted file mode 100644 index e703c6c4af6..00000000000 --- a/contribs/common/src/test/java/org/matsim/contrib/common/randomizingtransitrouter/RandomizingTransitRouterIT.java +++ /dev/null @@ -1,163 +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.common.randomizingtransitrouter; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.events.PersonEntersVehicleEvent; -import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler; -import org.matsim.contrib.common.randomizedtransitrouter.RandomizingTransitRouterModule; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.FacilitiesConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; -import org.matsim.core.config.groups.QSimConfigGroup.TrafficDynamics; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; -import org.matsim.core.config.groups.VspExperimentalConfigGroup.VspDefaultsCheckingLevel; -import org.matsim.core.controler.Controler; -import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule.DefaultSelector; -import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule.DefaultStrategy; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.pt.config.TransitConfigGroup.TransitRoutingAlgorithmType; -import org.matsim.testcases.MatsimTestUtils; -import org.matsim.vehicles.Vehicle; - - -/** - * @author nagel - * - */ -public class RandomizingTransitRouterIT { - private static final Logger log = LogManager.getLogger( RandomizingTransitRouterIT.class ) ; - - private static final class MyObserver implements PersonEntersVehicleEventHandler { -// private enum ObservedVehicle{ pt_1009_1 /*direct, fast, with wait*/, pt_2009_1 /*direct, slow*/, pt_3009_1 /*with interchange*/} ; - - Map,Double> cnts = new HashMap<>() ; - - @Override public void reset(int iteration) { - cnts.clear(); - } - - @Override public void handleEvent(PersonEntersVehicleEvent event) { - cnts.merge( event.getVehicleId() , 1. , Double::sum ); - } - - void printCounts() { - for ( Entry, Double> entry : cnts.entrySet() ) { - log.info( "Vehicle id: " + entry.getKey() + "; number of boards: " + entry.getValue() ) ; - } - } - - Map< Id, Double> getCounts() { - return this.cnts ; - } - } - - @Rule public MatsimTestUtils utils = new MatsimTestUtils() ; - - @Test -// @Ignore - public final void test() { - String outputDir = utils.getOutputDirectory() ; - - Config config = utils.createConfigWithPackageInputResourcePathAsContext(); - - config.network().setInputFile("network.xml"); - config.plans().setInputFile("population.xml"); - - config.transit().setRoutingAlgorithmType(TransitRoutingAlgorithmType.DijkstraBased); - config.transit().setTransitScheduleFile("transitschedule.xml"); - config.transit().setVehiclesFile("transitVehicles.xml"); - config.transit().setUseTransit(true); - - config.controler().setOutputDirectory( outputDir ); - config.controler().setLastIteration(20); - config.controler().setCreateGraphs(false); - config.controler().setDumpDataAtEnd(false); - - config.global().setNumberOfThreads(1); - - config.planCalcScore().addActivityParams( new ActivityParams("home").setTypicalDuration( 6*3600. ) ); - config.planCalcScore().addActivityParams( new ActivityParams("education_100").setTypicalDuration( 6*3600. ) ); - -// config.strategy().addStrategySettings( new StrategySettings( ConfigUtils.createAvailableStrategyId(config)).setStrategyName(DefaultStrategy.ReRoute ).setWeight(0.1 ) ); -// config.strategy().addStrategySettings( new StrategySettings( ConfigUtils.createAvailableStrategyId(config)).setStrategyName(DefaultSelector.ChangeExpBeta ).setWeight(0.9 ) ); - config.strategy().addStrategySettings( new StrategySettings().setStrategyName(DefaultStrategy.ReRoute ).setWeight(0.1 ) ); - config.strategy().addStrategySettings( new StrategySettings().setStrategyName(DefaultSelector.ChangeExpBeta ).setWeight(0.9 ) ); - // yy changing the above (= no longer using createAvailableStrategyId) changes the results. :-( :-( :-( - - config.qsim().setEndTime(18.*3600.); - - config.timeAllocationMutator().setMutationRange(7200); - config.timeAllocationMutator().setAffectingDuration(false); - config.plans().setRemovingUnneccessaryPlanAttributes(true); - config.qsim().setTrafficDynamics( TrafficDynamics.withHoles ); - config.qsim().setUsingFastCapacityUpdate(true); - -// config.facilities().setFacilitiesSource( FacilitiesConfigGroup.FacilitiesSource.none ); - // yyyy changing this setting changes result. Possible reasons: - // * The implicit activity coordinates may be elsewhere. - // * The "fudged" walk distances may be different. - // * It uses getNearestLinkEXACTLY, and thus activities may be attached to other links. - - config.vspExperimental().setWritingOutputEvents(true); - config.vspExperimental().setVspDefaultsCheckingLevel( VspDefaultsCheckingLevel.warn ); - - // --- - - Scenario scenario = ScenarioUtils.loadScenario( config ) ; - - // --- - - Controler controler = new Controler( scenario ) ; - - controler.addOverridingModule( new RandomizingTransitRouterModule() ); - - final MyObserver observer = new MyObserver(); - controler.getEvents().addHandler(observer); - - controler.run(); - - // --- - - observer.printCounts(); - - // yyyy the following is just a regression test, making sure that results remain stable. In general, the randomized transit router - // could be improved, for example along the lines of the randomized regular router, which uses a (hopefully unbiased) lognormal - // distribution rather than a biased uniform distribution as is used here. kai, jul'15 - - Assert.assertEquals(36., observer.getCounts().get( Id.create("1009", Vehicle.class) ), 0.1 ); - Assert.assertEquals( 8. /*6.*/ , observer.getCounts().get( Id.create("1012", Vehicle.class) ) , 0.1 ); - Assert.assertEquals(22. /*21.*/, observer.getCounts().get( Id.create("2009", Vehicle.class) ) , 0.1 ); - Assert.assertEquals(36., observer.getCounts().get( Id.create("3009", Vehicle.class) ) , 0.1 ); - - - } - -} diff --git a/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest66IT.java b/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest66IT.java index 7db3eb15ed1..935e4c61fb8 100644 --- a/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest66IT.java +++ b/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest66IT.java @@ -133,7 +133,7 @@ public class BetaTravelTest66IT { @Test public void testBetaTravel_66() { Config config = utils.loadConfig("../../examples/scenarios/equil/config.xml"); ConfigUtils.loadConfig(config, utils.getInputDirectory() + "config.xml"); - config.controler().setWritePlansInterval(0); + config.controller().setWritePlansInterval(0); // --- Scenario scenario = ScenarioUtils.loadScenario(config); /* @@ -151,9 +151,9 @@ public void install() { } }); controler.addControlerListener(new TestControlerListener()); - controler.getConfig().controler().setCreateGraphs(false); - controler.getConfig().controler().setDumpDataAtEnd(false); - controler.getConfig().controler().setWriteEventsInterval(0); + controler.getConfig().controller().setCreateGraphs(false); + controler.getConfig().controller().setDumpDataAtEnd(false); + controler.getConfig().controller().setWriteEventsInterval(0); controler.run(); } @@ -275,7 +275,7 @@ public StrategyManager get() { StrategyManager manager = new StrategyManager(); manager.setMaxPlansPerAgent(5); - PlanStrategyImpl strategy1 = new PlanStrategyImpl(new ExpBetaPlanSelector<>(config.planCalcScore())); + PlanStrategyImpl strategy1 = new PlanStrategyImpl(new ExpBetaPlanSelector<>(config.scoring())); manager.addStrategy( strategy1, null, 0.80 ); PlanStrategyImpl strategy2 = new PlanStrategyImpl(new RandomPlanSelector<>()); @@ -317,12 +317,12 @@ private static class TestControlerListener implements StartupListener, Iteration @Override public void notifyStartup(final StartupEvent event) { // do some test to ensure the scenario is correct - double beta_travel = event.getServices().getConfig().planCalcScore().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); + double beta_travel = event.getServices().getConfig().scoring().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); if ((beta_travel != -6.0) && (beta_travel != -66.0)) { throw new IllegalArgumentException("Unexpected value for beta_travel. Expected -6.0 or -66.0, actual value is " + beta_travel); } - int lastIter = event.getServices().getConfig().controler().getLastIteration(); + int lastIter = event.getServices().getConfig().controller().getLastIteration(); if (lastIter < 100) { throw new IllegalArgumentException("Controler.lastIteration must be at least 100. Current value is " + lastIter); } @@ -360,7 +360,7 @@ public void notifyIterationEnds(final IterationEndsEvent event) { event.getServices().getEvents().removeHandler(this.ttAnalyzer); } if (iteration == 100) { - double beta_travel = event.getServices().getConfig().planCalcScore().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); + double beta_travel = event.getServices().getConfig().scoring().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); /* *************************************************************** * AUTOMATIC VERIFICATION OF THE TESTS: * diff --git a/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest6IT.java b/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest6IT.java index 6c9792fb458..9db009cbcf8 100644 --- a/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest6IT.java +++ b/contribs/common/src/test/java/org/matsim/integration/always/BetaTravelTest6IT.java @@ -133,7 +133,7 @@ public class BetaTravelTest6IT { @Test public void testBetaTravel_6() { Config config = utils.loadConfig("../../examples/scenarios/equil/config.xml"); // default config ConfigUtils.loadConfig(config, utils.getInputDirectory() + "config.xml"); // specific setting for this test - config.controler().setWritePlansInterval(0); + config.controller().setWritePlansInterval(0); config.plans().setActivityDurationInterpretation( ActivityDurationInterpretation.tryEndTimeThenDuration ); /* * The input plans file is not sorted. After switching from TreeMap to LinkedHashMap @@ -150,9 +150,9 @@ public void install() { } }); controler.addControlerListener(new TestControlerListener()); - controler.getConfig().controler().setCreateGraphs(false); - controler.getConfig().controler().setDumpDataAtEnd(false); - controler.getConfig().controler().setWriteEventsInterval(0); + controler.getConfig().controller().setCreateGraphs(false); + controler.getConfig().controller().setDumpDataAtEnd(false); + controler.getConfig().controller().setWriteEventsInterval(0); controler.run(); } @@ -273,7 +273,7 @@ public StrategyManager get() { StrategyManager manager = new StrategyManager(); manager.setMaxPlansPerAgent(5); - PlanStrategyImpl strategy1 = new PlanStrategyImpl(new ExpBetaPlanSelector(config.planCalcScore())); + PlanStrategyImpl strategy1 = new PlanStrategyImpl(new ExpBetaPlanSelector(config.scoring())); manager.addStrategy( strategy1, null, 0.80 ); PlanStrategyImpl strategy2 = new PlanStrategyImpl(new RandomPlanSelector()); @@ -314,12 +314,12 @@ public TestControlerListener() { @Override public void notifyStartup(final StartupEvent event) { // do some test to ensure the scenario is correct - double beta_travel = event.getServices().getConfig().planCalcScore().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); + double beta_travel = event.getServices().getConfig().scoring().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); if ((beta_travel != -6.0) && (beta_travel != -66.0)) { throw new IllegalArgumentException("Unexpected value for beta_travel. Expected -6.0 or -66.0, actual value is " + beta_travel); } - int lastIter = event.getServices().getConfig().controler().getLastIteration(); + int lastIter = event.getServices().getConfig().controller().getLastIteration(); if (lastIter < 100) { throw new IllegalArgumentException("Controler.lastIteration must be at least 100. Current value is " + lastIter); } @@ -357,7 +357,7 @@ public void notifyIterationEnds(final IterationEndsEvent event) { event.getServices().getEvents().removeHandler(this.ttAnalyzer); } if (iteration == 100) { - double beta_travel = event.getServices().getConfig().planCalcScore().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); + double beta_travel = event.getServices().getConfig().scoring().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling(); /* *************************************************************** * AUTOMATIC VERIFICATION OF THE TESTS: * diff --git a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionControlerListener.java b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionControlerListener.java index a044e27c510..080d92d0c51 100644 --- a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionControlerListener.java +++ b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionControlerListener.java @@ -19,7 +19,7 @@ * *********************************************************************** */ /** - * + * */ package org.matsim.contrib.decongestion; @@ -40,7 +40,7 @@ import org.matsim.contrib.decongestion.handler.DelayAnalysis; import org.matsim.contrib.decongestion.handler.IntervalBasedTolling; import org.matsim.contrib.decongestion.tollSetting.DecongestionTollSetting; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; import org.matsim.core.controler.events.AfterMobsimEvent; import org.matsim.core.controler.events.IterationEndsEvent; import org.matsim.core.controler.events.IterationStartsEvent; @@ -59,117 +59,117 @@ /** * Interval-based decongestion pricing approach: - * + * * (1) Identify congested links and time intervals and set an initial toll for these links and time intervals. * (2) Let the demand adjust for x iterations. * (3) Adjust the tolls (different implementations of {@link DecongestionTollSetting}) * (4) GOTO (2) - * + * * All relevant parameters are specified in {@link DecongestionInfo}. - * - * + * + * * @author ikaddoura * */ public class DecongestionControlerListener implements StartupListener, AfterMobsimListener, IterationStartsListener, IterationEndsListener { - + private static final Logger log = LogManager.getLogger(DecongestionControlerListener.class); private final SortedMap iteration2totalDelay = new TreeMap<>(); private final SortedMap iteration2totalTollPayments = new TreeMap<>(); private final SortedMap iteration2totalTravelTime = new TreeMap<>(); private final SortedMap iteration2userBenefits = new TreeMap<>(); - + @Inject private DecongestionInfo congestionInfo; - + @Inject(optional=true) private DecongestionTollSetting tollComputation; - + @Inject(optional=true) private IntervalBasedTolling intervalBasedTolling; - + @Inject(optional=true) private DelayAnalysis delayComputation; - + private int nextDisableInnovativeStrategiesIteration = -1; private int nextEnableInnovativeStrategiesIteration = -1; - + private String outputDirectory; - + @Override public void notifyStartup(StartupEvent event) { - + log.info("decongestion settings: " + congestionInfo.getDecongestionConfigGroup().toString()); - - this.outputDirectory = this.congestionInfo.getScenario().getConfig().controler().getOutputDirectory(); + + this.outputDirectory = this.congestionInfo.getScenario().getConfig().controller().getOutputDirectory(); if (!outputDirectory.endsWith("/")) { log.info("Adjusting output directory."); outputDirectory = outputDirectory + "/"; - } + } } @Override public void notifyAfterMobsim(AfterMobsimEvent event) { - + if (event.getIteration() % this.congestionInfo.getDecongestionConfigGroup().getWriteOutputIteration() == 0. || event.getIteration() % this.congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval() == 0.) { computeDelays(event); } - - if (event.getIteration() == this.congestionInfo.getScenario().getConfig().controler().getFirstIteration()) { + + if (event.getIteration() == this.congestionInfo.getScenario().getConfig().controller().getFirstIteration()) { // skip first iteration - + } else if (event.getIteration() % this.congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval() == 0.) { - - int totalNumberOfIterations = this.congestionInfo.getScenario().getConfig().controler().getLastIteration() - this.congestionInfo.getScenario().getConfig().controler().getFirstIteration(); - int iterationCounter = event.getIteration() - this.congestionInfo.getScenario().getConfig().controler().getFirstIteration(); - + + int totalNumberOfIterations = this.congestionInfo.getScenario().getConfig().controller().getLastIteration() - this.congestionInfo.getScenario().getConfig().controller().getFirstIteration(); + int iterationCounter = event.getIteration() - this.congestionInfo.getScenario().getConfig().controller().getFirstIteration(); + if (iterationCounter < this.congestionInfo.getDecongestionConfigGroup().getFractionOfIterationsToEndPriceAdjustment() * totalNumberOfIterations && iterationCounter > this.congestionInfo.getDecongestionConfigGroup().getFractionOfIterationsToStartPriceAdjustment() * totalNumberOfIterations) { - + if (tollComputation != null) { log.info("+++ Iteration " + event.getIteration() + ". Update tolls per link and time bin."); tollComputation.updateTolls(); } } } - + if (event.getIteration() % this.congestionInfo.getDecongestionConfigGroup().getWriteOutputIteration() == 0.) { - CongestionInfoWriter.writeDelays(congestionInfo, event.getIteration(), this.outputDirectory + "ITERS/it." + event.getIteration() + "/", this.congestionInfo.getScenario().getConfig().controler().getRunId()); - CongestionInfoWriter.writeTolls(congestionInfo, event.getIteration(), this.outputDirectory + "ITERS/it." + event.getIteration() + "/", this.congestionInfo.getScenario().getConfig().controler().getRunId()); + CongestionInfoWriter.writeDelays(congestionInfo, event.getIteration(), this.outputDirectory + "ITERS/it." + event.getIteration() + "/", this.congestionInfo.getScenario().getConfig().controller().getRunId()); + CongestionInfoWriter.writeTolls(congestionInfo, event.getIteration(), this.outputDirectory + "ITERS/it." + event.getIteration() + "/", this.congestionInfo.getScenario().getConfig().controller().getRunId()); } } - private void computeDelays(AfterMobsimEvent event) { + private void computeDelays(AfterMobsimEvent event) { TravelTime travelTime = event.getServices().getLinkTravelTimes(); - int timeBinSize = this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize(); - + double timeBinSize = this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize(); + for (Link link : this.congestionInfo.getScenario().getNetwork().getLinks().values()) { - + Map time2avgDelay = new HashMap<>(); - + int timeBinCounter = 0; boolean linkHasAtLeastOneTimeBinWithNonZeroAvgDelay = false; - - for (int endTime = timeBinSize ; endTime <= this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getMaxTime(); endTime = endTime + timeBinSize ) { + + for (double endTime = timeBinSize ; endTime <= this.congestionInfo.getScenario().getConfig().travelTimeCalculator().getMaxTime(); endTime = endTime + timeBinSize ) { final double probedTime = endTime - timeBinSize / 2.; double freespeedTravelTime = link.getLength() / link.getFreespeed( probedTime ) ; final double congestedTravelTime = travelTime.getLinkTravelTime(link, probedTime, null, null); double avgDelay = congestedTravelTime - freespeedTravelTime; - + if (linkHasAtLeastOneTimeBinWithNonZeroAvgDelay == false && avgDelay > this.congestionInfo.getDecongestionConfigGroup().getToleratedAverageDelaySec()) { linkHasAtLeastOneTimeBinWithNonZeroAvgDelay = true; } - - time2avgDelay.put(timeBinCounter, avgDelay); + + time2avgDelay.put(timeBinCounter, avgDelay); timeBinCounter++; } - + if (this.congestionInfo.getlinkInfos().get(link.getId()) != null) { this.congestionInfo.getlinkInfos().get(link.getId()).setTime2avgDelay(time2avgDelay); } else { - + // only store the linkInfo for links with at least one time bin with a non-zero delay if (linkHasAtLeastOneTimeBinWithNonZeroAvgDelay) { LinkInfo linkInfo = new LinkInfo(link); @@ -182,70 +182,70 @@ private void computeDelays(AfterMobsimEvent event) { @Override public void notifyIterationEnds(IterationEndsEvent event) { - + if (this.delayComputation != null) { // Store and write out some aggregated numbers for analysis purposes. - + this.iteration2totalDelay.put(event.getIteration(), this.delayComputation.getTotalDelay()); double totalPayments = 0.; if (this.intervalBasedTolling != null) totalPayments = this.intervalBasedTolling.getTotalTollPayments(); this.iteration2totalTollPayments.put(event.getIteration(), totalPayments); this.iteration2totalTravelTime.put(event.getIteration(), this.delayComputation.getTotalTravelTime()); - + double monetizedUserBenefits = 0.; for (Person person : this.congestionInfo.getScenario().getPopulation().getPersons().values()) { if ( person.getSelectedPlan().getScore()==null ) { throw new RuntimeException( "score is null; don't know how to continue.") ; } - monetizedUserBenefits += person.getSelectedPlan().getScore() / this.congestionInfo.getScenario().getConfig().planCalcScore().getMarginalUtilityOfMoney(); + monetizedUserBenefits += person.getSelectedPlan().getScore() / this.congestionInfo.getScenario().getConfig().scoring().getMarginalUtilityOfMoney(); } this.iteration2userBenefits.put(event.getIteration(), monetizedUserBenefits); - + CongestionInfoWriter.writeIterationStats( this.iteration2totalDelay, this.iteration2totalTollPayments, this.iteration2totalTravelTime, this.iteration2userBenefits, outputDirectory, - this.congestionInfo.getScenario().getConfig().controler().getRunId() + this.congestionInfo.getScenario().getConfig().controller().getRunId() ); - + XYLineChart chart1 = new XYLineChart("Total travel time and total delay", "Iteration", "Hours"); double[] iterations1 = new double[event.getIteration() + 1]; double[] values1a = new double[event.getIteration() + 1]; double[] values1b = new double[event.getIteration() + 1]; - for (int i = this.congestionInfo.getScenario().getConfig().controler().getFirstIteration(); i <= event.getIteration(); i++) { + for (int i = this.congestionInfo.getScenario().getConfig().controller().getFirstIteration(); i <= event.getIteration(); i++) { iterations1[i] = i; values1a[i] = this.iteration2totalDelay.get(i) / 3600.; values1b[i] = this.iteration2totalTravelTime.get(i) / 3600.; } chart1.addSeries("Total delay", iterations1, values1a); chart1.addSeries("Total travel time", iterations1, values1b); - chart1.saveAsPng(outputDirectory + this.congestionInfo.getScenario().getConfig().controler().getRunId() + ".decongestion_travelTime_delay.png", 800, 600); - + chart1.saveAsPng(outputDirectory + this.congestionInfo.getScenario().getConfig().controller().getRunId() + ".decongestion_travelTime_delay.png", 800, 600); + XYLineChart chart2 = new XYLineChart("user benefits and toll revenues", "Iteration", "Monetary units"); double[] iterations2 = new double[event.getIteration() + 1]; double[] values2b = new double[event.getIteration() + 1]; double[] values2c = new double[event.getIteration() + 1]; - for (int i = this.congestionInfo.getScenario().getConfig().controler().getFirstIteration(); i <= event.getIteration(); i++) { + for (int i = this.congestionInfo.getScenario().getConfig().controller().getFirstIteration(); i <= event.getIteration(); i++) { iterations2[i] = i; values2b[i] = this.iteration2userBenefits.get(i); values2c[i] = this.iteration2totalTollPayments.get(i); } chart2.addSeries("User benefits", iterations2, values2b); chart2.addSeries("Toll payments (decongestion tolls only)", iterations2, values2c); - chart2.saveAsPng(outputDirectory + this.congestionInfo.getScenario().getConfig().controler().getRunId() + ".decongestion_userBenefits_tolls.png", 800, 600); + chart2.saveAsPng(outputDirectory + this.congestionInfo.getScenario().getConfig().controller().getRunId() + ".decongestion_userBenefits_tolls.png", 800, 600); } } @Override public void notifyIterationStarts(IterationStartsEvent event) { - + if (congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval() > 1) { - if (event.getIteration() == this.congestionInfo.getScenario().getConfig().controler().getFirstIteration()) { - - this.nextDisableInnovativeStrategiesIteration = (int) (congestionInfo.getScenario().getConfig().strategy().getFractionOfIterationsToDisableInnovation() * congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval()); + if (event.getIteration() == this.congestionInfo.getScenario().getConfig().controller().getFirstIteration()) { + + this.nextDisableInnovativeStrategiesIteration = (int) (congestionInfo.getScenario().getConfig().replanning().getFractionOfIterationsToDisableInnovation() * congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval()); log.info("next disable innovative strategies iteration: " + this.nextDisableInnovativeStrategiesIteration); if (this.nextDisableInnovativeStrategiesIteration != 0) { @@ -254,61 +254,61 @@ public void notifyIterationStarts(IterationStartsEvent event) { log.info("next enable innovative strategies iteration: " + this.nextEnableInnovativeStrategiesIteration); } else { - + if (event.getIteration() == this.nextDisableInnovativeStrategiesIteration) { // set weight to zero log.warn("Strategy weight adjustment (set to zero) in iteration " + event.getIteration()); - + for (GenericPlanStrategy strategy : event.getServices().getStrategyManager().getStrategies(null)) { - + String strategyName = strategy.toString(); if (isInnovativeStrategy(strategy)) { log.info("Setting weight for " + strategyName + " to zero."); event.getServices().getStrategyManager().changeWeightOfStrategy(strategy, null, 0.0); } } - + this.nextDisableInnovativeStrategiesIteration += congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval(); log.info("next disable innovative strategies iteration: " + this.nextDisableInnovativeStrategiesIteration); - + } else if (event.getIteration() == this.nextEnableInnovativeStrategiesIteration) { // set weight back to original value - if (event.getIteration() >= congestionInfo.getScenario().getConfig().strategy().getFractionOfIterationsToDisableInnovation() * (congestionInfo.getScenario().getConfig().controler().getLastIteration() - congestionInfo.getScenario().getConfig().controler().getFirstIteration())) { - + if (event.getIteration() >= congestionInfo.getScenario().getConfig().replanning().getFractionOfIterationsToDisableInnovation() * (congestionInfo.getScenario().getConfig().controller().getLastIteration() - congestionInfo.getScenario().getConfig().controller().getFirstIteration())) { + log.info("Strategies are switched off by global settings. Do not set back the strategy parameters to original values..."); - + } else { - + log.info("Strategy weight adjustment (set back to original value) in iteration " + event.getIteration()); - + for (GenericPlanStrategy strategy : event.getServices().getStrategyManager().getStrategies(null)) { - + String strategyName = strategy.toString(); if (isInnovativeStrategy(strategy)) { - + double originalValue = -1.0; - for (StrategySettings setting : event.getServices().getConfig().strategy().getStrategySettings()) { + for (StrategySettings setting : event.getServices().getConfig().replanning().getStrategySettings()) { log.info("setting: " + setting.getStrategyName()); log.info("strategyName: " + strategyName); if (strategyName.contains(setting.getStrategyName())) { originalValue = setting.getWeight(); } - } - + } + if (originalValue == -1.0) { throw new RuntimeException("Aborting..."); } - + log.warn("Setting weight for " + strategyName + " back to original value: " + originalValue); event.getServices().getStrategyManager().changeWeightOfStrategy(strategy, null, originalValue); } - } + } this.nextEnableInnovativeStrategiesIteration += congestionInfo.getDecongestionConfigGroup().getUpdatePriceInterval(); } } - } + } } } diff --git a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExample.java b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExample.java index a7ae5e473de..72aca4c0fa7 100644 --- a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExample.java +++ b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExample.java @@ -19,7 +19,7 @@ * *********************************************************************** */ /** - * + * */ package org.matsim.contrib.decongestion; @@ -41,7 +41,7 @@ /** * Starts an interval-based decongestion pricing simulation run. - * + * * @author ikaddoura * */ @@ -50,22 +50,22 @@ public class DecongestionRunExample { private static final Logger log = LogManager.getLogger(DecongestionRunExample.class); private static String configFile; - + public static void main(String[] args) throws IOException { - + if (args.length > 0) { log.info("Starting simulation run with the following arguments:"); - configFile = args[0]; + configFile = args[0]; log.info("config file: "+ configFile); } else { configFile = "path/to/config.xml"; } - + DecongestionRunExample main = new DecongestionRunExample(); main.run(); - + } private void run() { @@ -77,9 +77,9 @@ private void run() { decongestionSettings.setUpdatePriceInterval(1); decongestionSettings.setMsa(false); decongestionSettings.setTollBlendFactor(1.0); - + // decongestionSettings.setDecongestionApproach(DecongestionApproach.P_MC); - + decongestionSettings.setDecongestionApproach(DecongestionApproach.PID); decongestionSettings.setKd(0.005); decongestionSettings.setKi(0.005); @@ -87,38 +87,38 @@ private void run() { decongestionSettings.setIntegralApproach(IntegralApproach.UnusedHeadway); decongestionSettings.setIntegralApproachUnusedHeadwayFactor(10.0); decongestionSettings.setIntegralApproachAverageAlpha(0.0); - + // decongestionSettings.setDecongestionApproach(DecongestionApproach.BangBang); // decongestionSettings.setTOLL_ADJUSTMENT(1.0); // decongestionSettings.setINITIAL_TOLL(1.0); - + Config config = ConfigUtils.loadConfig(configFile); config.addModule(decongestionSettings); - + final Scenario scenario = ScenarioUtils.loadScenario(config); Controler controler = new Controler(scenario); - + // ############################################################# - + // congestion toll computation - + controler.addOverridingModule(new DecongestionModule(scenario)); - + // toll-adjusted routing - + controler.addOverridingModule(new AbstractModule(){ @Override public void install() { final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); } - }); - + }); + // ############################################################# - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.failIfDirectoryExists); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.failIfDirectoryExists); controler.run(); - + } } diff --git a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExampleFromConfig.java b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExampleFromConfig.java index 3dd8a48c2a1..339c6fff079 100644 --- a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExampleFromConfig.java +++ b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/DecongestionRunExampleFromConfig.java @@ -19,7 +19,7 @@ * *********************************************************************** */ /** - * + * */ package org.matsim.contrib.decongestion; @@ -39,7 +39,7 @@ /** * Starts an interval-based decongestion pricing simulation run. - * + * * @author ikaddoura * */ @@ -48,37 +48,37 @@ public class DecongestionRunExampleFromConfig { private static final Logger log = LogManager.getLogger(DecongestionRunExampleFromConfig.class); private static String configFile; - - public static void main(String[] args) throws IOException { + + public static void main(String[] args) throws IOException { if (args.length > 0) { log.info("Starting simulation run with the following arguments:"); - configFile = args[0]; + configFile = args[0]; log.info("config file: "+ configFile); } else { configFile = "path/to/config.xml"; } - + DecongestionRunExampleFromConfig main = new DecongestionRunExampleFromConfig(); main.run(); } private void run() throws IOException { - + Config config = ConfigUtils.loadConfig(configFile, new DecongestionConfigGroup()); - + final Scenario scenario = ScenarioUtils.loadScenario(config); Controler controler = new Controler(scenario); - + // ############################################################# - + // congestion toll computation - + controler.addOverridingModule(new DecongestionModule(scenario)); - + // toll-adjusted routing - + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); controler.addOverridingModule(new AbstractModule(){ @@ -86,11 +86,11 @@ private void run() throws IOException { public void install() { this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); } - }); - + }); + // ############################################################# - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.failIfDirectoryExists); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.failIfDirectoryExists); controler.run(); } } diff --git a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/routing/TollTimeDistanceTravelDisutility.java b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/routing/TollTimeDistanceTravelDisutility.java index 5635fdbae0a..f4036b3e1a0 100644 --- a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/routing/TollTimeDistanceTravelDisutility.java +++ b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/routing/TollTimeDistanceTravelDisutility.java @@ -35,7 +35,7 @@ import org.matsim.contrib.decongestion.data.LinkInfo; /** - * A cost calculator which respects time, distance and decongestion tolls. + * A cost calculator which respects time, distance and decongestion tolls. * * @author ikaddoura */ @@ -50,15 +50,15 @@ public final class TollTimeDistanceTravelDisutility implements TravelDisutility TollTimeDistanceTravelDisutility( final TravelTime timeCalculator, Config config, DecongestionInfo info ) { this.info = info; - this.marginalUtilityOfMoney = config.planCalcScore().getMarginalUtilityOfMoney(); + this.marginalUtilityOfMoney = config.scoring().getMarginalUtilityOfMoney(); final RandomizingTimeDistanceTravelDisutilityFactory builder = new RandomizingTimeDistanceTravelDisutilityFactory( TransportMode.car, config ); this.delegate = builder.createTravelDisutility(timeCalculator); this.timeBinSize = info.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize(); - this.sigma = config.plansCalcRoute().getRoutingRandomness(); - + this.sigma = config.routing().getRoutingRandomness(); + log.info("Using the toll-adjusted travel disutility (improved version) in the decongestion package."); } @@ -67,7 +67,7 @@ public double getLinkTravelDisutility(final Link link, final double time, final int timeBin = (int) (time / timeBinSize); double timeDistanceTravelDisutilityFromDelegate = this.delegate.getLinkTravelDisutility(link, time, person, vehicle); - + double logNormalRnd = 1. ; if ( sigma != 0. ) { logNormalRnd = (double) person.getCustomAttributes().get("logNormalRnd") ; @@ -75,17 +75,17 @@ public double getLinkTravelDisutility(final Link link, final double time, final // adjust the travel disutility for the toll double toll = 0.; - + LinkInfo linkInfo = info.getlinkInfos().get(link.getId()); if (linkInfo != null) { - - Double linkInfoTimeBinToll = linkInfo.getTime2toll().get(timeBin); + + Double linkInfoTimeBinToll = linkInfo.getTime2toll().get(timeBin); if (linkInfoTimeBinToll != null) { toll = linkInfoTimeBinToll; } } - - double tollAdjustedLinkTravelDisutility = timeDistanceTravelDisutilityFromDelegate + logNormalRnd * marginalUtilityOfMoney * toll; + + double tollAdjustedLinkTravelDisutility = timeDistanceTravelDisutilityFromDelegate + logNormalRnd * marginalUtilityOfMoney * toll; return tollAdjustedLinkTravelDisutility; } diff --git a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/tollSetting/DecongestionTollingP_MCP.java b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/tollSetting/DecongestionTollingP_MCP.java index 146bf63f012..00450067e9c 100644 --- a/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/tollSetting/DecongestionTollingP_MCP.java +++ b/contribs/decongestion/src/main/java/org/matsim/contrib/decongestion/tollSetting/DecongestionTollingP_MCP.java @@ -36,64 +36,64 @@ import org.matsim.contrib.decongestion.data.LinkInfo; /** - * + * * P-based toll adjustment, where e(t) = average delay and K_p = VTTS * number of delayed agents - * + * * @author ikaddoura */ public class DecongestionTollingP_MCP implements DecongestionTollSetting, LinkLeaveEventHandler { private static final Logger log = LogManager.getLogger(DecongestionTollingP_MCP.class); - + @Inject private DecongestionInfo congestionInfo; - - private Map, LinkInfo> linkId2infoPreviousTollComputation = new HashMap<>(); + + private Map, LinkInfo> linkId2infoPreviousTollComputation = new HashMap<>(); private int tollUpdateCounter = 0; private final Map, Map> linkId2time2leavingAgents = new HashMap<>(); @Override public void updateTolls() { - - final double vtts = ( this.congestionInfo.getScenario().getConfig().planCalcScore().getPerforming_utils_hr() - - this.congestionInfo.getScenario().getConfig().planCalcScore().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling() ) - / this.congestionInfo.getScenario().getConfig().planCalcScore().getMarginalUtilityOfMoney(); - + final double vtts = ( this.congestionInfo.getScenario().getConfig().scoring().getPerforming_utils_hr() + - this.congestionInfo.getScenario().getConfig().scoring().getModes().get(TransportMode.car).getMarginalUtilityOfTraveling() ) + / this.congestionInfo.getScenario().getConfig().scoring().getMarginalUtilityOfMoney(); + + final double toleratedAvgDelay = this.congestionInfo.getDecongestionConfigGroup().getToleratedAverageDelaySec(); final boolean msa = this.congestionInfo.getDecongestionConfigGroup().isMsa(); final double blendFactorFromConfig = this.congestionInfo.getDecongestionConfigGroup().getTollBlendFactor(); - + for (Id linkId : this.congestionInfo.getlinkInfos().keySet()) { - + LinkInfo linkInfo = this.congestionInfo.getlinkInfos().get(linkId); for (Integer intervalNr : linkInfo.getTime2avgDelay().keySet()) { - + // average delay - - double averageDelay = linkInfo.getTime2avgDelay().get(intervalNr); + + double averageDelay = linkInfo.getTime2avgDelay().get(intervalNr); if (averageDelay <= toleratedAvgDelay) { averageDelay = 0.0; } - + // toll - + double demand = 1.0; if (this.linkId2time2leavingAgents.get(linkId) != null && this.linkId2time2leavingAgents.get(linkId).get(intervalNr) != null) { demand = this.linkId2time2leavingAgents.get(linkId).get(intervalNr); } - + double toll = vtts * demand * averageDelay / 3600.; // prevent negative tolls - + if (toll < 0) { log.warn("Negative tolls... Are you sure everything works fine?"); toll = 0; } - + // smoothen the tolls - + Double previousToll = linkInfo.getTime2toll().get(intervalNr); double blendFactor; @@ -106,35 +106,35 @@ public void updateTolls() { } else { blendFactor = blendFactorFromConfig; } - + double smoothedToll; if (previousToll != null && previousToll >= 0.) { smoothedToll = toll * blendFactor + previousToll * (1 - blendFactor); } else { smoothedToll = toll; } - + // store the updated toll - + linkInfo.getTime2toll().put(intervalNr, smoothedToll); - } + } } - + log.info("Updating tolls completed."); this.tollUpdateCounter++; - + // store the current link information for the next toll computation - + linkId2infoPreviousTollComputation = new HashMap<>(); for ( Map.Entry< Id,LinkInfo> entry : this.congestionInfo.getlinkInfos().entrySet() ) { LinkInfo linkInfo = entry.getValue() ; - + Map time2previousDelay = new HashMap<>(); for (Integer intervalNr : linkInfo.getTime2avgDelay().keySet()) { time2previousDelay.put(intervalNr, linkInfo.getTime2avgDelay().get(intervalNr)); } - + LinkInfo linkInfoPreviousTollComputation = new LinkInfo(linkInfo.getLink()); linkInfoPreviousTollComputation.setTime2avgDelay(time2previousDelay); linkId2infoPreviousTollComputation.put(linkInfo.getLink().getId(), linkInfoPreviousTollComputation); @@ -151,27 +151,27 @@ public void handleEvent(LinkLeaveEvent event) { int timeBinNr = getIntervalNr(event.getTime()); Id linkId = event.getLinkId(); - + if (linkId2time2leavingAgents.get(linkId) != null) { - + if (linkId2time2leavingAgents.get(linkId).get(timeBinNr) != null) { int leavingAgents = linkId2time2leavingAgents.get(linkId).get(timeBinNr) + 1; linkId2time2leavingAgents.get(linkId).put(timeBinNr, leavingAgents); - + } else { linkId2time2leavingAgents.get(linkId).put(timeBinNr, 1); } - + } else { Map time2leavingAgents = new HashMap<>(); time2leavingAgents.put(timeBinNr, 1); linkId2time2leavingAgents.put(linkId, time2leavingAgents); - + } } - + private int getIntervalNr(double time) { - double timeBinSize = congestionInfo.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize(); + double timeBinSize = congestionInfo.getScenario().getConfig().travelTimeCalculator().getTraveltimeBinSize(); return (int) (time / timeBinSize); } diff --git a/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java b/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java index ac6697e3694..58477477f23 100644 --- a/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java +++ b/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java @@ -1,560 +1,561 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2013 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -/** - * - */ -package org.matsim.contrib.decongestion; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.matsim.analysis.ScoreStatsControlerListener.ScoreItem; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.decongestion.DecongestionConfigGroup.DecongestionApproach; -import org.matsim.contrib.decongestion.data.DecongestionInfo; -import org.matsim.contrib.decongestion.handler.DelayAnalysis; -import org.matsim.contrib.decongestion.handler.IntervalBasedTolling; -import org.matsim.contrib.decongestion.handler.IntervalBasedTollingAll; -import org.matsim.contrib.decongestion.handler.PersonVehicleTracker; -import org.matsim.contrib.decongestion.routing.TollTimeDistanceTravelDisutilityFactory; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollSetting; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingBangBang; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingPID; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.Controler; -import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.testcases.MatsimTestUtils; - -/** - * - * - * @author ikaddoura - * - */ -public class DecongestionPricingTestIT { - - @Rule - public MatsimTestUtils testUtils = new MatsimTestUtils(); - - /** - * Kp = 0.0123 - * - */ - @Test - public final void test0a() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0123); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", 50.5 * 0.0123, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); - - } - - /** - * Kp = 0.0123, other syntax - * - */ - @Test - public final void test0amodified() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0123); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // congestion toll computation - controler.addOverridingModule(new DecongestionModule(scenario)); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); - } - - /** - * Kp = 2 - * - */ - @Test - public final void test0b() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(2.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", 50.5 * 2, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); - } - - /** - * Kp = 2 - * - */ - @Test - public final void test0bmodified() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(2.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // congestion toll computation - controler.addOverridingModule(new DecongestionModule(scenario)); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); - } - - /** - * Kp = 0 / no tolling - * - */ - @Test - public final void test0c() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.addEventHandlerBinding().to(DelayAnalysis.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.31916666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", null, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84)); - - } - - /** - * Tests the PID controller - * - */ - @Test - public final void test1() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - decongestionSettings.setDecongestionApproach(DecongestionApproach.PID); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // decongestion pricing - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index) ; - Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -11749.431349675931, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - Assert.assertEquals("Wrong toll in time bin 61.", 12.600000000000009, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll in time bin 73.", 16.665000000000006, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); - } - - /** - * Tests the BangBang controller - * - */ - @Test - public final void test2() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controler().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - DecongestionInfo info = new DecongestionInfo(); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // decongestion pricing - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingBangBang.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - controler.getConfig().controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - final int index = config.controler().getLastIteration() - config.controler().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get( index ) ; - Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -54.97929444444, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - Assert.assertEquals("Wrong toll in time bin 61.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll in time bin 73.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); - } - -} +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2013 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +/** + * + */ +package org.matsim.contrib.decongestion; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.analysis.ScoreStatsControlerListener.ScoreItem; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.contrib.decongestion.DecongestionConfigGroup.DecongestionApproach; +import org.matsim.contrib.decongestion.data.DecongestionInfo; +import org.matsim.contrib.decongestion.handler.DelayAnalysis; +import org.matsim.contrib.decongestion.handler.IntervalBasedTolling; +import org.matsim.contrib.decongestion.handler.IntervalBasedTollingAll; +import org.matsim.contrib.decongestion.handler.PersonVehicleTracker; +import org.matsim.contrib.decongestion.routing.TollTimeDistanceTravelDisutilityFactory; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollSetting; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingBangBang; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingPID; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.testcases.MatsimTestUtils; + +/** + * + * + * @author ikaddoura + * + */ +public class DecongestionPricingTestIT { + + @Rule + public MatsimTestUtils testUtils = new MatsimTestUtils(); + + /** + * Kp = 0.0123 + * + */ + @Test + public final void test0a() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0123); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll.", 50.5 * 0.0123, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); + + } + + /** + * Kp = 0.0123, other syntax + * + */ + @Test + public final void test0amodified() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0123); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // congestion toll computation + controler.addOverridingModule(new DecongestionModule(scenario)); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); + } + + /** + * Kp = 2 + * + */ + @Test + public final void test0b() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(2.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll.", 50.5 * 2, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); + } + + /** + * Kp = 2 + * + */ + @Test + public final void test0bmodified() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(2.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // congestion toll computation + controler.addOverridingModule(new DecongestionModule(scenario)); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); + } + + /** + * Kp = 0 / no tolling + * + */ + @Test + public final void test0c() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.addEventHandlerBinding().to(DelayAnalysis.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.31916666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertNull("Wrong toll.", info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84)); + + } + + /** + * Tests the PID controller + * + */ + @Test + public final void test1() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + decongestionSettings.setDecongestionApproach(DecongestionApproach.PID); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // decongestion pricing + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index) ; + Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -12036.177448472225, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + Assert.assertEquals("Wrong toll in time bin 61.", 9.197000000000003, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll in time bin 73.", 12.963999999999984, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); + } + + /** + * Tests the BangBang controller + * + */ + @Test + public final void test2() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + DecongestionInfo info = new DecongestionInfo(); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // decongestion pricing + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingBangBang.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get( index ) ; + Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -55.215645833333184, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + Assert.assertEquals("Wrong toll in time bin 61.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll in time bin 73.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); + } + +} diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceConfigurator.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceConfigurator.java index 44838aa3186..4cf1f8704cb 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceConfigurator.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceConfigurator.java @@ -10,8 +10,8 @@ import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; import org.matsim.contribs.discrete_mode_choice.replanning.NonSelectedPlanSelector; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.StrategyConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule.DefaultSelector; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule.DefaultStrategy; @@ -19,7 +19,7 @@ /** * Utility class that makes it possible to set up various was of using the * Discrete Mode Choice extension with MATSim. - * + * * @author sebhoerl */ public final class DiscreteModeChoiceConfigurator { @@ -28,7 +28,7 @@ private DiscreteModeChoiceConfigurator() { } static public void configureAsSubtourModeChoiceReplacement(Config config) { - for (StrategySettings strategy : config.strategy().getStrategySettings()) { + for (StrategySettings strategy : config.replanning().getStrategySettings()) { if (strategy.getStrategyName().equals(DefaultStrategy.SubtourModeChoice)) { strategy.setStrategyName(DiscreteModeChoiceModule.STRATEGY_NAME); } @@ -89,24 +89,24 @@ static public void configureAsModeChoiceInTheLoop(Config config) { } static public void configureAsModeChoiceInTheLoop(Config config, double replanningRate) { - StrategyConfigGroup strategyConfigGroup = config.strategy(); - strategyConfigGroup.clearStrategySettings(); + ReplanningConfigGroup replanningConfigGroup = config.replanning(); + replanningConfigGroup.clearStrategySettings(); - strategyConfigGroup.setMaxAgentPlanMemorySize(1); - strategyConfigGroup.setFractionOfIterationsToDisableInnovation(Double.POSITIVE_INFINITY); - strategyConfigGroup.setPlanSelectorForRemoval(NonSelectedPlanSelector.NAME); + replanningConfigGroup.setMaxAgentPlanMemorySize(1); + replanningConfigGroup.setFractionOfIterationsToDisableInnovation(Double.POSITIVE_INFINITY); + replanningConfigGroup.setPlanSelectorForRemoval(NonSelectedPlanSelector.NAME); StrategySettings dmcStrategy = new StrategySettings(); dmcStrategy.setStrategyName(DiscreteModeChoiceModule.STRATEGY_NAME); dmcStrategy.setWeight(replanningRate); - strategyConfigGroup.addStrategySettings(dmcStrategy); + replanningConfigGroup.addStrategySettings(dmcStrategy); StrategySettings selectorStrategy = new StrategySettings(); selectorStrategy.setStrategyName(DefaultSelector.KeepLastSelected); selectorStrategy.setWeight(1.0 - replanningRate); - strategyConfigGroup.addStrategySettings(selectorStrategy); + replanningConfigGroup.addStrategySettings(selectorStrategy); - checkModeChoiceInTheLoop(strategyConfigGroup); + checkModeChoiceInTheLoop(replanningConfigGroup); DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() .get(DiscreteModeChoiceConfigGroup.GROUP_NAME); @@ -119,15 +119,15 @@ static public void configureAsModeChoiceInTheLoop(Config config, double replanni dmcConfig.setEnforceSinglePlan(true); } - public static void checkModeChoiceInTheLoop(StrategyConfigGroup strategyConfigGroup) { - if (strategyConfigGroup.getMaxAgentPlanMemorySize() != 1) { + public static void checkModeChoiceInTheLoop(ReplanningConfigGroup replanningConfigGroup) { + if (replanningConfigGroup.getMaxAgentPlanMemorySize() != 1) { throw new IllegalStateException( "Option strategy.maxAgentPlanMemorySize should be 1 if mode-choice-in-the-loop is enforced."); } Set activeStrategies = new HashSet<>(); - for (StrategySettings strategySettings : strategyConfigGroup.getStrategySettings()) { + for (StrategySettings strategySettings : replanningConfigGroup.getStrategySettings()) { if (strategySettings.getDisableAfter() != 0) { activeStrategies.add(strategySettings.getStrategyName()); } @@ -146,14 +146,14 @@ public static void checkModeChoiceInTheLoop(StrategyConfigGroup strategyConfigGr activeStrategies.remove(DefaultSelector.KeepLastSelected); activeStrategies.remove(DiscreteModeChoiceModule.STRATEGY_NAME); activeStrategies.remove(DefaultStrategy.ReRoute); - + if (activeStrategies.size() > 0) { throw new IllegalStateException( "All these strategies should be disabled (disableAfter == 0) if mode-choice-in-the-loop is enforced: " + activeStrategies); } - if (!strategyConfigGroup.getPlanSelectorForRemoval().equals(NonSelectedPlanSelector.NAME)) { + if (!replanningConfigGroup.getPlanSelectorForRemoval().equals(NonSelectedPlanSelector.NAME)) { throw new IllegalStateException( "Removal selector should be NonSelectedPlanSelector if mode-choice-in-the-loop is enforced."); } diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java index 58db9c55b85..abf34cb42f6 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/DiscreteModeChoiceModule.java @@ -11,7 +11,7 @@ /** * Main module of the Discrete Mode Choice extension. Should be added as an * overriding module before the MATSim controller is started. - * + * * @author sebhoerl */ public class DiscreteModeChoiceModule extends AbstractModule { @@ -24,7 +24,7 @@ public class DiscreteModeChoiceModule extends AbstractModule { public void install() { addPlanStrategyBinding(STRATEGY_NAME).toProvider(DiscreteModeChoiceStrategyProvider.class); - if (getConfig().strategy().getPlanSelectorForRemoval().equals(NonSelectedPlanSelector.NAME)) { + if (getConfig().replanning().getPlanSelectorForRemoval().equals(NonSelectedPlanSelector.NAME)) { bindPlanSelectorForRemoval().to(NonSelectedPlanSelector.class); } diff --git a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ModeChoiceInTheLoopChecker.java b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ModeChoiceInTheLoopChecker.java index 0d7df42a168..04617415d08 100644 --- a/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ModeChoiceInTheLoopChecker.java +++ b/contribs/discrete_mode_choice/src/main/java/org/matsim/contribs/discrete_mode_choice/modules/utils/ModeChoiceInTheLoopChecker.java @@ -4,7 +4,7 @@ import org.matsim.api.core.v01.population.Plan; import org.matsim.contribs.discrete_mode_choice.modules.DiscreteModeChoiceConfigurator; import org.matsim.contribs.discrete_mode_choice.replanning.NonSelectedPlanSelector; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.controler.events.StartupEvent; import org.matsim.core.controler.listener.StartupListener; import org.matsim.core.replanning.selectors.PlanSelector; @@ -14,16 +14,16 @@ /** * Internal listener that is used to do some runtime checks when * mode-choice-in-the-loop should be enforced. - * + * * @author sebhoerl * */ public class ModeChoiceInTheLoopChecker implements StartupListener { - private final StrategyConfigGroup strategyConfig; + private final ReplanningConfigGroup strategyConfig; private final PlanSelector removalSelector; @Inject - public ModeChoiceInTheLoopChecker(StrategyConfigGroup strategyConfig, PlanSelector removalSelector) { + public ModeChoiceInTheLoopChecker(ReplanningConfigGroup strategyConfig, PlanSelector removalSelector) { this.strategyConfig = strategyConfig; this.removalSelector = removalSelector; } diff --git a/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java b/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java index 9d931e70283..7b71a32670e 100644 --- a/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java +++ b/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java @@ -28,16 +28,15 @@ public void testSiouxFallsWithSubtourModeChoiceReplacement() { URL scenarioURL = ExamplesUtils.getTestScenarioURL("siouxfalls-2014"); Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(scenarioURL, "config_default.xml")); - config.transit().setRoutingAlgorithmType(TransitRoutingAlgorithmType.DijkstraBased); DiscreteModeChoiceConfigurator.configureAsSubtourModeChoiceReplacement(config); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(1); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(1); // save some bandwidth/time: - config.controler().setCreateGraphs( false ); - config.controler().setWritePlansInterval( 0 ); - config.controler().setWriteEventsInterval( 0 ); - config.controler().setDumpDataAtEnd( false ); + config.controller().setCreateGraphs( false ); + config.controller().setWritePlansInterval( 0 ); + config.controller().setWriteEventsInterval( 0 ); + config.controller().setDumpDataAtEnd( false ); config.qsim().setFlowCapFactor(10000.0); config.qsim().setStorageCapFactor(10000.0); @@ -57,14 +56,14 @@ public void install() { controller.run(); - assertEquals(42395, (int) listener.counts.get("pt")); - assertEquals(132284, (int) listener.counts.get("car")); - assertEquals(78809, (int) listener.counts.get("walk")); -// assertEquals(42520, (int) listener.counts.get("pt")); -// assertEquals(132100, (int) listener.counts.get("car")); -// assertEquals(79106, (int) listener.counts.get("walk")); - // ...setConstrainedModes(...) (inside configureAsSubtourModeChoiceReplacement(...)) used to ignore its arguments because of a typo. - // This is now corrected, but results are no longer backwards compatible. kai, jan'23 + + System.out.println((int) listener.counts.get("pt")); + System.out.println((int) listener.counts.get("car")); + System.out.println(listener.counts.get("walk")); + + assertEquals(44195, listener.counts.get("pt"), 2); + assertEquals(132316, listener.counts.get("car"), 2); + assertEquals(82139, listener.counts.get("walk"), 2); } diff --git a/contribs/drt-extensions/pom.xml b/contribs/drt-extensions/pom.xml index 7a33ebb7439..3e2cbf8dcf7 100644 --- a/contribs/drt-extensions/pom.xml +++ b/contribs/drt-extensions/pom.xml @@ -24,9 +24,35 @@ 16.0-SNAPSHOT
+ + org.matsim.contrib + informed-mode-choice + 16.0-SNAPSHOT + + + + org.matsim.contrib + simwrapper + 16.0-SNAPSHOT + + org.assertj assertj-core + test + + + + org.matsim.contrib + vsp + 16.0-SNAPSHOT + test + + + + org.mockito + mockito-core +
diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/DrtWithExtensionsConfigGroup.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/DrtWithExtensionsConfigGroup.java index 7a38e5626ee..0db7bda1d6a 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/DrtWithExtensionsConfigGroup.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/DrtWithExtensionsConfigGroup.java @@ -19,13 +19,14 @@ package org.matsim.contrib.drt.extension; +import java.util.Optional; + +import javax.annotation.Nullable; + import org.matsim.contrib.drt.extension.companions.DrtCompanionParams; import org.matsim.contrib.drt.extension.operations.DrtOperationsParams; import org.matsim.contrib.drt.run.DrtConfigGroup; -import javax.annotation.Nullable; -import java.util.Optional; - /** * @author Steffen Axer * @@ -56,5 +57,5 @@ public Optional getDrtCompanionParams() { public Optional getDrtOperationsParams() { return Optional.ofNullable(drtOperationsParams); } - + } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionControlerCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionControlerCreator.java index 9d14d6a5b37..058588cf40e 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionControlerCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionControlerCreator.java @@ -21,14 +21,12 @@ package org.matsim.contrib.drt.extension.companions; import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.drt.extension.companions.MultiModeDrtCompanionModule; import org.matsim.contrib.drt.run.DrtConfigs; import org.matsim.contrib.drt.run.DrtControlerCreator; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.drt.run.MultiModeDrtModule; import org.matsim.contrib.dvrp.run.DvrpModule; import org.matsim.contrib.dvrp.run.DvrpQSimComponents; -import org.matsim.contrib.otfvis.OTFVisLiveModule; import org.matsim.core.config.Config; import org.matsim.core.controler.Controler; import org.matsim.core.scenario.ScenarioUtils; @@ -42,7 +40,7 @@ public final class DrtCompanionControlerCreator { public static Controler createControler(Config config) { MultiModeDrtConfigGroup multiModeDrtConfig = MultiModeDrtConfigGroup.get(config); - DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.planCalcScore(), config.plansCalcRoute()); + DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing()); Scenario scenario = DrtControlerCreator.createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionRideGenerator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionRideGenerator.java index a1cac3748f6..5ed8926f465 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionRideGenerator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionRideGenerator.java @@ -53,6 +53,7 @@ public final class DrtCompanionRideGenerator implements BeforeMobsimListener, Af private static final Logger LOG = LogManager.getLogger(DrtCompanionRideGenerator.class); public final static String DRT_COMPANION_AGENT_PREFIX = "COMPANION"; + public static final String DRT_COMPANION_TYPE = "drtCompanion"; private final Scenario scenario; private final String drtModes; @@ -61,7 +62,8 @@ public final class DrtCompanionRideGenerator implements BeforeMobsimListener, Af private Set> companionAgentIds = new HashSet<>(); private WeightedRandomSelection sampler; - DrtCompanionRideGenerator(final String drtMode, final MainModeIdentifier mainModeIdentifier, final Scenario scenario, final Network network, + DrtCompanionRideGenerator(final String drtMode, final MainModeIdentifier mainModeIdentifier, + final Scenario scenario, final Network network, final DrtWithExtensionsConfigGroup drtWithExtensionsConfigGroup) { this.scenario = scenario; this.mainModeIdentifier = mainModeIdentifier; @@ -77,9 +79,11 @@ private void installSampler(DrtWithExtensionsConfigGroup drtWithExtensionsConfig if (!drtWithExtensionsConfigGroup.getDrtCompanionParams().orElseThrow().getDrtCompanionSamplingWeights() .isEmpty()) { this.sampler = DrtCompanionUtils.createIntegerSampler( - drtWithExtensionsConfigGroup.getDrtCompanionParams().orElseThrow().getDrtCompanionSamplingWeights()); + drtWithExtensionsConfigGroup.getDrtCompanionParams().orElseThrow() + .getDrtCompanionSamplingWeights()); } else { - throw new IllegalStateException("drtCompanionSamplingWeights are empty, please check your DrtCompanionParams"); + throw new IllegalStateException( + "drtCompanionSamplingWeights are empty, please check your DrtCompanionParams"); } } @@ -90,13 +94,18 @@ void addCompanionAgents() { for (TripStructureUtils.Trip trip : TripStructureUtils.getTrips(person.getSelectedPlan())) { String mainMode = mainModeIdentifier.identifyMainMode(trip.getTripElements()); if (this.drtModes.equals(mainMode)) { + int additionalCompanions = sampler.select(); + for (int i = 0; i < additionalCompanions; i++) { int currentCounter = drtCompanionAgents.getOrDefault(mainMode, 0); currentCounter++; drtCompanionAgents.put(mainMode, currentCounter); + int groupSize = additionalCompanions + 1; + int groupPart = i; + companions.add(createCompanionAgent(mainMode, person, trip, trip.getOriginActivity(), - trip.getDestinationActivity())); + trip.getDestinationActivity(), groupPart, groupSize)); } } } @@ -112,10 +121,11 @@ void addCompanionAgents() { } private Person createCompanionAgent(String drtMode, Person originalPerson, TripStructureUtils.Trip trip, - Activity fromActivity, Activity toActivity) { + Activity fromActivity, Activity toActivity, int groupPart, int groupSize) { String prefix = getCompanionPrefix(drtMode); String companionId = prefix + "_" + originalPerson.getId().toString() + "_" + UUID.randomUUID(); Person person = PopulationUtils.getFactory().createPerson(Id.createPersonId(companionId)); + DrtCompanionUtils.setDRTCompanionType(person, DRT_COMPANION_TYPE); // Copy attributes from person AttributesUtils.copyAttributesFromTo(originalPerson, person); @@ -130,7 +140,13 @@ private Person createCompanionAgent(String drtMode, Person originalPerson, TripS plan.getPlanElements().add(copyFromActivity); plan.getPlanElements().addAll(trip.getTripElements()); plan.getPlanElements().add(toActivity); + person.addPlan(plan); + + // Add group information to trip + DrtCompanionUtils.setAdditionalGroupPart(person, groupPart); + DrtCompanionUtils.setAdditionalGroupSize(person, groupSize); + return person; } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionUtils.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionUtils.java index 3afa7c15867..4f9d6e33317 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionUtils.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/companions/DrtCompanionUtils.java @@ -21,6 +21,7 @@ import java.util.List; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.common.util.WeightedRandomSelection; import org.matsim.core.gbl.MatsimRandom; @@ -31,6 +32,30 @@ */ public class DrtCompanionUtils { + public final static String ADDITIONAL_GROUP_SIZE_ATTRIBUTE = "additionalGroupSize"; + public final static String ADDITIONAL_GROUP_PART_ATTRIBUTE = "additionalGroupPart"; + public static final String COMPANION_TYPE_ATTRIBUTE = "companionType"; + + private DrtCompanionUtils() { + throw new IllegalStateException("Utility class"); + } + + public static boolean isDrtCompanion(Person person) { + return getDRTCompanionType(person) != null; + } + + public static void setDRTCompanionType(Person person, String drtCompanionType) { + if (drtCompanionType == null) { + person.getAttributes().removeAttribute(COMPANION_TYPE_ATTRIBUTE); + } else { + person.getAttributes().putAttribute(COMPANION_TYPE_ATTRIBUTE, drtCompanionType); + } + } + + public static String getDRTCompanionType(Person person) { + return (String) person.getAttributes().getAttribute(COMPANION_TYPE_ATTRIBUTE); + } + public static WeightedRandomSelection createIntegerSampler(final List distribution) { WeightedRandomSelection wrs = new WeightedRandomSelection<>(MatsimRandom.getLocalInstance()); for (int i = 0; i < distribution.size(); ++i) { @@ -38,4 +63,29 @@ public static WeightedRandomSelection createIntegerSampler(final List stops = readTransitStops(stopsFile); + writeStopsShp(stops, output.getPath("stops.shp")); + Map byLink = stops.stream().collect(Collectors.toMap(s -> s.getLinkId().toString(), Function.identity())); + + //needs to be a DoubleColumn because transposing later forces us to have the same column type for all (new) value columns + tableSupplyKPI.addColumns(DoubleColumn.create("Number of stops", new Integer[]{stops.size()})); + + writeTripsPerStop(stops, legs, output.getPath("trips_per_stop.csv")); + writeOD(byLink, legs, output.getPath("od.csv")); + } + + if (areaFile != null) { + // create copy of shp file so that it is accessible for sim wrapper + Collection allFeatures = ShapeFileReader.getAllFeatures(areaFile); + //do not convert coordinates! all MATSim output should be in the same CRS. if input was not in correct CRS, simulation would have crashed... + ShapeFileWriter.writeGeometries(allFeatures, output.getPath("serviceArea.shp").toString()); + //needs to be a DoubleColumn because transposing later forces us to have the same column type for all (new) value columns + tableSupplyKPI.addColumns(DoubleColumn.create("Number of areas", new Integer[]{allFeatures.size()})); + } + + tableSupplyKPI.addColumns(prepareSupplyKPITable(vehicleStats).columnArray()); + + printDemandKPICSV(customerStats, tableSupplyKPI, output.getPath("demand_kpi.csv")); + + tableSupplyKPI = tableSupplyKPI.transpose(true, false); + tableSupplyKPI.column(0).setName("info"); + tableSupplyKPI.column(1).setName("value"); + tableSupplyKPI.replaceColumn(0, tableSupplyKPI.stringColumn(0).capitalize()); + + tableSupplyKPI.write().csv(output.getPath("supply_kpi.csv").toFile()); + + return 0; + } + + private void writeStopsShp(Collection stops, Path path) throws IOException, SchemaException { + + Map map = new HashMap<>(); + map.put("url", path.toUri().toURL()); + + FileDataStoreFactorySpi factory = new ShapefileDataStoreFactory(); + ShapefileDataStore shp = (ShapefileDataStore) factory.createNewDataStore(map); + + // re-project to default crs (lat/lon) + SimpleFeatureType featureType = DataUtilities.createType("stop", "the_geom:Point:srid=4326,id:String,linkId:String,name:String"); + shp.createSchema(featureType); + + CoordinateTransformation ct = TransformationFactory.getCoordinateTransformation(crs.getInputCRS(), "EPSG:4326"); + + List features = new ArrayList<>(); + + GeometryFactory f = JTSFactoryFinder.getGeometryFactory(); + SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); + + // Build transit stop features + for (TransitStopFacility stop : stops) { + + Coord coord = ct.transform(stop.getCoord()); + + Point point = f.createPoint(new Coordinate(coord.getX(), coord.getY())); + featureBuilder.add(point); + featureBuilder.add(stop.getId().toString()); + featureBuilder.add(stop.getLinkId().toString()); + featureBuilder.add(stop.getName()); + + features.add(featureBuilder.buildFeature(stop.getId().toString())); + } + + try (Transaction transaction = new DefaultTransaction("create")) { + String typeName = shp.getTypeNames()[0]; + SimpleFeatureStore featureSource = (SimpleFeatureStore) shp.getFeatureSource(typeName); + featureSource.setTransaction(transaction); + featureSource.addFeatures(new ListFeatureCollection(featureType, features)); + transaction.commit(); + } + } + + private void writeTripsPerStop(List stops, Table legs, Path path) throws IOException { + + Object2IntMap departures = new Object2IntOpenHashMap<>(); + Object2IntMap arrivals = new Object2IntOpenHashMap<>(); + + for (Row leg : legs) { + departures.mergeInt(leg.getString("fromLinkId"), 1, Integer::sum); + arrivals.mergeInt(leg.getString("toLinkId"), 1, Integer::sum); + } + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + + csv.printRecord("stop_id", "departures", "arrivals"); + + for (TransitStopFacility stop : stops) { + csv.printRecord(stop.getId(), departures.getInt(stop.getLinkId().toString()), arrivals.getInt(stop.getLinkId().toString())); + } + } + } + + private void writeOD(Map stops, Table legs, Path path) throws IOException { + + Object2IntMap trips = new Object2IntOpenHashMap<>(); + + for (Row leg : legs) { + trips.mergeInt(new OD(leg.getString("fromLinkId"), leg.getString("toLinkId")), 1, Integer::sum); + } + + Comparator> cmp = Comparator.comparingInt(Object2IntMap.Entry::getIntValue); + + // Output only the top 200 relations, to avoid to large data + List> entries = trips.object2IntEntrySet().stream() + .sorted(cmp.reversed()).limit(200).toList(); + + try (CSVPrinter csv = new CSVPrinter(Files.newBufferedWriter(path), CSVFormat.DEFAULT)) { + + csv.printRecord("from_stop", "to_stop", "trips"); + + for (Object2IntMap.Entry e : entries) { + OD od = e.getKey(); + + if (e.getIntValue() > 0) + csv.printRecord(stops.get(od.origin).getLinkId(), stops.get(od.destination).getLinkId(), e.getIntValue()); + } + } + } + + private List readTransitStops(URL stopFile) { + + Scenario scenario = ScenarioUtils.createScenario(ConfigUtils.createConfig()); + new TransitScheduleReader(scenario).readURL(stopFile); + + + return new ArrayList<>(scenario.getTransitSchedule().getFacilities().values()); + } + + private record OD(String origin, String destination) { + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboard.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboard.java new file mode 100644 index 00000000000..b7e1aeda0f9 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboard.java @@ -0,0 +1,400 @@ +package org.matsim.contrib.drt.extension.dashboards; + + +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.core.config.ConfigGroup; +import org.matsim.simwrapper.Dashboard; +import org.matsim.simwrapper.Data; +import org.matsim.simwrapper.Header; +import org.matsim.simwrapper.Layout; +import org.matsim.simwrapper.viz.*; +import tech.tablesaw.plotly.components.Axis; +import tech.tablesaw.plotly.traces.BarTrace; +import tech.tablesaw.plotly.traces.ScatterTrace; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * General DRT dashboard for one specific service. + */ +public class DrtDashboard implements Dashboard { + + private final String crs; + private final URL matsimConfigContext; + + private final DrtConfigGroup drtConfigGroup; + private final int lastIteration; + + public DrtDashboard(DrtConfigGroup drtConfigGroup, URL matsimConfigContext, String crs, int lastIteration) { + this.drtConfigGroup = drtConfigGroup; + this.matsimConfigContext = matsimConfigContext; + this.crs = crs; + this.lastIteration = lastIteration; + } + + /** + * Auto generate title. + */ + public String getTitle() { + String name = context(); + return name.isBlank() ? "DRT" : "DRT - " + name; + } + + @Override + public String context() { + return drtConfigGroup.getMode().replace("drt", "").replace("Drt", ""); + } + + private String postProcess(Data data, String file) { + List args = new ArrayList<>(List.of("--drt-mode", drtConfigGroup.getMode(), "--input-crs", crs)); + + //it might be, that a serviceArea file is provided in the config, but the drt service is configured to be stopBased, nevertheless + switch (drtConfigGroup.operationalScheme) { + case stopbased -> +// drtConfigGroup.transitStopFile can not be null, otherwise simulation crashed, earlier + args.addAll(List.of("--stops-file", ConfigGroup.getInputFileURL(matsimConfigContext, drtConfigGroup.transitStopFile).toString())); + case door2door -> { + //TODO potentially show the entire drt network (all drt links have stops) + } + case serviceAreaBased -> +// drtConfigGroup.drtServiceAreaShapeFile can not be null, otherwise simulation crashed, earlier + //this copies the input shape file into the output directory. might not be ideal. but the input file might be anywhere (web, different local partition, ...) and simwrapper might not have access.... + args.addAll(List.of("--area-file", ConfigGroup.getInputFileURL(matsimConfigContext, drtConfigGroup.drtServiceAreaShapeFile).toString())); + } + + return data.compute(DrtAnalysisPostProcessing.class, file, args.toArray(new String[0])); + } + + @Override + public void configure(Header header, Layout layout) { + + header.title = getTitle(); + header.description = "Overview for the demand-responsive mode '" + drtConfigGroup.mode + "'"; + + layout.row("Results") + .el(Table.class, (viz, data) -> { + viz.title = "Service results"; + viz.description = "Final configuration and service KPI."; + viz.dataset = postProcess(data, "supply_kpi.csv"); + viz.style = "topsheet"; + viz.showAllRows = true; + viz.width = 1.; + }) + .el(Table.class, (viz, data) -> { + viz.title = "Demand results"; + viz.description = "Final demand statistics and KPI."; + viz.dataset = postProcess(data, "demand_kpi.csv"); + viz.style = "topsheet"; + viz.showAllRows = true; + viz.width = 1.; + }) + .el(MapPlot.class, (viz, data) -> { + switch (drtConfigGroup.operationalScheme) { + case stopbased -> { + viz.title = "Map of stops"; + viz.setShape(postProcess(data, "stops.shp"), "id"); + + viz.addDataset("trips", postProcess(data, "trips_per_stop.csv")); + + viz.display.radius.dataset = "trips"; + viz.display.radius.columnName = "departures"; + viz.display.radius.join = "stop_id"; + viz.display.radius.scaleFactor = 10d; + + } + case door2door -> { + //TODO add drtNetwork !? + } + case serviceAreaBased -> { + viz.title = "Service area"; + viz.setShape(postProcess(data, "serviceArea.shp"), "something"); + } + } + viz.center = data.context().getCenter(); + viz.zoom = data.context().mapZoomLevel; + viz.width = 2.; + }); + + //TODO: discuss: should we make XYTime plots out of those ?? + layout.row("Spatial demand distribution") + .el(Hexagons.class, (viz, data) -> { + viz.title = "Spatial demand distribution"; + viz.description = "Origins and destinations."; + viz.projection = this.crs; + viz.file = data.output("*output_drt_legs_" + drtConfigGroup.mode + ".csv"); + viz.addAggregation("OD", "origins", "fromX", "fromY", "destinations", "toX", "toY"); + + viz.center = data.context().getCenter(); + viz.zoom = data.context().mapZoomLevel; + viz.height = 7d; + }) + .el(Hexagons.class, (viz, data) -> { + viz.title = "Spatial rejection distribution"; + viz.description = "Requested (and rejected) origins and destinations."; + viz.projection = this.crs; + viz.file = data.output("ITERS/it." + lastIteration + "/*rejections_" + drtConfigGroup.mode + ".csv"); + viz.addAggregation("rejections", "origins", "fromX", "fromY", "destinations", "toX", "toY"); + + viz.center = data.context().getCenter(); + viz.zoom = data.context().mapZoomLevel; + }) + ; + +// This plot is not absolutely necesarry given the hex plots +// if (drtConfigGroup.operationalScheme == DrtConfigGroup.OperationalScheme.stopbased) +// layout.row("od").el(AggregateOD.class, (viz, data) -> { +// +// viz.shpFile = postProcess(data, "stops.shp"); +// viz.dbfFile = postProcess(data, "stops.shp").replace(".shp", ".dbf"); +// viz.csvFile = postProcess(data, "od.csv"); +// +// }); + + //Final Demand stats + layout.row("Final Demand And Wait Time Statistics") + .el(Plotly.class, (viz, data) -> { + viz.title = "Final Demand and Wait Stats over day time"; + viz.description = "Number of rides (customers) is displayed in bars, wait statistics in lines"; + + Plotly.DataSet waitStats = viz.addDataset(data.output("*_waitStats_" + drtConfigGroup.mode + ".csv")); + Plotly.DataSet rejections = viz.addDataset(data.output("*drt_rejections_perTimeBin_" + drtConfigGroup.mode + ".csv")); + + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("Time Bin").build()) + .yAxis(Axis.builder().title("Wait Time [s]").build()) + .yAxis2(Axis.builder().title("Nr of Rides/Rejections") + .side(Axis.Side.right) + .overlaying(ScatterTrace.YAxis.Y) + .build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("Average") + .build(), + waitStats.mapping() + .x("timebin") + .y("average_wait") + ); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("P5") + .build(), + waitStats.mapping() + .x("timebin") + .y("p_5") + ); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("P95") + .build(), + waitStats.mapping() + .x("timebin") + .y("p_95") + ); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .opacity(0.3) + .yAxis(ScatterTrace.YAxis.Y2.toString()) + .name("Rides") + .build(), + waitStats.mapping() + .x("timebin") + .y("legs") + ); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .opacity(0.3) + .yAxis(ScatterTrace.YAxis.Y2.toString()) + .name("Rejections") + .build(), + rejections.mapping() + .x("timebin") + .y("rejections") + ); + + }) + .el(Area.class, (viz, data) -> { + viz.title = "Vehicle occupancy"; //actually, without title the area plot won't work + viz.description = "Number of passengers on board at a time"; + viz.dataset = data.output("ITERS/it." + lastIteration + "/*occupancy_time_profiles_" + drtConfigGroup.mode + ".txt"); + viz.x = "time"; + viz.xAxisName = "Time"; + viz.yAxisName = "Vehicles [1]"; + }); + + + //from here on, we show the 'evolution' of statistics over iterations + //based on CL's feedback this is not helpful for low iteration numbers + //this implementation actually does not account for situations where firstIteration > 0. + if (lastIteration >= 3) { + + //Demand stats over iterations + layout.row("Demand and Wait Time Statistics per iteration") + .el(Plotly.class, (viz, data) -> { + viz.title = "Rides and rejections per iteration"; + viz.description = "Number of rides (customers) and rejections over the course of the simulation"; + + Plotly.DataSet dataset = viz.addDataset(data.output("*_customer_stats_" + drtConfigGroup.mode + ".csv")); + + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("Iteration").build()) + .yAxis(Axis.builder().title("Wait Time [s]").build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .name("Rejections") + .build(), + dataset.mapping() + .x("iteration") + .y("rejections") +// .color(Plotly.ColorScheme.RdBu) + ); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .name("Rides") + .build(), + dataset.mapping() + .x("iteration") + .y("rides") +// .color(Plotly.ColorScheme.RdBu) + ); + + }) + .el(Line.class, (viz, data) -> { + viz.title = "Waiting time statistics per iteration"; + viz.description = ""; + viz.dataset = data.output("*customer_stats_" + drtConfigGroup.mode + ".csv"); + viz.x = "iteration"; + viz.columns = List.of("wait_average","wait_median", "wait_p95"); + viz.legendName = List.of("Average", "Median", "95th Percentile"); + viz.xAxisName = "Iteration"; + viz.yAxisName = "Waiting Time [s]"; + }) + ; + + layout.row("Demand And Travel Time Statistics per iteration") + .el(Plotly.class, (viz, data) -> { + viz.title = "Travel time components per iteration"; + viz.description = "Comparing mean wait vs. mean in-vehicle travel time per customer"; + + Plotly.DataSet dataset = viz.addDataset(data.output("*_customer_stats_" + drtConfigGroup.mode + ".csv")); + + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("Iteration").build()) + .yAxis(Axis.builder().title("Time [s]") + .build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) +// .opacity(0.5) + .name("In Vehicle") + .build(), + dataset.mapping() + .x("iteration") + .y("inVehicleTravelTime_mean") +// .color(Plotly.ColorScheme.RdBu) + ); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) +// .opacity(0.5) + .name("Wait") + .build(), + dataset.mapping() + .x("iteration") + .y("wait_average") +// .color(Plotly.ColorScheme.RdBu) + ); + }) + .el(Line.class, (viz, data) -> { + viz.title = "Customer travel distance per iteration"; + viz.dataset = data.output("*customer_stats_" + drtConfigGroup.mode + ".csv"); + viz.description = "Customer traveled distance versus customer direct distance"; + viz.x = "iteration"; + viz.columns = List.of("directDistance_m_mean", "distance_m_mean"); + viz.legendName = List.of("Mean direct distance", "Mean driven distance"); + viz.xAxisName = "Iteration"; + viz.yAxisName = "distance [m]"; + }); + + + //Evolution of fleet stats + layout.row("Fleet Stats per Iteration") + .el(Plotly.class, (viz, data) -> { + viz.title = "Fleet stats per iteration"; + viz.description = "Number of " + drtConfigGroup.mode + " vehicles (customers) is displayed as bars, distance stats as lines"; + + Plotly.DataSet dataset = viz.addDataset(data.output("*_vehicle_stats_" + drtConfigGroup.mode + ".csv")); + + viz.layout = tech.tablesaw.plotly.components.Layout.builder() + .xAxis(Axis.builder().title("Iteration").build()) + .yAxis(Axis.builder().title("Total Distance [m]") +// .overlaying(ScatterTrace.YAxis.Y2) + .build()) + .yAxis2(Axis.builder().title("Nr Of Vehicles") + .side(Axis.Side.right) + .overlaying(ScatterTrace.YAxis.Y) + .build()) + .barMode(tech.tablesaw.plotly.components.Layout.BarMode.STACK) + .build(); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("Pax Dist.") + .build(), + dataset.mapping() + .x("iteration") + .y("totalPassengerDistanceTraveled") + ); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("Vehicle mileage") + .build(), + dataset.mapping() + .x("iteration") + .y("totalDistance") + ); + + viz.addTrace(ScatterTrace.builder(Plotly.INPUT, Plotly.INPUT) + .mode(ScatterTrace.Mode.LINE) + .name("Empty mileage") + .build(), + dataset.mapping() + .x("iteration") + .y("totalEmptyDistance") + ); + + viz.addTrace(BarTrace.builder(Plotly.OBJ_INPUT, Plotly.INPUT) + .opacity(0.2) + .yAxis(ScatterTrace.YAxis.Y2.toString()) + .name("Vehicles") + .build(), + dataset.mapping() + .x("iteration") + .y("vehicles") +// .color(Plotly.ColorScheme.RdBu) + ); + }) + .el(Line.class, (viz, data) -> { + viz.title = "Relative Statistics per iteration"; + viz.dataset = data.output("*vehicle_stats_" + drtConfigGroup.mode + ".csv"); + viz.description = "Pooling ratio (Pax distance / Vehicle mileage), Detour ratio, and Empty Ratio"; + viz.x = "iteration"; + viz.columns = List.of("d_p/d_t", "l_det", "emptyRatio"); + viz.legendName = List.of("Pooling ratio", "Detour ratio", "Empty Ratio"); + viz.xAxisName = "Iteration"; + viz.yAxisName = "Value"; + }); + } + + + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboardProvider.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboardProvider.java new file mode 100644 index 00000000000..93e186e2474 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/dashboards/DrtDashboardProvider.java @@ -0,0 +1,34 @@ +package org.matsim.contrib.drt.extension.dashboards; + +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.simwrapper.Dashboard; +import org.matsim.simwrapper.DashboardProvider; +import org.matsim.simwrapper.SimWrapper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Creates one dashboard per drt service. + */ +public class DrtDashboardProvider implements DashboardProvider { + @Override + public List getDashboards(Config config, SimWrapper simWrapper) { + + List result = new ArrayList<>(); + + if (ConfigUtils.hasModule(config, MultiModeDrtConfigGroup.class)) { + MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + + for (DrtConfigGroup drtConfig : multiModeDrtConfigGroup.getModalElements()) { + + result.add(new DrtDashboard(drtConfig, config.getContext(), config.global().getCoordinateSystem(), config.controller().getLastIteration())); + } + } + + return result; + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/EDrtActionCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/EDrtActionCreator.java index dcd376f1cef..2e90569768c 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/EDrtActionCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/EDrtActionCreator.java @@ -19,10 +19,8 @@ package org.matsim.contrib.drt.extension.edrt; -import org.matsim.contrib.drt.vrpagent.DrtActionCreator; +import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; -import org.matsim.contrib.dvrp.passenger.PassengerHandler; -import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.schedule.DriveTask; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; @@ -33,7 +31,6 @@ import org.matsim.contrib.dvrp.vrpagent.VrpLeg; import org.matsim.contrib.dynagent.DynAction; import org.matsim.contrib.dynagent.DynAgent; -import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; import org.matsim.contrib.evrp.ChargingActivity; import org.matsim.contrib.evrp.ChargingTask; import org.matsim.contrib.evrp.EvDvrpVehicle; @@ -45,12 +42,12 @@ * @author michalm */ public class EDrtActionCreator implements VrpAgentLogic.DynActionCreator { - private final DrtActionCreator drtActionCreator; + private final VrpAgentLogic.DynActionCreator delegate; private final MobsimTimer timer; - public EDrtActionCreator(PassengerHandler passengerHandler, MobsimTimer timer, DvrpConfigGroup dvrpCfg) { + public EDrtActionCreator(VrpAgentLogic.DynActionCreator delegate, MobsimTimer timer) { this.timer = timer; - drtActionCreator = new DrtActionCreator(passengerHandler, v -> createLeg(dvrpCfg.mobsimMode, v, timer)); + this.delegate = delegate; } @Override @@ -60,7 +57,7 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now task.initTaskTracker(new OfflineETaskTracker((EvDvrpVehicle)vehicle, timer)); return new ChargingActivity((ChargingTask)task); } else { - DynAction dynAction = drtActionCreator.createAction(dynAgent, vehicle, now); + DynAction dynAction = delegate.createAction(dynAgent, vehicle, now); if (task.getTaskTracker() == null) { task.initTaskTracker(new OfflineETaskTracker((EvDvrpVehicle)vehicle, timer)); } @@ -68,7 +65,7 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now } } - private static VrpLeg createLeg(String mobsimMode, DvrpVehicle vehicle, MobsimTimer timer) { + public static VrpLeg createLeg(String mobsimMode, DvrpVehicle vehicle, MobsimTimer timer) { DriveTask driveTask = (DriveTask)vehicle.getSchedule().getCurrentTask(); VrpLeg leg = new VrpLeg(mobsimMode, driveTask.getPath()); OnlineDriveTaskTracker onlineTracker = new OnlineDriveTaskTrackerImpl(vehicle, leg, diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/optimizer/EDrtVehicleDataEntryFactory.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/optimizer/EDrtVehicleDataEntryFactory.java index 7174f562e15..f552fb3ecae 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/optimizer/EDrtVehicleDataEntryFactory.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/optimizer/EDrtVehicleDataEntryFactory.java @@ -20,19 +20,18 @@ import java.util.List; -import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; import org.matsim.contrib.drt.optimizer.VehicleDataEntryFactoryImpl; -import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.schedule.Schedule; import org.matsim.contrib.dvrp.schedule.Schedule.ScheduleStatus; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.dvrp.schedule.Task.TaskStatus; -import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; +import org.matsim.contrib.ev.fleet.Battery; import org.matsim.contrib.evrp.ETask; import org.matsim.contrib.evrp.EvDvrpVehicle; import org.matsim.contrib.evrp.tracker.ETaskTracker; -import org.matsim.contrib.ev.fleet.Battery; import com.google.inject.Provider; @@ -52,17 +51,13 @@ public EVehicleEntry(VehicleEntry entry, double socBeforeFinalStay) { private final double minimumRelativeSoc; private final VehicleDataEntryFactoryImpl entryFactory; - public EDrtVehicleDataEntryFactory(DrtConfigGroup drtCfg, double minimumRelativeSoc) { + public EDrtVehicleDataEntryFactory(double minimumRelativeSoc) { this.minimumRelativeSoc = minimumRelativeSoc; - entryFactory = new VehicleDataEntryFactoryImpl(drtCfg); + entryFactory = new VehicleDataEntryFactoryImpl(); } @Override public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { - if (entryFactory.isNotEligibleForRequestInsertion(vehicle, currentTime)) { - return null; - } - Schedule schedule = vehicle.getSchedule(); int taskCount = schedule.getTaskCount(); if (taskCount > 1) { @@ -100,17 +95,15 @@ public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { } public static class EDrtVehicleDataEntryFactoryProvider implements Provider { - private final DrtConfigGroup drtCfg; private final double minimumRelativeSoc; - public EDrtVehicleDataEntryFactoryProvider(DrtConfigGroup drtCfg, double minimumRelativeSoc) { - this.drtCfg = drtCfg; + public EDrtVehicleDataEntryFactoryProvider(double minimumRelativeSoc) { this.minimumRelativeSoc = minimumRelativeSoc; } @Override public EDrtVehicleDataEntryFactory get() { - return new EDrtVehicleDataEntryFactory(drtCfg, minimumRelativeSoc); + return new EDrtVehicleDataEntryFactory(minimumRelativeSoc); } } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtControlerCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtControlerCreator.java index 638180019a7..8471438ebe4 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtControlerCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtControlerCreator.java @@ -42,7 +42,7 @@ public class EDrtControlerCreator { public static Controler createControler(Config config, boolean otfvis) { MultiModeDrtConfigGroup multiModeDrtConfig = MultiModeDrtConfigGroup.get(config); - DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.planCalcScore(), config.plansCalcRoute()); + DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing()); Scenario scenario = DrtControlerCreator.createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtModeOptimizerQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtModeOptimizerQSimModule.java index 7e3737978ef..71ee5abe1c1 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtModeOptimizerQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/EDrtModeOptimizerQSimModule.java @@ -31,6 +31,8 @@ import org.matsim.contrib.drt.optimizer.DefaultDrtOptimizer; import org.matsim.contrib.drt.optimizer.DrtModeOptimizerQSimModule; import org.matsim.contrib.drt.optimizer.DrtOptimizer; +import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryParams; +import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryQueue; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.depot.DepotFinder; @@ -38,29 +40,30 @@ import org.matsim.contrib.drt.optimizer.insertion.DefaultInsertionCostCalculator; import org.matsim.contrib.drt.optimizer.insertion.DefaultUnplannedRequestInserter; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; -import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryParams; -import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryQueue; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.optimizer.insertion.UnplannedRequestInserter; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy; import org.matsim.contrib.drt.passenger.DrtOfferAcceptor; +import org.matsim.contrib.drt.prebooking.PrebookingActionCreator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtTaskFactory; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; import org.matsim.contrib.drt.scheduler.DefaultRequestInsertionScheduler; import org.matsim.contrib.drt.scheduler.DrtScheduleInquiry; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; -import org.matsim.contrib.ev.infrastructure.ChargingInfrastructures; +import org.matsim.contrib.ev.infrastructure.ChargingInfrastructureUtils; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.modal.ModalProviders; @@ -69,6 +72,7 @@ import org.matsim.core.router.util.TravelTime; import com.google.inject.Inject; +import com.google.inject.Singleton; /** * @author Michal Maciejewski (michalm) @@ -88,15 +92,17 @@ protected void configureQSim() { getter.getModal(EmptyVehicleChargingScheduler.class)))); bindModal(DefaultDrtOptimizer.class).toProvider(modalProvider( - getter -> new DefaultDrtOptimizer(drtCfg, getter.getModal(Fleet.class), getter.get(MobsimTimer.class), + getter -> { + return new DefaultDrtOptimizer(drtCfg, getter.getModal(Fleet.class), getter.get(MobsimTimer.class), getter.getModal(DepotFinder.class), getter.getModal(RebalancingStrategy.class), getter.getModal(DrtScheduleInquiry.class), getter.getModal(ScheduleTimingUpdater.class), getter.getModal(EmptyVehicleRelocator.class), getter.getModal(UnplannedRequestInserter.class), - getter.getModal(DrtRequestInsertionRetryQueue.class)))).asEagerSingleton(); + getter.getModal(DrtRequestInsertionRetryQueue.class)); + })).asEagerSingleton(); bindModal(ChargingInfrastructure.class).toProvider(modalProvider( - getter -> ChargingInfrastructures.createModalNetworkChargers(getter.get(ChargingInfrastructure.class), - getter.getModal(Network.class), getMode()))).asEagerSingleton(); + getter -> ChargingInfrastructureUtils.createModalNetworkChargers(getter.get(ChargingInfrastructure.class ), + getter.getModal(Network.class), getMode() ))).asEagerSingleton(); // XXX if overridden to something else, make sure that the depots are equipped with chargers // otherwise vehicles will not re-charge @@ -129,7 +135,8 @@ public EmptyVehicleChargingScheduler get() { getter.getModal(RequestInsertionScheduler.class), getter.getModal(VehicleEntry.EntryFactory.class), getter.getModal(DrtInsertionSearch.class), getter.getModal(DrtRequestInsertionRetryQueue.class), getter.getModal(DrtOfferAcceptor.class), - getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool()))).asEagerSingleton(); + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(PassengerStopDurationProvider.class)))).asEagerSingleton(); bindModal(InsertionCostCalculator.class).toProvider(modalProvider( getter -> new DefaultInsertionCostCalculator(getter.getModal(CostCalculationStrategy.class)))); @@ -163,23 +170,40 @@ public EmptyVehicleRelocator get() { bindModal(DrtScheduleInquiry.class).to(DrtScheduleInquiry.class).asEagerSingleton(); + boolean scheduleWaitBeforeDrive = drtCfg.getPrebookingParams().map(p -> p.scheduleWaitBeforeDrive).orElse(false); bindModal(RequestInsertionScheduler.class).toProvider(modalProvider( getter -> new DefaultRequestInsertionScheduler(getter.getModal(Fleet.class), getter.get(MobsimTimer.class), getter.getModal(TravelTime.class), getter.getModal(ScheduleTimingUpdater.class), getter.getModal(DrtTaskFactory.class), - getter.getModal(StopDurationEstimator.class)))) + getter.getModal(StopTimeCalculator.class), scheduleWaitBeforeDrive))) .asEagerSingleton(); bindModal(DrtOfferAcceptor.class).toInstance(DrtOfferAcceptor.DEFAULT_ACCEPTOR); bindModal(ScheduleTimingUpdater.class).toProvider(modalProvider( getter -> new ScheduleTimingUpdater(getter.get(MobsimTimer.class), - new EDrtStayTaskEndTimeCalculator(getter.getModal(StopDurationEstimator.class))))).asEagerSingleton(); - - bindModal(VrpAgentLogic.DynActionCreator.class).toProvider(modalProvider( - getter -> new EDrtActionCreator(getter.getModal(PassengerHandler.class), getter.get(MobsimTimer.class), - getter.get(DvrpConfigGroup.class)))).asEagerSingleton(); - + new EDrtStayTaskEndTimeCalculator(getter.getModal(StopTimeCalculator.class))))).asEagerSingleton(); + + bindModal(VrpLegFactory.class).toProvider(modalProvider(getter -> { + DvrpConfigGroup dvrpCfg = getter.get(DvrpConfigGroup.class); + MobsimTimer timer = getter.get(MobsimTimer.class); + + // Makes basic DrtActionCreator create legs with consumption tracker + return v -> EDrtActionCreator.createLeg(dvrpCfg.mobsimMode, v, timer); + })).in(Singleton.class); + + bindModal(EDrtActionCreator.class).toProvider(modalProvider(getter -> { + VrpAgentLogic.DynActionCreator delegate = drtCfg.getPrebookingParams().isPresent() + ? getter.getModal(PrebookingActionCreator.class) + : getter.getModal(DrtActionCreator.class); + + // EDrtActionCreator wraps around delegate and initializes consumption trackers + // + adds ChargingActivity + return new EDrtActionCreator(delegate, getter.get(MobsimTimer.class)); + })).asEagerSingleton(); + + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(EDrtActionCreator.class)); + bindModal(VrpOptimizer.class).to(modalKey(DrtOptimizer.class)); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenario.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenario.java index 741a03e52b2..741c4047786 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenario.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenario.java @@ -54,14 +54,14 @@ public static void run(URL configUrl, boolean otfvis) { new OTFVisConfigGroup(), new EvConfigGroup()), otfvis); } - public static void run(Config config, boolean otfvis) { + public static Controler createControler(Config config, boolean otfvis) { Controler controler = EDrtControlerCreator.createControler(config, otfvis); for (DrtConfigGroup drtCfg : MultiModeDrtConfigGroup.get(config).getModalElements()) { controler.addOverridingModule(new AbstractDvrpModeModule(drtCfg.getMode()) { @Override public void install() { bind(EDrtVehicleDataEntryFactoryProvider.class).toInstance( - new EDrtVehicleDataEntryFactoryProvider(drtCfg, MIN_RELATIVE_SOC)); + new EDrtVehicleDataEntryFactoryProvider(MIN_RELATIVE_SOC)); } }); } @@ -75,6 +75,11 @@ public void install() { bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); } }); - controler.run(); + + return controler; + } + + public static void run(Config config, boolean otfvis) { + createControler(config, otfvis).run(); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtStayTaskEndTimeCalculator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtStayTaskEndTimeCalculator.java index 6961bd6cd0f..361ad6207a1 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtStayTaskEndTimeCalculator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtStayTaskEndTimeCalculator.java @@ -19,13 +19,13 @@ package org.matsim.contrib.drt.extension.edrt.schedule; import org.matsim.contrib.drt.schedule.DrtStayTaskEndTimeCalculator; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.schedule.StayTask; public class EDrtStayTaskEndTimeCalculator extends DrtStayTaskEndTimeCalculator { - public EDrtStayTaskEndTimeCalculator(StopDurationEstimator stopDurationEstimator) { - super(stopDurationEstimator); + public EDrtStayTaskEndTimeCalculator(StopTimeCalculator stopTimeCalculator) { + super(stopTimeCalculator); } @Override diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/scheduler/EmptyVehicleChargingScheduler.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/scheduler/EmptyVehicleChargingScheduler.java index f50d437a1a0..eea29745e80 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/scheduler/EmptyVehicleChargingScheduler.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/scheduler/EmptyVehicleChargingScheduler.java @@ -21,20 +21,23 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.schedule.DrtTaskFactory; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.schedule.Schedule; -import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl; import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; -import org.matsim.contrib.evrp.EvDvrpVehicle; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; +import org.matsim.contrib.evrp.EvDvrpVehicle; import org.matsim.core.mobsim.framework.MobsimTimer; +import java.util.Comparator; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -43,23 +46,27 @@ public class EmptyVehicleChargingScheduler { private final MobsimTimer timer; private final EDrtTaskFactoryImpl taskFactory; - private final Map, Charger> linkToChargerMap; + private final Map, List> linkToChargersMap; public EmptyVehicleChargingScheduler(MobsimTimer timer, DrtTaskFactory taskFactory, ChargingInfrastructure chargingInfrastructure) { this.timer = timer; this.taskFactory = (EDrtTaskFactoryImpl)taskFactory; - linkToChargerMap = chargingInfrastructure.getChargers() + linkToChargersMap = chargingInfrastructure.getChargers() .values() .stream() - .collect(Collectors.toMap(c -> c.getLink().getId(), c -> c)); + .collect(Collectors.groupingBy(c -> c.getLink().getId())); } public void chargeVehicle(DvrpVehicle vehicle) { DrtStayTask currentTask = (DrtStayTask)vehicle.getSchedule().getCurrentTask(); Link currentLink = currentTask.getLink(); - Charger charger = linkToChargerMap.get(currentLink.getId()); - if (charger != null) { + List chargers = linkToChargersMap.get(currentLink.getId()); + if (chargers != null) { + Optional freeCharger = chargers.stream().filter(c -> c.getLogic().getPluggedVehicles().isEmpty()).findFirst(); + + // Empty charger or at least smallest queue charger + Charger charger = freeCharger.orElseGet(() -> chargers.stream().min(Comparator.comparingInt(e -> e.getLogic().getQueuedVehicles().size())).orElseThrow()); ElectricVehicle ev = ((EvDvrpVehicle)vehicle).getElectricVehicle(); if (!charger.getLogic().getChargingStrategy().isChargingCompleted(ev)) { chargeVehicleImpl(vehicle, charger); @@ -67,6 +74,8 @@ public void chargeVehicle(DvrpVehicle vehicle) { } } + + private void chargeVehicleImpl(DvrpVehicle vehicle, Charger charger) { Schedule schedule = vehicle.getSchedule(); DrtStayTask stayTask = (DrtStayTask)schedule.getCurrentTask(); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java new file mode 100644 index 00000000000..d446d5f34bc --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java @@ -0,0 +1,177 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.apache.commons.math3.stat.descriptive.SummaryStatistics; +import org.apache.commons.math3.stat.regression.RegressionResults; +import org.apache.commons.math3.stat.regression.SimpleRegression; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.fare.DrtFareParams; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.speedup.DrtSpeedUp; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.utils.misc.OptionalTime; + +import java.util.Map; +import java.util.SplittableRandom; + +/** + * Estimates drt trips based only daily averages. No spatial or temporal differentiation is taken into account for the estimate. + * This estimator is suited for small scenarios with few vehicles and trips and consequently few data points. + */ +public class BasicDrtEstimator implements DrtEstimator, IterationEndsListener { + + private static final Logger log = LogManager.getLogger(BasicDrtEstimator.class); + + private final DrtEventSequenceCollector collector; + private final DrtEstimatorConfigGroup config; + private final DrtConfigGroup drtConfig; + + private final SplittableRandom rnd = new SplittableRandom(); + /** + * Currently valid estimates. + */ + private GlobalEstimate currentEst; + private RegressionResults fare; + + public BasicDrtEstimator(DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config, + DrtConfigGroup drtConfig) { + //zones = injector.getModal(DrtZonalSystem.class); + this.collector = collector; + this.config = config; + this.drtConfig = drtConfig; + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + + // Speed-up iteration need to be ignored for the estimates + if (drtConfig.getDrtSpeedUpParams().isPresent() && + DrtSpeedUp.isTeleportDrtUsers(drtConfig.getDrtSpeedUpParams().get(), + event.getServices().getConfig().controller(), event.getIteration())) { + return; + } + + GlobalEstimate est = new GlobalEstimate(); + + int n = 0; + + int nRejections = collector.getRejectedRequestSequences().size(); + int nSubmitted = collector.getRequestSubmissions().size(); + + for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { + + Map, DrtEventSequenceCollector.EventSequence.PersonEvents> personEvents = seq.getPersonEvents(); + for (Map.Entry, DrtEventSequenceCollector.EventSequence.PersonEvents> entry : personEvents.entrySet()) { + if (entry.getValue().getPickedUp().isPresent() && entry.getValue().getDroppedOff().isPresent()) { + double waitTime = entry.getValue().getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + est.waitTime.addValue(waitTime); + + double unsharedTime = seq.getSubmitted().getUnsharedRideTime(); + double travelTime = entry.getValue().getDroppedOff().get().getTime() - entry.getValue().getPickedUp().get().getTime(); + + est.detour.addValue(travelTime / unsharedTime); + + double fare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + est.fare.addData(seq.getSubmitted().getUnsharedRideDistance(), fare); + n++; + } + } + } + + // At least some data points are required + if (n <= 3) + return; + + fare = est.fare.regress(); + + double rejectionRate = (double) nRejections / nSubmitted; + + if (currentEst == null) { + est.meanWait = est.waitTime.getMean(); + est.stdWait = est.waitTime.getStandardDeviation(); + est.meanDetour = est.detour.getMean(); + est.stdDetour = est.detour.getStandardDeviation(); + est.rejectionRate = rejectionRate; + } else { + est.meanWait = config.decayFactor * est.waitTime.getMean() + (1 - config.decayFactor) * currentEst.waitTime.getMean(); + est.stdWait = config.decayFactor * est.waitTime.getStandardDeviation() + (1 - config.decayFactor) * currentEst.waitTime.getStandardDeviation(); + est.meanDetour = config.decayFactor * est.detour.getMean() + (1 - config.decayFactor) * currentEst.detour.getMean(); + est.stdDetour = config.decayFactor * est.detour.getStandardDeviation() + (1 - config.decayFactor) * currentEst.detour.getStandardDeviation(); + est.rejectionRate = config.decayFactor * rejectionRate + (1 - config.decayFactor) * currentEst.rejectionRate; + } + + log.info("Calculated {}", est); + currentEst = est; + } + + @Override + public Estimate estimate(DrtRoute route, OptionalTime departureTime) { + + if (currentEst == null) { + // If not estimates are present, use travel time alpha as detour + // beta is not used, because estimates are supposed to be minimums and not worst cases + double travelTime = Math.min(route.getDirectRideTime() + drtConfig.maxAbsoluteDetour, + route.getDirectRideTime() * drtConfig.maxTravelTimeAlpha); + + double fare = 0; + if (drtConfig.getDrtFareParams().isPresent()) { + DrtFareParams fareParams = drtConfig.getDrtFareParams().get(); + fare = fareParams.distanceFare_m * route.getDistance() + + fareParams.timeFare_h * route.getDirectRideTime() / 3600.0 + + fareParams.baseFare; + + fare = Math.max(fare, fareParams.minFarePerTrip); + } + + // for distance, also use the max travel time alpha + return new Estimate(route.getDistance() * drtConfig.maxTravelTimeAlpha, travelTime, drtConfig.maxWaitTime, fare, 0); + } + + double fare = 0; + if (this.fare != null) + fare = this.fare.getParameterEstimate(0) + this.fare.getParameterEstimate(1) * route.getDistance(); + + if (drtConfig.getDrtFareParams().isPresent()) { + fare = Math.max(fare, drtConfig.getDrtFareParams().get().minFarePerTrip); + } + + double detour = Math.max(1, rnd.nextGaussian(currentEst.meanDetour, config.randomization * currentEst.stdDetour)); + double waitTime = Math.max(0, rnd.nextGaussian(currentEst.meanWait, config.randomization * currentEst.stdWait)); + + return new Estimate(route.getDistance() * detour, route.getDirectRideTime() * detour, waitTime, fare, currentEst.rejectionRate); + } + + /** + * Helper class to hold statistics. + */ + private static final class GlobalEstimate { + + private final SummaryStatistics waitTime = new SummaryStatistics(); + private final SummaryStatistics detour = new SummaryStatistics(); + private final SimpleRegression fare = new SimpleRegression(true); + + private double meanWait; + private double stdWait; + private double meanDetour; + private double stdDetour; + private double rejectionRate; + + @Override + public String toString() { + return "GlobalEstimate{" + + "meanWait=" + meanWait + + ", stdWait=" + stdWait + + ", meanDetour=" + meanDetour + + ", stdDetour=" + stdDetour + + ", rejectionRate=" + rejectionRate + + '}'; + } + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java new file mode 100644 index 00000000000..d095a0037bd --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java @@ -0,0 +1,132 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.core.controler.events.AfterMobsimEvent; +import org.matsim.core.controler.events.ShutdownEvent; +import org.matsim.core.controler.events.StartupEvent; +import org.matsim.core.controler.listener.AfterMobsimListener; +import org.matsim.core.controler.listener.ShutdownListener; +import org.matsim.core.controler.listener.StartupListener; +import org.matsim.core.utils.misc.OptionalTime; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +/** + * Analyzes and outputs drt estimates errors metrics based on daily requests. + */ +public final class DrtEstimateAnalyzer implements StartupListener, ShutdownListener, AfterMobsimListener { + + private static final Logger log = LogManager.getLogger(DrtEstimateAnalyzer.class); + + // Might be useful but not needed currently + //private final DefaultMainLegRouter.RouteCreator creator; + private final DrtEstimator estimator; + private final DrtEventSequenceCollector collector; + private final DrtEstimatorConfigGroup config; + + private CSVPrinter csv; + + public DrtEstimateAnalyzer(DrtEstimator estimator, DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config) { + this.estimator = estimator; + this.collector = collector; + this.config = config; + } + + @Override + public void notifyStartup(StartupEvent event) { + + String filename = event.getServices().getControlerIO().getOutputFilename("drt_estimates_" + config.getMode() + ".csv"); + + try { + csv = new CSVPrinter(Files.newBufferedWriter(Path.of(filename), StandardCharsets.UTF_8), CSVFormat.DEFAULT); + csv.printRecord("iteration", + "wait_time_mae", "wait_time_err_q5", "wait_time_err_q50", "wait_time_err_q95", + "travel_time_mae", "travel_time_err_q5", "travel_time_err_q50", "travel_time_err_q95", + "fare_mae", "fare_err_q5", "fare_err_q50", "fare_err_q95" + ); + + } catch (IOException e) { + throw new UncheckedIOException("Could not open output file for estimates.", e); + } + } + + @Override + public void notifyShutdown(ShutdownEvent event) { + try { + csv.close(); + } catch (IOException e) { + log.warn("Could not close drt estimate file", e); + } + } + + /** + * Needs to run before any estimators updates. + */ + @Override + public void notifyAfterMobsim(AfterMobsimEvent event) { + + try { + csv.printRecord(calcMetrics(event.getIteration())); + csv.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Return row of metrics for the csv file. + */ + private Iterable calcMetrics(int iteration) { + + DescriptiveStatistics waitTime = new DescriptiveStatistics(); + DescriptiveStatistics travelTime = new DescriptiveStatistics(); + DescriptiveStatistics fare = new DescriptiveStatistics(); + + for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { + Map, DrtEventSequenceCollector.EventSequence.PersonEvents> personEvents = seq.getPersonEvents(); + for (Map.Entry, DrtEventSequenceCollector.EventSequence.PersonEvents> entry : personEvents.entrySet()) { + if (entry.getValue().getPickedUp().isPresent() && entry.getValue().getDroppedOff().isPresent()) { + + // many attributes are not filled, when using the constructor + DrtRoute route = new DrtRoute(seq.getSubmitted().getFromLinkId(), seq.getSubmitted().getToLinkId()); + route.setDirectRideTime(seq.getSubmitted().getUnsharedRideTime()); + route.setDistance(seq.getSubmitted().getUnsharedRideDistance()); + + double valWaitTime = entry.getValue().getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + double valTravelTime = entry.getValue().getDroppedOff().get().getTime() - entry.getValue().getPickedUp().get().getTime(); + double valFare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + + DrtEstimator.Estimate estimate = estimator.estimate(route, OptionalTime.defined(seq.getSubmitted().getTime())); + + waitTime.addValue(Math.abs(estimate.waitingTime() - valWaitTime)); + travelTime.addValue(Math.abs(estimate.travelTime() - valTravelTime)); + fare.addValue(Math.abs(estimate.fare() - valFare)); + } + } + } + + return List.of( + iteration, + waitTime.getMean(), waitTime.getPercentile(5), waitTime.getPercentile(50), waitTime.getPercentile(95), + travelTime.getMean(), travelTime.getPercentile(5), travelTime.getPercentile(50), travelTime.getPercentile(95), + fare.getMean(), fare.getPercentile(5), fare.getPercentile(50), fare.getPercentile(95) + ); + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java new file mode 100644 index 00000000000..fef209ed211 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java @@ -0,0 +1,35 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.core.controler.listener.ControlerListener; +import org.matsim.core.utils.misc.OptionalTime; + +/** + * Interface to estimate a DRT service's detour, waiting time and costs. + */ +public interface DrtEstimator extends ControlerListener { + + /** + * Provide an estimate for a drt route with specific pickup and dropoff point. + * + * @param route drt route + * @param departureTime estimated departure time + * @return An {@link Estimate} instance + */ + Estimate estimate(DrtRoute route, OptionalTime departureTime); + + + /** + * Estimate for various attributes for a drt trip. + * + * @param distance travel distance in meter + * @param travelTime travel time in seconds + * @param waitingTime waiting time in seconds + * @param fare money, which is negative if the customer needs to pay it + * @param rejectionRate probability of a trip being rejected + */ + record Estimate(double distance, double travelTime, double waitingTime, double fare, double rejectionRate) { + + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java new file mode 100644 index 00000000000..ed1771ae34f --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java @@ -0,0 +1,58 @@ +package org.matsim.contrib.drt.extension.estimator; + +import com.google.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.dvrp.run.DvrpMode; +import org.matsim.core.scoring.functions.ModeUtilityParameters; +import org.matsim.core.utils.misc.OptionalTime; +import org.matsim.modechoice.EstimatorContext; +import org.matsim.modechoice.ModeAvailability; +import org.matsim.modechoice.estimators.LegEstimator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + + +/** + * Aggregate class for informed-mode-choice that makes sure to invoke the correct estimator for each drt mode. + */ +public class MultiModalDrtLegEstimator implements LegEstimator { + + private static final Logger log = LogManager.getLogger(MultiModalDrtLegEstimator.class); + + protected final Map estimators = new HashMap<>(); + + @Inject + public MultiModalDrtLegEstimator(Map estimators) { + for (Map.Entry e : estimators.entrySet()) { + this.estimators.put(e.getKey().value(), e.getValue()); + } + } + + @Override + public double estimate(EstimatorContext context, String mode, Leg leg, ModeAvailability option) { + + if (!(leg.getRoute() instanceof DrtRoute route)) + throw new IllegalStateException("Drt leg routes must be of type DrtRoute."); + + OptionalTime departureTime = leg.getDepartureTime(); + + DrtEstimator estimator = Objects.requireNonNull(estimators.get(mode), String.format("No drt estimator found for mode %s. Check warnings for errors.", mode)); + + DrtEstimator.Estimate est = estimator.estimate(route, departureTime); + ModeUtilityParameters params = context.scoring.modeParams.get(mode); + + // By default, waiting time is scored as travel time + return params.constant + + params.marginalUtilityOfDistance_m * est.distance() + + params.marginalUtilityOfTraveling_s * est.travelTime() + + params.marginalUtilityOfTraveling_s * est.waitingTime() + + context.scoring.marginalUtilityOfMoney * params.monetaryDistanceCostRate * est.distance() + + context.scoring.marginalUtilityOfMoney * est.fare(); + + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java new file mode 100644 index 00000000000..107aa2eb53a --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java @@ -0,0 +1,61 @@ +package org.matsim.contrib.drt.extension.estimator.run; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.contrib.dvrp.run.Modal; +import org.matsim.contrib.util.ReflectiveConfigGroupWithConfigurableParameterSets; + +public class DrtEstimatorConfigGroup extends ReflectiveConfigGroupWithConfigurableParameterSets implements Modal { + + /** + * Type of estimator, which will be installed in {@link DrtEstimatorModule}. + */ + public enum EstimatorType { + BASIC, + + /** + * Custom estimator, that needs to provided via binding. + */ + CUSTOM + } + + public static final String GROUP_NAME = "drtEstimator"; + + public DrtEstimatorConfigGroup() { + super(GROUP_NAME); + } + + public DrtEstimatorConfigGroup(String mode) { + super(GROUP_NAME); + this.mode = mode; + } + + @Parameter + @Comment("Mode of the drt service to estimate.") + @NotBlank + public String mode = TransportMode.drt; + + @Parameter + @Comment("Estimator typed to be used. In case of 'CUSTOM', guice bindings needs to be provided.") + @NotNull + public EstimatorType estimator = EstimatorType.BASIC; + + @Parameter + @Comment("Decay of the exponential moving average.") + @Positive + public double decayFactor = 0.7; + + @Parameter + @Comment("Factor multiplied with standard deviation to randomize estimates.") + @PositiveOrZero + public double randomization = 0.1; + + @Override + public String getMode() { + return mode; + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java new file mode 100644 index 00000000000..14bc2ae330d --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java @@ -0,0 +1,80 @@ +package org.matsim.contrib.drt.extension.estimator.run; + +import com.google.inject.Singleton; +import com.google.inject.multibindings.MapBinder; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.BasicDrtEstimator; +import org.matsim.contrib.drt.extension.estimator.DrtEstimateAnalyzer; +import org.matsim.contrib.drt.extension.estimator.DrtEstimator; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpMode; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; + +import java.util.Optional; + +/** + * Main module that needs to be installed if any estimator is to be used. + */ +public class DrtEstimatorModule extends AbstractModule { + + @Override + public void install() { + + MultiModeDrtConfigGroup drtConfigs = MultiModeDrtConfigGroup.get(getConfig()); + MultiModeDrtEstimatorConfigGroup configs = ConfigUtils.addOrGetModule(getConfig(), MultiModeDrtEstimatorConfigGroup.class); + + for (DrtConfigGroup cfg : drtConfigs.getModalElements()) { + + Optional estCfg = configs.getModalElement(cfg.mode); + + estCfg.ifPresent(drtEstimatorConfigGroup -> install(new ModeModule(cfg, drtEstimatorConfigGroup))); + } + } + + static final class ModeModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup cfg; + private final DrtEstimatorConfigGroup group; + + public ModeModule(DrtConfigGroup cfg, DrtEstimatorConfigGroup group) { + super(group.mode); + this.cfg = cfg; + this.group = group; + } + + @Override + public void install() { + + // try with default injections and overwrite + if (group.estimator == DrtEstimatorConfigGroup.EstimatorType.BASIC) { + bindModal(DrtEstimator.class).toProvider(modalProvider( + getter -> new BasicDrtEstimator(getter.getModal(DrtEventSequenceCollector.class), group, cfg) + )).in(Singleton.class); + } + + // DRT Estimators will be available as Map + MapBinder.newMapBinder(this.binder(), DvrpMode.class, DrtEstimator.class) + .addBinding(DvrpModes.mode(getMode())) + .to(modalKey(DrtEstimator.class)); + + addControlerListenerBinding().to(modalKey(DrtEstimator.class)); + + bindModal(DrtEstimatorConfigGroup.class).toInstance(group); + + // Needs to run before estimators + bindModal(DrtEstimateAnalyzer.class) + .toProvider( + modalProvider(getter -> new DrtEstimateAnalyzer(getter.getModal(DrtEstimator.class), getter.getModal(DrtEventSequenceCollector.class), group)) + ) + .in(Singleton.class); + + addControlerListenerBinding().to(modalKey(DrtEstimateAnalyzer.class)); + + } + + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java new file mode 100644 index 00000000000..b998bf78648 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java @@ -0,0 +1,103 @@ +/* + * *********************************************************************** * + * project: org.matsim.* + * *********************************************************************** * + * * + * copyright : (C) 2018 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** * + */ + +package org.matsim.contrib.drt.extension.estimator.run; + +import java.util.Collection; +import java.util.Optional; +import java.util.function.Supplier; + +import org.matsim.contrib.dvrp.run.MultiModal; +import org.matsim.contrib.dvrp.run.MultiModals; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigGroup; +import org.matsim.core.config.ReflectiveConfigGroup; + +import com.google.common.base.Verify; + +/** + * @author Michal Maciejewski (michalm) + */ +public final class MultiModeDrtEstimatorConfigGroup extends ReflectiveConfigGroup implements MultiModal { + public static final String GROUP_NAME = "drtEstimators"; + + /** + * @param config + * @return MultiModeDrtConfigGroup if exists. Otherwise fails + */ + public static MultiModeDrtEstimatorConfigGroup get(Config config) { + return (MultiModeDrtEstimatorConfigGroup)config.getModule(GROUP_NAME); + } + + private final Supplier drtConfigSupplier; + + public MultiModeDrtEstimatorConfigGroup() { + this(DrtEstimatorConfigGroup::new); + } + + public MultiModeDrtEstimatorConfigGroup(Supplier drtConfigSupplier) { + super(GROUP_NAME); + this.drtConfigSupplier = drtConfigSupplier; + } + + @Override + protected void checkConsistency(Config config) { + super.checkConsistency(config); + Verify.verify(config.getModule(DrtEstimatorConfigGroup.GROUP_NAME) == null, + "In the multi-mode DRT setup, DrtEstimatorConfigGroup must not be defined at the config top level"); + MultiModals.requireAllModesUnique(this); + } + + @Override + public ConfigGroup createParameterSet(String type) { + if (type.equals(DrtEstimatorConfigGroup.GROUP_NAME)) { + return drtConfigSupplier.get(); + } else { + throw new IllegalArgumentException("Unsupported parameter set type: " + type); + } + } + + @Override + public void addParameterSet(ConfigGroup set) { + if (set instanceof DrtEstimatorConfigGroup) { + super.addParameterSet(set); + } else { + throw new IllegalArgumentException("Unsupported parameter set class: " + set); + } + } + + public void addParameterSet(DrtEstimatorConfigGroup set) { + addParameterSet((ConfigGroup) set); + } + + @Override + @SuppressWarnings("unchecked") + public Collection getModalElements() { + return (Collection)getParameterSets(DrtEstimatorConfigGroup.GROUP_NAME); + } + + /** + * Find estimator config for specific mode. + */ + public Optional getModalElement(String mode) { + return getModalElements().stream().filter(m -> m.getMode().equals(mode)).findFirst(); + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java index ac265cf4a2c..58705f89cb3 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java @@ -164,7 +164,7 @@ public void afterSim() { } private boolean switchOffFISS() { - return (this.fissConfigGroup.switchOffFISSLastIteration && this.matsimServices.getConfig().controler().getLastIteration() == this.matsimServices.getIterationNumber()); + return (this.fissConfigGroup.switchOffFISSLastIteration && this.matsimServices.getConfig().controller().getLastIteration() == this.matsimServices.getIterationNumber()); } @Override diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISSQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISSQSimModule.java index 72d85e6376c..6dace8691ad 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISSQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISSQSimModule.java @@ -31,8 +31,8 @@ public class FISSQSimModule extends AbstractQSimModule { @Override protected void configureQSim() { - addNamedComponent(FISS.class, COMPONENT_NAME); - } + addQSimComponentBinding( COMPONENT_NAME ).to( FISS.class ); + } @Provides @Singleton diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java index f535c15d5ca..1a008c63cdd 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java @@ -1,6 +1,7 @@ package org.matsim.contrib.drt.extension.operations.eshifts.charging; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; import org.matsim.contrib.dvrp.optimizer.Request; @@ -15,7 +16,10 @@ import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftBreakTask; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * based on {@link DrtStopActivity} and {@link ChargingActivity} @@ -50,7 +54,7 @@ public ChargingBreakActivity(ChargingTask chargingTask, PassengerHandler passeng protected boolean isLastStep(double now) { if(chargingDelegate.getEndTime() < now && now >= endTime) { for (var request : pickupRequests.values()) { - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } } @@ -64,13 +68,13 @@ public void finalizeAction(double now) { } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { if (!isLastStep(now)) { return;// pick up only at the end of stop activity } - var request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); @@ -81,7 +85,7 @@ public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, d protected void beforeFirstStep(double now) { // TODO probably we should simulate it more accurately (passenger by passenger, not all at once...) for (var request : dropoffRequests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } @@ -95,9 +99,9 @@ protected void simStep(double now) { chargingDelegate.doSimStep(now); } - private AcceptedDrtRequest getRequestForPassenger(Id passengerId) { + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { return pickupRequests.values().stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } 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/eshifts/run/ShiftEDrtModeOptimizerQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/run/ShiftEDrtModeOptimizerQSimModule.java index 4fc1aafaad1..ab537fd439d 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/run/ShiftEDrtModeOptimizerQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/run/ShiftEDrtModeOptimizerQSimModule.java @@ -1,6 +1,7 @@ package org.matsim.contrib.drt.extension.operations.eshifts.run; import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.drt.extension.edrt.EDrtActionCreator; import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory; import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl; import org.matsim.contrib.drt.extension.edrt.scheduler.EmptyVehicleChargingScheduler; @@ -20,23 +21,29 @@ import org.matsim.contrib.drt.extension.operations.shifts.dispatcher.DrtShiftDispatcher; import org.matsim.contrib.drt.extension.operations.shifts.dispatcher.DrtShiftDispatcherImpl; import org.matsim.contrib.drt.extension.operations.shifts.optimizer.ShiftVehicleDataEntryFactory; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtActionCreator; import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtTaskFactory; import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskScheduler; import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShifts; import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.prebooking.PrebookingActionCreator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtTaskFactory; +import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; +import com.google.inject.Singleton; + /** * @author nkuehnel / MOIA */ @@ -69,7 +76,7 @@ drtShiftParams, new EDrtShiftStartLogic(new DefaultShiftStartLogic()), new EDrtAssignShiftToVehicleLogic(new DefaultAssignShiftToVehicleLogic(drtShiftParams), drtShiftParams)), getter.getModal(Fleet.class)))).asEagerSingleton(); - bindModal(VehicleEntry.EntryFactory.class).toProvider(modalProvider(getter -> new ShiftVehicleDataEntryFactory(new EDrtVehicleDataEntryFactory(drtCfg, 0)))).asEagerSingleton(); + bindModal(VehicleEntry.EntryFactory.class).toProvider(modalProvider(getter -> new ShiftVehicleDataEntryFactory(new EDrtVehicleDataEntryFactory(0)))).asEagerSingleton(); final ShiftEDrtTaskFactoryImpl taskFactory = new ShiftEDrtTaskFactoryImpl(new EDrtTaskFactoryImpl()); bindModal(DrtTaskFactory.class).toInstance(taskFactory); @@ -81,10 +88,26 @@ drtShiftParams, new EDrtShiftStartLogic(new DefaultShiftStartLogic()), getter.get(MobsimTimer.class), taskFactory, drtShiftParams, getter.getModal(ChargingInfrastructure.class), getter.getModal(OperationFacilities.class), getter.getModal(Fleet.class)) )).asEagerSingleton(); + + // See EDrtModeOptimizerQSimModule + bindModal(VrpLegFactory.class).toProvider(modalProvider(getter -> { + DvrpConfigGroup dvrpCfg = getter.get(DvrpConfigGroup.class); + MobsimTimer timer = getter.get(MobsimTimer.class); + + // Makes basic DrtActionCreator create legs with consumption tracker + return v -> EDrtActionCreator.createLeg(dvrpCfg.mobsimMode, v, timer); + })).in(Singleton.class); + + bindModal(ShiftEDrtActionCreator.class).toProvider(modalProvider(getter -> { + VrpAgentLogic.DynActionCreator delegate = drtCfg.getPrebookingParams().isPresent() + ? getter.getModal(PrebookingActionCreator.class) + : getter.getModal(DrtActionCreator.class); - bindModal(VrpAgentLogic.DynActionCreator.class).toProvider(modalProvider( - getter -> new ShiftEDrtActionCreator(getter.getModal(PassengerHandler.class), - getter.get(MobsimTimer.class), getter.get(DvrpConfigGroup.class))) - ).asEagerSingleton(); + return new ShiftEDrtActionCreator( + new ShiftDrtActionCreator(getter.getModal(PassengerHandler.class), delegate), + getter.get(MobsimTimer.class), getter.getModal(PassengerHandler.class)); + })).asEagerSingleton(); + + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(ShiftEDrtActionCreator.class)); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/EDrtShiftChangeoverTaskImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/EDrtShiftChangeoverTaskImpl.java index 9cc818dace7..007a9f2fe3f 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/EDrtShiftChangeoverTaskImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/EDrtShiftChangeoverTaskImpl.java @@ -82,4 +82,14 @@ public void addDropoffRequest(AcceptedDrtRequest request) { public void addPickupRequest(AcceptedDrtRequest request) { delegate.addPickupRequest(request); } + + @Override + public void removePickupRequest(Id requestId) { + delegate.removePickupRequest(requestId); + } + + @Override + public void removeDropoffRequest(Id requestId) { + delegate.removeDropoffRequest(requestId); + } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtActionCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtActionCreator.java index ba473f314ba..4604771bed9 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtActionCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtActionCreator.java @@ -2,26 +2,17 @@ import org.matsim.contrib.drt.extension.operations.eshifts.charging.ChargingBreakActivity; import org.matsim.contrib.drt.extension.operations.eshifts.charging.ChargingChangeoverActivity; +import org.matsim.contrib.drt.extension.operations.eshifts.charging.ChargingWaitForShiftActivity; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtActionCreator; import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.passenger.PassengerHandler; -import org.matsim.contrib.dvrp.run.DvrpConfigGroup; -import org.matsim.contrib.dvrp.schedule.DriveTask; import org.matsim.contrib.dvrp.schedule.Task; -import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; -import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTrackerImpl; -import org.matsim.contrib.dvrp.tracker.OnlineTrackerListener; -import org.matsim.contrib.dvrp.tracker.TaskTrackers; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic.DynActionCreator; -import org.matsim.contrib.dvrp.vrpagent.VrpLeg; import org.matsim.contrib.dynagent.DynAction; import org.matsim.contrib.dynagent.DynAgent; -import org.matsim.contrib.drt.extension.operations.eshifts.charging.ChargingWaitForShiftActivity; import org.matsim.contrib.evrp.EvDvrpVehicle; import org.matsim.contrib.evrp.tracker.OfflineETaskTracker; -import org.matsim.contrib.evrp.tracker.OnlineEDriveTaskTracker; -import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtActionCreator; import org.matsim.core.mobsim.framework.MobsimTimer; /** @@ -29,19 +20,12 @@ */ public class ShiftEDrtActionCreator implements DynActionCreator { - private final DynActionCreator drtActionCreator; + private final ShiftDrtActionCreator delegate; private final MobsimTimer timer; private final PassengerHandler passengerHandler; - public ShiftEDrtActionCreator(PassengerHandler passengerHandler, MobsimTimer timer, DvrpConfigGroup dvrpCfg) { - this.timer = timer; - this.passengerHandler = passengerHandler; - drtActionCreator = new ShiftDrtActionCreator(passengerHandler, new DrtActionCreator(passengerHandler, - v -> createLeg(dvrpCfg.mobsimMode, v, timer))); - } - - public ShiftEDrtActionCreator(DynActionCreator delegate, MobsimTimer timer, PassengerHandler passengerHandler) { - this.drtActionCreator = delegate; + public ShiftEDrtActionCreator(ShiftDrtActionCreator delegate, MobsimTimer timer, PassengerHandler passengerHandler) { + this.delegate = delegate; this.timer = timer; this.passengerHandler = passengerHandler; } @@ -63,21 +47,10 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now return new ChargingWaitForShiftActivity(((EDrtWaitForShiftStayTask) task).getChargingTask()); } - DynAction dynAction = drtActionCreator.createAction(dynAgent, vehicle, now); + DynAction dynAction = delegate.createAction(dynAgent, vehicle, now); if (task.getTaskTracker() == null) { task.initTaskTracker(new OfflineETaskTracker((EvDvrpVehicle) vehicle, timer)); } return dynAction; } - - private static VrpLeg createLeg(String mobsimMode, DvrpVehicle vehicle, MobsimTimer timer) { - DriveTask driveTask = (DriveTask) vehicle.getSchedule().getCurrentTask(); - VrpLeg leg = new VrpLeg(mobsimMode, driveTask.getPath()); - OnlineDriveTaskTracker onlineTracker = new OnlineDriveTaskTrackerImpl(vehicle, leg, - OnlineTrackerListener.NO_LISTENER, timer); - OnlineEDriveTaskTracker onlineETracker = new OnlineEDriveTaskTracker((EvDvrpVehicle) vehicle, timer, - onlineTracker); - TaskTrackers.initOnlineDriveTaskTracking(vehicle, leg, onlineETracker); - return leg; - } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtTaskFactoryImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtTaskFactoryImpl.java index e7ff875f574..87490980a22 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtTaskFactoryImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/schedule/ShiftEDrtTaskFactoryImpl.java @@ -1,25 +1,25 @@ package org.matsim.contrib.drt.extension.operations.eshifts.schedule; import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; +import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl; +import org.matsim.contrib.drt.extension.operations.operationFacilities.OperationFacility; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftBreakTask; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftChangeOverTask; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtTaskFactory; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.WaitForShiftStayTask; import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShift; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftBreak; import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.schedule.DrtStopTask; import org.matsim.contrib.drt.schedule.DrtTaskType; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; -import org.matsim.contrib.drt.extension.edrt.schedule.EDrtChargingTask; -import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.evrp.ChargingTask; import org.matsim.contrib.evrp.ChargingTaskImpl; import org.matsim.contrib.evrp.EvDvrpVehicle; -import org.matsim.contrib.drt.extension.operations.operationFacilities.OperationFacility; -import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftBreakTask; -import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftChangeOverTask; -import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtTaskFactory; -import org.matsim.contrib.drt.extension.operations.shifts.schedule.WaitForShiftStayTask; -import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftBreak; /** * @author nkuehnel / MOIA diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/BreakCorridorXY.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/BreakCorridorXY.java index 3a3b0762936..320b23977f9 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/BreakCorridorXY.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/BreakCorridorXY.java @@ -10,12 +10,12 @@ import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftsSpecification; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import jakarta.inject.Provider; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.util.HashMap; import java.util.Map; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java index 2db2c3d8fec..de7026de9e2 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java @@ -52,7 +52,7 @@ public ShiftAnalysisControlerListener(Config config, DrtConfigGroup drtConfigGro @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controler().isCreateGraphs(); + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); writeAndPlotShiftDurationComparison(shiftDurationXY.getShift2plannedVsActualDuration(), filename(event, "shiftDurationComparison", ".png"), diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftDurationXY.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftDurationXY.java index f370d7f6b4f..d2ff4dda184 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftDurationXY.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftDurationXY.java @@ -9,11 +9,11 @@ import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftsSpecification; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.util.HashMap; import java.util.Map; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogram.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogram.java index d4a519129fc..ce228469e50 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogram.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogram.java @@ -6,12 +6,12 @@ import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShift; import org.matsim.core.config.Config; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.io.UncheckedIOException; import org.matsim.core.utils.misc.Time; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.util.Set; /** diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramChart.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramChart.java index bd877f2843d..d39236c91ee 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramChart.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramChart.java @@ -9,11 +9,11 @@ import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; -import org.matsim.core.utils.io.UncheckedIOException; import java.awt.*; import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; /** * @author nkuehnel / MOIA diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java index 18525c18a7f..15aef81a055 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java @@ -35,7 +35,7 @@ public void notifyIterationStarts(final IterationStartsEvent event) { public void notifyIterationEnds(final IterationEndsEvent event) { this.shiftHistogram.write(matsimServices.getControlerIO().getIterationFilename(event.getIteration(), drtConfigGroup.getMode() + "_" + "shiftHistogram.txt")); this.printStats(); - boolean createGraphs = event.getServices().getConfig().controler().isCreateGraphs(); + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); if (createGraphs) { ShiftHistogramChart.writeGraphic(this.shiftHistogram, matsimServices.getControlerIO().getIterationFilename(event.getIteration(),drtConfigGroup.getMode() + "_" + "shiftHistogram.png")); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java index a7b7c8248ed..f75637f76d7 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java @@ -56,7 +56,7 @@ public ShiftEfficiencyAnalysisControlerListener(DrtConfigGroup drtConfigGroup, @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controler().isCreateGraphs(); + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); writeAndPlotShiftEfficiency( shiftEfficiencyTracker.getCurrentRecord().getRevenueByShift(), diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsReader.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsReader.java index 115fb61760f..f707a8c8108 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsReader.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsReader.java @@ -39,6 +39,7 @@ public class DrtShiftsReader extends MatsimXmlParser { private DrtShiftSpecificationImpl.Builder currentBuilder; public DrtShiftsReader( final DrtShiftsSpecification shiftsSpecification){ + super(ValidationType.NO_VALIDATION); log.info("Using " + this.getClass().getName()); this.shiftsSpecification = shiftsSpecification; this.setValidating(false); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsWriter.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsWriter.java index 92d3c43812d..0d5f56e5d75 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsWriter.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/DrtShiftsWriter.java @@ -11,9 +11,9 @@ import org.matsim.core.gbl.Gbl; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.io.MatsimXmlWriter; -import org.matsim.core.utils.io.UncheckedIOException; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.*; import java.util.stream.Collectors; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesReader.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesReader.java index da758a65b21..ccddc17ce06 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesReader.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesReader.java @@ -40,6 +40,7 @@ public class OperationFacilitiesReader extends MatsimXmlParser { private OperationFacilitySpecificationImpl.Builder currentBuilder; public OperationFacilitiesReader(final OperationFacilitiesSpecification operationFacilities) { + super(ValidationType.NO_VALIDATION); log.info("Using " + this.getClass().getName()); this.operationFacilities = operationFacilities; this.setValidating(false); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesWriter.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesWriter.java index 22c3a1712e5..91b247738d1 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesWriter.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/io/OperationFacilitiesWriter.java @@ -11,9 +11,9 @@ import org.matsim.core.gbl.Gbl; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.io.MatsimXmlWriter; -import org.matsim.core.utils.io.UncheckedIOException; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.*; import java.util.stream.Collectors; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/optimizer/ShiftRequestInsertionScheduler.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/optimizer/ShiftRequestInsertionScheduler.java index a14576a135f..a4feba591cf 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/optimizer/ShiftRequestInsertionScheduler.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/optimizer/ShiftRequestInsertionScheduler.java @@ -3,15 +3,14 @@ import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.DRIVE; import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; -import java.util.Collections; import java.util.List; import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.drt.extension.operations.shifts.fleet.ShiftDvrpVehicle; -import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskScheduler; import org.matsim.contrib.drt.extension.operations.shifts.schedule.OperationalStop; import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftChangeOverTask; import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftDrtTaskFactory; +import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskScheduler; import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.Waypoint; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; @@ -19,8 +18,8 @@ import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; import org.matsim.contrib.dvrp.path.VrpPaths; import org.matsim.contrib.dvrp.schedule.Schedule; @@ -41,17 +40,17 @@ public class ShiftRequestInsertionScheduler implements RequestInsertionScheduler private final TravelTime travelTime; private final ScheduleTimingUpdater scheduleTimingUpdater; private final ShiftDrtTaskFactory taskFactory; - private StopDurationEstimator stopDurationEstimator; + private StopTimeCalculator stopTimeCalculator; public ShiftRequestInsertionScheduler(MobsimTimer timer, TravelTime travelTime, ScheduleTimingUpdater scheduleTimingUpdater, ShiftDrtTaskFactory taskFactory, - StopDurationEstimator stopDurationEstimator) { + StopTimeCalculator stopTimeCalculator) { this.timer = timer; this.travelTime = travelTime; this.scheduleTimingUpdater = scheduleTimingUpdater; this.taskFactory = taskFactory; - this.stopDurationEstimator = stopDurationEstimator; + this.stopTimeCalculator = stopTimeCalculator; } @@ -120,9 +119,15 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour throw new RuntimeException("Cannot serve request!"); } } else { - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, - stopTask.getDropoffRequests().values(), stopTask.getPickupRequests().values()); - stopTask.setEndTime(Math.max(stopTask.getBeginTime() + stopDuration, request.getEarliestStartTime())); + /* + * 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(); + stopTask.setEndTime(stopTimeCalculator.updateEndTimeForPickup(vehicleEntry.vehicle, stopTask, + insertionTime, request.getRequest())); } /// ADDED @@ -220,9 +225,9 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour // insert pickup stop task double startTime = beforePickupTask.getEndTime(); int taskIdx = beforePickupTask.getTaskIdx() + 1; - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, Collections.emptySet(), Collections.singleton(request)); - DrtStopTask pickupStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, - Math.max(startTime + stopDuration, request.getEarliestStartTime()), request.getFromLink()); + double stopEndTime = stopTimeCalculator.initEndTimeForPickup(vehicleEntry.vehicle, startTime, request.getRequest()); + DrtStopTask pickupStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, + stopEndTime, request.getFromLink()); schedule.addTask(taskIdx, pickupStopTask); pickupStopTask.addPickupRequest(request); @@ -261,8 +266,17 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou if (request.getToLink() == stopTask.getLink()) { // no detour; no new stop task // add dropoff request to stop task, and extend the stop task (when incremental stop task duration is used) stopTask.addDropoffRequest(request); - double updatedStopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, stopTask.getDropoffRequests().values(), stopTask.getPickupRequests().values()); - stopTask.setEndTime(stopTask.getBeginTime() + updatedStopDuration); + + /* + * 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(); + stopTask.setEndTime(stopTimeCalculator.updateEndTimeForDropoff(vehicleEntry.vehicle, stopTask, + insertionTime, request.getRequest())); + return stopTask; } else { // add drive task to dropoff location @@ -302,9 +316,9 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou // insert dropoff stop task double startTime = driveToDropoffTask.getEndTime(); int taskIdx = driveToDropoffTask.getTaskIdx() + 1; - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, Collections.singleton(request), Collections.emptySet()); - DrtStopTask dropoffStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, - startTime + stopDuration, request.getToLink()); + double stopEndTime = stopTimeCalculator.initEndTimeForDropoff(vehicleEntry.vehicle, startTime, request.getRequest()); + DrtStopTask dropoffStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, + stopEndTime, request.getToLink()); schedule.addTask(taskIdx, dropoffStopTask); dropoffStopTask.addDropoffRequest(request); @@ -327,7 +341,7 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou final DrtStopTask nextStopTask = stops.get(dropoffIdx).task; Link toLink = nextStopTask.getLink(); // dropoff->j+1 - VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getToLink(), toLink, startTime + stopDuration, + VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getToLink(), toLink, dropoffStopTask.getEndTime(), detourData.detourFromDropoff, travelTime); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/run/ShiftDrtModeOptimizerQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/run/ShiftDrtModeOptimizerQSimModule.java index cb23ac6aa6e..eced5b90a1d 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/run/ShiftDrtModeOptimizerQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/run/ShiftDrtModeOptimizerQSimModule.java @@ -1,6 +1,5 @@ package org.matsim.contrib.drt.extension.operations.shifts.run; -import com.google.common.collect.ImmutableMap; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.extension.operations.DrtOperationsParams; @@ -24,22 +23,32 @@ import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftDrtScheduleInquiry; import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskScheduler; import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskSchedulerImpl; -import org.matsim.contrib.drt.extension.operations.shifts.shift.*; -import org.matsim.contrib.drt.optimizer.*; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DefaultShiftBreakImpl; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShift; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftBreakSpecification; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftImpl; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShifts; +import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftsSpecification; +import org.matsim.contrib.drt.optimizer.DefaultDrtOptimizer; +import org.matsim.contrib.drt.optimizer.DrtOptimizer; +import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryQueue; +import org.matsim.contrib.drt.optimizer.VehicleDataEntryFactoryImpl; +import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.depot.DepotFinder; import org.matsim.contrib.drt.optimizer.insertion.CostCalculationStrategy; import org.matsim.contrib.drt.optimizer.insertion.DefaultInsertionCostCalculator; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.optimizer.insertion.UnplannedRequestInserter; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy; +import org.matsim.contrib.drt.prebooking.PrebookingActionCreator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtStayTaskEndTimeCalculator; import org.matsim.contrib.drt.schedule.DrtTaskFactory; import org.matsim.contrib.drt.schedule.DrtTaskFactoryImpl; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; import org.matsim.contrib.drt.scheduler.DrtScheduleInquiry; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; import org.matsim.contrib.dvrp.fleet.Fleet; @@ -50,13 +59,18 @@ import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.contrib.dvrp.tracker.OnlineTrackerListener; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.modal.ModalProviders; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; +import com.google.common.collect.ImmutableMap; +import com.google.inject.Singleton; + /** * @author nkuehnel, fzwick / MOIA */ @@ -98,7 +112,8 @@ public DrtShifts get() { }).asEagerSingleton(); addModalComponent(DrtOptimizer.class, modalProvider( - getter -> new ShiftDrtOptimizer( + getter -> { + return new ShiftDrtOptimizer( new DefaultDrtOptimizer(drtCfg, getter.getModal(Fleet.class), getter.get(MobsimTimer.class), getter.getModal(DepotFinder.class), getter.getModal(RebalancingStrategy.class), getter.getModal(DrtScheduleInquiry.class), getter.getModal(ScheduleTimingUpdater.class), @@ -106,7 +121,8 @@ public DrtShifts get() { getter.getModal(DrtRequestInsertionRetryQueue.class) ), getter.getModal(DrtShiftDispatcher.class), - getter.getModal(ScheduleTimingUpdater.class)))); + getter.getModal(ScheduleTimingUpdater.class)); + })); bindModal(DrtShiftDispatcher.class).toProvider(modalProvider( getter -> new DrtShiftDispatcherImpl(getter.getModal(DrtShifts.class), getter.getModal(Fleet.class), @@ -119,7 +135,7 @@ shiftsParams, new DefaultShiftStartLogic(), new DefaultAssignShiftToVehicleLogic getter -> new ShiftInsertionCostCalculator(getter.get(MobsimTimer.class), new DefaultInsertionCostCalculator(getter.getModal(CostCalculationStrategy.class))))); - bindModal(VehicleEntry.EntryFactory.class).toInstance(new ShiftVehicleDataEntryFactory(new VehicleDataEntryFactoryImpl(drtCfg))); + bindModal(VehicleEntry.EntryFactory.class).toInstance(new ShiftVehicleDataEntryFactory(new VehicleDataEntryFactoryImpl())); final ShiftDrtTaskFactoryImpl taskFactory = new ShiftDrtTaskFactoryImpl(new DrtTaskFactoryImpl()); bindModal(DrtTaskFactory.class).toInstance(taskFactory); @@ -138,21 +154,34 @@ shiftsParams, new DefaultShiftStartLogic(), new DefaultAssignShiftToVehicleLogic getter -> new ShiftRequestInsertionScheduler( getter.get(MobsimTimer.class), getter.getModal(TravelTime.class), getter.getModal(ScheduleTimingUpdater.class), getter.getModal(ShiftDrtTaskFactory.class), - getter.getModal(StopDurationEstimator.class))) + getter.getModal(StopTimeCalculator.class))) ).asEagerSingleton(); bindModal(ScheduleTimingUpdater.class).toProvider(modalProvider( getter -> new ScheduleTimingUpdater(getter.get(MobsimTimer.class), new ShiftDrtStayTaskEndTimeCalculator(shiftsParams, - new DrtStayTaskEndTimeCalculator(getter.getModal(StopDurationEstimator.class))))) + new DrtStayTaskEndTimeCalculator(getter.getModal(StopTimeCalculator.class))))) ).asEagerSingleton(); - - bindModal(VrpAgentLogic.DynActionCreator.class).toProvider(modalProvider( - (getter) -> new ShiftDrtActionCreator(getter.getModal(PassengerHandler.class), - new DrtActionCreator(getter.getModal(PassengerHandler.class), getter.get(MobsimTimer.class), - getter.get(DvrpConfigGroup.class)))) - ).asEagerSingleton(); - + + // see DrtModeOptimizerQSimModule + bindModal(VrpLegFactory.class).toProvider(modalProvider(getter -> { + DvrpConfigGroup dvrpCfg = getter.get(DvrpConfigGroup.class); + MobsimTimer timer = getter.get(MobsimTimer.class); + + return v -> VrpLegFactory.createWithOnlineTracker(dvrpCfg.mobsimMode, v, OnlineTrackerListener.NO_LISTENER, + timer); + })).in(Singleton.class); + + bindModal(ShiftDrtActionCreator.class).toProvider(modalProvider((getter) -> { + VrpAgentLogic.DynActionCreator delegate = drtCfg.getPrebookingParams().isPresent() + ? getter.getModal(PrebookingActionCreator.class) + : getter.getModal(DrtActionCreator.class); + + // adds shift tasks + return new ShiftDrtActionCreator(getter.getModal(PassengerHandler.class), delegate); + })).asEagerSingleton(); + + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(ShiftDrtActionCreator.class)); bindModal(Fleet.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) { @Override diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftBreakTaskImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftBreakTaskImpl.java index e295a531ef5..b718954da1b 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftBreakTaskImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftBreakTaskImpl.java @@ -63,4 +63,14 @@ public void addDropoffRequest(AcceptedDrtRequest request) { public void addPickupRequest(AcceptedDrtRequest request) { delegate.addPickupRequest(request); } + + @Override + public void removePickupRequest(Id requestId) { + delegate.removePickupRequest(requestId); + } + + @Override + public void removeDropoffRequest(Id requestId) { + delegate.removeDropoffRequest(requestId); + } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftChangeoverTaskImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftChangeoverTaskImpl.java index 76818598dd6..0af9ed0b7bf 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftChangeoverTaskImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/schedule/ShiftChangeoverTaskImpl.java @@ -1,5 +1,9 @@ package org.matsim.contrib.drt.extension.operations.shifts.schedule; +import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STOP; + +import java.util.Map; + import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.drt.extension.operations.operationFacilities.OperationFacility; @@ -11,10 +15,6 @@ import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.schedule.DefaultStayTask; -import java.util.Map; - -import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STOP; - /** * A task representing stopping and waiting for a new shift. * @author nkuehnel / MOIA @@ -64,5 +64,15 @@ public void addDropoffRequest(AcceptedDrtRequest request) { public void addPickupRequest(AcceptedDrtRequest request) { delegate.addPickupRequest(request); } + + @Override + public void removePickupRequest(Id requestId) { + delegate.removePickupRequest(requestId); + } + + @Override + public void removeDropoffRequest(Id requestId) { + delegate.removeDropoffRequest(requestId); + } } 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-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/scheduler/ShiftTaskSchedulerImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/scheduler/ShiftTaskSchedulerImpl.java index 8b076be9b0d..f5101d68987 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/scheduler/ShiftTaskSchedulerImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/scheduler/ShiftTaskSchedulerImpl.java @@ -4,7 +4,6 @@ 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.contrib.drt.extension.operations.DrtOperationsParams; import org.matsim.contrib.drt.extension.operations.shifts.config.ShiftsParams; import org.matsim.contrib.drt.extension.operations.shifts.fleet.ShiftDvrpVehicle; import org.matsim.contrib.drt.extension.operations.shifts.schedule.*; @@ -26,7 +25,7 @@ import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; import org.matsim.contrib.dvrp.util.LinkTimePair; import org.matsim.core.mobsim.framework.MobsimTimer; -import org.matsim.core.router.FastAStarEuclideanFactory; +import org.matsim.core.router.speedy.SpeedyALTFactory; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.TravelDisutility; import org.matsim.core.router.util.TravelTime; @@ -60,7 +59,7 @@ public ShiftTaskSchedulerImpl(Network network, TravelTime travelTime, TravelDisu this.taskFactory = taskFactory; this.network = network; this.drtShiftParams = drtShiftParams; - this.router = new FastAStarEuclideanFactory().createPathCalculator(network, travelDisutility, travelTime); + this.router = new SpeedyALTFactory().createPathCalculator(network, travelDisutility, travelTime); ShiftSchedules.initSchedules(operationFacilities, fleet, taskFactory); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtModeOptimizerQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtModeOptimizerQSimModule.java index c05e23b42cc..d5b560d31fc 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtModeOptimizerQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtModeOptimizerQSimModule.java @@ -26,7 +26,7 @@ import org.matsim.contrib.drt.schedule.DrtStayTaskEndTimeCalculator; import org.matsim.contrib.drt.schedule.DrtTaskFactory; import org.matsim.contrib.drt.schedule.DrtTaskFactoryImpl; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; @@ -34,12 +34,17 @@ import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.contrib.dvrp.tracker.OnlineTrackerListener; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; +import com.google.common.base.Preconditions; +import com.google.inject.Singleton; + /** * @author Michal Maciejewski (michalm) */ @@ -61,15 +66,22 @@ protected void configureQSim() { getter.getModal(ScheduleTimingUpdater.class)))); bindModal(DrtTaskFactory.class).toInstance(new DrtTaskFactoryImpl()); + + bindModal(VrpLegFactory.class).toProvider(modalProvider(getter -> { + DvrpConfigGroup dvrpCfg = getter.get(DvrpConfigGroup.class); + MobsimTimer timer = getter.get(MobsimTimer.class); + + return v -> VrpLegFactory.createWithOnlineTracker(dvrpCfg.mobsimMode, v, OnlineTrackerListener.NO_LISTENER, + timer); + })).in(Singleton.class); - bindModal(VrpAgentLogic.DynActionCreator.class).toProvider(modalProvider( - getter -> new DrtActionCreator(getter.getModal(PassengerHandler.class), getter.get(MobsimTimer.class), - getter.get(DvrpConfigGroup.class)))).asEagerSingleton(); + Preconditions.checkState(drtCfg.getPrebookingParams().isEmpty(), "cannot use preplanned schedules with prebooking"); + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(DrtActionCreator.class)); bindModal(VrpOptimizer.class).to(modalKey(DrtOptimizer.class)); bindModal(ScheduleTimingUpdater.class).toProvider(modalProvider( getter -> new ScheduleTimingUpdater(getter.get(MobsimTimer.class), - new DrtStayTaskEndTimeCalculator(getter.getModal(StopDurationEstimator.class))))).asEagerSingleton(); + new DrtStayTaskEndTimeCalculator(getter.getModal(StopTimeCalculator.class))))).asEagerSingleton(); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java index 0103e5bb66a..5dd5f1c8bf7 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java @@ -22,9 +22,7 @@ import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -118,7 +116,7 @@ public void requestSubmitted(Request request) { "Pre-planned request (%s) not assigned to any vehicle and not marked as unassigned.", preplannedRequest); eventsManager.processEvent(new PassengerRequestRejectedEvent(timer.getTimeOfDay(), mode, request.getId(), - drtRequest.getPassengerId(), "Marked as unassigned")); + drtRequest.getPassengerIds(), "Marked as unassigned")); return; } @@ -130,7 +128,7 @@ public void requestSubmitted(Request request) { //TODO in the current implementation we do not know the scheduled pickup and dropoff times eventsManager.processEvent( new PassengerRequestScheduledEvent(timer.getTimeOfDay(), drtRequest.getMode(), drtRequest.getId(), - drtRequest.getPassengerId(), vehicleId, Double.NaN, Double.NaN)); + drtRequest.getPassengerIds(), vehicleId, Double.NaN, Double.NaN)); } @Override @@ -203,7 +201,7 @@ public record PreplannedSchedules(Map> pre Map unassignedRequests) { } - public record PreplannedRequestKey(Id passengerId, Id fromLinkId, Id toLinkId) { + public record PreplannedRequestKey(Set> passengerIds, Id fromLinkId, Id toLinkId) { } // also input to the external optimiser @@ -212,7 +210,7 @@ public record PreplannedRequest(PreplannedRequestKey key, double earliestStartTi } static PreplannedRequest createFromRequest(DrtRequest request) { - return new PreplannedRequest(new PreplannedRequestKey(request.getPassengerId(), request.getFromLink().getId(), + return new PreplannedRequest(new PreplannedRequestKey(Set.copyOf(request.getPassengerIds()), request.getFromLink().getId(), request.getToLink().getId()), request.getEarliestStartTime(), request.getLatestStartTime(), request.getLatestArrivalTime()); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtControlerCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtControlerCreator.java index fbf6325b77a..bbf5fa4c5c2 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtControlerCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtControlerCreator.java @@ -62,7 +62,7 @@ public final class PreplannedDrtControlerCreator { */ public static Controler createControler(Config config, boolean otfvis) { MultiModeDrtConfigGroup multiModeDrtConfig = MultiModeDrtConfigGroup.get(config); - DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.planCalcScore(), config.plansCalcRoute()); + DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing()); Scenario scenario = createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtModeModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtModeModule.java index d3cb7e1582f..c346b2501c9 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtModeModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/PreplannedDrtModeModule.java @@ -23,7 +23,8 @@ import org.matsim.contrib.drt.fare.DrtFareHandler; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.run.DrtModeRoutingModule; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.FleetModule; import org.matsim.contrib.dvrp.router.DvrpModeRoutingNetworkModule; import org.matsim.contrib.dvrp.router.TimeAsTravelDisutility; @@ -56,8 +57,7 @@ public void install() { install(new DvrpModeRoutingNetworkModule(getMode(), drtCfg.useModeFilteredSubnetwork)); bindModal(TravelTime.class).to(Key.get(TravelTime.class, Names.named(DvrpTravelTimeModule.DVRP_ESTIMATED))); bindModal(TravelDisutilityFactory.class).toInstance(TimeAsTravelDisutility::new); - bindModal(StopDurationEstimator.class).toInstance( - (vehicle, dropoffRequests, pickupRequests) -> drtCfg.stopDuration); + bindModal(StopTimeCalculator.class).toInstance(new DefaultStopTimeCalculator(drtCfg.stopDuration)); install(new FleetModule(getMode(), drtCfg.vehiclesFile == null ? null : diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/RunPreplannedDrtExample.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/RunPreplannedDrtExample.java index 04f7852c45a..e0463eb46f0 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/RunPreplannedDrtExample.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/run/RunPreplannedDrtExample.java @@ -43,7 +43,7 @@ public static void run(URL configUrl, boolean otfvis, int lastIteration, Map preplannedSchedulesByMode) { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); Controler controler = PreplannedDrtControlerCreator.createControler(config, otfvis); diff --git a/contribs/drt-extensions/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider b/contribs/drt-extensions/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider new file mode 100644 index 00000000000..16dd09fd165 --- /dev/null +++ b/contribs/drt-extensions/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider @@ -0,0 +1 @@ +org.matsim.contrib.drt.extension.dashboards.DrtDashboardProvider \ No newline at end of file diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java new file mode 100644 index 00000000000..7131c33a368 --- /dev/null +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java @@ -0,0 +1,152 @@ +package org.matsim.contrib.drt.extension; + +import com.google.common.collect.Sets; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Link; +import org.matsim.application.MATSimApplication; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.routing.DrtRouteFactory; +import org.matsim.contrib.drt.run.DrtConfigs; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtModule; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModule; +import org.matsim.contrib.dvrp.run.DvrpQSimComponents; +import org.matsim.contrib.dvrp.trafficmonitoring.DvrpModeLimitedMaxSpeedTravelTimeModule; +import org.matsim.contrib.vsp.scenario.SnzActivities; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.examples.ExamplesUtils; +import org.matsim.simwrapper.SimWrapperModule; +import org.matsim.modechoice.InformedModeChoiceConfigGroup; +import org.matsim.testcases.MatsimTestUtils; +import org.matsim.vehicles.VehicleType; + +import javax.annotation.Nullable; +import java.net.URL; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * A test scenario based on kelheim example. + */ +public class DrtTestScenario extends MATSimApplication { + + + private final Consumer prepareControler; + private final Consumer prepareConfig; + + public static void main(String[] args) { + run(DrtTestScenario.class, args); + } + + public DrtTestScenario() { + this(controler -> {}, config -> {}); + } + + public DrtTestScenario(Consumer prepareControler, Consumer prepareConfig) { + this.prepareControler = prepareControler; + this.prepareConfig = prepareConfig; + } + + public DrtTestScenario(@Nullable Config config) { + super(config); + this.prepareControler = controler -> {}; + this.prepareConfig = c -> {}; + } + + public static Config loadConfig(MatsimTestUtils utils) { + + URL context = ExamplesUtils.getTestScenarioURL("kelheim"); + Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(context, "config-with-drt.xml")); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + return config; + } + + @Override + protected Config prepareConfig(Config config) { + + SnzActivities.addScoringParams(config); + + config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("car interaction").setTypicalDuration(60)); + config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("other").setTypicalDuration(600 * 3)); + + config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("freight_start").setTypicalDuration(60 * 15)); + config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("freight_end").setTypicalDuration(60 * 15)); + + InformedModeChoiceConfigGroup imc = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class); + imc.setModes(Set.of("drt", "av", "car", "pt", "bike", "walk")); + + MultiModeDrtConfigGroup multiModeDrtConfig = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); + + DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing()); + + config.routing().setAccessEgressType(RoutingConfigGroup.AccessEgressType.accessEgressModeToLink); + + prepareConfig.accept(config); + + return config; + } + + @Override + protected void prepareScenario(Scenario scenario) { + + // Freight needs to be added manually + for (Link link : scenario.getNetwork().getLinks().values()) { + Set modes = link.getAllowedModes(); + + // allow freight traffic together with cars + if (modes.contains("car")) { + HashSet newModes = Sets.newHashSet(modes); + newModes.add("freight"); + + link.setAllowedModes(newModes); + } + } + + scenario.getPopulation() + .getFactory() + .getRouteFactories() + .setRouteFactory(DrtRoute.class, new DrtRouteFactory()); + } + + @Override + protected void prepareControler(Controler controler) { + + prepareControler.accept(controler); + + // DRT specific + + Config config = controler.getConfig(); + + MultiModeDrtConfigGroup multiModeDrtConfig = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + controler.addOverridingModule(new DvrpModule()); + controler.addOverridingModule(new MultiModeDrtModule()); + controler.configureQSimComponents(DvrpQSimComponents.activateAllModes(multiModeDrtConfig)); + + // Add speed limit to av vehicle + double maxSpeed = controler.getScenario() + .getVehicles() + .getVehicleTypes() + .get(Id.create("autonomous_vehicle", VehicleType.class)) + .getMaximumVelocity(); + + controler.addOverridingModule( + new DvrpModeLimitedMaxSpeedTravelTimeModule("av", config.qsim().getTimeStepSize(), + maxSpeed)); + + controler.addOverridingModule(new SimWrapperModule()); + + } +} diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java index de5534ab0e7..9ac24985fe4 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/companions/RunDrtWithCompanionExampleIT.java @@ -24,9 +24,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import org.assertj.core.api.Assertions; @@ -64,8 +62,8 @@ public void testRunDrtWithCompanions() { DrtWithExtensionsConfigGroup drtWithExtensionsConfigGroup = (DrtWithExtensionsConfigGroup) multiModeDrtConfigGroup.getModalElements().iterator().next(); drtWithExtensionsConfigGroup.addParameterSet(new DrtCompanionParams()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); Controler controler = DrtCompanionControlerCreator.createControler(config); controler.run(); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/dashboards/DashboardTests.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/dashboards/DashboardTests.java new file mode 100644 index 00000000000..c26a623fec8 --- /dev/null +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/dashboards/DashboardTests.java @@ -0,0 +1,87 @@ +package org.matsim.contrib.drt.extension.dashboards; + +import org.assertj.core.api.Assertions; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.application.MATSimApplication; +import org.matsim.contrib.drt.extension.DrtTestScenario; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.Controler; +import org.matsim.simwrapper.SimWrapperConfigGroup; +import org.matsim.testcases.MatsimTestUtils; + +import java.nio.file.Path; + +public class DashboardTests { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + private void run() { + + Config config = DrtTestScenario.loadConfig(utils); + config.controller().setLastIteration(4); + config.controller().setWritePlansInterval(4); + config.controller().setWriteEventsInterval(4); + + SimWrapperConfigGroup group = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class); + group.defaultParams().sampleSize = 0.001; + group.defaultParams().mapCenter = "11.891000, 48.911000"; + + //we have 2 operators ('av' + 'drt'), configure one of them to be areaBased (the other remains stopBased) + MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + for (DrtConfigGroup drtCfg : multiModeDrtConfigGroup.getModalElements()) { + if (drtCfg.getMode().equals("av")){ + drtCfg.operationalScheme = DrtConfigGroup.OperationalScheme.serviceAreaBased; + drtCfg.drtServiceAreaShapeFile = "drt-zones/drt-zonal-system.shp"; + } + } + + Controler controler = MATSimApplication.prepare(new DrtTestScenario(config), config); + controler.run(); + } + + @Test + public void drtDefaults() { + run(); + + // TODO: add test headers!? + + Path drtOutputPath = Path.of(utils.getOutputDirectory(), "analysis", "drt"); + { + //test general output files + Assertions.assertThat(drtOutputPath) + .isDirectoryContaining("glob:**supply_kpi.csv"); + Assertions.assertThat(drtOutputPath) + .isDirectoryContaining("glob:**demand_kpi.csv"); + } + { + //test output files specific for stopBased services + Assertions.assertThat(drtOutputPath) + .isDirectoryContaining("glob:**stops.shp"); + Assertions.assertThat(drtOutputPath) + .isDirectoryContaining("glob:**trips_per_stop.csv"); + Assertions.assertThat(drtOutputPath) + .isDirectoryContaining("glob:**trips_per_stop.csv"); + } + + Path avOutputPath = Path.of(utils.getOutputDirectory(), "analysis", "drt-av"); + { + //test general output files + Assertions.assertThat(avOutputPath) + .isDirectoryContaining("glob:**supply_kpi.csv"); + Assertions.assertThat(avOutputPath) + .isDirectoryContaining("glob:**demand_kpi.csv"); + } + + { + //test output files specific for stopBased services + Assertions.assertThat(avOutputPath) + .isDirectoryContaining("glob:**serviceArea.shp"); + } + } + +} diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java index 49940884fc4..3a3471007fd 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/edrt/run/RunEDrtScenarioIT.java @@ -18,11 +18,28 @@ package org.matsim.contrib.drt.extension.edrt.run; +import static org.junit.Assert.assertEquals; + import java.net.URL; import org.junit.Test; +import org.matsim.contrib.drt.prebooking.PrebookingParams; +import org.matsim.contrib.drt.prebooking.logic.ProbabilityBasedPrebookingLogic; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.ev.EvConfigGroup; +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.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; +import org.matsim.vis.otfvis.OTFVisConfigGroup; /** * @author michalm @@ -33,4 +50,64 @@ public void test() { URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), "mielec_edrt_config.xml"); RunEDrtScenario.run(configUrl, false); } + + @Test + public void testWithPrebooking() { + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), "mielec_edrt_config.xml"); + + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup(), new EvConfigGroup()); + + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(config); + drtConfig.addParameterSet(new PrebookingParams()); + + Controler controller = RunEDrtScenario.createControler(config, false); + ProbabilityBasedPrebookingLogic.install(controller, drtConfig, 0.5, 4.0 * 3600.0); + + PrebookingTracker tracker = new PrebookingTracker(); + tracker.install(controller); + + controller.run(); + + assertEquals(74, tracker.immediateScheduled); + assertEquals(198, tracker.prebookedScheduled); + assertEquals(116, tracker.immediateRejected); + assertEquals(7, tracker.prebookedRejected); + } + + static private class PrebookingTracker implements PassengerRequestRejectedEventHandler, PassengerRequestScheduledEventHandler { + int immediateScheduled = 0; + int prebookedScheduled = 0; + int immediateRejected = 0; + int prebookedRejected = 0; + + @Override + public void handleEvent(PassengerRequestScheduledEvent event) { + if (event.getRequestId().toString().contains("prebooked")) { + prebookedScheduled++; + } else { + immediateScheduled++; + } + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (event.getRequestId().toString().contains("prebooked")) { + prebookedRejected++; + } else { + immediateRejected++; + } + } + + void install(Controler controller) { + PrebookingTracker thisTracker = this; + + controller.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(thisTracker); + } + }); + } + } } diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java new file mode 100644 index 00000000000..8d61ef68123 --- /dev/null +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java @@ -0,0 +1,90 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.application.MATSimApplication; +import org.matsim.contrib.drt.extension.DrtTestScenario; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorModule; +import org.matsim.contrib.drt.extension.estimator.run.MultiModeDrtEstimatorConfigGroup; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.controler.Controler; +import org.matsim.modechoice.InformedModeChoiceModule; +import org.matsim.modechoice.ModeOptions; +import org.matsim.modechoice.estimators.DefaultLegScoreEstimator; +import org.matsim.modechoice.estimators.FixedCostsEstimator; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MultiModalDrtLegEstimatorTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + private Controler controler; + + @Before + public void setUp() throws Exception { + + Config config = DrtTestScenario.loadConfig(utils); + + config.controller().setLastIteration(3); + + controler = MATSimApplication.prepare(new DrtTestScenario(MultiModalDrtLegEstimatorTest::prepare, MultiModalDrtLegEstimatorTest::prepare), config); + } + + private static void prepare(Controler controler) { + InformedModeChoiceModule.Builder builder = InformedModeChoiceModule.newBuilder() + .withFixedCosts(FixedCostsEstimator.DailyConstant.class, "car") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.AlwaysAvailable.class, "bike", "walk", "pt") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.ConsiderYesAndNo.class, "car") + .withLegEstimator(MultiModalDrtLegEstimator.class, ModeOptions.AlwaysAvailable.class, "drt", "av"); + + controler.addOverridingModule(builder.build()); + controler.addOverridingModule(new DrtEstimatorModule()); + } + + private static void prepare(Config config) { + + MultiModeDrtEstimatorConfigGroup estimators = ConfigUtils.addOrGetModule(config, MultiModeDrtEstimatorConfigGroup.class); + + estimators.addParameterSet(new DrtEstimatorConfigGroup("drt")); + estimators.addParameterSet(new DrtEstimatorConfigGroup("av")); + + // Set subtour mode selection as strategy + List strategies = config.replanning().getStrategySettings().stream() + .filter(s -> !s.getStrategyName().toLowerCase().contains("mode") + ).collect(Collectors.toList()); + + strategies.add(new ReplanningConfigGroup.StrategySettings() + .setStrategyName(InformedModeChoiceModule.SELECT_SUBTOUR_MODE_STRATEGY) + .setSubpopulation("person") + .setWeight(0.2)); + + config.replanning().clearStrategySettings(); + strategies.forEach(s -> config.replanning().addStrategySettings(s)); + + } + + @Test + public void run() { + + String out = utils.getOutputDirectory(); + + controler.run(); + + assertThat(new File(out, "kelheim-mini-drt.drt_estimates_drt.csv")) + .exists() + .isNotEmpty(); + + + } +} diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/fiss/RunFissDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/fiss/RunFissDrtScenarioIT.java index c6ad3f7dff8..c5ea83cbce9 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/fiss/RunFissDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/fiss/RunFissDrtScenarioIT.java @@ -20,9 +20,9 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -91,10 +91,10 @@ public void test() { modes.add("drt"); config.travelTimeCalculator().setAnalyzedModes(modes); - PlanCalcScoreConfigGroup.ModeParams scoreParams = new PlanCalcScoreConfigGroup.ModeParams("drt"); - config.planCalcScore().addModeParams(scoreParams); - PlanCalcScoreConfigGroup.ModeParams scoreParams2 = new PlanCalcScoreConfigGroup.ModeParams("walk"); - config.planCalcScore().addModeParams(scoreParams2); + ScoringConfigGroup.ModeParams scoreParams = new ScoringConfigGroup.ModeParams("drt"); + config.scoring().addModeParams(scoreParams); + ScoringConfigGroup.ModeParams scoreParams2 = new ScoringConfigGroup.ModeParams("walk"); + config.scoring().addModeParams(scoreParams2); config.plans().setInputFile(plansFile); config.network().setInputFile(networkFile); @@ -102,33 +102,33 @@ public void test() { config.qsim().setSimStarttimeInterpretation(QSimConfigGroup.StarttimeInterpretation.onlyUseStarttime); config.qsim().setSimEndtimeInterpretation(QSimConfigGroup.EndtimeInterpretation.minOfEndtimeAndMobsimFinished); - final PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + final ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(8 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams other = new PlanCalcScoreConfigGroup.ActivityParams("other"); + final ScoringConfigGroup.ActivityParams other = new ScoringConfigGroup.ActivityParams("other"); other.setTypicalDuration(4 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams education = new PlanCalcScoreConfigGroup.ActivityParams("education"); + final ScoringConfigGroup.ActivityParams education = new ScoringConfigGroup.ActivityParams("education"); education.setTypicalDuration(6 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams shopping = new PlanCalcScoreConfigGroup.ActivityParams("shopping"); + final ScoringConfigGroup.ActivityParams shopping = new ScoringConfigGroup.ActivityParams("shopping"); shopping.setTypicalDuration(2 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + final ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(2 * 3600); - config.planCalcScore().addActivityParams(home); - config.planCalcScore().addActivityParams(other); - config.planCalcScore().addActivityParams(education); - config.planCalcScore().addActivityParams(shopping); - config.planCalcScore().addActivityParams(work); + config.scoring().addActivityParams(home); + config.scoring().addActivityParams(other); + config.scoring().addActivityParams(education); + config.scoring().addActivityParams(shopping); + config.scoring().addActivityParams(work); - final StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + final ReplanningConfigGroup.StrategySettings stratSets = new ReplanningConfigGroup.StrategySettings(); stratSets.setWeight(1); stratSets.setStrategyName("ChangeExpBeta"); - config.strategy().addStrategySettings(stratSets); + config.replanning().addStrategySettings(stratSets); - config.controler().setLastIteration(2); - config.controler().setWriteEventsInterval(1); + config.controller().setLastIteration(2); + config.controller().setWriteEventsInterval(1); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory("test/output/holzkirchen_shifts"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory("test/output/holzkirchen_shifts"); DrtOperationsParams operationsParams = (DrtOperationsParams) drtWithShiftsConfigGroup.createParameterSet(DrtOperationsParams.SET_NAME); ShiftsParams shiftsParams = (ShiftsParams) operationsParams.createParameterSet(ShiftsParams.SET_NAME); @@ -173,7 +173,7 @@ public void install() { } run.run(); - Assert.assertEquals(23961, linkCounter.getLinkLeaveCount()); + Assert.assertEquals(23817, linkCounter.getLinkLeaveCount()); } static class LinkCounter implements LinkLeaveEventHandler { diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/eshifts/run/RunEShiftDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/eshifts/run/RunEShiftDrtScenarioIT.java index 1f57b4d54c7..4abe57ba415 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/eshifts/run/RunEShiftDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/eshifts/run/RunEShiftDrtScenarioIT.java @@ -20,9 +20,9 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -94,10 +94,10 @@ public void test() { modes.add("drt"); config.travelTimeCalculator().setAnalyzedModes(modes); - PlanCalcScoreConfigGroup.ModeParams scoreParams = new PlanCalcScoreConfigGroup.ModeParams("drt"); - config.planCalcScore().addModeParams(scoreParams); - PlanCalcScoreConfigGroup.ModeParams scoreParams2 = new PlanCalcScoreConfigGroup.ModeParams("walk"); - config.planCalcScore().addModeParams(scoreParams2); + ScoringConfigGroup.ModeParams scoreParams = new ScoringConfigGroup.ModeParams("drt"); + config.scoring().addModeParams(scoreParams); + ScoringConfigGroup.ModeParams scoreParams2 = new ScoringConfigGroup.ModeParams("walk"); + config.scoring().addModeParams(scoreParams2); config.plans().setInputFile(plansFile); config.network().setInputFile(networkFile); @@ -106,33 +106,33 @@ public void test() { config.qsim().setSimEndtimeInterpretation(QSimConfigGroup.EndtimeInterpretation.minOfEndtimeAndMobsimFinished); - final PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + final ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(8 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams other = new PlanCalcScoreConfigGroup.ActivityParams("other"); + final ScoringConfigGroup.ActivityParams other = new ScoringConfigGroup.ActivityParams("other"); other.setTypicalDuration(4 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams education = new PlanCalcScoreConfigGroup.ActivityParams("education"); + final ScoringConfigGroup.ActivityParams education = new ScoringConfigGroup.ActivityParams("education"); education.setTypicalDuration(6 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams shopping = new PlanCalcScoreConfigGroup.ActivityParams("shopping"); + final ScoringConfigGroup.ActivityParams shopping = new ScoringConfigGroup.ActivityParams("shopping"); shopping.setTypicalDuration(2 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + final ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(2 * 3600); - config.planCalcScore().addActivityParams(home); - config.planCalcScore().addActivityParams(other); - config.planCalcScore().addActivityParams(education); - config.planCalcScore().addActivityParams(shopping); - config.planCalcScore().addActivityParams(work); + config.scoring().addActivityParams(home); + config.scoring().addActivityParams(other); + config.scoring().addActivityParams(education); + config.scoring().addActivityParams(shopping); + config.scoring().addActivityParams(work); - final StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + final ReplanningConfigGroup.StrategySettings stratSets = new ReplanningConfigGroup.StrategySettings(); stratSets.setWeight(1); stratSets.setStrategyName("ChangeExpBeta"); - config.strategy().addStrategySettings(stratSets); + config.replanning().addStrategySettings(stratSets); - config.controler().setLastIteration(1); - config.controler().setWriteEventsInterval(1); + config.controller().setLastIteration(1); + config.controller().setWriteEventsInterval(1); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory("test/output/holzkirchen_eshifts"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory("test/output/holzkirchen_eshifts"); DrtOperationsParams operationsParams = (DrtOperationsParams) drtWithShiftsConfigGroup.createParameterSet(DrtOperationsParams.SET_NAME); ShiftsParams shiftsParams = (ShiftsParams) operationsParams.createParameterSet(ShiftsParams.SET_NAME); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunMultiHubShiftDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunMultiHubShiftDrtScenarioIT.java index 8b0e6b3a179..91c2dd16be9 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunMultiHubShiftDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunMultiHubShiftDrtScenarioIT.java @@ -17,9 +17,9 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.examples.ExamplesUtils; @@ -83,10 +83,10 @@ public void test() { modes.add("drt"); config.travelTimeCalculator().setAnalyzedModes(modes); - PlanCalcScoreConfigGroup.ModeParams scoreParams = new PlanCalcScoreConfigGroup.ModeParams("drt"); - config.planCalcScore().addModeParams(scoreParams); - PlanCalcScoreConfigGroup.ModeParams scoreParams2 = new PlanCalcScoreConfigGroup.ModeParams("walk"); - config.planCalcScore().addModeParams(scoreParams2); + ScoringConfigGroup.ModeParams scoreParams = new ScoringConfigGroup.ModeParams("drt"); + config.scoring().addModeParams(scoreParams); + ScoringConfigGroup.ModeParams scoreParams2 = new ScoringConfigGroup.ModeParams("walk"); + config.scoring().addModeParams(scoreParams2); config.plans().setInputFile(plansFile); config.network().setInputFile(networkFile); @@ -95,33 +95,33 @@ public void test() { config.qsim().setSimEndtimeInterpretation(QSimConfigGroup.EndtimeInterpretation.minOfEndtimeAndMobsimFinished); - final PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + final ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(8 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams other = new PlanCalcScoreConfigGroup.ActivityParams("other"); + final ScoringConfigGroup.ActivityParams other = new ScoringConfigGroup.ActivityParams("other"); other.setTypicalDuration(4 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams education = new PlanCalcScoreConfigGroup.ActivityParams("education"); + final ScoringConfigGroup.ActivityParams education = new ScoringConfigGroup.ActivityParams("education"); education.setTypicalDuration(6 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams shopping = new PlanCalcScoreConfigGroup.ActivityParams("shopping"); + final ScoringConfigGroup.ActivityParams shopping = new ScoringConfigGroup.ActivityParams("shopping"); shopping.setTypicalDuration(2 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + final ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(2 * 3600); - config.planCalcScore().addActivityParams(home); - config.planCalcScore().addActivityParams(other); - config.planCalcScore().addActivityParams(education); - config.planCalcScore().addActivityParams(shopping); - config.planCalcScore().addActivityParams(work); + config.scoring().addActivityParams(home); + config.scoring().addActivityParams(other); + config.scoring().addActivityParams(education); + config.scoring().addActivityParams(shopping); + config.scoring().addActivityParams(work); - final StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + final ReplanningConfigGroup.StrategySettings stratSets = new ReplanningConfigGroup.StrategySettings(); stratSets.setWeight(1); stratSets.setStrategyName("ChangeExpBeta"); - config.strategy().addStrategySettings(stratSets); + config.replanning().addStrategySettings(stratSets); - config.controler().setLastIteration(1); - config.controler().setWriteEventsInterval(1); + config.controller().setLastIteration(1); + config.controller().setWriteEventsInterval(1); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory("test/output/holzkirchen_shifts_multiHub"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory("test/output/holzkirchen_shifts_multiHub"); DrtOperationsParams operationsParams = (DrtOperationsParams) drtWithShiftsConfigGroup.createParameterSet(DrtOperationsParams.SET_NAME); ShiftsParams shiftsParams = (ShiftsParams) operationsParams.createParameterSet(ShiftsParams.SET_NAME); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java index 3205bae0a4f..e6b9caf51af 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java @@ -17,9 +17,9 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.examples.ExamplesUtils; @@ -84,10 +84,10 @@ public void test() { modes.add("drt"); config.travelTimeCalculator().setAnalyzedModes(modes); - PlanCalcScoreConfigGroup.ModeParams scoreParams = new PlanCalcScoreConfigGroup.ModeParams("drt"); - config.planCalcScore().addModeParams(scoreParams); - PlanCalcScoreConfigGroup.ModeParams scoreParams2 = new PlanCalcScoreConfigGroup.ModeParams("walk"); - config.planCalcScore().addModeParams(scoreParams2); + ScoringConfigGroup.ModeParams scoreParams = new ScoringConfigGroup.ModeParams("drt"); + config.scoring().addModeParams(scoreParams); + ScoringConfigGroup.ModeParams scoreParams2 = new ScoringConfigGroup.ModeParams("walk"); + config.scoring().addModeParams(scoreParams2); config.plans().setInputFile(plansFile); config.network().setInputFile(networkFile); @@ -95,33 +95,33 @@ public void test() { config.qsim().setSimStarttimeInterpretation(QSimConfigGroup.StarttimeInterpretation.onlyUseStarttime); config.qsim().setSimEndtimeInterpretation(QSimConfigGroup.EndtimeInterpretation.minOfEndtimeAndMobsimFinished); - final PlanCalcScoreConfigGroup.ActivityParams home = new PlanCalcScoreConfigGroup.ActivityParams("home"); + final ScoringConfigGroup.ActivityParams home = new ScoringConfigGroup.ActivityParams("home"); home.setTypicalDuration(8 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams other = new PlanCalcScoreConfigGroup.ActivityParams("other"); + final ScoringConfigGroup.ActivityParams other = new ScoringConfigGroup.ActivityParams("other"); other.setTypicalDuration(4 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams education = new PlanCalcScoreConfigGroup.ActivityParams("education"); + final ScoringConfigGroup.ActivityParams education = new ScoringConfigGroup.ActivityParams("education"); education.setTypicalDuration(6 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams shopping = new PlanCalcScoreConfigGroup.ActivityParams("shopping"); + final ScoringConfigGroup.ActivityParams shopping = new ScoringConfigGroup.ActivityParams("shopping"); shopping.setTypicalDuration(2 * 3600); - final PlanCalcScoreConfigGroup.ActivityParams work = new PlanCalcScoreConfigGroup.ActivityParams("work"); + final ScoringConfigGroup.ActivityParams work = new ScoringConfigGroup.ActivityParams("work"); work.setTypicalDuration(2 * 3600); - config.planCalcScore().addActivityParams(home); - config.planCalcScore().addActivityParams(other); - config.planCalcScore().addActivityParams(education); - config.planCalcScore().addActivityParams(shopping); - config.planCalcScore().addActivityParams(work); + config.scoring().addActivityParams(home); + config.scoring().addActivityParams(other); + config.scoring().addActivityParams(education); + config.scoring().addActivityParams(shopping); + config.scoring().addActivityParams(work); - final StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + final ReplanningConfigGroup.StrategySettings stratSets = new ReplanningConfigGroup.StrategySettings(); stratSets.setWeight(1); stratSets.setStrategyName("ChangeExpBeta"); - config.strategy().addStrategySettings(stratSets); + config.replanning().addStrategySettings(stratSets); - config.controler().setLastIteration(1); - config.controler().setWriteEventsInterval(1); + config.controller().setLastIteration(1); + config.controller().setWriteEventsInterval(1); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory("test/output/holzkirchen_shifts"); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory("test/output/holzkirchen_shifts"); DrtOperationsParams operationsParams = (DrtOperationsParams) drtWithShiftsConfigGroup.createParameterSet(DrtOperationsParams.SET_NAME); ShiftsParams shiftsParams = (ShiftsParams) operationsParams.createParameterSet(ShiftsParams.SET_NAME); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java index 11bd8d5328b..cd0b743f9f2 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java @@ -25,11 +25,7 @@ import static org.matsim.contrib.drt.extension.preplanned.optimizer.PreplannedDrtOptimizer.PreplannedStop; import java.net.URL; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -53,34 +49,34 @@ public void testRun() { // create preplanned requests (they will be mapped to drt requests created during simulation) var preplannedRequest_0 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_0"), Id.createLinkId("114"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_0")), Id.createLinkId("114"), Id.createLinkId("349")), 0.0, 900.0, 844.4); var preplannedRequest_1 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_1"), Id.createLinkId("144"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_1")), Id.createLinkId("144"), Id.createLinkId("437")), 300.0, 1200.0, 1011.8); var preplannedRequest_2 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_2"), Id.createLinkId("223"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_2")), Id.createLinkId("223"), Id.createLinkId("347")), 600.0, 1500.0, 1393.7); var preplannedRequest_3 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_3"), Id.createLinkId("234"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_3")), Id.createLinkId("234"), Id.createLinkId("119")), 900.0, 1800.0, 1825.0); var preplannedRequest_4 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_4"), Id.createLinkId("314"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_4")), Id.createLinkId("314"), Id.createLinkId("260")), 1200.0, 2100.0, 1997.6); var preplannedRequest_5 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_5"), Id.createLinkId("333"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_5")), Id.createLinkId("333"), Id.createLinkId("438")), 1500.0, 2400.0, 2349.6); var preplannedRequest_6 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_6"), Id.createLinkId("325"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_6")), Id.createLinkId("325"), Id.createLinkId("111")), 1800.0, 2700.0, 2600.2); var preplannedRequest_7 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_7"), Id.createLinkId("412"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_7")), Id.createLinkId("412"), Id.createLinkId("318")), 2100.0, 3000.0, 2989.9); var preplannedRequest_8 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_8"), Id.createLinkId("455"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_8")), Id.createLinkId("455"), Id.createLinkId("236")), 2400.0, 3300.0, 3110.5); var preplannedRequest_9 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_9"), Id.createLinkId("139"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_9")), Id.createLinkId("139"), Id.createLinkId("330")), 2700.0, 3600.0, 3410.5); var preplannedRequests = List.of(preplannedRequest_0, preplannedRequest_1, preplannedRequest_2, preplannedRequest_3, preplannedRequest_4, preplannedRequest_5, preplannedRequest_6); diff --git a/contribs/drt/pom.xml b/contribs/drt/pom.xml index 03fce68a589..a80d854f1b5 100644 --- a/contribs/drt/pom.xml +++ b/contribs/drt/pom.xml @@ -18,11 +18,78 @@ 16.0-SNAPSHOT
+ + org.matsim.contrib + otfvis + 16.0-SNAPSHOT + + + + org.matsim.contrib + common + 16.0-SNAPSHOT + + org.apache.commons commons-lang3 + + org.apache.commons + commons-math3 + + + + com.opencsv + opencsv + + + + org.geotools + gt-main + ${geotools.version} + + + + org.geotools + gt-opengis + ${geotools.version} + + + + one.util + streamex + 0.8.2 + + + + org.locationtech.jts + jts-core + ${jts.version} + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.fasterxml.jackson.core + jackson-core + + + + org.jfree + jfreechart + 1.5.4 + + org.assertj diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java index c9199656800..1f33b8cf008 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java @@ -19,31 +19,7 @@ package org.matsim.contrib.drt.analysis; -import static java.util.stream.Collectors.toList; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.io.BufferedWriter; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.TreeMap; -import java.util.function.Function; -import java.util.stream.Collectors; - +import com.google.common.base.Preconditions; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.apache.logging.log4j.LogManager; @@ -59,7 +35,6 @@ import org.jfree.data.xy.XYSeries; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.PersonDepartureEvent; import org.matsim.api.core.v01.events.PersonMoneyEvent; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -70,12 +45,13 @@ import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtStayTask; +import org.matsim.contrib.dvrp.analysis.VehicleOccupancyProfileCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; -import org.matsim.contrib.dvrp.analysis.VehicleOccupancyProfileCalculator; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; import org.matsim.core.config.Config; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.controler.MatsimServices; @@ -87,7 +63,20 @@ import org.matsim.core.utils.misc.Time; import org.matsim.vehicles.Vehicle; -import com.google.common.base.Preconditions; +import java.awt.*; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toList; /** * @author jbischoff @@ -120,7 +109,7 @@ public class DrtAnalysisControlerListener implements IterationEndsListener, Shut this.vehicleOccupancyProfileCalculator = vehicleOccupancyProfileCalculator; this.drtCfg = drtCfg; this.qSimCfg = config.qsim(); - runId = Optional.ofNullable(config.controler().getRunId()).orElse(notAvailableString); + runId = Optional.ofNullable(config.controller().getRunId()).orElse(notAvailableString); maxcap = findMaxVehicleCapacity(fleet); format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); @@ -131,39 +120,47 @@ public class DrtAnalysisControlerListener implements IterationEndsListener, Shut this.delimiter = config.global().getDefaultDelimiter(); } - private record DrtLeg(Id request, double departureTime, Id person, Id vehicle, Id fromLinkId, Coord fromCoord, + private record DrtLeg(Id request, double submissionTime, double departureTime, Id person, Id vehicle, Id fromLinkId, Coord fromCoord, Id toLinkId, Coord toCoord, double waitTime, double unsharedDistanceEstimate_m, double unsharedTimeEstimate_m, - double arrivalTime, double fare, double latestDepartureTime, double latestArrivalTime) { + double arrivalTime, double fare, double earliestDepartureTime, double latestDepartureTime, double latestArrivalTime) { } - private static DrtLeg newDrtLeg(EventSequence sequence, Function, ? extends Link> linkProvider) { + private static List newDrtLegs(EventSequence sequence, Function, ? extends Link> linkProvider) { Preconditions.checkArgument(sequence.isCompleted()); + List legs = new ArrayList<>(); DrtRequestSubmittedEvent submittedEvent = sequence.getSubmitted(); - PersonDepartureEvent departureEvent = sequence.getDeparture().get(); - PassengerPickedUpEvent pickedUpEvent = sequence.getPickedUp().get(); + + Map, EventSequence.PersonEvents> personEvents = sequence.getPersonEvents(); + var request = submittedEvent.getRequestId(); - var departureTime = departureEvent.getTime(); - var person = submittedEvent.getPersonId(); - var vehicle = pickedUpEvent.getVehicleId(); + var submissionTime = submittedEvent.getTime(); var fromLinkId = submittedEvent.getFromLinkId(); var fromCoord = linkProvider.apply(fromLinkId).getToNode().getCoord(); var toLinkId = submittedEvent.getToLinkId(); var toCoord = linkProvider.apply(toLinkId).getToNode().getCoord(); - var waitTime = pickedUpEvent.getTime() - departureEvent.getTime(); var unsharedDistanceEstimate_m = submittedEvent.getUnsharedRideDistance(); var unsharedTimeEstimate_m = submittedEvent.getUnsharedRideTime(); - var arrivalTime = sequence.getDroppedOff().get().getTime(); // PersonMoneyEvent has negative amount because the agent's money is reduced -> for the operator that is a positive amount var fare = sequence.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + var earliestDepartureTime = sequence.getSubmitted().getEarliestDepartureTime(); var latestDepartureTime = sequence.getSubmitted().getLatestPickupTime(); var latestArrivalTime = sequence.getSubmitted().getLatestDropoffTime(); - return new DrtLeg(request, departureTime, person, vehicle, fromLinkId, fromCoord, toLinkId, toCoord, waitTime, unsharedDistanceEstimate_m, - unsharedTimeEstimate_m, arrivalTime, fare, latestDepartureTime, latestArrivalTime); + + for (Id person : submittedEvent.getPersonIds()) { + var departureTime = personEvents.get(person).getDeparture().get().getTime(); + PassengerPickedUpEvent pickedUp = personEvents.get(person).getPickedUp().get(); + var vehicle = pickedUp.getVehicleId(); + var waitTime = pickedUp.getTime() - personEvents.get(person).getDeparture().get().getTime(); + var arrivalTime = personEvents.get(person).getDroppedOff().get().getTime(); + legs.add(new DrtLeg(request, submissionTime, departureTime, person, vehicle, fromLinkId, fromCoord, toLinkId, toCoord, waitTime, unsharedDistanceEstimate_m, + unsharedTimeEstimate_m, arrivalTime, fare, earliestDepartureTime, latestDepartureTime, latestArrivalTime)); + } + return legs; } @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controler().isCreateGraphs(); + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); writeAndPlotWaitTimeEstimateComparison(drtEventSequenceCollector.getPerformedRequestSequences().values(), filename(event, "waitTimeComparison", ".png"), filename(event, "waitTimeComparison", ".csv"), createGraphs); @@ -172,26 +169,37 @@ public void notifyIterationEnds(IterationEndsEvent event) { .values() .stream() .filter(EventSequence::isCompleted) - .map(sequence -> newDrtLeg(sequence, network.getLinks()::get)) + .map(sequence -> newDrtLegs(sequence, network.getLinks()::get)) + .flatMap(Collection::stream) .sorted(Comparator.comparing(leg -> leg.departureTime)) .collect(toList()); + List rejectionEvents = drtEventSequenceCollector.getRejectedRequestSequences() + .values() + .stream() + .map(eventSequence -> eventSequence.getRejected().get()) + .sorted(Comparator.comparing(rejectionEvent -> rejectionEvent.getTime())) + .collect(toList()); + collection2Text(drtEventSequenceCollector.getRejectedRequestSequences().values(), filename(event, "drt_rejections", ".csv"), - String.join(delimiter, "time", "personId", "fromLinkId", "toLinkId", "fromX", "fromY", "toX", "toY"), seq -> { + String.join(delimiter, "time", "personIds", "requestId", "fromLinkId", "toLinkId", "fromX", "fromY", "toX", "toY", "cause"), seq -> { DrtRequestSubmittedEvent submission = seq.getSubmitted(); Coord fromCoord = network.getLinks().get(submission.getFromLinkId()).getToNode().getCoord(); Coord toCoord = network.getLinks().get(submission.getToLinkId()).getToNode().getCoord(); + PassengerRequestRejectedEvent rejection = seq.getRejected().get(); return String.join(delimiter, submission.getTime() + "",// - submission.getPersonId() + "",// + submission.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")) + "",// + submission.getRequestId() + "",// submission.getFromLinkId() + "",// submission.getToLinkId() + "",// fromCoord.getX() + "",// fromCoord.getY() + "",// toCoord.getX() + "",// - toCoord.getY() + ""); + toCoord.getY() + "",// + rejection.getCause()); }); - double rejectionRate = (double)drtEventSequenceCollector.getRejectedRequestSequences().size() + double rejectionRate = (double) drtEventSequenceCollector.getRejectedRequestSequences().size() / drtEventSequenceCollector.getRequestSubmissions().size(); String legsSummarize = summarizeLegs(legs, drtVehicleStats.getTravelDistances(), drtEventSequenceCollector.getDrtFarePersonMoneyEvents(), delimiter); @@ -212,8 +220,11 @@ public void notifyIterationEnds(IterationEndsEvent event) { String occStats = summarizeDetailedOccupancyStats(drtVehicleStats.getVehicleStates(), delimiter, maxcap); writeIterationVehicleStats(vehStats, occStats, event.getIteration()); if (drtCfg.plotDetailedCustomerStats) { - String header = String.join(delimiter, "departureTime",// + String header = String.join(delimiter, // + "submissionTime", // + "departureTime",// "personId",// + "requestId",// "vehicleId",// "fromLinkId",// "fromX",// @@ -227,12 +238,15 @@ public void notifyIterationEnds(IterationEndsEvent event) { "travelDistance_m",// "directTravelDistance_m",// "fareForLeg", // + "earliestDepartureTime", "latestDepartureTime", // "latestArrivalTime"); collection2Text(legs, filename(event, "drt_legs", ".csv"), header, leg -> String.join(delimiter,// + (Double)leg.submissionTime + "",// (Double)leg.departureTime + "",// leg.person + "",// + leg.request + "",// leg.vehicle + "",// leg.fromLinkId + "",// format.format(leg.fromCoord.getX()),// @@ -246,12 +260,14 @@ public void notifyIterationEnds(IterationEndsEvent event) { format.format(drtVehicleStats.getTravelDistances().get(leg.request)),// format.format(leg.unsharedDistanceEstimate_m),// format.format(leg.fare), // + format.format(leg.earliestDepartureTime), // format.format(leg.latestDepartureTime), // format.format(leg.latestArrivalTime))); } writeVehicleDistances(drtVehicleStats.getVehicleStates(), filename(event, "vehicleDistanceStats", ".csv"), delimiter); analyseDetours(network, legs, drtVehicleStats.getTravelDistances(), drtCfg, filename(event, "drt_detours"), createGraphs, delimiter); analyseWaitTimes(filename(event, "waitStats"), legs, 1800, createGraphs, delimiter); + analyseRejections(filename(event, "drt_rejections_perTimeBin"), rejectionEvents, 1800, createGraphs, delimiter); analyseConstraints(filename(event, "constraints"), legs, createGraphs); double endTime = qSimCfg.getEndTime() @@ -333,7 +349,7 @@ private void writeIterationPassengerStats(String summarizeLegs, int it) { private void writeIterationVehicleStats(String summarizeVehicles, String vehOcc, int it) { try (var bw = getAppendingBufferedWriter("drt_vehicle_stats", ".csv")) { if (!vheaderWritten) { - bw.write(line("runId", "iteration", "vehicles", "totalDistance", "totalEmptyDistance", "emptyRatio", "totalPassengerDistanceTraveled", + bw.write(line("runId", "iteration", "vehicles", "totalServiceDuration", "totalDistance", "totalEmptyDistance", "emptyRatio", "totalPassengerDistanceTraveled", "averageDrivenDistance", "averageEmptyDistance", "averagePassengerDistanceTraveled", "d_p/d_t", "l_det", "minShareIdleVehicles", "minCountIdleVehicles")); } @@ -365,11 +381,17 @@ private void writeAndPlotWaitTimeEstimateComparison(Collection pe bw.append(line("RequestId", "actualWaitTime", "estimatedWaitTime", "deviate")); for (EventSequence seq : performedRequestEventSequences) { - if (seq.getPickedUp().isPresent()) { - double actualWaitTime = seq.getPickedUp().get().getTime() - seq.getDeparture().get().getTime(); - double estimatedWaitTime = seq.getScheduled().get().getPickupTime() - seq.getDeparture().get().getTime(); - bw.append(line(seq.getSubmitted().getRequestId(), actualWaitTime, estimatedWaitTime, actualWaitTime - estimatedWaitTime)); - times.add(actualWaitTime, estimatedWaitTime); + List> personIds = seq.getSubmitted().getPersonIds(); + for (Id person : personIds) { + if(seq.getPersonEvents().containsKey(person)) { + EventSequence.PersonEvents personEvents = seq.getPersonEvents().get(person); + if(personEvents.getPickedUp().isPresent() && personEvents.getDeparture().isPresent()) { + double actualWaitTime = personEvents.getPickedUp().get().getTime() - personEvents.getDeparture().get().getTime(); + double estimatedWaitTime = seq.getScheduled().get().getPickupTime() - seq.getSubmitted().getEarliestDepartureTime(); + bw.append(line(seq.getSubmitted().getRequestId(), actualWaitTime, estimatedWaitTime, actualWaitTime - estimatedWaitTime)); + times.add(actualWaitTime, estimatedWaitTime); + } + } } } @@ -399,6 +421,7 @@ public void notifyShutdown(ShutdownEvent event) { dumpOutput(event.getIteration(), "waitTimeComparison", ".png"); dumpOutput(event.getIteration(), "waitTimeComparison", ".csv"); dumpOutput(event.getIteration(), "drt_rejections", ".csv"); + dumpOutput(event.getIteration(), "drt_rejections_perTimeBin", ".csv"); dumpOutput(event.getIteration(), "drt_legs", ".csv"); dumpOutput(event.getIteration(), "vehicleDistanceStats", ".csv"); dumpOutput(event.getIteration(), "drt_detours", ".csv"); @@ -453,6 +476,33 @@ private static Map> splitLegsIntoBins(Collection le return splitLegs; } + private static Map> splitEventsIntoBins(List rejectionEvents, int binSize_s) { + Map> rejections = new TreeMap<>(); + + int startTime = ((int)(rejectionEvents.get(0).getTime() / binSize_s)) * binSize_s; + int endTime = ((int)(rejectionEvents.get(rejectionEvents.size() - 1).getTime() / binSize_s) + 1) * binSize_s; + + for (int time = startTime; time < endTime; time = time + binSize_s) { + + // rejection list in this timebin + List rejectionList = new ArrayList<>(); + + //Iterate through each rejection + for (PassengerRequestRejectedEvent rejectedEvent : rejectionEvents){ + double rejectionTime = rejectedEvent.getTime(); + if (rejectionTime > endTime || rejectionTime < startTime) { + LogManager.getLogger(DrtAnalysisControlerListener.class).error("wrong end / start Times for analysis"); + } + + if (rejectionTime > time && rejectionTime < time + binSize_s) { + rejectionList.add(rejectedEvent); + } + } + rejections.put((double)time, rejectionList); + } + return rejections; + } + private static void analyzeBoardingsAndDeboardings(List legs, String delimiter, double startTime, double endTime, double timeBinSize, String boardingsFile, String deboardingsFile, Network network) { if (endTime < startTime) { @@ -708,6 +758,55 @@ private static void analyseWaitTimes(String fileName, List legs, int bin } + private static void analyseRejections(String fileName, List rejectionEvents, int binsize_s, boolean createGraphs, String delimiter) { + if (rejectionEvents.size() == 0) + return; + + Map> splitEvents = splitEventsIntoBins(rejectionEvents, binsize_s); + + DecimalFormat format = new DecimalFormat(); + format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); + format.setMinimumIntegerDigits(1); + format.setMaximumFractionDigits(2); + format.setGroupingUsed(false); + + SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm:ss"); + + BufferedWriter bw = IOUtils.getBufferedWriter(fileName + ".csv"); + TimeSeriesCollection dataset = new TimeSeriesCollection(); + TimeSeries rejections = new TimeSeries("number of rejections"); + + try { + bw.write(String.join(delimiter, "timebin", "rejections")); + + for(Map.Entry> e : splitEvents.entrySet()){ + int drt_numOfRejection = 0; + if (!e.getValue().isEmpty()) { + drt_numOfRejection = e.getValue().size(); + } + + Minute h = new Minute(sdf2.parse(Time.writeTime(e.getKey()))); + + rejections.addOrUpdate(h, Double.valueOf(drt_numOfRejection)); + bw.newLine(); + bw.write(String.join(delimiter, Time.writeTime(e.getKey()) + "",// + format.format(drt_numOfRejection) +"")); + } + + bw.flush(); + bw.close(); + if (createGraphs) { + dataset.addSeries(rejections); + JFreeChart chart = chartProfile(splitEvents.size(), dataset, "Number of rejections", "Number"); + ChartSaveUtils.saveAsPNG(chart, fileName, 1500, 1000); + } + + } catch (IOException | ParseException e) { + + e.printStackTrace(); + } + } + private static JFreeChart chartProfile(int length, TimeSeriesCollection dataset, String descriptor, String yax) { JFreeChart chart = ChartFactory.createTimeSeriesChart(descriptor, "Time", yax, dataset); @@ -796,12 +895,15 @@ private static String summarizeVehicles(Map, DrtVehicleDistanceStats format.setMaximumFractionDigits(2); format.setGroupingUsed(false); + DescriptiveStatistics totalServiceDuration = new DescriptiveStatistics(); DescriptiveStatistics driven = new DescriptiveStatistics(); DescriptiveStatistics passengerTraveledDistance = new DescriptiveStatistics(); DescriptiveStatistics occupied = new DescriptiveStatistics(); DescriptiveStatistics empty = new DescriptiveStatistics(); + for (DrtVehicleDistanceStats.VehicleState state : vehicleDistances.values()) { + totalServiceDuration.addValue(state.serviceDuration); driven.addValue(state.totalDistance); passengerTraveledDistance.addValue(state.totalPassengerTraveledDistance); occupied.addValue(state.totalOccupiedDistance); @@ -809,6 +911,7 @@ private static String summarizeVehicles(Map, DrtVehicleDistanceStats } double d_p_d_t = passengerTraveledDistance.getSum() / driven.getSum(); return String.join(del, vehicleDistances.size() + "",// + format.format(totalServiceDuration.getSum()) + "",// format.format(driven.getSum()) + "",// format.format(empty.getSum()) + "",// format.format(empty.getSum() / driven.getSum()) + "",// diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java index f70931a4e06..b8b095cd1dc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java @@ -24,12 +24,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -52,22 +52,62 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerWaitingEvent; +import org.matsim.contrib.dvrp.passenger.PassengerWaitingEventHandler; import com.google.common.base.Preconditions; +import com.google.common.base.Verify; /** - * Creates PerformedRequestEventSequence (for scheduled requests) and RejectedRequestEventSequence (for rejected requests). - * Almost all data for request/leg analysis is there (except info on actual paths), so should be quite reusable. + * Creates PerformedRequestEventSequence (for scheduled requests) and + * RejectedRequestEventSequence (for rejected requests). Almost all data for + * request/leg analysis is there (except info on actual paths), so should be + * quite reusable. + * + * Without prebooking, the order of sequences is always the same: First the + * agent *departs* then the request is *submitted*, then it is *rejected* or + * picked up. With prebooking, the order of departure and submission can be + * reversed: An agent first submits the request, and only later departs on the + * leg. Since *departure* is core MATSim, the respective event has no + * information about the request identifier. It could now happen that two + * submission with same characteristics have been submitted (person id, origin + * id). Then it is not clear which request belongs to the current departure. For + * that purpose the PassengerWaiting event has been introduced which is fired + * right after a departure has been processed by a DRT-related PassengerEngine. + * This events allows to link the latest departure of an agent to a request id. * * @author jbischoff * @author Michal Maciejewski + * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public class DrtEventSequenceCollector implements PassengerRequestRejectedEventHandler, PassengerRequestScheduledEventHandler, - DrtRequestSubmittedEventHandler, PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, + DrtRequestSubmittedEventHandler, PassengerWaitingEventHandler, PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, PersonMoneyEventHandler, PersonDepartureEventHandler { public static class EventSequence { + + public static class PersonEvents { + + @Nullable + private PersonDepartureEvent departure; + @Nullable + private PassengerPickedUpEvent pickedUp; + @Nullable + private PassengerDroppedOffEvent droppedOff; + + public Optional getDeparture() { + return Optional.ofNullable(departure); + } + + public Optional getPickedUp() { + return Optional.ofNullable(pickedUp); + } + + public Optional getDroppedOff() { + return Optional.ofNullable(droppedOff); + } + } private final DrtRequestSubmittedEvent submitted; @Nullable @@ -75,12 +115,8 @@ public static class EventSequence { @Nullable private PassengerRequestRejectedEvent rejected; - @Nullable - private PersonDepartureEvent departure; - @Nullable - private PassengerPickedUpEvent pickedUp; - @Nullable - private PassengerDroppedOffEvent droppedOff; + private final Map, PersonEvents> personEvents = new HashMap<>(); + @Nullable private List drtFares = new LinkedList<>(); @@ -88,15 +124,17 @@ public static class EventSequence { this.submitted = Objects.requireNonNull(submitted); } - public EventSequence(PersonDepartureEvent departure, DrtRequestSubmittedEvent submitted, + public EventSequence(Id personId, PersonDepartureEvent departure, DrtRequestSubmittedEvent submitted, PassengerRequestScheduledEvent scheduled, PassengerPickedUpEvent pickedUp, PassengerDroppedOffEvent droppedOff, List drtFares) { this.submitted = Objects.requireNonNull(submitted); this.scheduled = scheduled; - this.departure = departure; - this.pickedUp = pickedUp; - this.droppedOff = droppedOff; this.drtFares = new ArrayList<>(drtFares); + PersonEvents personEvents = new PersonEvents(); + personEvents.departure = departure; + personEvents.pickedUp = pickedUp; + personEvents.droppedOff = droppedOff; + this.personEvents.put(personId, personEvents); } public DrtRequestSubmittedEvent getSubmitted() { @@ -111,24 +149,17 @@ public Optional getRejected() { return Optional.ofNullable(rejected); } - public Optional getDeparture() { - return Optional.ofNullable(departure); - } - - public Optional getPickedUp() { - return Optional.ofNullable(pickedUp); + public Map, PersonEvents> getPersonEvents() { + return personEvents; } - public Optional getDroppedOff() { - return Optional.ofNullable(droppedOff); - } public List getDrtFares() { return Collections.unmodifiableList(drtFares); } public boolean isCompleted() { - return droppedOff != null; + return personEvents.values().stream().allMatch(pe -> pe.droppedOff != null); } } @@ -137,8 +168,8 @@ public boolean isCompleted() { private final Map, EventSequence> sequences = new HashMap<>(); private final List drtFarePersonMoneyEvents = new ArrayList<>(); - private final Map, List> sequencesWithoutDeparture = new HashMap<>(); - private final Map, List> departuresWithoutSequence = new HashMap<>(); + private final Map, PersonDepartureEvent> latestDepartures = new HashMap<>(); + private final Map, List> waitingForSubmission = new HashMap<>(); public DrtEventSequenceCollector(String mode) { this.mode = mode; @@ -158,6 +189,8 @@ public Map, EventSequence> getRejectedRequestSequences() { public Map, EventSequence> getPerformedRequestSequences() { return sequences.entrySet().stream() // .filter(e -> e.getValue().getRejected().isEmpty()) // + .filter(e -> e.getValue().personEvents.values().stream().allMatch(pe -> pe.departure != null)) // + .filter(e -> e.getValue().personEvents.values().stream().allMatch(pe -> pe.pickedUp != null)) // .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); } @@ -169,8 +202,8 @@ public List getDrtFarePersonMoneyEvents() { public void reset(int iteration) { sequences.clear(); drtFarePersonMoneyEvents.clear(); - sequencesWithoutDeparture.clear(); - departuresWithoutSequence.clear(); + latestDepartures.clear(); + waitingForSubmission.clear(); } @Override @@ -179,14 +212,14 @@ public void handleEvent(DrtRequestSubmittedEvent event) { EventSequence sequence = new EventSequence(event); sequences.put(event.getRequestId(), sequence); - PersonDepartureEvent departureEvent = popDepartureForSubmission(event); - - if (departureEvent == null) { - // We have a submitted request, but no departure event yet (i.e. a prebooking). We start the - // sequence and note down the person id to fill in the departure event later on. - sequencesWithoutDeparture.computeIfAbsent(event.getPersonId(), id -> new LinkedList<>()).add(sequence); - } else { - sequence.departure = departureEvent; + // if we already have a departure + List waiting = waitingForSubmission.remove(event.getRequestId()); + if(waiting != null) { + for (PersonDepartureEvent departure : waiting) { + sequence.getPersonEvents().computeIfAbsent( + departure.getPersonId(), personId -> new EventSequence.PersonEvents() + ).departure = departure; + } } } } @@ -194,15 +227,31 @@ public void handleEvent(DrtRequestSubmittedEvent event) { @Override public void handleEvent(PersonDepartureEvent event) { if (event.getLegMode().equals(mode)) { - EventSequence sequence = popSequenceForDeparture(event); - - if (sequence == null) { - // We have a departure event, but no submission yet (i.e. and instant booking). - // We note down the departure event here to recover it later when the submission - // is down (usually right after). - departuresWithoutSequence.computeIfAbsent(event.getPersonId(), id -> new LinkedList<>()).add(event); - } else { - sequence.departure = event; + // note down the departure event here, for now we don't know which request it + // belongs to, see below + Preconditions.checkState(!latestDepartures.containsKey(event.getPersonId()), + "Attempt to register a departure event for " + mode + " and person " + event.getPersonId() + + ", but there is still a departure that has not been consumed"); + latestDepartures.put(event.getPersonId(), event); + } + } + + @Override + public void handleEvent(PassengerWaitingEvent event) { + if (event.getMode().equals(mode)) { + EventSequence sequence = sequences.get(event.getRequestId()); + for (Id personId : event.getPersonIds()) { + // must exist, otherwise something is wrong + PersonDepartureEvent departureEvent = Objects.requireNonNull(latestDepartures.remove(personId)); + + if (sequence != null) { + // prebooked request, we already have the submission + Verify.verifyNotNull(sequence.submitted); + sequence.personEvents.computeIfAbsent(personId, p -> new EventSequence.PersonEvents()).departure = departureEvent; + } else { + // immediate request, submission event should come soon + waitingForSubmission.computeIfAbsent(event.getRequestId(), requestId -> new ArrayList<>()).add(departureEvent); + } } } } @@ -224,14 +273,14 @@ public void handleEvent(PassengerRequestRejectedEvent event) { @Override public void handleEvent(PassengerPickedUpEvent event) { if (event.getMode().equals(mode)) { - sequences.get(event.getRequestId()).pickedUp = event; + sequences.get(event.getRequestId()).personEvents.get(event.getPersonId()).pickedUp = event; } } @Override public void handleEvent(PassengerDroppedOffEvent event) { if (event.getMode().equals(mode)) { - sequences.get(event.getRequestId()).droppedOff = event; + sequences.get(event.getRequestId()).personEvents.get(event.getPersonId()).droppedOff = event; } } @@ -254,72 +303,4 @@ public void handleEvent(PersonMoneyEvent event) { } } } - - /* - * This function is helper that finds a PersonDepartureEvent for a given - * DrtRequestSubmittedEvent. This means that the departure has happened before - * submission, which is the usually case for instant requests. - */ - private PersonDepartureEvent popDepartureForSubmission(DrtRequestSubmittedEvent event) { - List potentialDepartures = departuresWithoutSequence.get(event.getPersonId()); - PersonDepartureEvent result = null; - - if (potentialDepartures != null) { - Iterator iterator = potentialDepartures.iterator(); - - while (iterator.hasNext()) { - PersonDepartureEvent departureEvent = iterator.next(); - - if (event.getFromLinkId().equals(departureEvent.getLinkId())) { - if (result != null) { - throw new IllegalStateException( - "Ambiguous matching between submission and departure - not sure how to solve this"); - } - - iterator.remove(); - result = departureEvent; - } - } - - if (potentialDepartures.size() == 0) { - departuresWithoutSequence.remove(event.getPersonId()); - } - } - - return result; - } - - /* - * This function is helper that finds a sequence given a PersonDepartureEvent. - * This means that a sequence has started (the request has been submitted) - * before the agent has departed, i.e. this is a pre-booking of some sort. - */ - private EventSequence popSequenceForDeparture(PersonDepartureEvent event) { - EventSequence result = null; - List potentialSequences = sequencesWithoutDeparture.get(event.getPersonId()); - - if (potentialSequences != null) { - Iterator iterator = potentialSequences.iterator(); - - while (iterator.hasNext()) { - EventSequence sequence = iterator.next(); - - if (sequence.submitted.getFromLinkId().equals(event.getLinkId())) { - if (result != null) { - throw new IllegalStateException( - "Ambiguous matching between submission and departure - not sure how to solve this"); - } - - iterator.remove(); - result = sequence; - } - } - - if (potentialSequences.size() == 0) { - sequencesWithoutDeparture.remove(event.getPersonId()); - } - } - - return result; - } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtVehicleDistanceStats.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtVehicleDistanceStats.java index ff81d3fa9dc..d167a3d7700 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtVehicleDistanceStats.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtVehicleDistanceStats.java @@ -60,9 +60,11 @@ static class VehicleState { double totalOccupiedDistance = 0; double totalPassengerTraveledDistance = 0; //in (passenger x meters) final double[] totalDistanceByOccupancy; + final double serviceDuration; - private VehicleState(int maxCapacity) { - totalDistanceByOccupancy = new double[maxCapacity + 1]; + private VehicleState(int maxCapacity, double serviceTime) { + this.totalDistanceByOccupancy = new double[maxCapacity + 1]; + this.serviceDuration = serviceTime; } private void linkEntered(Link link) { @@ -101,10 +103,10 @@ public void reset(int iteration) { private void initializeVehicles() { int maxCapacity = DrtAnalysisControlerListener.findMaxVehicleCapacity(fleetSpecification); fleetSpecification.getVehicleSpecifications() - .keySet() + .values() .stream() - .map(Id::createVehicleId) - .forEach(id -> vehicleStates.put(id, new VehicleState(maxCapacity))); + .forEach(spec -> vehicleStates.put(Id.createVehicleId(spec.getId()), + new VehicleState(maxCapacity, spec.getServiceEndTime() - spec.getServiceBeginTime()))); } @Override diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java index be03eda92f5..40c885b5531 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java @@ -39,6 +39,8 @@ import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Polygon; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector.EventSequence; import org.matsim.contrib.drt.run.DrtConfigGroup; @@ -138,11 +140,13 @@ private Map createZonalStats() { zoneStats.put(zoneIdForOutsideOfZonalSystem, new DescriptiveStatistics()); for (EventSequence seq : requestAnalyzer.getPerformedRequestSequences().values()) { - if (seq.getPickedUp().isPresent()) { - DrtZone zone = zones.getZoneForLinkId(seq.getSubmitted().getFromLinkId()); - final String zoneStr = zone != null ? zone.getId() : zoneIdForOutsideOfZonalSystem; - double waitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); - zoneStats.get(zoneStr).addValue(waitTime); + for (Map.Entry, EventSequence.PersonEvents> entry : seq.getPersonEvents().entrySet()) { + if(entry.getValue().getPickedUp().isPresent()) { + DrtZone zone = zones.getZoneForLinkId(seq.getSubmitted().getFromLinkId()); + final String zoneStr = zone != null ? zone.getId() : zoneIdForOutsideOfZonalSystem; + double waitTime = entry.getValue().getPickedUp().get() .getTime() - seq.getSubmitted().getTime(); + zoneStats.get(zoneStr).addValue(waitTime); + } } } return zoneStats; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DefaultDrtOptimizer.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DefaultDrtOptimizer.java index 0c7fd8da55e..436601c82d2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DefaultDrtOptimizer.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DefaultDrtOptimizer.java @@ -19,14 +19,17 @@ package org.matsim.contrib.drt.optimizer; +import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; + +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.drt.optimizer.depot.DepotFinder; -import org.matsim.contrib.drt.optimizer.depot.Depots; import org.matsim.contrib.drt.optimizer.insertion.UnplannedRequestInserter; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy.Relocation; @@ -38,7 +41,6 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.optimizer.Request; -import org.matsim.contrib.dvrp.passenger.RequestQueue; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; @@ -61,7 +63,7 @@ public class DefaultDrtOptimizer implements DrtOptimizer { private final UnplannedRequestInserter requestInserter; private final DrtRequestInsertionRetryQueue insertionRetryQueue; - private final RequestQueue unplannedRequests; + private final Queue unplannedRequests = new LinkedList<>(); public DefaultDrtOptimizer(DrtConfigGroup drtCfg, Fleet fleet, MobsimTimer mobsimTimer, DepotFinder depotFinder, RebalancingStrategy rebalancingStrategy, DrtScheduleInquiry scheduleInquiry, ScheduleTimingUpdater scheduleTimingUpdater, @@ -78,23 +80,21 @@ public DefaultDrtOptimizer(DrtConfigGroup drtCfg, Fleet fleet, MobsimTimer mobsi this.insertionRetryQueue = insertionRetryQueue; rebalancingInterval = drtCfg.getRebalancingParams().map(rebalancingParams -> rebalancingParams.interval).orElse(null); - unplannedRequests = RequestQueue.withLimitedAdvanceRequestPlanningHorizon(drtCfg.advanceRequestPlanningHorizon); } @Override public void notifyMobsimBeforeSimStep(@SuppressWarnings("rawtypes") MobsimBeforeSimStepEvent e) { - unplannedRequests.updateQueuesOnNextTimeSteps(e.getSimulationTime()); - boolean scheduleTimingUpdated = false; - if (!unplannedRequests.getSchedulableRequests().isEmpty() || insertionRetryQueue.hasRequestsToRetryNow(e.getSimulationTime())) { + if (!unplannedRequests.isEmpty() || insertionRetryQueue.hasRequestsToRetryNow(e.getSimulationTime())) { for (DvrpVehicle v : fleet.getVehicles().values()) { scheduleTimingUpdater.updateTimings(v); } scheduleTimingUpdated = true; - requestInserter.scheduleUnplannedRequests(unplannedRequests.getSchedulableRequests()); + requestInserter.scheduleUnplannedRequests(unplannedRequests); } + relocateVehiclesToDepot(drtCfg.returnToDepotEvaluationInterval, drtCfg.returnToDepotTimeout); if (rebalancingInterval != null && e.getSimulationTime() % rebalancingInterval == 0) { if (!scheduleTimingUpdated) { for (DvrpVehicle v : fleet.getVehicles().values()) { @@ -116,7 +116,7 @@ private void rebalanceFleet() { for (Relocation r : relocations) { Link currentLink = ((DrtStayTask)r.vehicle.getSchedule().getCurrentTask()).getLink(); if (currentLink != r.link) { - relocator.relocateVehicle(r.vehicle, r.link); + relocator.relocateVehicle(r.vehicle, r.link, EmptyVehicleRelocator.RELOCATE_VEHICLE_TASK_TYPE); } } } @@ -124,21 +124,35 @@ private void rebalanceFleet() { @Override public void requestSubmitted(Request request) { - unplannedRequests.addRequest((DrtRequest)request); + unplannedRequests.add((DrtRequest)request); } @Override public void nextTask(DvrpVehicle vehicle) { scheduleTimingUpdater.updateBeforeNextTask(vehicle); - vehicle.getSchedule().nextTask(); + } - // if STOP->STAY then choose the best depot - if (drtCfg.idleVehiclesReturnToDepots && Depots.isSwitchingFromStopToStay(vehicle)) { - Link depotLink = depotFinder.findDepot(vehicle); - if (depotLink != null) { - relocator.relocateVehicle(vehicle, depotLink); - } + private void relocateVehiclesToDepot(double evaluationInterval, double timeout) { + if (drtCfg.idleVehiclesReturnToDepots && mobsimTimer.getTimeOfDay() % evaluationInterval == 0) { + fleet.getVehicles().values().stream() + .filter(scheduleInquiry::isIdle) + .filter(v -> stayTimeoutExceeded(v, timeout)) + .forEach(v -> { + Link depotLink = depotFinder.findDepot(v); + if (depotLink != null) { + relocator.relocateVehicle(v, depotLink, EmptyVehicleRelocator.RELOCATE_VEHICLE_TO_DEPOT_TASK_TYPE); + } + }); + } + } + + boolean stayTimeoutExceeded(DvrpVehicle vehicle, double timeout) { + if (STAY.isBaseTypeOf(vehicle.getSchedule().getCurrentTask())) { + double now = mobsimTimer.getTimeOfDay(); + double taskStart = vehicle.getSchedule().getCurrentTask().getBeginTime(); + return (now - taskStart) > timeout; } + return false; } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DrtModeOptimizerQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DrtModeOptimizerQSimModule.java index 57744a798df..8534468ce81 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DrtModeOptimizerQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/DrtModeOptimizerQSimModule.java @@ -31,28 +31,33 @@ import org.matsim.contrib.drt.optimizer.insertion.UnplannedRequestInserter; import org.matsim.contrib.drt.optimizer.insertion.extensive.ExtensiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.extensive.ExtensiveInsertionSearchQSimModule; +import org.matsim.contrib.drt.optimizer.insertion.repeatedselective.RepeatedSelectiveInsertionSearchParams; +import org.matsim.contrib.drt.optimizer.insertion.repeatedselective.RepeatedSelectiveInsertionSearchQSimModule; import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchQSimModule; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy; import org.matsim.contrib.drt.passenger.DrtOfferAcceptor; +import org.matsim.contrib.drt.prebooking.PrebookingActionCreator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtStayTaskEndTimeCalculator; import org.matsim.contrib.drt.schedule.DrtTaskFactory; import org.matsim.contrib.drt.schedule.DrtTaskFactoryImpl; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; import org.matsim.contrib.drt.scheduler.DefaultRequestInsertionScheduler; import org.matsim.contrib.drt.scheduler.DrtScheduleInquiry; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.contrib.dvrp.tracker.OnlineTrackerListener; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; import org.matsim.core.modal.ModalProviders; @@ -61,6 +66,7 @@ import org.matsim.core.router.util.TravelTime; import com.google.inject.Inject; +import com.google.inject.Singleton; /** * @author Michal Maciejewski (michalm) @@ -76,11 +82,13 @@ public DrtModeOptimizerQSimModule(DrtConfigGroup drtCfg) { @Override protected void configureQSim() { addModalComponent(DrtOptimizer.class, modalProvider( - getter -> new DefaultDrtOptimizer(drtCfg, getter.getModal(Fleet.class), getter.get(MobsimTimer.class), + getter -> { + return new DefaultDrtOptimizer(drtCfg, getter.getModal(Fleet.class), getter.get(MobsimTimer.class), getter.getModal(DepotFinder.class), getter.getModal(RebalancingStrategy.class), getter.getModal(DrtScheduleInquiry.class), getter.getModal(ScheduleTimingUpdater.class), getter.getModal(EmptyVehicleRelocator.class), getter.getModal(UnplannedRequestInserter.class), - getter.getModal(DrtRequestInsertionRetryQueue.class)))); + getter.getModal(DrtRequestInsertionRetryQueue.class)); + })); bindModal(DepotFinder.class).toProvider( modalProvider(getter -> new NearestStartLinkAsDepot(getter.getModal(Fleet.class)))).asEagerSingleton(); @@ -97,14 +105,15 @@ protected void configureQSim() { getter.getModal(RequestInsertionScheduler.class), getter.getModal(VehicleEntry.EntryFactory.class), getter.getModal(DrtInsertionSearch.class), getter.getModal(DrtRequestInsertionRetryQueue.class), getter.getModal(DrtOfferAcceptor.class), - getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool()))).asEagerSingleton(); + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(PassengerStopDurationProvider.class)))).asEagerSingleton(); bindModal(InsertionCostCalculator.class).toProvider(modalProvider( getter -> new DefaultInsertionCostCalculator(getter.getModal(CostCalculationStrategy.class)))); install(getInsertionSearchQSimModule(drtCfg)); - bindModal(VehicleEntry.EntryFactory.class).toInstance(new VehicleDataEntryFactoryImpl(drtCfg)); + bindModal(VehicleEntry.EntryFactory.class).toInstance(new VehicleDataEntryFactoryImpl()); bindModal(CostCalculationStrategy.class).to(drtCfg.rejectRequestIfMaxWaitOrTravelTimeViolated ? CostCalculationStrategy.RejectSoftConstraintViolations.class : @@ -129,23 +138,34 @@ public EmptyVehicleRelocator get() { }).asEagerSingleton(); bindModal(DrtScheduleInquiry.class).to(DrtScheduleInquiry.class).asEagerSingleton(); - + + boolean scheduleWaitBeforeDrive = drtCfg.getPrebookingParams().map(p -> p.scheduleWaitBeforeDrive).orElse(false); bindModal(RequestInsertionScheduler.class).toProvider(modalProvider( getter -> new DefaultRequestInsertionScheduler(getter.getModal(Fleet.class), getter.get(MobsimTimer.class), getter.getModal(TravelTime.class), getter.getModal(ScheduleTimingUpdater.class), getter.getModal(DrtTaskFactory.class), - getter.getModal(StopDurationEstimator.class)))) + getter.getModal(StopTimeCalculator.class), scheduleWaitBeforeDrive))) .asEagerSingleton(); bindModal(DrtOfferAcceptor.class).toInstance(DrtOfferAcceptor.DEFAULT_ACCEPTOR); bindModal(ScheduleTimingUpdater.class).toProvider(modalProvider( getter -> new ScheduleTimingUpdater(getter.get(MobsimTimer.class), - new DrtStayTaskEndTimeCalculator(getter.getModal(StopDurationEstimator.class))))).asEagerSingleton(); - - bindModal(VrpAgentLogic.DynActionCreator.class).toProvider(modalProvider( - getter -> new DrtActionCreator(getter.getModal(PassengerHandler.class), getter.get(MobsimTimer.class), - getter.get(DvrpConfigGroup.class)))).asEagerSingleton(); + new DrtStayTaskEndTimeCalculator(getter.getModal(StopTimeCalculator.class))))).asEagerSingleton(); + + bindModal(VrpLegFactory.class).toProvider(modalProvider(getter -> { + DvrpConfigGroup dvrpCfg = getter.get(DvrpConfigGroup.class); + MobsimTimer timer = getter.get(MobsimTimer.class); + + return v -> VrpLegFactory.createWithOnlineTracker(dvrpCfg.mobsimMode, v, OnlineTrackerListener.NO_LISTENER, + timer); + })).in(Singleton.class); + + if (drtCfg.getPrebookingParams().isEmpty()) { + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(DrtActionCreator.class)); + } else { + bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(PrebookingActionCreator.class)); + } bindModal(VrpOptimizer.class).to(modalKey(DrtOptimizer.class)); } @@ -158,6 +178,9 @@ public static AbstractDvrpModeQSimModule getInsertionSearchQSimModule(DrtConfigG case SelectiveInsertionSearchParams.SET_NAME: return new SelectiveInsertionSearchQSimModule(drtCfg); + case RepeatedSelectiveInsertionSearchParams.SET_NAME: + return new RepeatedSelectiveInsertionSearchQSimModule(drtCfg); + default: throw new RuntimeException( "Unsupported DRT insertion search type: " + drtCfg.getDrtInsertionSearchParams().getName()); 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..663b480ff74 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 @@ -18,13 +18,13 @@ package org.matsim.contrib.drt.optimizer; +import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STOP; import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.getBaseTypeOrElseThrow; import java.util.ArrayList; import java.util.List; -import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.schedule.DrtStopTask; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.schedule.DriveTask; @@ -36,31 +36,13 @@ import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; import org.matsim.contrib.dvrp.util.LinkTimePair; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; /** * @author michalm */ public class VehicleDataEntryFactoryImpl implements VehicleEntry.EntryFactory { - private final double lookAhead; - - public VehicleDataEntryFactoryImpl(DrtConfigGroup drtCfg) { - if (drtCfg.rejectRequestIfMaxWaitOrTravelTimeViolated) { - lookAhead = drtCfg.maxWaitTime - drtCfg.stopDuration; - Preconditions.checkArgument(lookAhead >= 0, - "maxWaitTime must not be smaller than stopDuration"); - } else { - // if no rejection due to max wait time, the look ahead is infinite - lookAhead = Double.POSITIVE_INFINITY; - } - } - public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { - if (isNotEligibleForRequestInsertion(vehicle, currentTime)) { - return null; - } - Schedule schedule = vehicle.getSchedule(); final LinkTimePair start; final Task startTask; @@ -88,9 +70,21 @@ public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { List tasks = schedule.getTasks(); List stopTasks = new ArrayList<>(); + + // find stop tasks and note down stay time before each task + double accumulatedStayTime = 0.0; + if (startTask != null && STAY.isBaseTypeOf(startTask)) { + accumulatedStayTime = Math.max(0.0, startTask.getEndTime() - currentTime); + } + + List precedingStayTimes = new ArrayList<>(); for (Task task : tasks.subList(nextTaskIdx, tasks.size())) { - if (STOP.isBaseTypeOf(task)) { + if (STAY.isBaseTypeOf(task)) { + accumulatedStayTime += task.getEndTime() - task.getBeginTime(); + } else if (STOP.isBaseTypeOf(task)) { stopTasks.add((DrtStopTask)task); + precedingStayTimes.add(accumulatedStayTime); + accumulatedStayTime = 0.0; } } @@ -100,31 +94,37 @@ 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, precedingStayTimes); return new VehicleEntry(vehicle, new Waypoint.Start(startTask, start.link, start.time, outgoingOccupancy), - ImmutableList.copyOf(stops), slackTimes); - } - - public boolean isNotEligibleForRequestInsertion(DvrpVehicle vehicle, double currentTime) { - return currentTime + lookAhead < vehicle.getServiceBeginTime() || currentTime >= vehicle.getServiceEndTime(); + ImmutableList.copyOf(stops), slackTimes, precedingStayTimes, currentTime); } - 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, List precedingStayTimes) { + 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; + slackTime += precedingStayTimes.get(i); // reset slack before prebooked request + 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..60f2d0f9766 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 @@ -20,6 +20,8 @@ package org.matsim.contrib.drt.optimizer; +import java.util.List; + import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import com.google.common.collect.ImmutableList; @@ -36,15 +38,19 @@ public interface EntryFactory { public final Waypoint.Start start; public final ImmutableList stops; public final Waypoint.End end; - private final double[] slackTimes;// for all insertion points + private final double[] slackTimes;// for all insertion points (start, stops, end) + private final List precedingStayTimes;// for all stops + public final double createTime; public VehicleEntry(DvrpVehicle vehicle, Waypoint.Start start, ImmutableList stops, - double[] slackTimes) { + double[] slackTimes, List precedingStayTimes, double createTime) { this.vehicle = vehicle; this.start = start; this.stops = stops; this.end = Waypoint.End.OPEN_END; this.slackTimes = slackTimes; + this.precedingStayTimes = precedingStayTimes; + this.createTime = createTime; } protected VehicleEntry(VehicleEntry that) { @@ -53,6 +59,8 @@ protected VehicleEntry(VehicleEntry that) { this.stops = that.stops; this.end = that.end; this.slackTimes = that.slackTimes; + this.precedingStayTimes = that.precedingStayTimes; + this.createTime = that.createTime; } public Waypoint getWaypoint(int index) { @@ -64,6 +72,14 @@ public boolean isAfterLastStop(int index) { } public double getSlackTime(int index) { - return slackTimes[index]; + return slackTimes[index + 1]; + } + + public double getStartSlackTime() { + return slackTimes[0]; + } + + public double getPrecedingStayTime(int index) { + return precedingStayTimes.get(index); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/depot/Depots.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/depot/Depots.java index 50919dde3af..3b2152eec90 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/depot/Depots.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/depot/Depots.java @@ -50,6 +50,13 @@ public static boolean isSwitchingFromStopToStay(DvrpVehicle vehicle) { if (!STAY.isBaseTypeOf(currentTask)) { return false; } + + // only if stay task is last task: with prebooking we may also idle during the day, but + // currently all the downstream relocation/charging logic assumes that we only stay at + // the end of the schedule + if (currentTask.getTaskIdx() < schedule.getTaskCount() - 1) { + return false; + } // previous task was STOP int previousTaskIdx = currentTask.getTaskIdx() - 1; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultIncrementalStopDurationEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultIncrementalStopDurationEstimator.java deleted file mode 100644 index 166847dd548..00000000000 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultIncrementalStopDurationEstimator.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.matsim.contrib.drt.optimizer.insertion; - -import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.dvrp.fleet.DvrpVehicle; - -/** - * Assumes a fixed stop duration for each pickup/dropoff. - * @author nkuehnel / MOIA - */ -public class DefaultIncrementalStopDurationEstimator implements IncrementalStopDurationEstimator { - - private final double fixedStopDuration; - - public DefaultIncrementalStopDurationEstimator(double fixedStopDuration) { - this.fixedStopDuration = fixedStopDuration; - } - - @Override - public double calcForPickup(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest pickupRequest) { - if(stopTask != null) { - return 0; - } else { - return fixedStopDuration; - } - } - - @Override - public double calcForDropoff(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest dropoffRequest) { - if(stopTask != null) { - return 0; - } else { - return fixedStopDuration; - } - } -} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java index 849d2dc02c4..1b16c140d50 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultInsertionCostCalculator.java @@ -50,8 +50,14 @@ public DefaultInsertionCostCalculator(CostCalculationStrategy costCalculationStr public double calculate(DrtRequest drtRequest, Insertion insertion, DetourTimeInfo detourTimeInfo) { var vEntry = insertion.vehicleEntry; + // in case of prebooking, we may have intermediate stay times after pickup + // insertion that may reduce the effective pickup delay that remains that the + // dropoff insertion point + double effectiveDropoffTimeLoss = InsertionDetourTimeCalculator.calculateRemainingPickupTimeLossAtDropoff( + insertion, detourTimeInfo.pickupDetourInfo) + detourTimeInfo.dropoffDetourInfo.dropoffTimeLoss; + if (vEntry.getSlackTime(insertion.pickup.index) < detourTimeInfo.pickupDetourInfo.pickupTimeLoss - || vEntry.getSlackTime(insertion.dropoff.index) < detourTimeInfo.getTotalTimeLoss()) { + || vEntry.getSlackTime(insertion.dropoff.index) < effectiveDropoffTimeLoss) { return INFEASIBLE_SOLUTION_COST; } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java index 056d5ae0d4e..0f8443f0141 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java @@ -38,6 +38,7 @@ import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; @@ -64,21 +65,22 @@ public class DefaultUnplannedRequestInserter implements UnplannedRequestInserter private final DrtRequestInsertionRetryQueue insertionRetryQueue; private final DrtOfferAcceptor drtOfferAcceptor; private final ForkJoinPool forkJoinPool; + private final PassengerStopDurationProvider stopDurationProvider; public DefaultUnplannedRequestInserter(DrtConfigGroup drtCfg, Fleet fleet, MobsimTimer mobsimTimer, EventsManager eventsManager, RequestInsertionScheduler insertionScheduler, VehicleEntry.EntryFactory vehicleEntryFactory, DrtInsertionSearch insertionSearch, DrtRequestInsertionRetryQueue insertionRetryQueue, DrtOfferAcceptor drtOfferAcceptor, - ForkJoinPool forkJoinPool) { + ForkJoinPool forkJoinPool, PassengerStopDurationProvider stopDurationProvider) { this(drtCfg.getMode(), fleet, mobsimTimer::getTimeOfDay, eventsManager, insertionScheduler, vehicleEntryFactory, - insertionRetryQueue, insertionSearch, drtOfferAcceptor, forkJoinPool); + insertionRetryQueue, insertionSearch, drtOfferAcceptor, forkJoinPool, stopDurationProvider); } @VisibleForTesting DefaultUnplannedRequestInserter(String mode, Fleet fleet, DoubleSupplier timeOfDay, EventsManager eventsManager, RequestInsertionScheduler insertionScheduler, VehicleEntry.EntryFactory vehicleEntryFactory, DrtRequestInsertionRetryQueue insertionRetryQueue, DrtInsertionSearch insertionSearch, - DrtOfferAcceptor drtOfferAcceptor, ForkJoinPool forkJoinPool) { + DrtOfferAcceptor drtOfferAcceptor, ForkJoinPool forkJoinPool, PassengerStopDurationProvider stopDurationProvider) { this.mode = mode; this.fleet = fleet; this.timeOfDay = timeOfDay; @@ -89,6 +91,7 @@ public DefaultUnplannedRequestInserter(DrtConfigGroup drtCfg, Fleet fleet, Mobsi this.insertionSearch = insertionSearch; this.drtOfferAcceptor = drtOfferAcceptor; this.forkJoinPool = forkJoinPool; + this.stopDurationProvider = stopDurationProvider; } @Override @@ -124,12 +127,12 @@ private void scheduleUnplannedRequest(DrtRequest req, Map, Vehic if (best.isEmpty()) { if (!insertionRetryQueue.tryAddFailedRequest(req, now)) { eventsManager.processEvent( - new PassengerRequestRejectedEvent(now, mode, req.getId(), req.getPassengerId(), + new PassengerRequestRejectedEvent(now, mode, req.getId(), req.getPassengerIds(), NO_INSERTION_FOUND_CAUSE)); log.debug("No insertion found for drt request " + req - + " from passenger id=" - + req.getPassengerId() + + " with passenger ids=" + + req.getPassengerIds().stream().map(Object::toString).collect(Collectors.joining(",")) + " fromLinkId=" + req.getFromLink().getId()); } @@ -151,10 +154,16 @@ private void scheduleUnplannedRequest(DrtRequest req, Map, Vehic vehicleEntries.remove(vehicle.getId()); } + double expectedPickupTime = pickupDropoffTaskPair.pickupTask.getBeginTime(); + expectedPickupTime = Math.max(expectedPickupTime, acceptedRequest.get().getEarliestStartTime()); + expectedPickupTime += stopDurationProvider.calcPickupDuration(vehicle, req); + + double expectedDropoffTime = pickupDropoffTaskPair.dropoffTask.getBeginTime(); + expectedDropoffTime += stopDurationProvider.calcDropoffDuration(vehicle, req); + eventsManager.processEvent( - new PassengerRequestScheduledEvent(now, mode, req.getId(), req.getPassengerId(), vehicle.getId(), - pickupDropoffTaskPair.pickupTask.getEndTime(), - pickupDropoffTaskPair.dropoffTask.getBeginTime())); + new PassengerRequestScheduledEvent(now, mode, req.getId(), req.getPassengerIds(), vehicle.getId(), + expectedPickupTime, expectedDropoffTime)); } } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/IncrementalStopDurationEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/IncrementalStopDurationEstimator.java deleted file mode 100644 index 00e80d799d3..00000000000 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/IncrementalStopDurationEstimator.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.matsim.contrib.drt.optimizer.insertion; - -import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.dvrp.fleet.DvrpVehicle; - -public interface IncrementalStopDurationEstimator { - - //if stopTask is null - it's a new (potential) stop - double calcForPickup(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest pickupRequest); - - //if stopTask is null - it's a new (potential) stop - double calcForDropoff(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest dropoffRequest); -} 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 828a712b3a6..2de0d4dd640 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 @@ -8,17 +8,18 @@ import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.InsertionPoint; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData.InsertionDetourData; - -import com.google.common.base.MoreObjects; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.stops.StopTimeCalculator; + +import com.google.common.base.MoreObjects; /** * @author Michal Maciejewski (michalm) */ public class InsertionDetourTimeCalculator { - private final IncrementalStopDurationEstimator stopDurationEstimator; + private final StopTimeCalculator stopTimeCalculator; // If the detour data uses approximated detour times (e.g. beeline or matrix-based), provide the same estimator for // replaced drives to avoid systematic errors @@ -26,9 +27,9 @@ public class InsertionDetourTimeCalculator { @Nullable private final DetourTimeEstimator replacedDriveTimeEstimator; - public InsertionDetourTimeCalculator(IncrementalStopDurationEstimator incrementalStopDurationEstimator, + public InsertionDetourTimeCalculator(StopTimeCalculator stopTimeCalculator, @Nullable DetourTimeEstimator replacedDriveTimeEstimator) { - this.stopDurationEstimator = incrementalStopDurationEstimator; + this.stopTimeCalculator = stopTimeCalculator; this.replacedDriveTimeEstimator = replacedDriveTimeEstimator; } @@ -57,15 +58,15 @@ public PickupDetourInfo calcPickupDetourInfo(VehicleEntry vEntry, InsertionPoint double toPickupDepartureTime = pickup.previousWaypoint.getDepartureTime(); if (pickup.newWaypoint.getLink() == pickup.previousWaypoint.getLink()) { // no detour, but possible additional stop duration - double additionalStopDuration = calcAdditionalPickupStopDurationIfSameLinkAsPrevious(vEntry, pickup.index, drtRequest); - double departureTime = toPickupDepartureTime + additionalStopDuration; - return new PickupDetourInfo(departureTime, additionalStopDuration); + var times = calculatePickupIfSameLink(vEntry, pickup.index, toPickupDepartureTime, drtRequest); + return new PickupDetourInfo(times.departureTime, times.additionalStopDuration); } - + + double arrivalTime = toPickupDepartureTime + toPickupTT; + double departureTime = stopTimeCalculator.initEndTimeForPickup(vEntry.vehicle, arrivalTime, drtRequest); + double stopDuration = departureTime - arrivalTime; double replacedDriveTT = calculateReplacedDriveDuration(vEntry, pickup.index, toPickupDepartureTime); - double stopDuration = stopDurationEstimator.calcForPickup(vEntry.vehicle, null, drtRequest); double pickupTimeLoss = toPickupTT + stopDuration + fromPickupTT - replacedDriveTT; - double departureTime = toPickupDepartureTime + toPickupTT + stopDuration; return new PickupDetourInfo(departureTime, pickupTimeLoss); } @@ -80,75 +81,100 @@ public DropoffDetourInfo calcDropoffDetourInfo(Insertion insertion, double toDro VehicleEntry vEntry = insertion.vehicleEntry; if (dropoff.newWaypoint.getLink() == dropoff.previousWaypoint.getLink()) { - double dropoffTimeLoss = calcAdditionalDropoffStopDurationIfSameLinkAsPrevious(vEntry, insertion.dropoff.index, drtRequest); - double arrivalTime = dropoff.previousWaypoint.getArrivalTime() + pickupDetourInfo.pickupTimeLoss; - return new DropoffDetourInfo(arrivalTime, dropoffTimeLoss); + double remainingPickupTimeLoss = calculateRemainingPickupTimeLossAtDropoff(insertion, pickupDetourInfo); + double arrivalTime = dropoff.previousWaypoint.getArrivalTime() + remainingPickupTimeLoss; + + DrtStopTask stopTask = findStopTaskIfSameLinkAsPrevious(vEntry, dropoff.index); + double departureTime = stopTimeCalculator.updateEndTimeForDropoff(vEntry.vehicle, stopTask, arrivalTime, drtRequest); + + double initialStopDuration = stopTask.getEndTime() - stopTask.getBeginTime(); + double additionalStopDuration = departureTime - arrivalTime - initialStopDuration; + + return new DropoffDetourInfo(arrivalTime, additionalStopDuration); } double toDropoffDepartureTime = dropoff.previousWaypoint.getDepartureTime(); + double remainingPickupTimeLoss = calculateRemainingPickupTimeLossAtDropoff(insertion, pickupDetourInfo); + double arrivalTime = toDropoffDepartureTime + remainingPickupTimeLoss + toDropoffTT; + double departureTime = stopTimeCalculator.initEndTimeForDropoff(vEntry.vehicle, arrivalTime, drtRequest); + double stopDuration = departureTime - arrivalTime; double replacedDriveTT = calculateReplacedDriveDuration(vEntry, dropoff.index, toDropoffDepartureTime); - double dropoffTimeLoss = toDropoffTT + stopDurationEstimator.calcForDropoff(vEntry.vehicle, null, drtRequest) + fromDropoffTT - replacedDriveTT; - double arrivalTime = toDropoffDepartureTime + pickupDetourInfo.pickupTimeLoss + toDropoffTT; + double dropoffTimeLoss = toDropoffTT + stopDuration + fromDropoffTT - replacedDriveTT; return new DropoffDetourInfo(arrivalTime, dropoffTimeLoss); } private PickupDetourInfo calcPickupDetourInfoIfPickupToDropoffDetour(VehicleEntry vEntry, InsertionPoint pickup, double toPickupTT, double fromPickupTT, DrtRequest drtRequest) { - final double additionalPickupStopDuration; + double toPickupDepartureTime = pickup.previousWaypoint.getDepartureTime(); + + final double departureTime; + final double additionalStopDuration; + if (pickup.newWaypoint.getLink() == pickup.previousWaypoint.getLink()) { // no drive to pickup, but possible additional stop duration - additionalPickupStopDuration = calcAdditionalPickupStopDurationIfSameLinkAsPrevious(vEntry, pickup.index, drtRequest); + var times = calculatePickupIfSameLink(vEntry, pickup.index, toPickupDepartureTime, drtRequest); + departureTime = times.departureTime; + additionalStopDuration = times.additionalStopDuration; } else { - additionalPickupStopDuration = stopDurationEstimator.calcForPickup(vEntry.vehicle, null, drtRequest); + departureTime = stopTimeCalculator.initEndTimeForPickup(vEntry.vehicle, toPickupDepartureTime + toPickupTT, drtRequest); + additionalStopDuration = departureTime - toPickupDepartureTime - toPickupTT; } - - double toPickupDepartureTime = pickup.previousWaypoint.getDepartureTime(); + double replacedDriveTT = calculateReplacedDriveDuration(vEntry, pickup.index, toPickupDepartureTime); - - double pickupTimeLoss = toPickupTT + additionalPickupStopDuration + fromPickupTT - replacedDriveTT; - double departureTime = toPickupDepartureTime + toPickupTT + additionalPickupStopDuration; + double pickupTimeLoss = toPickupTT + additionalStopDuration + fromPickupTT - replacedDriveTT; return new PickupDetourInfo(departureTime, pickupTimeLoss); } private DropoffDetourInfo calcDropoffDetourInfoIfPickupToDropoffDetour(double fromPickupToDropoffTT, double fromDropoffTT, PickupDetourInfo pickupDetourInfo, VehicleEntry vEntry, DrtRequest drtRequest) { - double dropoffTimeLoss = stopDurationEstimator.calcForDropoff(vEntry.vehicle, null, drtRequest) + fromDropoffTT; double arrivalTime = pickupDetourInfo.departureTime + fromPickupToDropoffTT; + double departureTime = stopTimeCalculator.initEndTimeForDropoff(vEntry.vehicle, arrivalTime, drtRequest); + double stopDuration = departureTime - arrivalTime; + double dropoffTimeLoss = stopDuration + fromDropoffTT; return new DropoffDetourInfo(arrivalTime, dropoffTimeLoss); } - - private double calcAdditionalPickupStopDurationIfSameLinkAsPrevious(VehicleEntry vEntry, int insertionIdx, DrtRequest drtRequest) { - DrtStopTask stopTask; - - if (insertionIdx == 0) { - var startTask = vEntry.start.task; - - if (startTask.isPresent() && STOP.isBaseTypeOf(startTask.get())) { - stopTask = (DrtStopTask) startTask.get(); - } else { - stopTask = null; - } + + private PickupTimeInfo calculatePickupIfSameLink(VehicleEntry vEntry, int pickupIndex, double toPickupDepartureTime, DrtRequest request) { + DrtStopTask stopTask = findStopTaskIfSameLinkAsPrevious(vEntry, pickupIndex); + + if (stopTask == null) { + // case 1: previous waypoint is a drive, we create a new stop + // insertion time is the end time of previous waypoint + double departureTime = stopTimeCalculator.initEndTimeForPickup(vEntry.vehicle, toPickupDepartureTime, + request); + double stopDuration = departureTime - toPickupDepartureTime; + return new PickupTimeInfo(departureTime, stopDuration); + } else if (pickupIndex > 0) { + // case 2: previous waypoint is a planned stop, not started yet + // insertion time is the beginning of the planned stop + double insertionTime = stopTask.getBeginTime(); + double departureTime = stopTimeCalculator.updateEndTimeForPickup(vEntry.vehicle, stopTask, + insertionTime, request); + double additionalStopDuration = departureTime - stopTask.getEndTime(); + return new PickupTimeInfo(departureTime, additionalStopDuration); } else { - stopTask = vEntry.stops.get(insertionIdx - 1).task; + // case 3: previous waypoint is an ongoing (started) stop + // 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); } - return stopDurationEstimator.calcForPickup(vEntry.vehicle, stopTask, drtRequest); } - - private double calcAdditionalDropoffStopDurationIfSameLinkAsPrevious(VehicleEntry vEntry, int insertionIdx, DrtRequest drtRequest) { - DrtStopTask stopTask; - + + private DrtStopTask findStopTaskIfSameLinkAsPrevious(VehicleEntry vEntry, int insertionIdx) { if (insertionIdx == 0) { var startTask = vEntry.start.task; - + if (startTask.isPresent() && STOP.isBaseTypeOf(startTask.get())) { - stopTask = (DrtStopTask) startTask.get(); - } else { - stopTask = null; + // case 1: we have a preceding and ongoing (started) stop task + return (DrtStopTask) startTask.get(); } } else { - stopTask = vEntry.stops.get(insertionIdx - 1).task; + // case 2: we have a preceding (planned) stop task + return (DrtStopTask) vEntry.stops.get(insertionIdx - 1).task; } - return stopDurationEstimator.calcForDropoff(vEntry.vehicle, stopTask, drtRequest); + + return null; // otherwise, there is no stop task before } private double calculateReplacedDriveDuration(VehicleEntry vEntry, int insertionIdx, double detourStartTime) { @@ -164,7 +190,33 @@ private double calculateReplacedDriveDuration(VehicleEntry vEntry, int insertion double replacedDriveStartTime = vEntry.getWaypoint(insertionIdx).getDepartureTime(); double replacedDriveEndTime = vEntry.stops.get(insertionIdx).task.getBeginTime(); - return replacedDriveEndTime - replacedDriveStartTime; + + // reduce by the idle time before the next stop, to get the actual drive time + return replacedDriveEndTime - replacedDriveStartTime - vEntry.getPrecedingStayTime(insertionIdx); + } + + /* + * When inserting a pickup, we generate a "pickup loss" which describes by how + * much time we have to shift all following tasks to the future. + * + * In the case that some of the following stops are prebooked, however, there + * may be a stay time buffer between the insertion point and the stop. Hence, if + * a following stop only happens in four hours, we may not need to shift the + * task to the future. A preceding stay time, hence, reduces the introduced + * pickup loss. + * + * The present function calculates the remaining pickup loss at the dropoff + * insertion point after deducting all the stay times up to the dropoff. + */ + public static double calculateRemainingPickupTimeLossAtDropoff(Insertion insertion, PickupDetourInfo pickupDetourInfo) { + VehicleEntry vEntry = insertion.vehicleEntry; + double remainingPickupTimeLoss = pickupDetourInfo.pickupTimeLoss; + + for (int i = insertion.pickup.index + 1; i < insertion.dropoff.index; i++) { + remainingPickupTimeLoss = Math.max(remainingPickupTimeLoss - vEntry.getPrecedingStayTime(i), 0.0); + } + + return remainingPickupTimeLoss; } public static class PickupDetourInfo { @@ -233,4 +285,7 @@ public String toString() { .toString(); } } + + private record PickupTimeInfo(double departureTime, double 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 783159aa607..60e27e4bc5a 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 @@ -20,6 +20,7 @@ package org.matsim.contrib.drt.optimizer.insertion; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.matsim.contrib.drt.optimizer.VehicleEntry; @@ -27,6 +28,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.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; @@ -140,23 +145,57 @@ public String toString() { private final DetourTimeEstimator detourTimeEstimator; private final InsertionDetourTimeCalculator detourTimeCalculator; - public InsertionGenerator(IncrementalStopDurationEstimator stopDurationEstimator, DetourTimeEstimator detourTimeEstimator) { + public InsertionGenerator(StopTimeCalculator stopTimeCalculator, DetourTimeEstimator detourTimeEstimator) { this.detourTimeEstimator = detourTimeEstimator; - detourTimeCalculator = new InsertionDetourTimeCalculator(stopDurationEstimator, detourTimeEstimator); + detourTimeCalculator = new InsertionDetourTimeCalculator(stopTimeCalculator, detourTimeEstimator); } public List generateInsertions(DrtRequest drtRequest, VehicleEntry vEntry) { int stopCount = vEntry.stops.size(); List insertions = new ArrayList<>(); + + if (drtRequest.getPassengerCount() > vEntry.vehicle.getCapacity()) { + //exit early + return Collections.EMPTY_LIST; + } + int occupancy = vEntry.start.occupancy; + for (int i = 0; i < stopCount; i++) {// insertions up to before last stop Waypoint.Stop nextStop = nextStop(vEntry, i); - if (occupancy < vEntry.vehicle.getCapacity()) {// only not fully loaded arcs + // (1) only not fully loaded arcs + boolean allowed = occupancy + drtRequest.getPassengerCount() <= vEntry.vehicle.getCapacity(); + + // (2) check if the request wants to depart after the departure time of the next + // stop. We can early on filter out the current insertion, because we will + // neither be able to insert our stop before the next stop nor merge the request + // into it. + allowed &= drtRequest.getEarliestStartTime() <= nextStop.getDepartureTime(); + + if (allowed) { if (drtRequest.getFromLink() != nextStop.task.getLink()) {// next stop at different link generateDropoffInsertions(drtRequest, vEntry, i, insertions); + } else { + // this is the case where we insert a new request *before* a stop that is + // on the same link as the pickup link. Initially, the reasoning was that the + // new request will be merged *into* the existing task if all constraints hold, + // i.e. the request will be appended. So only the insertion *after* this task is + // necessary to evaluate. However, with prebooking, the situation is different: + // if the next task is prebooked (in the future), we may want to insert another + // task here on the same link (maybe a pickup followed by its dropoff) but much + // earlier. In that case it is actually a valid insertion. + + if (drtRequest.getEarliestStartTime() < nextStop.getArrivalTime()) { + // the new request wants to depart before the start of the next stop, which may + // be a viable insertion. Note that if the requested wanted to depart after the + // start of the next stop, but before its end, this is a special case that is + // covered further downstream as a special case of merging the pickup into the + // existing stop task. + + generateDropoffInsertions(drtRequest, vEntry, i, insertions); + } } - // else: do not evaluate insertion _before_stop i, evaluate only insertion _after_ stop i } occupancy = nextStop.outgoingOccupancy; @@ -173,12 +212,18 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, double toPickupDepartureTime = pickupInsertion.previousWaypoint.getDepartureTime(); double toPickupTT = detourTimeEstimator.estimateTime(pickupInsertion.previousWaypoint.getLink(), request.getFromLink(), toPickupDepartureTime); + double earliestPickupStartTime = Math.max(toPickupDepartureTime + toPickupTT, request.getEarliestStartTime()); double fromPickupTT = detourTimeEstimator.estimateTime(request.getFromLink(), pickupInsertion.nextWaypoint.getLink(), - toPickupDepartureTime + toPickupTT); //TODO stopDuration not included + earliestPickupStartTime); //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 if (vEntry.getSlackTime(i) >= pickupDetourInfo.pickupTimeLoss) { @@ -201,7 +246,7 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, //calculate it once for all j > i pickupInsertion = createPickupInsertion(request, vEntry, i, false); fromPickupTT = detourTimeEstimator.estimateTime(request.getFromLink(), pickupInsertion.nextWaypoint.getLink(), - toPickupDepartureTime + toPickupTT); //TODO stopDuration not included + earliestPickupStartTime); //TODO stopDuration not included pickupDetourInfo = detourTimeCalculator.calcPickupDetourInfo(vEntry, pickupInsertion, toPickupTT, fromPickupTT, false, request); @@ -213,7 +258,7 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, // i -> pickup -> i+1 && j -> dropoff -> j+1 // check the capacity constraints if i < j (already validated for `i == j`) Waypoint.Stop currentStop = currentStop(vEntry, j); - if (currentStop.outgoingOccupancy == vEntry.vehicle.getCapacity()) { + if (currentStop.outgoingOccupancy + request.getPassengerCount() > vEntry.vehicle.getCapacity()) { if (request.getToLink() == currentStop.task.getLink()) { //special case -- we can insert dropoff exactly at node j addInsertion(insertions, @@ -251,6 +296,26 @@ 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) { var dropoffInsertion = createDropoffInsertion(request, vehicleEntry, pickupInsertion, dropoffIdx); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java index 877821f83be..c58eaeb9bdc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProvider.java @@ -32,6 +32,8 @@ import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.zone.skims.TravelTimeMatrix; import org.matsim.core.router.util.TravelTime; @@ -43,12 +45,12 @@ class ExtensiveInsertionProvider { static ExtensiveInsertionProvider create(DrtConfigGroup drtCfg, InsertionCostCalculator insertionCostCalculator, TravelTimeMatrix travelTimeMatrix, TravelTime travelTime, ForkJoinPool forkJoinPool, - IncrementalStopDurationEstimator incrementalStopDurationEstimator) { + StopTimeCalculator stopTimeCalculator) { var insertionParams = (ExtensiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(); var admissibleTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( insertionParams.admissibleBeelineSpeedFactor, travelTimeMatrix, travelTime); return new ExtensiveInsertionProvider((ExtensiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(), - insertionCostCalculator, new InsertionGenerator(incrementalStopDurationEstimator, admissibleTimeEstimator), + insertionCostCalculator, new InsertionGenerator(stopTimeCalculator, admissibleTimeEstimator), forkJoinPool); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearch.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearch.java index d72a07bd2af..35b0cefdde2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearch.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearch.java @@ -26,6 +26,8 @@ import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.insertion.*; import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; /** * @author michalm @@ -38,10 +40,10 @@ final class ExtensiveInsertionSearch implements DrtInsertionSearch { public ExtensiveInsertionSearch(ExtensiveInsertionProvider insertionProvider, MultiInsertionDetourPathCalculator detourPathCalculator, InsertionCostCalculator insertionCostCalculator, - IncrementalStopDurationEstimator incrementalStopDurationEstimator) { + StopTimeCalculator stopTimeCalculator) { this.insertionProvider = insertionProvider; this.detourPathCalculator = detourPathCalculator; - this.detourTimeCalculator = new InsertionDetourTimeCalculator(incrementalStopDurationEstimator, null); + this.detourTimeCalculator = new InsertionDetourTimeCalculator(stopTimeCalculator, null); this.bestInsertionFinder = new BestInsertionFinder(insertionCostCalculator); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java index c2f47c2fd2c..cfeaa94ec38 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionSearchQSimModule.java @@ -23,9 +23,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; -import org.matsim.contrib.drt.optimizer.insertion.IncrementalStopDurationEstimator; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.zone.skims.TravelTimeMatrix; @@ -51,9 +52,9 @@ protected void configureQSim() { var insertionCostCalculator = getter.getModal(InsertionCostCalculator.class); var provider = ExtensiveInsertionProvider.create(drtCfg, insertionCostCalculator, getter.getModal(TravelTimeMatrix.class), getter.getModal(TravelTime.class), - getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(IncrementalStopDurationEstimator.class)); + getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(StopTimeCalculator.class)); return new ExtensiveInsertionSearch(provider, getter.getModal(MultiInsertionDetourPathCalculator.class), - insertionCostCalculator, getter.getModal(IncrementalStopDurationEstimator.class)); + insertionCostCalculator, getter.getModal(StopTimeCalculator.class)); })).asEagerSingleton(); addModalComponent(MultiInsertionDetourPathCalculator.class, diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/DetourTimeEstimatorWithAdaptiveTravelTimes.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/DetourTimeEstimatorWithAdaptiveTravelTimes.java new file mode 100644 index 00000000000..2187a889841 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/DetourTimeEstimatorWithAdaptiveTravelTimes.java @@ -0,0 +1,69 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.drt.optimizer.insertion.repeatedselective; + +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Node; +import org.matsim.contrib.drt.optimizer.insertion.DetourTimeEstimator; +import org.matsim.contrib.dvrp.path.VrpPaths; +import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrix; +import org.matsim.core.router.util.TravelTime; + +import static org.matsim.contrib.dvrp.path.VrpPaths.FIRST_LINK_TT; + +/** + * @author steffenaxer + */ +public class DetourTimeEstimatorWithAdaptiveTravelTimes implements DetourTimeEstimator { + + private final TravelTime travelTime; + private final AdaptiveTravelTimeMatrix adaptiveTravelTimeMatrix; + private final double speedFactor; + + DetourTimeEstimatorWithAdaptiveTravelTimes(double speedFactor, AdaptiveTravelTimeMatrix adaptiveTravelTimeMatrix, + TravelTime travelTime) { + this.speedFactor = speedFactor; + this.adaptiveTravelTimeMatrix = adaptiveTravelTimeMatrix; + this.travelTime = travelTime; + } + + public static DetourTimeEstimatorWithAdaptiveTravelTimes create(double speedFactor, AdaptiveTravelTimeMatrix updatableTravelTimeMatrix, + TravelTime travelTime) { + return new DetourTimeEstimatorWithAdaptiveTravelTimes(speedFactor, updatableTravelTimeMatrix, travelTime); + } + + @Override + public double estimateTime(Link from, Link to, double departureTime) { + + if (from == to) { + return 0; + } + + double duration = FIRST_LINK_TT; + duration += this.getTravelTime(from.getToNode(), to.getFromNode(), departureTime + duration); + duration += VrpPaths.getLastLinkTT(travelTime, to, departureTime + duration); + return duration / speedFactor; + } + + double getTravelTime(Node fromNode, Node toNode, double departureTime) { + return this.adaptiveTravelTimeMatrix.getTravelTime(fromNode, toNode, departureTime); + } + +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.java new file mode 100644 index 00000000000..aa3e2d8db06 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionProvider.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 org.matsim.contrib.drt.optimizer.insertion.repeatedselective; + +import com.google.common.annotations.VisibleForTesting; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.optimizer.insertion.*; +import org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder.InsertionWithCost; +import static org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder.INSERTION_WITH_COST_COMPARATOR; +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrix; +import org.matsim.core.router.util.TravelTime; + +import static org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator.INFEASIBLE_SOLUTION_COST; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; + +/** + * @author steffenaxer + */ +class RepeatedSelectiveInsertionProvider { + private static final double SPEED_FACTOR = 1.; + private final InsertionCostCalculator insertionCostCalculator; + private final InsertionGenerator insertionGenerator; + private final ForkJoinPool forkJoinPool; + @VisibleForTesting + RepeatedSelectiveInsertionProvider(InsertionCostCalculator insertionCostCalculator, + InsertionGenerator insertionGenerator, + ForkJoinPool forkJoinPool) { + this.insertionCostCalculator = insertionCostCalculator; + this.insertionGenerator = insertionGenerator; + this.forkJoinPool = forkJoinPool; + } + + public static RepeatedSelectiveInsertionProvider create( InsertionCostCalculator insertionCostCalculator, AdaptiveTravelTimeMatrix updatableTravelTimeMatrix, + TravelTime travelTime, + ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator) { + var detourTimeEstimatorWithUpdatedTravelTimes = DetourTimeEstimatorWithAdaptiveTravelTimes.create( + SPEED_FACTOR, updatableTravelTimeMatrix, travelTime); + return new RepeatedSelectiveInsertionProvider(insertionCostCalculator, + new InsertionGenerator(stopTimeCalculator, detourTimeEstimatorWithUpdatedTravelTimes), forkJoinPool); + } + + public List getInsertions(DrtRequest drtRequest, Collection vehicleEntries) { + // Parallel outer stream over vehicle entries. The inner stream (flatmap) is + // sequential. + List preFilteredInsertions = forkJoinPool.submit(() -> vehicleEntries.parallelStream() + // generate feasible insertions (wrt occupancy limits) with admissible detour + // times + .flatMap(e -> insertionGenerator.generateInsertions(drtRequest, e).stream()) + .map(i -> new InsertionWithCost(i, + this.insertionCostCalculator.calculate(drtRequest, i.insertion, i.detourTimeInfo))) + // optimistic pre-filtering wrt admissible cost function (SPEED_FACTOR 1.0) + .filter(iWithCost -> iWithCost.cost < INFEASIBLE_SOLUTION_COST) + // sort all found insertions + .sorted(INSERTION_WITH_COST_COMPARATOR) + .map(iWithCost -> iWithCost.insertionWithDetourData) + // collect + .collect(Collectors.toList())).join(); + + if (preFilteredInsertions.isEmpty()) { + return List.of(); + } + + return preFilteredInsertions; + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearch.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearch.java new file mode 100644 index 00000000000..33d3ab25fbb --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearch.java @@ -0,0 +1,171 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.drt.optimizer.insertion.repeatedselective; + +import com.opencsv.CSVWriter; +import org.apache.commons.math3.stat.descriptive.SummaryStatistics; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.optimizer.insertion.*; +import org.matsim.contrib.drt.optimizer.insertion.selective.SingleInsertionDetourPathCalculator; +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.dvrp.path.OneToManyPathSearch; +import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrix; +import org.matsim.contrib.zone.skims.TravelTimeMatrix; +import org.matsim.core.controler.MatsimServices; +import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener; +import org.matsim.core.router.util.LeastCostPathCalculator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator.INFEASIBLE_SOLUTION_COST; +import static org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DetourTimeInfo; + +/** + * @author steffenaxer + */ +final class RepeatedSelectiveInsertionSearch implements DrtInsertionSearch, MobsimBeforeCleanupListener { + private final RepeatedSelectiveInsertionProvider insertionProvider; + private final SingleInsertionDetourPathCalculator detourPathCalculator; + private final InsertionDetourTimeCalculator detourTimeCalculator; + private final InsertionCostCalculator insertionCostCalculator; + private final MatsimServices matsimServices; + private final String mode; + private final TravelTimeMatrix travelTimeMatrix; + private final AdaptiveTravelTimeMatrix adaptiveTravelTimeMatrix; + private final RepeatedSelectiveInsertionSearchParams insertionSearchParams; + + public RepeatedSelectiveInsertionSearch(RepeatedSelectiveInsertionProvider insertionProvider, + SingleInsertionDetourPathCalculator detourPathCalculator, InsertionCostCalculator insertionCostCalculator, + DrtConfigGroup drtCfg, MatsimServices matsimServices, StopTimeCalculator stopTimeCalculator, TravelTimeMatrix travelTimeMatrix, AdaptiveTravelTimeMatrix adaptiveTravelTimeMatrix) { + this.insertionSearchParams = (RepeatedSelectiveInsertionSearchParams) drtCfg.getDrtInsertionSearchParams(); + this.insertionProvider = insertionProvider; + this.detourPathCalculator = detourPathCalculator; + this.insertionCostCalculator = insertionCostCalculator; + this.detourTimeCalculator = new InsertionDetourTimeCalculator(stopTimeCalculator, null); + this.matsimServices = matsimServices; + this.mode = drtCfg.getMode(); + this.travelTimeMatrix = travelTimeMatrix; + this.adaptiveTravelTimeMatrix = adaptiveTravelTimeMatrix; + } + + @Override + public Optional findBestInsertion(DrtRequest drtRequest, + Collection vehicleEntries) { + List sortedInsertions = insertionProvider.getInsertions(drtRequest, vehicleEntries); + if (sortedInsertions.isEmpty()) { + return Optional.empty(); + } + return validateInsertionWithPathCalculation(sortedInsertions, drtRequest, insertionSearchParams.retryInsertion); + } + + Optional validateInsertionWithPathCalculation(List selectedInsertionList, + DrtRequest drtRequest, int tryKBest) { + int n = Math.min(selectedInsertionList.size(), tryKBest); + for (int i = 0; i < n; i++) { + var selectedInsertion = selectedInsertionList.get(i); + var insertion = selectedInsertion.insertion; + var insertionDetourData = detourPathCalculator.calculatePaths(drtRequest, insertion); + var insertionWithDetourData = new InsertionWithDetourData(insertion, insertionDetourData, + detourTimeCalculator.calculateDetourTimeInfo(insertion, insertionDetourData, drtRequest)); + + collectDifferences(drtRequest, selectedInsertion.detourTimeInfo, insertionWithDetourData.detourTimeInfo); + + double insertionCost = insertionCostCalculator.calculate(drtRequest, insertion, + insertionWithDetourData.detourTimeInfo); + + // For each realized routing, we update the adaptiveTravelTimeMatrix + // The idea is to get a passively updated travel time estimation, without additional routing costs + updateMatrix(drtRequest, travelTimeMatrix, adaptiveTravelTimeMatrix, insertionWithDetourData); + + if (insertionCost < INFEASIBLE_SOLUTION_COST) { + return Optional.of(insertionWithDetourData); + } + } + return Optional.empty(); + } + + private final Map pickupTimeLossStats = new LinkedHashMap<>(); + private final Map dropoffTimeLossStats = new LinkedHashMap<>(); + + private void collectDifferences(DrtRequest request, DetourTimeInfo matrixTimeInfo, DetourTimeInfo networkTimeInfo) { + addRelativeDiff(matrixTimeInfo.pickupDetourInfo.pickupTimeLoss, networkTimeInfo.pickupDetourInfo.pickupTimeLoss, + networkTimeInfo.pickupDetourInfo.departureTime, pickupTimeLossStats); + addRelativeDiff(matrixTimeInfo.dropoffDetourInfo.dropoffTimeLoss, + networkTimeInfo.dropoffDetourInfo.dropoffTimeLoss, networkTimeInfo.dropoffDetourInfo.arrivalTime, + dropoffTimeLossStats); + } + + private void updateMatrix(DrtRequest request, TravelTimeMatrix travelTimeMatrix, AdaptiveTravelTimeMatrix updatableTravelTimeMatrix, InsertionWithDetourData insertionWithDetourData) { + updateMatrix(request, travelTimeMatrix, updatableTravelTimeMatrix, insertionWithDetourData.detourData.detourToPickup); + updateMatrix(request, travelTimeMatrix, updatableTravelTimeMatrix, insertionWithDetourData.detourData.detourFromPickup); + updateMatrix(request, travelTimeMatrix, updatableTravelTimeMatrix, insertionWithDetourData.detourData.detourToDropoff); + updateMatrix(request, travelTimeMatrix, updatableTravelTimeMatrix, insertionWithDetourData.detourData.detourFromDropoff); + } + + private static void updateMatrix(DrtRequest request, TravelTimeMatrix travelTimeMatrix, AdaptiveTravelTimeMatrix updatableTravelTimeMatrix, OneToManyPathSearch.PathData pathData) { + if(pathData!=null && pathData.getPath().links!=null) + { + LeastCostPathCalculator.Path path = pathData.getPath(); + double ttRoutedEstimate = pathData.getTravelTime(); + updatableTravelTimeMatrix.setTravelTime(path.getFromNode(), path.getToNode(), ttRoutedEstimate, request.getEarliestStartTime()); + } + } + + + private void addRelativeDiff(double matrixVal, double networkVal, double eventTime, + Map summaryStatsMap) { + int timeBin = (int)(eventTime / 3600); + var stats = summaryStatsMap.computeIfAbsent(timeBin, b -> new SummaryStatistics()); + if (matrixVal == 0 && networkVal == 0) { + stats.addValue(0); + } else if (networkVal != 0) { + stats.addValue((matrixVal - networkVal) / networkVal); + } + } + + @Override + public void notifyMobsimBeforeCleanup(@SuppressWarnings("rawtypes") MobsimBeforeCleanupEvent event) { + String filename = matsimServices.getControlerIO() + .getIterationFilename(matsimServices.getIterationNumber(), + mode + "_selective_insertion_detour_time_estimation_errors.csv"); + try (CSVWriter writer = new CSVWriter(Files.newBufferedWriter(Paths.get(filename)), ';', '"', '"', "\n");) { + writer.writeNext(new String[] { "type", "hour", "count", "mean", "std_dev", "min", "max" }, false); + pickupTimeLossStats.forEach((hour, stats) -> printStats(writer, "pickup", hour, stats)); + dropoffTimeLossStats.forEach((hour, stats) -> printStats(writer, "dropoff", hour, stats)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void printStats(CSVWriter writer, String type, int hour, SummaryStatistics stats) { + writer.writeNext(new String[] { type, hour + "", stats.getN() + "", stats.getMean() + "", + stats.getStandardDeviation() + "", stats.getMin() + "", stats.getMax() + "" }, false); + } +} diff --git a/contribs/eventsBasedPTRouter/src/main/java/org/matsim/contrib/eventsBasedPTRouter/waitTimes/WaitTimeData.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchParams.java similarity index 69% rename from contribs/eventsBasedPTRouter/src/main/java/org/matsim/contrib/eventsBasedPTRouter/waitTimes/WaitTimeData.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchParams.java index 645a1a39efd..8e530711aff 100644 --- a/contribs/eventsBasedPTRouter/src/main/java/org/matsim/contrib/eventsBasedPTRouter/waitTimes/WaitTimeData.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchParams.java @@ -1,39 +1,39 @@ -/* *********************************************************************** * - * project: org.matsim.* - * WaitTimeCalculator.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2012 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.eventsBasedPTRouter.waitTimes; - -import java.io.Serializable; - -/** - * Structure for saving waiting times - * - * @author sergioo - */ - -public interface WaitTimeData extends Serializable { - - //Methods - void resetWaitTimes(); - void addWaitTime(final int timeSlot, final double waitTime); - double getWaitTime(final int timeSlot); - int getNumData(final int timeSlot); - -} +/* *********************************************************************** * + * 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 org.matsim.contrib.drt.optimizer.insertion.repeatedselective; + +import jakarta.validation.constraints.Positive; +import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearchParams; + +/** + * @author steffenaxer + */ +public class RepeatedSelectiveInsertionSearchParams extends DrtInsertionSearchParams { + public static final String SET_NAME = "RepeatedSelectiveInsertionSearch"; + + @Parameter + @Positive + public int retryInsertion = 5; + + public RepeatedSelectiveInsertionSearchParams() { + super(SET_NAME); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java new file mode 100644 index 00000000000..2f15a6638d8 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/repeatedselective/RepeatedSelectiveInsertionSearchQSimModule.java @@ -0,0 +1,79 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.drt.optimizer.insertion.repeatedselective; + +import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; +import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; +import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; +import org.matsim.contrib.drt.optimizer.insertion.selective.SingleInsertionDetourPathCalculator; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrix; +import org.matsim.contrib.zone.skims.TravelTimeMatrix; +import org.matsim.core.controler.MatsimServices; +import org.matsim.core.modal.ModalProviders; +import org.matsim.core.router.costcalculators.TravelDisutilityFactory; +import org.matsim.core.router.util.TravelDisutility; +import org.matsim.core.router.util.TravelTime; + +/** + * @author steffenaxer + */ +public class RepeatedSelectiveInsertionSearchQSimModule extends AbstractDvrpModeQSimModule { + private final DrtConfigGroup drtCfg; + + public RepeatedSelectiveInsertionSearchQSimModule(DrtConfigGroup drtCfg) { + super(drtCfg.getMode()); + this.drtCfg = drtCfg; + } + + @Override + protected void configureQSim() { + addModalComponent(RepeatedSelectiveInsertionSearch.class, modalProvider(getter -> { + RepeatedSelectiveInsertionProvider provider = RepeatedSelectiveInsertionProvider.create( + getter.getModal(InsertionCostCalculator.class), getter.getModal(AdaptiveTravelTimeMatrix.class), + getter.getModal(TravelTime.class), getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), + getter.getModal(StopTimeCalculator.class)); + var insertionCostCalculator = getter.getModal(InsertionCostCalculator.class); + return new RepeatedSelectiveInsertionSearch(provider, + getter.getModal(SingleInsertionDetourPathCalculator.class), + insertionCostCalculator, drtCfg, getter.get(MatsimServices.class), + getter.getModal(StopTimeCalculator.class), getter.getModal(TravelTimeMatrix.class), + getter.getModal(AdaptiveTravelTimeMatrix.class)); + })); + bindModal(DrtInsertionSearch.class).to(modalKey(RepeatedSelectiveInsertionSearch.class)); + + addModalComponent(SingleInsertionDetourPathCalculator.class, + new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) { + @Override + public SingleInsertionDetourPathCalculator get() { + var travelTime = getModalInstance(TravelTime.class); + Network network = getModalInstance(Network.class); + TravelDisutility travelDisutility = getModalInstance( + TravelDisutilityFactory.class).createTravelDisutility(travelTime); + return new SingleInsertionDetourPathCalculator(network, travelTime, travelDisutility, drtCfg); + } + }); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java index 857c78831b8..b6efbb923f8 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProvider.java @@ -28,6 +28,8 @@ import org.matsim.contrib.drt.optimizer.insertion.*; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.zone.skims.TravelTimeMatrix; import org.matsim.core.router.util.TravelTime; @@ -39,12 +41,12 @@ class SelectiveInsertionProvider { public static SelectiveInsertionProvider create(DrtConfigGroup drtCfg, InsertionCostCalculator insertionCostCalculator, TravelTimeMatrix travelTimeMatrix, TravelTime travelTime, - ForkJoinPool forkJoinPool, IncrementalStopDurationEstimator incrementalStopDurationEstimator) { + ForkJoinPool forkJoinPool, StopTimeCalculator stopTimeCalculator) { var insertionParams = (SelectiveInsertionSearchParams)drtCfg.getDrtInsertionSearchParams(); var restrictiveDetourTimeEstimator = DetourTimeEstimator.createMatrixBasedEstimator( insertionParams.restrictiveBeelineSpeedFactor, travelTimeMatrix, travelTime); return new SelectiveInsertionProvider(new BestInsertionFinder(insertionCostCalculator), - new InsertionGenerator(incrementalStopDurationEstimator, restrictiveDetourTimeEstimator), forkJoinPool); + new InsertionGenerator(stopTimeCalculator, restrictiveDetourTimeEstimator), forkJoinPool); } private final BestInsertionFinder initialInsertionFinder; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearch.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearch.java index 4daefce676b..a03e49742c1 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearch.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearch.java @@ -35,6 +35,8 @@ import org.matsim.contrib.drt.optimizer.insertion.*; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.core.controler.MatsimServices; import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent; import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener; @@ -55,11 +57,11 @@ final class SelectiveInsertionSearch implements DrtInsertionSearch, MobsimBefore public SelectiveInsertionSearch(SelectiveInsertionProvider insertionProvider, SingleInsertionDetourPathCalculator detourPathCalculator, InsertionCostCalculator insertionCostCalculator, - DrtConfigGroup drtCfg, MatsimServices matsimServices, IncrementalStopDurationEstimator incrementalStopDurationEstimator) { + DrtConfigGroup drtCfg, MatsimServices matsimServices, StopTimeCalculator stopTimeCalculator) { this.insertionProvider = insertionProvider; this.detourPathCalculator = detourPathCalculator; this.insertionCostCalculator = insertionCostCalculator; - this.detourTimeCalculator = new InsertionDetourTimeCalculator(incrementalStopDurationEstimator, null); + this.detourTimeCalculator = new InsertionDetourTimeCalculator(stopTimeCalculator, null); this.matsimServices = matsimServices; this.mode = drtCfg.getMode(); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java index d0cbacc2dad..f92cc82413c 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionSearchQSimModule.java @@ -23,9 +23,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.optimizer.QSimScopeForkJoinPoolHolder; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch; -import org.matsim.contrib.drt.optimizer.insertion.IncrementalStopDurationEstimator; import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator; import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.zone.skims.TravelTimeMatrix; @@ -51,7 +52,7 @@ protected void configureQSim() { addModalComponent(SelectiveInsertionSearch.class, modalProvider(getter -> { SelectiveInsertionProvider provider = SelectiveInsertionProvider.create(drtCfg, getter.getModal(InsertionCostCalculator.class), getter.getModal(TravelTimeMatrix.class), - getter.getModal(TravelTime.class), getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(IncrementalStopDurationEstimator.class)); + getter.getModal(TravelTime.class), getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(), getter.getModal(StopTimeCalculator.class)); // Use 0 as the cost for the selected insertion: // - In the selective strategy, there is at most 1 insertion pre-selected. So no need to compute as there is // no other insertion to compare with. @@ -60,7 +61,7 @@ protected void configureQSim() { // Re (*) currently, free-speed travel times are quite accurate. We still need to adjust them to different times of day. InsertionCostCalculator zeroCostInsertionCostCalculator = (drtRequest, insertion, detourTimeInfo) -> 0; return new SelectiveInsertionSearch(provider, getter.getModal(SingleInsertionDetourPathCalculator.class), - zeroCostInsertionCostCalculator, drtCfg, getter.get(MatsimServices.class), getter.getModal(IncrementalStopDurationEstimator.class)); + zeroCostInsertionCostCalculator, drtCfg, getter.get(MatsimServices.class), getter.getModal(StopTimeCalculator.class)); })); bindModal(DrtInsertionSearch.class).to(modalKey(SelectiveInsertionSearch.class)); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SingleInsertionDetourPathCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SingleInsertionDetourPathCalculator.java index 293a0ac34ad..39461dd6894 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SingleInsertionDetourPathCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/selective/SingleInsertionDetourPathCalculator.java @@ -51,7 +51,7 @@ /** * @author Michal Maciejewski (michalm) */ -class SingleInsertionDetourPathCalculator implements MobsimBeforeCleanupListener { +public class SingleInsertionDetourPathCalculator implements MobsimBeforeCleanupListener { public static final int MAX_THREADS = 4; @@ -64,8 +64,8 @@ class SingleInsertionDetourPathCalculator implements MobsimBeforeCleanupListener private final ExecutorService executorService; - SingleInsertionDetourPathCalculator(Network network, TravelTime travelTime, - TravelDisutility travelDisutility, DrtConfigGroup drtCfg) { + public SingleInsertionDetourPathCalculator(Network network, TravelTime travelTime, + TravelDisutility travelDisutility, DrtConfigGroup drtCfg) { this(network, travelTime, travelDisutility, drtCfg.numberOfThreads, new SpeedyALTFactory()); } @@ -81,7 +81,7 @@ class SingleInsertionDetourPathCalculator implements MobsimBeforeCleanupListener executorService = Executors.newFixedThreadPool(Math.min(numberOfThreads, MAX_THREADS)); } - InsertionDetourData calculatePaths(DrtRequest drtRequest, Insertion insertion) { + public InsertionDetourData calculatePaths(DrtRequest drtRequest, Insertion insertion) { Link pickup = drtRequest.getFromLink(); Link dropoff = drtRequest.getToLink(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/CustomRebalancingStrategyParams.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/CustomRebalancingStrategyParams.java new file mode 100644 index 00000000000..266229bc349 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/CustomRebalancingStrategyParams.java @@ -0,0 +1,33 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.drt.optimizer.rebalancing; + +import org.matsim.core.config.ReflectiveConfigGroup; + +/** + * Custom rebalancing strategy parameters. User is responsible for installing rebalancing module and parameters. + */ +public final class CustomRebalancingStrategyParams extends ReflectiveConfigGroup + implements RebalancingParams.RebalancingStrategyParams { + public static final String SET_NAME = "CustomRebalancingStrategy"; + + public CustomRebalancingStrategyParams() { + super(SET_NAME); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardRebalancingStrategy.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardRebalancingStrategy.java index a53076a6bec..fa2e4d84547 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardRebalancingStrategy.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardRebalancingStrategy.java @@ -46,7 +46,6 @@ public class FeedforwardRebalancingStrategy implements RebalancingStrategy { private final RebalancingParams generalParams; private final int timeBinSize; - private final double rebalanceInterval; private final double scaling; private final Random rnd = new Random(1234); private final int feedforwardSignalLead; @@ -57,7 +56,7 @@ public class FeedforwardRebalancingStrategy implements RebalancingStrategy { private final DrtZoneTargetLinkSelector drtZoneTargetLinkSelector; private final FastHeuristicZonalRelocationCalculator fastHeuristicRelocationCalculator; - private final Map>> feedforwardSignal; + private final Map>> feedforwardSignal; public FeedforwardRebalancingStrategy(DrtZonalSystem zonalSystem, Fleet fleet, RebalancingParams generalParams, FeedforwardRebalancingStrategyParams strategySpecificParams, @@ -70,11 +69,9 @@ public FeedforwardRebalancingStrategy(DrtZonalSystem zonalSystem, Fleet fleet, R this.fleet = fleet; timeBinSize = strategySpecificParams.timeBinSize; - rebalanceInterval = generalParams.interval; - - scaling = strategySpecificParams.feedforwardSignalStrength * rebalanceInterval / timeBinSize; + scaling = strategySpecificParams.feedforwardSignalStrength * (double)generalParams.interval / timeBinSize; log.info("The feedforward signal strength is: " - + Double.toString(strategySpecificParams.feedforwardSignalStrength)); + + strategySpecificParams.feedforwardSignalStrength); feedforwardSignal = feedforwardSignalHandler.getFeedforwardSignal(); feedforwardSignalLead = strategySpecificParams.feedforwardSignalLead; @@ -92,7 +89,7 @@ public FeedforwardRebalancingStrategy(DrtZonalSystem zonalSystem, Fleet fleet, R @Override public List calcRelocations(Stream rebalancableVehicles, double time) { List relocationList = new ArrayList<>(); - double timeBin = Math.floor((time + feedforwardSignalLead) / timeBinSize); + int timeBin = (int)Math.floor((time + feedforwardSignalLead) / timeBinSize); Map> rebalancableVehiclesPerZone = RebalancingUtils .groupRebalancableVehicles(zonalSystem, generalParams, rebalancableVehicles, time); Map> actualRebalancableVehiclesPerZone = new HashMap<>(); @@ -129,9 +126,9 @@ public List calcRelocations(Stream rebalancab if (feedforwardSignal.containsKey(timeBin)) { // Generate relocations based on the "rebalancePlanCore" for (Flow rebalanceInfo : feedforwardSignal.get(timeBin)) { - DrtZone departureZone = rebalanceInfo.origin; - DrtZone arrivalZone = rebalanceInfo.destination; - int vehicleToSend = (int) Math.floor(scaling * rebalanceInfo.amount + rnd.nextDouble()); + DrtZone departureZone = rebalanceInfo.origin(); + DrtZone arrivalZone = rebalanceInfo.destination(); + int vehicleToSend = (int) Math.floor(scaling * rebalanceInfo.amount() + rnd.nextDouble()); // Note: we use probability to solve the problem of non-integer value of // vehileToSend after scaling. diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardSignalHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardSignalHandler.java index 21cc87903ca..d670f9be2ec 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardSignalHandler.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/Feedforward/FeedforwardSignalHandler.java @@ -21,7 +21,7 @@ public class FeedforwardSignalHandler implements IterationStartsListener { private static final Logger log = LogManager.getLogger(FeedforwardSignalHandler.class); private final DrtZonalSystem zonalSystem; - private final Map>> feedforwardSignal = new HashMap<>(); + private final Map>> feedforwardSignal = new HashMap<>(); private final int timeBinSize; private final NetDepartureReplenishDemandEstimator netDepartureReplenishDemandEstimator; @@ -39,36 +39,33 @@ public FeedforwardSignalHandler(DrtZonalSystem zonalSystem, } private void calculateFeedforwardSignal() { - netDepartureReplenishDemandEstimator.update(1); + netDepartureReplenishDemandEstimator.updateForNextIteration(); feedforwardSignal.clear(); int progressCounter = 0; int numOfTimeBin = simulationEndTime * 3600 / timeBinSize; log.info("Start calculating rebalnace plan now"); - for (int i = 0; i < numOfTimeBin; i++) { - double timeBin = i; - ToDoubleFunction netDepartureInputFunction = netDepartureReplenishDemandEstimator.getExpectedDemandForTimeBin( - timeBin); + for (int t = 0; t < numOfTimeBin; t++) { + ToDoubleFunction netDepartureInputFunction = netDepartureReplenishDemandEstimator.getExpectedDemandForTimeBin(t); List vehicleSurpluses = zonalSystem.getZones() .values() .stream() .map(z -> new DrtZoneVehicleSurplus(z, (int)netDepartureInputFunction.applyAsDouble(z) * -1)) .collect(toList()); - feedforwardSignal.put(timeBin, TransportProblem.solveForVehicleSurplus(vehicleSurpluses)); + feedforwardSignal.put(t, TransportProblem.solveForVehicleSurplus(vehicleSurpluses)); progressCounter++; log.debug("Calculating: " + (double)progressCounter * timeBinSize / simulationEndTime / 36 + "% complete"); } log.info("Rebalance plan calculation is now complete! "); } - public Map>> getFeedforwardSignal() { + public Map>> getFeedforwardSignal() { return feedforwardSignal; } @Override public void notifyIterationStarts(IterationStartsEvent event) { - int iteration = event.getIteration(); - if (iteration > 0) { + if (event.getIteration() > 0) { calculateFeedforwardSignal(); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingModule.java index a263bca4e35..b9c008bc3a1 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingModule.java @@ -54,6 +54,8 @@ public void install() { install(new DrtModePlusOneRebalanceModule(drtCfg)); } else if (rebalancingParams.getRebalancingStrategyParams() instanceof FeedforwardRebalancingStrategyParams) { install(new DrtModeFeedforwardRebalanceModule(drtCfg)); + } else if (rebalancingParams.getRebalancingStrategyParams() instanceof CustomRebalancingStrategyParams) { + // User is responsible for installing custom module } else { throw new RuntimeException( "Unsupported rebalancingStrategyParams: " + rebalancingParams.getRebalancingStrategyParams()); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingParams.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingParams.java index 09c826427c4..07b422912d9 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingParams.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/RebalancingParams.java @@ -79,6 +79,9 @@ private void initSingletonParameterSets() { addDefinition(PlusOneRebalancingStrategyParams.SET_NAME, PlusOneRebalancingStrategyParams::new, () -> (ConfigGroup)rebalancingStrategyParams, params -> rebalancingStrategyParams = (RebalancingStrategyParams)params); + addDefinition(CustomRebalancingStrategyParams.SET_NAME, CustomRebalancingStrategyParams::new, + () -> (ConfigGroup)rebalancingStrategyParams, + params -> rebalancingStrategyParams = (RebalancingStrategyParams)params); } @Override diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java index b16bd44b6f9..2529a02b8d3 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java @@ -5,8 +5,6 @@ import java.util.Map; import java.util.function.ToDoubleFunction; -import org.apache.commons.lang3.mutable.MutableInt; -import org.apache.commons.lang3.tuple.Triple; import org.matsim.api.core.v01.Id; import org.matsim.contrib.drt.analysis.zonal.DrtZonalSystem; import org.matsim.contrib.drt.analysis.zonal.DrtZone; @@ -20,16 +18,18 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; -public class NetDepartureReplenishDemandEstimator implements PassengerRequestScheduledEventHandler, - DrtRequestSubmittedEventHandler, PassengerRequestRejectedEventHandler { +public class NetDepartureReplenishDemandEstimator + implements PassengerRequestScheduledEventHandler, DrtRequestSubmittedEventHandler, PassengerRequestRejectedEventHandler { + + private record Trip(int timeBin, DrtZone fromZone, DrtZone toZone) { + } private final DrtZonalSystem zonalSystem; private final String mode; private final int timeBinSize; - private final Map> currentZoneNetDepartureMap = new HashMap<>(); - private final Map> previousZoneNetDepartureMap = new HashMap<>(); - private final Map, Triple> potentialDrtTripsMap = new HashMap<>(); - private static final MutableInt ZERO = new MutableInt(0); + private final Map> currentZoneNetDepartureMap = new HashMap<>(); + private final Map> previousZoneNetDepartureMap = new HashMap<>(); + private final Map, Trip> potentialDrtTripsMap = new HashMap<>(); public NetDepartureReplenishDemandEstimator(DrtZonalSystem zonalSystem, DrtConfigGroup drtCfg, FeedforwardRebalancingStrategyParams strategySpecificParams) { @@ -42,49 +42,44 @@ public NetDepartureReplenishDemandEstimator(DrtZonalSystem zonalSystem, DrtConfi @Override public void handleEvent(PassengerRequestRejectedEvent event) { if (event.getMode().equals(mode)) { - // If a request is rejected, remove the request info from the temporary storage place - potentialDrtTripsMap.remove(event.getPersonId()); + // Ignore rejected request. + potentialDrtTripsMap.remove(event.getRequestId()); } } @Override public void handleEvent(DrtRequestSubmittedEvent event) { - // Here, we get a potential DRT trip. We will first note it down in the - // temporary data base (Potential DRT Trips Map) if (event.getMode().equals(mode)) { - double timeBin = Math.floor(event.getTime() / timeBinSize); + // At the submission time, this is only a potential trip. + int timeBin = (int)Math.floor(event.getTime() / timeBinSize); DrtZone departureZoneId = zonalSystem.getZoneForLinkId(event.getFromLinkId()); DrtZone arrivalZoneId = zonalSystem.getZoneForLinkId(event.getToLinkId()); - potentialDrtTripsMap.put(event.getRequestId(), Triple.of(timeBin, departureZoneId, arrivalZoneId)); + potentialDrtTripsMap.put(event.getRequestId(), new Trip(timeBin, departureZoneId, arrivalZoneId)); } } @Override public void handleEvent(PassengerRequestScheduledEvent event) { - // When the request is scheduled (i.e. accepted), add this travel information to - // the database; - // Then remove the travel information from the potential trips Map if (event.getMode().equals(mode)) { - Triple triple = potentialDrtTripsMap.remove(event.getRequestId()); - double timeBin = triple.getLeft(); - DrtZone departureZone = triple.getMiddle(); - DrtZone arrivalZone = triple.getRight(); + // A potential trip has been scheduled and needs to be considered in the departure maps. + var trip = potentialDrtTripsMap.remove(event.getRequestId()); - var zoneNetDepartureMapSlice = currentZoneNetDepartureMap.computeIfAbsent(timeBin, t -> new HashMap<>()); - zoneNetDepartureMapSlice.computeIfAbsent(departureZone, z -> new MutableInt()).increment(); - zoneNetDepartureMapSlice.computeIfAbsent(arrivalZone, z -> new MutableInt()).decrement(); + var zoneNetDepartureMapSlice = currentZoneNetDepartureMap.computeIfAbsent(trip.timeBin, t -> new HashMap<>()); + zoneNetDepartureMapSlice.merge(trip.fromZone, 1, Integer::sum); + zoneNetDepartureMapSlice.merge(trip.toZone, -1, Integer::sum); } } - public void update(int iteration) { + public void updateForNextIteration() { previousZoneNetDepartureMap.clear(); previousZoneNetDepartureMap.putAll(currentZoneNetDepartureMap); currentZoneNetDepartureMap.clear(); + + potentialDrtTripsMap.clear(); } - public ToDoubleFunction getExpectedDemandForTimeBin(double timeBin) { - Map expectedDemandForTimeBin = previousZoneNetDepartureMap.getOrDefault(timeBin, - Collections.emptyMap()); - return zone -> expectedDemandForTimeBin.getOrDefault(zone, ZERO).intValue(); + public ToDoubleFunction getExpectedDemandForTimeBin(int timeBin) { + var expectedDemandForTimeBin = previousZoneNetDepartureMap.getOrDefault(timeBin, Collections.emptyMap()); + return zone -> expectedDemandForTimeBin.getOrDefault(zone, 0); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimator.java similarity index 96% rename from contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimator.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimator.java index b728492e9e0..003c1649507 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimator.java @@ -45,8 +45,8 @@ * @author jbischoff * @author michalm */ -public final class PreviousIterationDRTDemandEstimator implements ZonalDemandEstimator, PersonDepartureEventHandler { - private static final Logger logger = LogManager.getLogger(PreviousIterationDRTDemandEstimator.class); +public final class PreviousIterationDrtDemandEstimator implements ZonalDemandEstimator, PersonDepartureEventHandler { + private static final Logger logger = LogManager.getLogger(PreviousIterationDrtDemandEstimator.class); private final DrtZonalSystem zonalSystem; private final String mode; @@ -54,7 +54,7 @@ public final class PreviousIterationDRTDemandEstimator implements ZonalDemandEst private Map> currentIterationDepartures = new HashMap<>(); private Map> previousIterationDepartures = new HashMap<>(); - public PreviousIterationDRTDemandEstimator(DrtZonalSystem zonalSystem, DrtConfigGroup drtCfg, + public PreviousIterationDrtDemandEstimator(DrtZonalSystem zonalSystem, DrtConfigGroup drtCfg, int demandEstimationPeriod) { this.zonalSystem = zonalSystem; mode = drtCfg.getMode(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/AggregatedMinCostRelocationCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/AggregatedMinCostRelocationCalculator.java index b22f58b3fd3..4a16cb05b5d 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/AggregatedMinCostRelocationCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/AggregatedMinCostRelocationCalculator.java @@ -64,11 +64,11 @@ private List calcRelocations(Map> rebalan List> flows) { List relocations = new ArrayList<>(); for (TransportProblem.Flow flow : flows) { - List rebalancableVehicles = rebalancableVehiclesPerZone.get(flow.origin); + List rebalancableVehicles = rebalancableVehiclesPerZone.get(flow.origin()); - Link targetLink = targetLinkSelector.selectTargetLink(flow.destination); + Link targetLink = targetLinkSelector.selectTargetLink(flow.destination()); - for (int f = 0; f < flow.amount; f++) { + for (int f = 0; f < flow.amount(); f++) { // TODO use BestDispatchFinder (needs to be moved from taxi to dvrp) instead DvrpVehicle nearestVehicle = findNearestVehicle(rebalancableVehicles, targetLink); relocations.add(new Relocation(nearestVehicle, targetLink)); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/DrtModeMinCostFlowRebalancingModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/DrtModeMinCostFlowRebalancingModule.java index 25d9476b595..d624e6a000a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/DrtModeMinCostFlowRebalancingModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/DrtModeMinCostFlowRebalancingModule.java @@ -25,7 +25,7 @@ import org.matsim.contrib.drt.analysis.zonal.DrtZoneTargetLinkSelector; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy; -import org.matsim.contrib.drt.optimizer.rebalancing.demandestimator.PreviousIterationDRTDemandEstimator; +import org.matsim.contrib.drt.optimizer.rebalancing.demandestimator.PreviousIterationDrtDemandEstimator; import org.matsim.contrib.drt.optimizer.rebalancing.demandestimator.ZonalDemandEstimator; import org.matsim.contrib.drt.optimizer.rebalancing.targetcalculator.DemandEstimatorAsTargetCalculator; import org.matsim.contrib.drt.optimizer.rebalancing.targetcalculator.EqualRebalancableVehicleDistributionTargetCalculator; @@ -100,11 +100,11 @@ protected void configureQSim() { switch (strategyParams.zonalDemandEstimatorType) { case PreviousIterationDemand: - bindModal(PreviousIterationDRTDemandEstimator.class).toProvider(modalProvider( - getter -> new PreviousIterationDRTDemandEstimator(getter.getModal(DrtZonalSystem.class), drtCfg, + bindModal(PreviousIterationDrtDemandEstimator.class).toProvider(modalProvider( + getter -> new PreviousIterationDrtDemandEstimator(getter.getModal(DrtZonalSystem.class), drtCfg, strategyParams.demandEstimationPeriod))).asEagerSingleton(); - bindModal(ZonalDemandEstimator.class).to(modalKey(PreviousIterationDRTDemandEstimator.class)); - addEventHandlerBinding().to(modalKey(PreviousIterationDRTDemandEstimator.class)); + bindModal(ZonalDemandEstimator.class).to(modalKey(PreviousIterationDrtDemandEstimator.class)); + addEventHandlerBinding().to(modalKey(PreviousIterationDrtDemandEstimator.class)); break; case None: diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/TransportProblem.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/TransportProblem.java index 574440c6520..b48f0f2e635 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/TransportProblem.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/TransportProblem.java @@ -25,8 +25,8 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; -import org.matsim.contrib.drt.analysis.zonal.DrtZone; import org.matsim.contrib.common.util.DistanceUtils; +import org.matsim.contrib.drt.analysis.zonal.DrtZone; import graphs.flows.MinCostFlow; import graphs.flows.MinCostFlow.Edge; @@ -53,16 +53,7 @@ private static int calcStraightLineDistance(DrtZone zone1, DrtZone zone2) { return (int)DistanceUtils.calculateDistance(zone1.getCentroid(), zone2.getCentroid()); } - public static class Flow { - public final P origin; - public final C destination; - public final int amount; - - private Flow(P origin, C destination, int amount) { - this.origin = origin; - this.destination = destination; - this.amount = amount; - } + public record Flow(P origin, C destination, int amount) { } private final ToIntBiFunction costFunction; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/plusOne/PlusOneRebalancingStrategy.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/plusOne/PlusOneRebalancingStrategy.java index 8c98e8259aa..2c67ee8640f 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/plusOne/PlusOneRebalancingStrategy.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/plusOne/PlusOneRebalancingStrategy.java @@ -6,6 +6,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; @@ -24,6 +26,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; import org.matsim.core.events.MobsimScopeEventHandler; +import com.google.common.base.Preconditions; + /** * This strategy is based on the Plus One Rebalancing Algorithm in AMoDeus. * At each rebalancing period, the algorithm will send idling vehicles to the departure places of the request departed @@ -32,68 +36,77 @@ * @author Chengqi Lu */ public class PlusOneRebalancingStrategy - implements RebalancingStrategy, PassengerRequestScheduledEventHandler, DrtRequestSubmittedEventHandler, - PassengerRequestRejectedEventHandler, MobsimScopeEventHandler { + implements RebalancingStrategy, PassengerRequestScheduledEventHandler, DrtRequestSubmittedEventHandler, PassengerRequestRejectedEventHandler, + MobsimScopeEventHandler { private static final Logger log = LogManager.getLogger(PlusOneRebalancingStrategy.class); private final String mode; private final Network network; private final LinkBasedRelocationCalculator linkBasedRelocationCalculator; - private final List> targetLinkIdList = new ArrayList<>(); - private final Map, Id> potentialDrtTripMap = new HashMap<>(); + private record Target(Id link, double scheduledTime) { + } + + private final Queue targets = new ConcurrentLinkedQueue<>(); + private final Map, Id> potentialTargetLinks = new HashMap<>(); - public PlusOneRebalancingStrategy(String mode, Network network, - LinkBasedRelocationCalculator linkBasedRelocationCalculator) { + public PlusOneRebalancingStrategy(String mode, Network network, LinkBasedRelocationCalculator linkBasedRelocationCalculator) { this.mode = mode; this.network = network; this.linkBasedRelocationCalculator = linkBasedRelocationCalculator; } + private double lastCalculationTime = Double.NEGATIVE_INFINITY; + @Override public List calcRelocations(Stream rebalancableVehicles, double time) { - List rebalancableVehicleList = rebalancableVehicles.collect(toList()); + var targetLinks = new ArrayList(); + double prevTime = lastCalculationTime; + + // Because events can be received at the same time we calculate relocations, we want to only consider target links from events that are older + // than the current time + while (!targets.isEmpty() && targets.peek().scheduledTime < time) { + var targetLink = targets.poll(); + + // These state checks are meant to ensure we correctly reason about concurrency: + // 1. ensure all old target links (from previous rebalancing calculations are processed) + Preconditions.checkState(targetLink.scheduledTime >= lastCalculationTime); + // 2. ensure target links are sorted by scheduled time + Preconditions.checkState(targetLink.scheduledTime >= prevTime); - final List> copiedTargetLinkIdList; - synchronized (this) { - //may happen in parallel to handling PassengerRequestScheduledEvent emitted by UnplannedRequestInserter - copiedTargetLinkIdList = new ArrayList<>(targetLinkIdList); - targetLinkIdList.clear(); // clear the target map for next rebalancing cycle + targetLinks.add(network.getLinks().get(targetLink.link)); + prevTime = targetLink.scheduledTime; } - final List targetLinkList = copiedTargetLinkIdList.stream() - .map(network.getLinks()::get) - .collect(toList()); + lastCalculationTime = time; - log.debug("There are in total " + targetLinkList.size() + " rebalance targets at this time period"); + log.debug("There are in total " + targetLinks.size() + " rebalance targets at this time period"); + var rebalancableVehicleList = rebalancableVehicles.collect(toList()); log.debug("There are " + rebalancableVehicleList.size() + " vehicles that can be rebalanced"); // calculate the matching result - return linkBasedRelocationCalculator.calcRelocations(targetLinkList, rebalancableVehicleList); + return linkBasedRelocationCalculator.calcRelocations(targetLinks, rebalancableVehicleList); } @Override public void handleEvent(DrtRequestSubmittedEvent event) { if (event.getMode().equals(mode)) { - potentialDrtTripMap.put(event.getRequestId(), event.getFromLinkId()); + potentialTargetLinks.put(event.getRequestId(), event.getFromLinkId()); } } @Override public void handleEvent(PassengerRequestScheduledEvent event) { if (event.getMode().equals(mode)) { - Id linkId = potentialDrtTripMap.remove(event.getRequestId()); - synchronized (this) { - // event was emitted by UnplannedRequestInserter, it may arrive during calcRelocations() - targetLinkIdList.add(linkId); - } + Id linkId = potentialTargetLinks.remove(event.getRequestId()); + targets.add(new Target(linkId, event.getTime())); } } @Override public void handleEvent(PassengerRequestRejectedEvent event) { if (event.getMode().equals(mode)) { - potentialDrtTripMap.remove(event.getRequestId()); + potentialTargetLinks.remove(event.getRequestId()); } } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java index 4b3e594020b..81dd831859a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java @@ -20,12 +20,13 @@ package org.matsim.contrib.drt.passenger; +import com.google.common.base.MoreObjects; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; -import com.google.common.base.MoreObjects; +import java.util.List; /** * @author Michal Maciejewski (michalm) @@ -98,8 +99,8 @@ public Link getToLink() { return request.getToLink(); } - public Id getPassengerId() { - return request.getPassengerId(); + public List> getPassengerIds() { + return request.getPassengerIds(); } public String getMode() { diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java index 64a64ee58ff..e1c034f2a92 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java @@ -20,13 +20,15 @@ package org.matsim.contrib.drt.passenger; +import com.google.common.base.MoreObjects; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerRequest; -import com.google.common.base.MoreObjects; +import java.util.*; +import java.util.stream.Collectors; /** * @author michalm @@ -38,7 +40,7 @@ public class DrtRequest implements PassengerRequest { private final double latestStartTime; private final double latestArrivalTime; - private final Id passengerId; + private final List> passengerIds = new ArrayList<>(); private final String mode; private final Link fromLink; @@ -50,7 +52,7 @@ private DrtRequest(Builder builder) { earliestStartTime = builder.earliestStartTime; latestStartTime = builder.latestStartTime; latestArrivalTime = builder.latestArrivalTime; - passengerId = builder.passengerId; + passengerIds.addAll(builder.passengerIds); mode = builder.mode; fromLink = builder.fromLink; toLink = builder.toLink; @@ -67,7 +69,7 @@ public static Builder newBuilder(DrtRequest copy) { builder.earliestStartTime = copy.getEarliestStartTime(); builder.latestStartTime = copy.getLatestStartTime(); builder.latestArrivalTime = copy.getLatestArrivalTime(); - builder.passengerId = copy.getPassengerId(); + builder.passengerIds = new ArrayList<>(copy.getPassengerIds()); builder.mode = copy.getMode(); builder.fromLink = copy.getFromLink(); builder.toLink = copy.getToLink(); @@ -109,8 +111,8 @@ public Link getToLink() { } @Override - public Id getPassengerId() { - return passengerId; + public List> getPassengerIds() { + return List.copyOf(passengerIds); } @Override @@ -118,6 +120,11 @@ public String getMode() { return mode; } + @Override + public int getPassengerCount() { + return passengerIds.size(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -126,7 +133,7 @@ public String toString() { .add("earliestStartTime", earliestStartTime) .add("latestStartTime", latestStartTime) .add("latestArrivalTime", latestArrivalTime) - .add("passengerId", passengerId) + .add("passengerIds", passengerIds.stream().map(Object::toString).collect(Collectors.joining(","))) .add("mode", mode) .add("fromLink", fromLink) .add("toLink", toLink) @@ -139,7 +146,7 @@ public static final class Builder { private double earliestStartTime; private double latestStartTime; private double latestArrivalTime; - private Id passengerId; + private List> passengerIds = new ArrayList<>(); private String mode; private Link fromLink; private Link toLink; @@ -172,8 +179,8 @@ public Builder latestArrivalTime(double val) { return this; } - public Builder passengerId(Id val) { - passengerId = val; + public Builder passengerIds(List> val) { + passengerIds = new ArrayList<>(val); return this; } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java index 22a1fad6ceb..8a2cff7227e 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java @@ -31,6 +31,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; import org.matsim.core.api.experimental.events.EventsManager; +import java.util.List; + /** * @author michalm */ @@ -45,19 +47,19 @@ public DrtRequestCreator(String mode, EventsManager eventsManager) { } @Override - public DrtRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, Link toLink, - double departureTime, double submissionTime) { + public DrtRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, + double departureTime, double submissionTime) { DrtRoute drtRoute = (DrtRoute)route; double latestDepartureTime = departureTime + drtRoute.getMaxWaitTime(); double latestArrivalTime = departureTime + drtRoute.getTravelTime().seconds(); eventsManager.processEvent( - new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerId, fromLink.getId(), toLink.getId(), - drtRoute.getDirectRideTime(), drtRoute.getDistance(), latestDepartureTime, latestArrivalTime)); + new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerIds, fromLink.getId(), toLink.getId(), + drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime)); DrtRequest request = DrtRequest.newBuilder() .id(id) - .passengerId(passengerId) + .passengerIds(passengerIds) .mode(mode) .fromLink(fromLink) .toLink(toLink) 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..0f1e36e5706 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 @@ -20,14 +20,17 @@ package org.matsim.contrib.drt.passenger; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity; -import org.matsim.contrib.dvrp.schedule.StayTask; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; @@ -42,11 +45,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,27 +57,27 @@ 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 protected void beforeFirstStep(double now) { // TODO probably we should simulate it more accurately (passenger by passenger, not all at once...) for (var request : dropoffRequests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), 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)) { + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } } @@ -82,23 +85,23 @@ protected void simStep(double now) { } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - if (now < expectedEndTime) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + if (now < endTime.get()) { return;// pick up only at the end of stop activity } - var request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } - private AcceptedDrtRequest getRequestForPassenger(Id passengerId) { + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { return pickupRequests.values() .stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java index fe7d745c436..09f04e814d9 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java @@ -19,9 +19,6 @@ package org.matsim.contrib.drt.passenger.events; -import java.util.Map; -import java.util.Objects; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.network.Link; @@ -29,6 +26,8 @@ import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent; +import java.util.*; + /** * @author michalm */ @@ -38,21 +37,24 @@ public class DrtRequestSubmittedEvent extends PassengerRequestSubmittedEvent { public static final String ATTRIBUTE_UNSHARED_RIDE_TIME = "unsharedRideTime"; public static final String ATTRIBUTE_UNSHARED_RIDE_DISTANCE = "unsharedRideDistance"; + public static final String ATTRIBUTE_EARLIEST_DEPARTURE_TIME = "earliestDepartureTime"; public static final String ATTRIBUTE_LATEST_PICKUP_TIME = "latestPickupTime"; public static final String ATTRIBUTE_LATEST_DROPOFF_TIME = "latestDropoffTime"; private final double unsharedRideTime; private final double unsharedRideDistance; + private final double earliestDepartureTime; private final double latestPickupTime; private final double latestDropoffTime; - public DrtRequestSubmittedEvent(double time, String mode, Id requestId, Id personId, + public DrtRequestSubmittedEvent(double time, String mode, Id requestId, List> personIds, Id fromLinkId, Id toLinkId, double unsharedRideTime, double unsharedRideDistance, - double latestPickupTime, double latestDropoffTime) { - super(time, mode, requestId, personId, fromLinkId, toLinkId); + double earliestDepartureTime, double latestPickupTime, double latestDropoffTime) { + super(time, mode, requestId, personIds, fromLinkId, toLinkId); this.unsharedRideTime = unsharedRideTime; this.unsharedRideDistance = unsharedRideDistance; + this.earliestDepartureTime = earliestDepartureTime; this.latestPickupTime = latestPickupTime; this.latestDropoffTime = latestDropoffTime; } @@ -76,6 +78,10 @@ public final double getUnsharedRideDistance() { return unsharedRideDistance; } + public final double getEarliestDepartureTime() { + return earliestDepartureTime; + } + public final double getLatestPickupTime() { return latestPickupTime; } @@ -89,6 +95,7 @@ public Map getAttributes() { Map attr = super.getAttributes(); attr.put(ATTRIBUTE_UNSHARED_RIDE_TIME, unsharedRideTime + ""); attr.put(ATTRIBUTE_UNSHARED_RIDE_DISTANCE, unsharedRideDistance + ""); + attr.put(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, earliestDepartureTime + ""); attr.put(ATTRIBUTE_LATEST_PICKUP_TIME, latestPickupTime + ""); attr.put(ATTRIBUTE_LATEST_DROPOFF_TIME, latestDropoffTime + ""); return attr; @@ -99,14 +106,19 @@ public static DrtRequestSubmittedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } Id fromLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_FROM_LINK)); Id toLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_TO_LINK)); double unsharedRideTime = Double.parseDouble(attributes.get(ATTRIBUTE_UNSHARED_RIDE_TIME)); double unsharedRideDistance = Double.parseDouble(attributes.get(ATTRIBUTE_UNSHARED_RIDE_DISTANCE)); + double earliestDepartureTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, "NaN")); double latestPickupTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_PICKUP_TIME, "NaN")); double latestDropoffTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_DROPOFF_TIME, "NaN")); - return new DrtRequestSubmittedEvent(time, mode, requestId, personId, fromLinkId, toLinkId, unsharedRideTime, - unsharedRideDistance, latestPickupTime, latestDropoffTime); + return new DrtRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId, unsharedRideTime, + unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java new file mode 100644 index 00000000000..e58f8835dfb --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java @@ -0,0 +1,36 @@ +package org.matsim.contrib.drt.prebooking; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.AbstractPassengerRequestEvent; + +/** + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PassengerRequestBookedEvent extends AbstractPassengerRequestEvent { + public static final String EVENT_TYPE = "PassengerRequest booked"; + + public PassengerRequestBookedEvent(double time, String mode, Id requestId, Id personId) { + super(time, mode, requestId, List.of(personId)); + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + public static PassengerRequestBookedEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); + Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); + Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + return new PassengerRequestBookedEvent(time, mode, requestId, personId); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEventHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEventHandler.java new file mode 100644 index 00000000000..19a47c3fb9c --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEventHandler.java @@ -0,0 +1,10 @@ +package org.matsim.contrib.drt.prebooking; + +import org.matsim.core.events.handler.EventHandler; + +/** + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public interface PassengerRequestBookedEventHandler extends EventHandler { + void handleEvent(final PassengerRequestBookedEvent event); +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingActionCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingActionCreator.java new file mode 100644 index 00000000000..aa29bc7253f --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingActionCreator.java @@ -0,0 +1,57 @@ +package org.matsim.contrib.drt.prebooking; + +import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.getBaseTypeOrElseThrow; + +import org.matsim.contrib.drt.prebooking.abandon.AbandonVoter; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.schedule.DrtTaskBaseType; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.vrpagent.DrtActionCreator; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; +import org.matsim.contrib.dvrp.schedule.Task; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dynagent.DynAction; +import org.matsim.contrib.dynagent.DynAgent; + +/** + * For prebooking, we implement an alternative DynActivity that handles entering + * / exiting passengers more flexibly than the standard DrtStopActivity. + * + * Specifically, we track when a person is ready for departure and then add the + * expected duration of the interaction into a queue. The agent only actually + * enters the vehicle after this time has elapsed. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PrebookingActionCreator implements VrpAgentLogic.DynActionCreator { + private final VrpAgentLogic.DynActionCreator delegate; + private final PassengerHandler passengerHandler; + private final PassengerStopDurationProvider stopDurationProvider; + private final PrebookingManager prebookingManager; + private final AbandonVoter abandonVoter; + + public PrebookingActionCreator(PassengerHandler passengerHandler, VrpAgentLogic.DynActionCreator delegate, + PassengerStopDurationProvider stopDurationProvider, PrebookingManager prebookingManager, + AbandonVoter abandonVoter) { + this.delegate = delegate; + this.passengerHandler = passengerHandler; + this.stopDurationProvider = stopDurationProvider; + this.prebookingManager = prebookingManager; + this.abandonVoter = abandonVoter; + } + + @Override + public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now) { + Task task = vehicle.getSchedule().getCurrentTask(); + + if (getBaseTypeOrElseThrow(task).equals(DrtTaskBaseType.STOP)) { + DrtStopTask stopTask = (DrtStopTask) task; + return new PrebookingStopActivity(passengerHandler, dynAgent, stopTask, stopTask.getDropoffRequests(), + stopTask.getPickupRequests(), DrtActionCreator.DRT_STOP_NAME, () -> stopTask.getEndTime(), + stopDurationProvider, vehicle, prebookingManager, abandonVoter); + } + + return delegate.createAction(dynAgent, vehicle, now); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java new file mode 100644 index 00000000000..1fc580715f4 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java @@ -0,0 +1,457 @@ +package org.matsim.contrib.drt.prebooking; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.annotation.Nullable; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.IdSet; +import org.matsim.api.core.v01.events.PersonStuckEvent; +import org.matsim.api.core.v01.events.handler.PersonStuckEventHandler; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; +import org.matsim.contrib.drt.prebooking.unscheduler.RequestUnscheduler; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; +import org.matsim.contrib.dvrp.passenger.PassengerRequest; +import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.framework.MobsimAgent.State; +import org.matsim.core.mobsim.framework.MobsimTimer; +import org.matsim.core.mobsim.framework.events.MobsimAfterSimStepEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimAfterSimStepListener; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils; +import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine; + +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; + +/** + * This class manages prebooked requests. One instance of PrebookingManager + * exists per mode. The entry point is PrebookingManager::prebook to which you + * need to pass a person, a leg with the respective DRT mode, the + * requested/expected earliest departure time, and the time at which the request + * should be submitted / taken into account in the system. + * + * Preplanned requests can be submitted any time before the planned + * departure/submission times. + * + * Internally, the prebooking manager will create a request identifier and + * return the request once the agent actually wants to depart on the planned + * leg. The link between a leg and a request is managed by inserting a special + * attribute in the leg instance. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PrebookingManager implements MobsimEngine, MobsimAfterSimStepListener, AdvanceRequestProvider, + PassengerRequestScheduledEventHandler, PassengerRequestRejectedEventHandler, PersonStuckEventHandler { + private final String mode; + + private final Network network; + private final EventsManager eventsManager; + + private final VrpOptimizer optimizer; + private final RequestUnscheduler unscheduler; + + private final MobsimTimer mobsimTimer; + + public PrebookingManager(String mode, Network network, PassengerRequestCreator requestCreator, + VrpOptimizer optimizer, MobsimTimer mobsimTimer, PassengerRequestValidator requestValidator, + EventsManager eventsManager, RequestUnscheduler unscheduler) { + this.network = network; + this.mode = mode; + this.requestCreator = requestCreator; + this.optimizer = optimizer; + this.requestAttribute = PREBOOKED_REQUEST_PREFIX + ":" + mode; + this.requestValidator = requestValidator; + this.mobsimTimer = mobsimTimer; + this.eventsManager = eventsManager; + this.unscheduler = unscheduler; + } + + // Functionality for ID management + + private static final String PREBOOKED_REQUEST_PREFIX = "prebookedRequestId"; + private final AtomicInteger currentRequestIndex = new AtomicInteger(-1); + private final String requestAttribute; + + private Id createRequestId() { + return Id.create(mode + "_prebooked_" + currentRequestIndex.incrementAndGet(), Request.class); + } + + public boolean isPrebookedRequest(Id requestId) { + return requestId.toString().startsWith(mode + "_prebooked_"); + } + + public Id getRequestId(Leg leg) { + String rawRequestId = (String) leg.getAttributes().getAttribute(requestAttribute); + + if (rawRequestId == null) { + return null; + } + + return Id.create(rawRequestId, Request.class); + } + + // Event handling: We track events in parallel and process them later in + // notifyMobsimAfterSimStep + + private final ConcurrentLinkedQueue scheduledEvents = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue> rejectedEventIds = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue> stuckPersonsIds = new ConcurrentLinkedQueue<>(); + + @Override + public void handleEvent(PassengerRequestScheduledEvent event) { + scheduledEvents.add(event); + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + rejectedEventIds.add(event.getRequestId()); + } + + @Override + public void handleEvent(PersonStuckEvent event) { + stuckPersonsIds.add(event.getPersonId()); + } + + // Event handling: We don't want to process events in notifyMobsimAfterSimStep, + // so we do it at the next time step + private record RejectionItem(Id requestId, List> personIds, String cause) { + } + + private final ConcurrentLinkedQueue rejections = new ConcurrentLinkedQueue<>(); + + private void processRejection(PassengerRequest request, String cause) { + rejections.add(new RejectionItem(request.getId(), request.getPassengerIds(), cause)); + } + + private void flushRejections(double now) { + for (RejectionItem item : rejections) { + eventsManager.processEvent( + new PassengerRequestRejectedEvent(now, mode, item.requestId, item.personIds, item.cause)); + } + + rejections.clear(); + } + + // Booking functionality + + private final PassengerRequestCreator requestCreator; + private final PassengerRequestValidator requestValidator; + + // collects new bookings that need to be submitted + private final ConcurrentLinkedQueue bookingQueue = new ConcurrentLinkedQueue<>(); + + public void prebook(MobsimAgent person, Leg leg, double earliestDepartureTime) { + Preconditions.checkArgument(leg.getMode().equals(mode), "Invalid mode for this prebooking manager"); + Preconditions.checkState(!person.getState().equals(State.ABORT), "Cannot prebook aborted agent"); + + Id requestId = createRequestId(); + double now = mobsimTimer.getTimeOfDay(); + + eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, person.getId())); + + PassengerRequest request = requestCreator.createRequest(requestId, List.of(person.getId()), leg.getRoute(), + getLink(leg.getRoute().getStartLinkId()), getLink(leg.getRoute().getEndLinkId()), earliestDepartureTime, + now); + + Set violations = requestValidator.validateRequest(request); + + Plan plan = WithinDayAgentUtils.getModifiablePlan(person); + int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(person); + int prebookingLegIndex = plan.getPlanElements().indexOf(leg); + + if (prebookingLegIndex <= currentLegIndex) { + violations = new HashSet<>(violations); + violations.add("past leg"); // the leg for which the booking was made has already happened + } + + if (!violations.isEmpty()) { + String cause = String.join(", ", violations); + processRejection(request, cause); + } else { + leg.getAttributes().putAttribute(requestAttribute, request.getId().toString()); + bookingQueue.add(request); + } + } + + private void processBookingQueue(double now) { + for (PassengerRequest request : bookingQueue) { + + synchronized (optimizer) { // needed? + optimizer.requestSubmitted(request); + } + + requests.put(request.getId(), new RequestItem(request)); + } + + bookingQueue.clear(); + } + + private Link getLink(Id linkId) { + return Preconditions.checkNotNull(network.getLinks().get(linkId), + "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", + linkId, mode); + } + + // Interface with PassengerEngine + + @Override + @Nullable + public PassengerRequest retrieveRequest(MobsimAgent agent, Leg leg) { + Preconditions.checkArgument(leg.getMode().equals(mode), "Invalid mode for this prebooking manager"); + + Id requestId = getRequestId(leg); + + if (requestId == null) { + return null; + } + + RequestItem item = requests.get(requestId); + + if (item == null) { + return null; + } + + return item.request; + } + + // Housekeeping of requests + + private IdMap requests = new IdMap<>(Request.class); + + private class RequestItem { + final PassengerRequest request; + + Id vehicleId = null; + boolean onboard = false; + + RequestItem(PassengerRequest request) { + this.request = request; + } + } + + void notifyPickup(double now, AcceptedDrtRequest request) { + RequestItem item = requests.get(request.getId()); + + if (item != null) { + // may be null, we treat all (also non-prebooked) requests here + item.onboard = true; + } + } + + void notifyDropoff(Id requestId) { + requests.remove(requestId); + } + + private IdSet unscheduleUponVehicleAssignment = new IdSet<>(Request.class); + + private void processScheduledRequests(double now) { + for (PassengerRequestScheduledEvent event : scheduledEvents) { + RequestItem item = requests.get(event.getRequestId()); + + if (item != null) { + item.vehicleId = event.getVehicleId(); + } + + if (unscheduleUponVehicleAssignment.contains(event.getRequestId())) { + // this is the case if a request has been rejected / canceled after submission + // but before scheduling + unscheduler.unscheduleRequest(now, event.getVehicleId(), event.getRequestId()); + unscheduleUponVehicleAssignment.remove(event.getRequestId()); + } + } + + scheduledEvents.clear(); + } + + // Functionality for canceling requests + + private static final String CANCEL_REASON = "canceled"; + private final List cancelQueue = new LinkedList<>(); + + private void processCanceledRequests(double now) { + for (CancelItem cancelItem : cancelQueue) { + Id requestId = cancelItem.requestId; + RequestItem item = requests.remove(requestId); + + if (item != null) { // might be null if abandoned before canceling + Verify.verify(!item.onboard, "cannot cancel onboard request"); + + // unschedule if requests is scheduled already + if (item.vehicleId != null) { + unscheduler.unscheduleRequest(now, item.vehicleId, requestId); + } else { + unscheduleUponVehicleAssignment.add(requestId); + } + + String reason = CANCEL_REASON; + + if (cancelItem.reason != null) { + reason = CANCEL_REASON + ":" + cancelItem.reason; + } + + processRejection(item.request, reason); + } + } + + cancelQueue.clear(); + } + + public void cancel(Leg leg) { + cancel(leg, null); + } + + public void cancel(Leg leg, String reason) { + Id requestId = getRequestId(leg); + + if (requestId != null) { + cancel(requestId, reason); + } + } + + public void cancel(Id requestId, String reason) { + cancelQueue.add(new CancelItem(requestId, reason)); + } + + public void cancel(Id requestId) { + cancel(requestId, null); + } + + private record CancelItem(Id requestId, String reason) { + } + + // Functionality for abandoning requests + + private static final String ABANDONED_REASON = "abandoned by vehicle"; + private final ConcurrentLinkedQueue> abandonQueue = new ConcurrentLinkedQueue<>(); + + void abandon(Id requestId) { + abandonQueue.add(requestId); + } + + private void processAbandonedRequests(double now) { + for (Id requestId : abandonQueue) { + RequestItem item = Objects.requireNonNull(requests.remove(requestId)); + Verify.verify(!item.onboard, "cannot abandon request that is already onboard"); + + // remove request from scheduled if already scheduled + if (item.vehicleId != null) { + unscheduler.unscheduleRequest(now, item.vehicleId, item.request.getId()); + } else { + unscheduleUponVehicleAssignment.add(item.request.getId()); + } + + processRejection(item.request, ABANDONED_REASON); + } + + abandonQueue.clear(); + } + + // Rejections + + private void processRejections(double now) { + for (Id requestId : rejectedEventIds) { + RequestItem item = requests.remove(requestId); + + if (item != null) { + // should this fail gracefully? + Verify.verify(!item.onboard, "cannot reject onboard request"); + + // unschedule if already scheduled + if (item.vehicleId != null) { + unscheduler.unscheduleRequest(now, item.vehicleId, requestId); + } else { + unscheduleUponVehicleAssignment.add(requestId); + } + } + } + + rejectedEventIds.clear(); + } + + // Stuck + + private void processStuckAgents(double now) { + bookingQueue.removeIf(request -> stuckPersonsIds.containsAll(request.getPassengerIds())); + + for (RequestItem item : requests.values()) { + if (stuckPersonsIds.containsAll(item.request.getPassengerIds())) { + cancel(item.request.getId()); + } + } + + stuckPersonsIds.clear(); + } + + // Engine code + + @Override + public void onPrepareSim() { + eventsManager.addHandler(this); + } + + @Override + public void doSimStep(double now) { + // avoid method as it runs in parallel with events, only process rejections + flushRejections(now); + } + + @Override + public void notifyMobsimAfterSimStep(@SuppressWarnings("rawtypes") MobsimAfterSimStepEvent e) { + // here we are back in the main thread and all events + // have been processed + + double now = mobsimTimer.getTimeOfDay(); + + // first process scheduled events (this happened, we cannot change it) + processScheduledRequests(now); + + // process rejected requests, potential problem if a person has entered the + // vehicle in just this simstep, but also a rejection has been sent + processRejections(now); + + // process stuck agents, they are added to the cancel queue + processStuckAgents(now); + + // process abandoned requests (by vehicle), here we are sure that the person + // cannot have entered in this iteration + processAbandonedRequests(now); + + // process cancel requests, same situation as for rejections + processCanceledRequests(now); + + // submit requests + processBookingQueue(now); + } + + @Override + public void afterSim() { + eventsManager.removeHandler(this); + } + + @Override + public void setInternalInterface(InternalInterface internalInterface) { + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java new file mode 100644 index 00000000000..b3f6faa4bc7 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java @@ -0,0 +1,110 @@ +package org.matsim.contrib.drt.prebooking; + +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Population; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.prebooking.abandon.AbandonVoter; +import org.matsim.contrib.drt.prebooking.abandon.MaximumDelayAbandonVoter; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PopulationIteratorFactory; +import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue; +import org.matsim.contrib.drt.prebooking.unscheduler.ComplexRequestUnscheduler; +import org.matsim.contrib.drt.prebooking.unscheduler.RequestUnscheduler; +import org.matsim.contrib.drt.prebooking.unscheduler.SimpleRequestUnscheduler; +import org.matsim.contrib.drt.schedule.DrtTaskFactory; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.vrpagent.DrtActionCreator; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.PassengerEngine; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; +import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.mobsim.framework.MobsimTimer; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.TravelTime; + +import com.google.inject.Singleton; + +public class PrebookingModeQSimModule extends AbstractDvrpModeQSimModule { + private final PrebookingParams prebookingParams; + + public PrebookingModeQSimModule(String mode, PrebookingParams prebookingParams) { + super(mode); + this.prebookingParams = prebookingParams; + } + + @Override + protected void configureQSim() { + bindModal(PrebookingActionCreator.class).toProvider(modalProvider(getter -> { + PassengerHandler passengerHandler = (PassengerEngine) getter.getModal(PassengerHandler.class); + DrtActionCreator delegate = getter.getModal(DrtActionCreator.class); + PassengerStopDurationProvider stopDurationProvider = getter.getModal(PassengerStopDurationProvider.class); + PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); + AbandonVoter abandonVoter = getter.getModal(AbandonVoter.class); + + return new PrebookingActionCreator(passengerHandler, delegate, stopDurationProvider, prebookingManager, + abandonVoter); + })).in(Singleton.class); + + bindModal(PrebookingManager.class).toProvider(modalProvider(getter -> { + Network network = getter.getModal(Network.class); + PassengerRequestCreator requestCreator = getter.getModal(PassengerRequestCreator.class); + VrpOptimizer optimizer = getter.getModal(VrpOptimizer.class); + PassengerRequestValidator requestValidator = getter.getModal(PassengerRequestValidator.class); + EventsManager eventsManager = getter.get(EventsManager.class); + RequestUnscheduler requestUnscheduler = getter.getModal(RequestUnscheduler.class); + MobsimTimer mobsimTimer = getter.get(MobsimTimer.class); + + return new PrebookingManager(getMode(), network, requestCreator, optimizer, mobsimTimer, requestValidator, + eventsManager, requestUnscheduler); + })).in(Singleton.class); + addModalQSimComponentBinding().to(modalKey(PrebookingManager.class)); + + bindModal(PrebookingQueue.class).toProvider(modalProvider(getter -> { + return new PrebookingQueue(getter.getModal(PrebookingManager.class)); + })).in(Singleton.class); + addModalQSimComponentBinding().to(modalKey(PrebookingQueue.class)); + + bindModal(PopulationIteratorFactory.class).toProvider(modalProvider(getter -> { + return new PopulationIteratorFactory(getter.get(Population.class), getter.get(QSim.class)); + })); + + bindModal(MaximumDelayAbandonVoter.class).toProvider(modalProvider(getter -> { + double maximumDelay = prebookingParams.maximumPassengerDelay; + return new MaximumDelayAbandonVoter(maximumDelay); + })).in(Singleton.class); + bindModal(AbandonVoter.class).to(modalKey(MaximumDelayAbandonVoter.class)); + + bindModal(SimpleRequestUnscheduler.class).toProvider(modalProvider(getter -> { + DvrpVehicleLookup vehicleLookup = getter.get(DvrpVehicleLookup.class); + return new SimpleRequestUnscheduler(vehicleLookup); + })).in(Singleton.class); + + bindModal(ComplexRequestUnscheduler.class).toProvider(modalProvider(getter -> { + DvrpVehicleLookup vehicleLookup = getter.get(DvrpVehicleLookup.class); + VehicleEntry.EntryFactory entryFactory = getter.getModal(VehicleEntry.EntryFactory.class); + DrtTaskFactory taskFactory = getter.getModal(DrtTaskFactory.class); + LeastCostPathCalculator router = getter.getModal(LeastCostPathCalculator.class); + TravelTime travelTime = getter.getModal(TravelTime.class); + ScheduleTimingUpdater timingUpdater = getter.getModal(ScheduleTimingUpdater.class); + + return new ComplexRequestUnscheduler(vehicleLookup, entryFactory, taskFactory, router, travelTime, + timingUpdater, prebookingParams.scheduleWaitBeforeDrive); + })).in(Singleton.class); + + switch (prebookingParams.unschedulingMode) { + case StopBased: + bindModal(RequestUnscheduler.class).to(modalKey(SimpleRequestUnscheduler.class)); + break; + case Routing: + bindModal(RequestUnscheduler.class).to(modalKey(ComplexRequestUnscheduler.class)); + break; + default: + throw new IllegalStateException("No binding for selected RequestUnscheduler"); + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingParams.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingParams.java new file mode 100644 index 00000000000..fdf678bb272 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingParams.java @@ -0,0 +1,41 @@ +package org.matsim.contrib.drt.prebooking; + +import org.matsim.core.config.ReflectiveConfigGroup; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; + +public class PrebookingParams extends ReflectiveConfigGroup { + public static final String SET_NAME = "prebooking"; + + public PrebookingParams() { + super(SET_NAME); + } + + @Parameter + @Comment("Defines whether vehicles drive immediately to the next" + + " (prebooked) future task and wait for the planned stop to begin, or wait at the current" + + " position and depart to arrive on time at the following stop. The latter behavior (not" + + " the default) may lead to larger ucnertainty in highly congested scenarios.") + public boolean scheduleWaitBeforeDrive = false; // in the future, this could also become a double value indicating + // how many minutes before the next stop the vehicle should plan to + // be there + + @Parameter + @Comment("Request gets rejected if a vehicle waits longer than the indicated duration at the stop") + @NotNull + @Positive + public double maximumPassengerDelay = Double.POSITIVE_INFINITY; + + public enum UnschedulingMode { + StopBased, Routing + } + + @Parameter + @Comment("When unscheduling requests because they have been canceled," + + " we either simply remove the requests from the planned stops" + + " along the vehicle's schedule or we adaptively reconfigure and reroute the vehicle's schedule.") + @NotNull + public UnschedulingMode unschedulingMode = UnschedulingMode.StopBased; + +} \ No newline at end of file diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java new file mode 100644 index 00000000000..3fef9b0c932 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java @@ -0,0 +1,165 @@ +package org.matsim.contrib.drt.prebooking; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.Identifiable; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; +import org.matsim.contrib.drt.prebooking.abandon.AbandonVoter; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; +import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity; +import org.matsim.contrib.dvrp.schedule.StayTask; +import org.matsim.contrib.dynagent.DynAgent; +import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity; +import org.matsim.core.mobsim.framework.MobsimPassengerAgent; + +import com.google.common.base.Verify; + +/** + * Modified version of DrtStopActivity which handles parallel mounting and + * alighting of passengers according to individual pickup and dropoff times. + * + * @author Sebastian Hörl, IRT SystemX (sebhoerl) + */ +public class PrebookingStopActivity extends FirstLastSimStepDynActivity implements PassengerPickupActivity { + private final DynAgent driver; + private final DvrpVehicle vehicle; + + private final Map, ? extends AcceptedDrtRequest> pickupRequests; + private final Map, ? extends AcceptedDrtRequest> dropoffRequests; + + private final IdMap enterTimes = new IdMap<>(Request.class); + private final IdMap leaveTimes = new IdMap<>(Request.class); + private final Set> enteredRequests = new HashSet<>(); + + private final PrebookingManager prebookingManager; + private final PassengerHandler passengerHandler; + + private final PassengerStopDurationProvider stopDurationProvider; + private final AbandonVoter abandonVoter; + + private final Supplier endTime; + + public PrebookingStopActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask task, + Map, ? extends AcceptedDrtRequest> dropoffRequests, + Map, ? extends AcceptedDrtRequest> pickupRequests, String activityType, + Supplier endTime, PassengerStopDurationProvider stopDurationProvider, DvrpVehicle vehicle, + PrebookingManager prebookingManager, AbandonVoter abandonVoter) { + super(activityType); + this.passengerHandler = passengerHandler; + this.driver = driver; + this.dropoffRequests = dropoffRequests; + this.pickupRequests = pickupRequests; + this.stopDurationProvider = stopDurationProvider; + this.vehicle = vehicle; + this.prebookingManager = prebookingManager; + this.abandonVoter = abandonVoter; + this.endTime = endTime; + } + + @Override + protected boolean isLastStep(double now) { + return updatePickupRequests(now) && leaveTimes.size() == 0 && now >= endTime.get(); + } + + @Override + protected void beforeFirstStep(double now) { + initDropoffRequests(now); + updatePickupRequests(now); + } + + private void initDropoffRequests(double now) { + for (var request : dropoffRequests.values()) { + double leaveTime = now + stopDurationProvider.calcDropoffDuration(vehicle, request.getRequest()); + leaveTimes.put(request.getId(), leaveTime); + } + + processDropoffRequests(now); + } + + private void processDropoffRequests(double now) { + var iterator = leaveTimes.entrySet().iterator(); + + while (iterator.hasNext()) { + var entry = iterator.next(); + + if (entry.getValue() <= now) { // Request should leave now + passengerHandler.dropOffPassengers(driver, entry.getKey(), now); + prebookingManager.notifyDropoff(entry.getKey()); + iterator.remove(); + } + } + } + + private boolean updatePickupRequests(double now) { + var pickupIterator = pickupRequests.values().iterator(); + + while (pickupIterator.hasNext()) { + var request = pickupIterator.next(); + + if (!enteredRequests.contains(request.getId()) && !enterTimes.containsKey(request.getId())) { + // this is a new request that has been added after the activity has been created + // or that had not arrived yet + + if (passengerHandler.notifyWaitForPassengers(this, this.driver, request.getId())) { + // agent starts to enter + queuePickup(request, now); + } else if (now > request.getEarliestStartTime()) { + if (abandonVoter.abandonRequest(now, vehicle, request)) { + prebookingManager.abandon(request.getId()); + } + } + } + } + + var enterIterator = enterTimes.entrySet().iterator(); + + while (enterIterator.hasNext()) { + var entry = enterIterator.next(); + + if (entry.getValue() <= now) { + // let agent enter now + Verify.verify(passengerHandler.tryPickUpPassengers(this, driver, entry.getKey(), now)); + enteredRequests.add(entry.getKey()); + enterIterator.remove(); + } + } + + return enterTimes.size() == 0 && pickupRequests.size() == enteredRequests.size(); + } + + private void queuePickup(AcceptedDrtRequest request, double now) { + prebookingManager.notifyPickup(now, request); + double enterTime = now + stopDurationProvider.calcPickupDuration(vehicle, request.getRequest()); + enterTimes.put(request.getId(), enterTime); + } + + @Override + protected void simStep(double now) { + // dynamics are handled in isLastStep -> updatePickupRequests + } + + @Override + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); + queuePickup(request, now); + } + + + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { + return pickupRequests.values() + .stream() + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/AbandonVoter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/AbandonVoter.java new file mode 100644 index 00000000000..20ba9a2baca --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/AbandonVoter.java @@ -0,0 +1,8 @@ +package org.matsim.contrib.drt.prebooking.abandon; + +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public interface AbandonVoter { + boolean abandonRequest(double time, DvrpVehicle vehicle, AcceptedDrtRequest request); +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/MaximumDelayAbandonVoter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/MaximumDelayAbandonVoter.java new file mode 100644 index 00000000000..f23c6623e22 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/abandon/MaximumDelayAbandonVoter.java @@ -0,0 +1,19 @@ +package org.matsim.contrib.drt.prebooking.abandon; + +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class MaximumDelayAbandonVoter implements AbandonVoter { + private final double maximumDelay; + + public MaximumDelayAbandonVoter(double maximumDelay) { + this.maximumDelay = maximumDelay; + } + + @Override + public boolean abandonRequest(double time, DvrpVehicle vehicle, AcceptedDrtRequest request) { + double requestedDepartureTime = request.getEarliestStartTime(); + double delay = time - requestedDepartureTime; + return delay > maximumDelay; + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java new file mode 100644 index 00000000000..a160ac5fce4 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java @@ -0,0 +1,118 @@ +package org.matsim.contrib.drt.prebooking.analysis; + +import java.util.LinkedList; +import java.util.List; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.prebooking.PassengerRequestBookedEvent; +import org.matsim.contrib.drt.prebooking.PassengerRequestBookedEventHandler; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerWaitingEvent; +import org.matsim.contrib.dvrp.passenger.PassengerWaitingEventHandler; + +public class PrebookingAnalysisHandler implements PassengerRequestBookedEventHandler, + PassengerRequestSubmittedEventHandler, PassengerRequestScheduledEventHandler, + PassengerRequestRejectedEventHandler, PassengerWaitingEventHandler { + private final String mode; + private final IdMap sequences = new IdMap<>(Request.class); + + public PrebookingAnalysisHandler(String mode) { + this.mode = mode; + } + + @Override + public void handleEvent(PassengerRequestBookedEvent event) { + sequences.put(event.getRequestId(), new Sequence(event)); + } + + @Override + public void handleEvent(PassengerRequestSubmittedEvent event) { + if (!event.getMode().equals(mode)) { + return; + } + + Sequence sequence = sequences.get(event.getRequestId()); + + if (sequence != null) { + sequence.submitted = event; + } + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (!event.getMode().equals(mode)) { + return; + } + + Sequence sequence = sequences.get(event.getRequestId()); + + if (sequence != null && sequence.rejected == null) { // only use first + sequence.rejected = event; + } + } + + @Override + public void handleEvent(PassengerRequestScheduledEvent event) { + if (!event.getMode().equals(mode)) { + return; + } + + Sequence sequence = sequences.get(event.getRequestId()); + + if (sequence != null) { + sequence.scheduled = event; + } + } + + @Override + public void handleEvent(PassengerWaitingEvent event) { + if (!event.getMode().equals(mode)) { + return; + } + + Sequence sequence = sequences.get(event.getRequestId()); + + if (sequence != null) { + sequence.waiting = event; + } + } + + public List getRecords() { + List records = new LinkedList<>(); + + for (Sequence sequence : sequences) { + records.add(new RequestRecord(sequence.booked.getRequestId(), sequence.booked.getPersonIds(), + sequence.submitted != null ? sequence.submitted.getTime() : null, + sequence.scheduled != null ? sequence.scheduled.getTime() : null, + sequence.rejected != null ? sequence.rejected.getTime() : null, + sequence.waiting != null ? sequence.waiting.getTime() : null, + sequence.rejected != null ? sequence.rejected.getCause() : null)); + } + + return records; + } + + public record RequestRecord(Id requestId, List> personIds, Double submissionTime, Double scheduledTime, + Double rejectedTime, Double departureTime, String rejectedReason) { + } + + private class Sequence { + final PassengerRequestBookedEvent booked; + PassengerRequestSubmittedEvent submitted; + PassengerRequestScheduledEvent scheduled; + PassengerRequestRejectedEvent rejected; + PassengerWaitingEvent waiting; + + Sequence(PassengerRequestBookedEvent booked) { + this.booked = booked; + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisListener.java new file mode 100644 index 00000000000..f90dab2c2ce --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisListener.java @@ -0,0 +1,42 @@ +package org.matsim.contrib.drt.prebooking.analysis; + +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; + +public class PrebookingAnalysisListener implements IterationStartsListener, IterationEndsListener { + private final String mode; + + private final OutputDirectoryHierarchy outputHierarchy; + private final EventsManager eventsManager; + + private PrebookingAnalysisHandler handler; + + public PrebookingAnalysisListener(String mode, EventsManager eventsManager, + OutputDirectoryHierarchy outputHierarchy) { + this.mode = mode; + this.eventsManager = eventsManager; + this.outputHierarchy = outputHierarchy; + } + + @Override + public void notifyIterationStarts(IterationStartsEvent event) { + handler = new PrebookingAnalysisHandler(mode); + eventsManager.addHandler(handler); + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + eventsManager.removeHandler(handler); + + String outputPath = outputHierarchy.getIterationFilename(event.getIteration(), getOutputFileName()); + new PrebookingAnalysisWriter(outputPath).write(handler.getRecords()); + } + + private String getOutputFileName() { + return String.format("prebooking_%s.csv", mode); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java new file mode 100644 index 00000000000..5127841f292 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java @@ -0,0 +1,48 @@ +package org.matsim.contrib.drt.prebooking.analysis; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import org.matsim.core.utils.io.IOUtils; + +public class PrebookingAnalysisWriter { + private final String outputPath; + + public PrebookingAnalysisWriter(String outputPath) { + this.outputPath = outputPath; + } + + public void write(List records) { + try { + BufferedWriter writer = IOUtils.getBufferedWriter(outputPath); + + writer.write(String.join(",", new String[] { // + "request_id", // + "person_id", // + "submission_time", // + "scheduled_time", // + "rejected_time", // + "departure_time", // + "rejected_reason" // + }) + "\n"); + + for (var record : records) { + writer.write(String.join(",", new String[] { // + record.requestId().toString(), // + record.personIds().stream().map(Object::toString).collect(Collectors.joining("-")), // + record.submissionTime() == null ? "" : String.valueOf(record.submissionTime()), // + record.scheduledTime() == null ? "" : String.valueOf(record.scheduledTime()), // + record.rejectedTime() == null ? "" : String.valueOf(record.rejectedTime()), // + record.departureTime() == null ? "" : String.valueOf(record.departureTime()), // + record.rejectedReason() // + }) + "\n"); + } + + writer.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingModeAnalysisModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingModeAnalysisModule.java new file mode 100644 index 00000000000..5382f928d2d --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingModeAnalysisModule.java @@ -0,0 +1,25 @@ +package org.matsim.contrib.drt.prebooking.analysis; + +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.controler.OutputDirectoryHierarchy; + +import com.google.inject.Singleton; + +public class PrebookingModeAnalysisModule extends AbstractDvrpModeModule { + public PrebookingModeAnalysisModule(String mode) { + super(mode); + } + + @Override + public void install() { + addControlerListenerBinding().to(modalKey(PrebookingAnalysisListener.class)); + + bindModal(PrebookingAnalysisListener.class).toProvider(modalProvider(getter -> { + EventsManager eventsManager = getter.get(EventsManager.class); + OutputDirectoryHierarchy outputHierarchy = getter.get(OutputDirectoryHierarchy.class); + + return new PrebookingAnalysisListener(getMode(), eventsManager, outputHierarchy); + })).in(Singleton.class); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AdaptivePrebookingLogic.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AdaptivePrebookingLogic.java new file mode 100644 index 00000000000..349000a5ac7 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AdaptivePrebookingLogic.java @@ -0,0 +1,118 @@ +package org.matsim.contrib.drt.prebooking.logic; + +import org.matsim.api.core.v01.events.ActivityStartEvent; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.contrib.drt.prebooking.PrebookingManager; +import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.events.MobsimScopeEventHandler; +import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; + +import com.google.common.base.Preconditions; + +/** + * This is a prebooking logic that, whenever an agent starts an activity, checks + * whether there is a DRT leg coming up before the next main (non-stage) + * activity. If so, the upcoming leg is prebooked in advance with the + * submissionSlack parameter defining how much in advance. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class AdaptivePrebookingLogic implements PrebookingLogic, ActivityStartEventHandler, MobsimScopeEventHandler { + private final QSim qsim; + private final PrebookingManager prebookingManager; + private final PrebookingQueue prebookingQueue; + private final TimeInterpretation timeInterpretation; + + private final String mode; + + private final double submissionSlack; + + private AdaptivePrebookingLogic(String mode, QSim qsim, PrebookingManager prebookingManager, + PrebookingQueue prebookingQueue, TimeInterpretation timeInterpretation, double submissionSlack) { + this.prebookingManager = prebookingManager; + this.prebookingQueue = prebookingQueue; + this.qsim = qsim; + this.mode = mode; + this.timeInterpretation = timeInterpretation; + this.submissionSlack = submissionSlack; + } + + @Override + public void handleEvent(ActivityStartEvent event) { + MobsimAgent agent = qsim.getAgents().get(event.getPersonId()); + + Plan plan = WithinDayAgentUtils.getModifiablePlan(agent); + int planElementIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(agent); + + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + timeTracker.setTime(event.getTime()); + + /* + * Now we starting to traverse the remaining plan to see if we find a DRT leg + * that comes after the currently started activity. If so, we prebook it with + * the configured submission slack. We start searching one we reach the next + * non-stage activity type. + */ + + for (int i = planElementIndex + 1; i < plan.getPlanElements().size(); i++) { + PlanElement element = plan.getPlanElements().get(i); + + if (element instanceof Activity) { + Activity activity = (Activity) element; + + if (!TripStructureUtils.isStageActivityType(activity.getType())) { + break; // only consider legs coming directly after the ongoing activity + } + } else if (element instanceof Leg) { + Leg leg = (Leg) element; + + if (mode.equals(leg.getMode())) { + double departureTime = timeTracker.getTime().seconds(); + + if (prebookingManager.getRequestId(leg) == null) { + // only book legs that are not already prebooked + + double submissionTime = Math.max(event.getTime(), departureTime - submissionSlack); + if (submissionTime > departureTime) { + prebookingQueue.schedule(submissionTime, agent, leg, departureTime); + } + } + } + } + + timeTracker.addElement(element); + } + } + + static public AbstractDvrpModeQSimModule createModule(DrtConfigGroup drtConfig, double subissionSlack) { + return new AbstractDvrpModeQSimModule(drtConfig.getMode()) { + @Override + protected void configureQSim() { + bindModal(AdaptivePrebookingLogic.class).toProvider(modalProvider(getter -> { + Preconditions.checkState(drtConfig.getPrebookingParams().isPresent()); + + return new AdaptivePrebookingLogic(drtConfig.getMode(), getter.get(QSim.class), + getter.getModal(PrebookingManager.class), getter.getModal(PrebookingQueue.class), + getter.get(TimeInterpretation.class), subissionSlack); + })); + addMobsimScopeEventHandlerBinding().to(modalKey(AdaptivePrebookingLogic.class)); + } + }; + } + + static public void install(Controler controller, DrtConfigGroup drtConfig, double subissionSlack) { + controller.addOverridingQSimModule(createModule(drtConfig, subissionSlack)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AttributeBasedPrebookingLogic.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AttributeBasedPrebookingLogic.java new file mode 100644 index 00000000000..a48cbe50e46 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/AttributeBasedPrebookingLogic.java @@ -0,0 +1,137 @@ +package org.matsim.contrib.drt.prebooking.logic; + +import java.util.Optional; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PopulationIteratorFactory; +import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.events.MobsimInitializedEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimInitializedListener; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.router.TripStructureUtils.Trip; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; + +import com.google.common.base.Preconditions; + +/** + * This class represents a prebooking logic that will search for the + * "prebooking:submissionTime:[mode]" attribute in the origin activity of a + * trip. If a DRT leg is found in the trip, the submission time for this request + * will be read from the attribute. In order to make use of prebooked requests, + * you need to define these attributes before the start of the QSim iteration. + * + * To indicate the expected departure time (for which the vehicle is requested), + * you can also define the "prebooking:plannedDepartureTime:[mode]" attribute. + * If it is not specified, the logic will try to figure out the departure time + * by traversing the activities and legs according to the configured time + * interpretation. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class AttributeBasedPrebookingLogic implements PrebookingLogic, MobsimInitializedListener { + static private final String SUBMISSION_TIME_ATTRIBUTE_PREFIX = "prebooking:submissionTime"; + static private final String PLANNED_DEPARTURE_ATTRIBUTE_PREFIX = "prebooking:plannedDepartureTime"; + + static public String getSubmissionAttribute(String mode) { + return SUBMISSION_TIME_ATTRIBUTE_PREFIX + mode; + } + + static public String getPlannedDepartureAttribute(String mode) { + return PLANNED_DEPARTURE_ATTRIBUTE_PREFIX + mode; + } + + static public Optional getSubmissionTime(String mode, Trip trip) { + return Optional.ofNullable((Double) trip.getTripAttributes().getAttribute(getSubmissionAttribute(mode))); + } + + static public Optional getPlannedDepartureTime(String mode, Trip trip) { + return Optional.ofNullable((Double) trip.getTripAttributes().getAttribute(getPlannedDepartureAttribute(mode))); + } + + private final PrebookingQueue prebookingQueue; + private final PopulationIteratorFactory populationIteratorFactory; + private final TimeInterpretation timeInterpretation; + + private final String mode; + + private AttributeBasedPrebookingLogic(String mode, PrebookingQueue prebookingQueue, + PopulationIteratorFactory populationIteratorFactory, TimeInterpretation timeInterpretation) { + this.prebookingQueue = prebookingQueue; + this.populationIteratorFactory = populationIteratorFactory; + this.mode = mode; + this.timeInterpretation = timeInterpretation; + + } + + @Override + public void notifyMobsimInitialized(@SuppressWarnings("rawtypes") MobsimInitializedEvent e) { + PopulationIterator populationIterator = populationIteratorFactory.create(); + + while (populationIterator.hasNext()) { + var personItem = populationIterator.next(); + + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + + for (Trip trip : TripStructureUtils.getTrips(personItem.plan())) { + timeTracker.addActivity(trip.getOriginActivity()); + boolean foundLeg = false; + + for (PlanElement element : trip.getTripElements()) { + if (element instanceof Leg) { + Leg leg = (Leg) element; + + if (mode.equals(leg.getMode())) { + Preconditions.checkState(!foundLeg, + "Attribute-based prebooking logic only works with one DRT leg per trip"); + foundLeg = true; + + Optional submissionTime = getSubmissionTime(mode, trip); + Optional plannedDepartureTime = getPlannedDepartureTime(mode, trip); + + if (submissionTime.isPresent()) { + if (plannedDepartureTime.isPresent()) { + Preconditions.checkState(plannedDepartureTime.get() > submissionTime.get(), + "Planned departure time must be after submission time"); + } + + prebookingQueue.schedule(submissionTime.get(), personItem.agent(), leg, + plannedDepartureTime.orElse(timeTracker.getTime().seconds())); + } + } + } + + timeTracker.addElement(element); + } + + } + } + + prebookingQueue.performInitialSubmissions(); + } + + static public AbstractDvrpModeQSimModule createModule(DrtConfigGroup drtConfig) { + return new AbstractDvrpModeQSimModule(drtConfig.getMode()) { + @Override + protected void configureQSim() { + bindModal(AttributeBasedPrebookingLogic.class).toProvider(modalProvider(getter -> { + Preconditions.checkState(drtConfig.getPrebookingParams().isPresent()); + + return new AttributeBasedPrebookingLogic(drtConfig.getMode(), + getter.getModal(PrebookingQueue.class), getter.getModal(PopulationIteratorFactory.class), + getter.get(TimeInterpretation.class)); + })); + addModalQSimComponentBinding().to(modalKey(AttributeBasedPrebookingLogic.class)); + } + }; + } + + static public void install(Controler controller, DrtConfigGroup drtConfig) { + controller.addOverridingQSimModule(createModule(drtConfig)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PersonBasedPrebookingLogic.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PersonBasedPrebookingLogic.java new file mode 100644 index 00000000000..a7faed87853 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PersonBasedPrebookingLogic.java @@ -0,0 +1,113 @@ +package org.matsim.contrib.drt.prebooking.logic; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PopulationIteratorFactory; +import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.events.MobsimInitializedEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimInitializedListener; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; + +import com.google.common.base.Preconditions; + +/** + * This is a prebooking logic that detects a person-level attribute which + * indicates whether requests to a certain DRT mode for that person are + * prebooked. It can be installed by calling + * + * PersonBasedPrebookingLogic.install + * + * on the controller with the respective DRT mode and a value indicating the + * prebooking slack, i.e., the time between the expected departure and when the + * request is submitted. + */ +public class PersonBasedPrebookingLogic implements PrebookingLogic, MobsimInitializedListener { + static private final String ATTRIBUTE_PREFIX = "prebooking:"; + + static public String getPersonAttribute(String mode) { + return ATTRIBUTE_PREFIX + mode; + } + + static public String getPersonAttribute(DrtConfigGroup drtConfig) { + return getPersonAttribute(drtConfig.getMode()); + } + + static public boolean isPrebooked(String mode, Person person) { + Boolean isPrebooked = (Boolean) person.getAttributes().getAttribute(getPersonAttribute(mode)); + return isPrebooked == true; + } + + private final PrebookingQueue prebookingQueue; + private final PopulationIteratorFactory populationIteratorFactory; + private final TimeInterpretation timeInterpretation; + + private final String mode; + private final double submissionSlack; + + private PersonBasedPrebookingLogic(String mode, PrebookingQueue prebookingQueue, + PopulationIteratorFactory populationIteratorFactory, TimeInterpretation timeInterpretation, + double submissionSlack) { + this.prebookingQueue = prebookingQueue; + this.populationIteratorFactory = populationIteratorFactory; + this.mode = mode; + this.timeInterpretation = timeInterpretation; + this.submissionSlack = submissionSlack; + + } + + @Override + public void notifyMobsimInitialized(@SuppressWarnings("rawtypes") MobsimInitializedEvent e) { + PopulationIterator populationIterator = populationIteratorFactory.create(); + + while (populationIterator.hasNext()) { + var personItem = populationIterator.next(); + + if (isPrebooked(mode, personItem.plan().getPerson())) { + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + + for (PlanElement element : personItem.plan().getPlanElements()) { + if (element instanceof Leg) { + Leg leg = (Leg) element; + + if (leg.getMode().equals(mode)) { + double earliestDepartureTime = leg.getDepartureTime().seconds(); + double submissionTime = earliestDepartureTime - submissionSlack; + + prebookingQueue.schedule(submissionTime, personItem.agent(), leg, earliestDepartureTime); + } + } + + timeTracker.addElement(element); + } + } + } + + prebookingQueue.performInitialSubmissions(); + } + + static public AbstractDvrpModeQSimModule createModule(DrtConfigGroup drtConfig, double prebookingSlack) { + return new AbstractDvrpModeQSimModule(drtConfig.getMode()) { + @Override + protected void configureQSim() { + bindModal(PersonBasedPrebookingLogic.class).toProvider(modalProvider(getter -> { + Preconditions.checkState(drtConfig.getPrebookingParams().isPresent()); + + return new PersonBasedPrebookingLogic(drtConfig.getMode(), getter.getModal(PrebookingQueue.class), + getter.getModal(PopulationIteratorFactory.class), getter.get(TimeInterpretation.class), + prebookingSlack); + })); + addModalQSimComponentBinding().to(modalKey(PersonBasedPrebookingLogic.class)); + } + }; + } + + static public void install(Controler controller, DrtConfigGroup drtConfig, double prebookingSlack) { + controller.addOverridingQSimModule(createModule(drtConfig, prebookingSlack)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PrebookingLogic.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PrebookingLogic.java new file mode 100644 index 00000000000..d46edeea35f --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/PrebookingLogic.java @@ -0,0 +1,11 @@ +package org.matsim.contrib.drt.prebooking.logic; + +/** + * An interface that tags a prebooking logic, but doesn't have any functionality + * itself. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public interface PrebookingLogic { + +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/ProbabilityBasedPrebookingLogic.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/ProbabilityBasedPrebookingLogic.java new file mode 100644 index 00000000000..2a62cb6b2d6 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/ProbabilityBasedPrebookingLogic.java @@ -0,0 +1,101 @@ +package org.matsim.contrib.drt.prebooking.logic; + +import java.util.Random; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PopulationIteratorFactory; +import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.events.MobsimInitializedEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimInitializedListener; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; + +import com.google.common.base.Preconditions; + +/** + * This class represents a prebooking logic that searches for DRT legs in the + * population and decides based on a predefiend probability if each trip is + * prebooked or not. Furthermore, you can configure how much in advance to the + * planned departure time the request is submitted using the submissionSlack + * parameter. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class ProbabilityBasedPrebookingLogic implements PrebookingLogic, MobsimInitializedListener { + private final PrebookingQueue prebookingQueue; + private final PopulationIteratorFactory populationIteratorFactory; + private final TimeInterpretation timeInterpretation; + private final Random random; + + private final String mode; + private final double probability; + private final double submissionSlack; + + private ProbabilityBasedPrebookingLogic(String mode, PrebookingQueue prebookingQueue, + PopulationIteratorFactory populationIteratorFactory, TimeInterpretation timeInterpretation, Random random, + double probability, double submissionSlack) { + this.prebookingQueue = prebookingQueue; + this.populationIteratorFactory = populationIteratorFactory; + this.mode = mode; + this.timeInterpretation = timeInterpretation; + this.random = random; + this.probability = probability; + this.submissionSlack = submissionSlack; + } + + @Override + public void notifyMobsimInitialized(@SuppressWarnings("rawtypes") MobsimInitializedEvent e) { + PopulationIterator populationIterator = populationIteratorFactory.create(); + + while (populationIterator.hasNext()) { + var personItem = populationIterator.next(); + + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + + for (PlanElement element : personItem.plan().getPlanElements()) { + if (element instanceof Leg) { + Leg leg = (Leg) element; + + if (mode.equals(leg.getMode()) && random.nextDouble() <= probability) { + double departureTime = timeTracker.getTime().seconds(); + double submissionTime = departureTime - submissionSlack; + + prebookingQueue.schedule(submissionTime, personItem.agent(), leg, departureTime); + } + } + + timeTracker.addElement(element); + } + } + + prebookingQueue.performInitialSubmissions(); + } + + static public AbstractDvrpModeQSimModule createModule(DrtConfigGroup drtConfig, double probability, + double submissionSlack) { + return new AbstractDvrpModeQSimModule(drtConfig.getMode()) { + @Override + protected void configureQSim() { + bindModal(ProbabilityBasedPrebookingLogic.class).toProvider(modalProvider(getter -> { + Preconditions.checkState(drtConfig.getPrebookingParams().isPresent()); + + return new ProbabilityBasedPrebookingLogic(drtConfig.getMode(), + getter.getModal(PrebookingQueue.class), getter.getModal(PopulationIteratorFactory.class), + getter.get(TimeInterpretation.class), new Random(getConfig().global().getRandomSeed()), + probability, submissionSlack); + })); + addModalQSimComponentBinding().to(modalKey(ProbabilityBasedPrebookingLogic.class)); + } + }; + } + + static public void install(Controler controller, DrtConfigGroup drtConfig, double probability, + double prebookingSlack) { + controller.addOverridingQSimModule(createModule(drtConfig, probability, prebookingSlack)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PopulationIterator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PopulationIterator.java new file mode 100644 index 00000000000..faf2c1ce87c --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PopulationIterator.java @@ -0,0 +1,57 @@ +package org.matsim.contrib.drt.prebooking.logic.helpers; + +import java.util.Iterator; + +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PersonItem; +import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.agents.HasModifiablePlan; + +/** + * This is a helper class that allows to loop through all persons that are + * active in a QSim. It is used to prebook drt legs for specific legs. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PopulationIterator implements Iterator { + private final QSim qsim; + private final Iterator internalIterator; + + private PopulationIterator(Population population, QSim qsim) { + this.qsim = qsim; + this.internalIterator = population.getPersons().values().iterator(); + } + + @Override + public boolean hasNext() { + return internalIterator.hasNext(); + } + + @Override + public PersonItem next() { + Person person = internalIterator.next(); + MobsimAgent agent = qsim.getAgents().get(person.getId()); + Plan plan = ((HasModifiablePlan) agent).getModifiablePlan(); + return new PersonItem(agent, plan); + } + + public record PersonItem(MobsimAgent agent, Plan plan) { + } + + static public class PopulationIteratorFactory { + private final Population population; + private final QSim qsim; + + public PopulationIteratorFactory(Population population, QSim qsim) { + this.population = population; + this.qsim = qsim; + } + + public PopulationIterator create() { + return new PopulationIterator(population, qsim); + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java new file mode 100644 index 00000000000..ed04764c9b4 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java @@ -0,0 +1,81 @@ +package org.matsim.contrib.drt.prebooking.logic.helpers; + +import java.util.PriorityQueue; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.contrib.drt.prebooking.PrebookingManager; +import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; + +import com.google.common.base.Preconditions; + +/** + * This service helps to define a PrebookingLogic where at some point in time + * (usually at the beginning of the simulaton), it is known in advance that a + * request will happen at a specific time. Once we know that the request will be + * sent, we can use the present class to put it in a queue, making sure the + * request will be *booked* the the time we want. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PrebookingQueue implements MobsimBeforeSimStepListener { + private final PrebookingManager prebookingManager; + + private PriorityQueue queue = new PriorityQueue<>(); + private Integer sequence = 0; + + private double currentTime = Double.NEGATIVE_INFINITY; + + public PrebookingQueue(PrebookingManager prebookingManager) { + this.prebookingManager = prebookingManager; + } + + @Override + public void notifyMobsimBeforeSimStep(@SuppressWarnings("rawtypes") MobsimBeforeSimStepEvent event) { + performSubmissions(event.getSimulationTime()); + } + + private void performSubmissions(double time) { + currentTime = time; + + while (!queue.isEmpty() && queue.peek().submissionTime <= time) { + var item = queue.poll(); + prebookingManager.prebook(item.agent(), item.leg(), item.departuretime()); + } + } + + /** + * May be called by a submission logic to directly perform submission after the + * MobsimInitializedEvent, otherwise this is called automatically per time step + * to see if there are requests queued that need to be submitted. + */ + public void performInitialSubmissions() { + performSubmissions(Double.NEGATIVE_INFINITY); + } + + public void schedule(double submissionTime, MobsimAgent agent, Leg leg, double departureTime) { + Preconditions.checkArgument(submissionTime > currentTime, "Can only submit future requests"); + + synchronized (queue) { // synchronizing for queue and sequence + queue.add(new ScheduledSubmission(submissionTime, agent, leg, departureTime, sequence++)); + } + } + + private record ScheduledSubmission(double submissionTime, MobsimAgent agent, Leg leg, double departuretime, + int sequence) implements Comparable { + @Override + public int compareTo(ScheduledSubmission o) { + // sort by submissionTime, but otherwise keep the order of submission + int comparison = Double.compare(submissionTime, o.submissionTime); + + if (comparison != 0) { + return comparison; + } + + // comparing by sequence, because a PriorityQueue is *not* preserving the order + // of two elements with the same comparison value + return Integer.compare(sequence, o.sequence); + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/ComplexRequestUnscheduler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/ComplexRequestUnscheduler.java new file mode 100644 index 00000000000..4fb0cae5339 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/ComplexRequestUnscheduler.java @@ -0,0 +1,319 @@ +package org.matsim.contrib.drt.prebooking.unscheduler; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.optimizer.Waypoint; +import org.matsim.contrib.drt.schedule.DrtDriveTask; +import org.matsim.contrib.drt.schedule.DrtStayTask; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.schedule.DrtTaskBaseType; +import org.matsim.contrib.drt.schedule.DrtTaskFactory; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; +import org.matsim.contrib.dvrp.path.VrpPaths; +import org.matsim.contrib.dvrp.schedule.DriveTask; +import org.matsim.contrib.dvrp.schedule.Schedule; +import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.contrib.dvrp.schedule.Schedules; +import org.matsim.contrib.dvrp.schedule.StayTask; +import org.matsim.contrib.dvrp.schedule.Task; +import org.matsim.contrib.dvrp.schedule.Task.TaskStatus; +import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; +import org.matsim.contrib.dvrp.util.LinkTimePair; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.TravelTime; + +import com.google.common.base.Verify; + +/** + * This RequestUnscheduler searches for a request in a vehicle's schedule and + * removes the request from the relevant stop tasks. Furthermore, the stops are + * removed if they don't carry any other pickups or dropoffs. Accordingly, the + * schedule will also be rerouted. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class ComplexRequestUnscheduler implements RequestUnscheduler { + private final DvrpVehicleLookup vehicleLookup; + private final VehicleEntry.EntryFactory vehicleEntryFactory; + + private final DrtTaskFactory taskFactory; + + private final LeastCostPathCalculator router; + private final TravelTime travelTime; + private final ScheduleTimingUpdater timingUpdater; + + private final boolean scheduleWaitBeforeDrive; + + public ComplexRequestUnscheduler(DvrpVehicleLookup vehicleLookup, VehicleEntry.EntryFactory vehicleEntryFactory, + DrtTaskFactory taskFactory, LeastCostPathCalculator router, TravelTime travelTime, + ScheduleTimingUpdater timingUpdater, boolean scheduleWaitBeforeDrive) { + this.vehicleLookup = vehicleLookup; + this.vehicleEntryFactory = vehicleEntryFactory; + this.taskFactory = taskFactory; + this.travelTime = travelTime; + this.router = router; + this.timingUpdater = timingUpdater; + this.scheduleWaitBeforeDrive = scheduleWaitBeforeDrive; + } + + @Override + public void unscheduleRequest(double now, Id vehicleId, Id requestId) { + DvrpVehicle vehicle = vehicleLookup.lookupVehicle(vehicleId); + VehicleEntry vEntry = vehicleEntryFactory.create(vehicle, now); + + Waypoint.Stop pickupStop = null; + Waypoint.Stop dropoffStop = null; + + DrtStopTask pickupStopTask = null; + DrtStopTask dropoffStopTask = null; + + for (Waypoint.Stop stop : vEntry.stops) { + if (stop.task.getPickupRequests().containsKey(requestId)) { + Verify.verify(pickupStop == null); + Verify.verify(pickupStopTask == null); + + pickupStop = stop; + pickupStopTask = stop.task; + } + + if (stop.task.getDropoffRequests().containsKey(requestId)) { + Verify.verify(dropoffStop == null); + Verify.verify(dropoffStopTask == null); + + dropoffStop = stop; + dropoffStopTask = stop.task; + } + } + + Verify.verifyNotNull(pickupStopTask, "Could not find request that I'm supposed to unschedule"); + Verify.verifyNotNull(dropoffStopTask, "Could not find request that I'm supposed to unschedule"); + Verify.verifyNotNull(pickupStop); + Verify.verifyNotNull(dropoffStop); + + // remove request from stop, this we do in any case + pickupStopTask.removePickupRequest(requestId); + dropoffStopTask.removeDropoffRequest(requestId); + + // remove pickup + // - either we didn't find a stop (because task is running), then we have + // removed the pickup and the StopAction will handle the situation + // - or we found a stop, then it is not started yet and we can remove it + + boolean removePickup = pickupStopTask.getPickupRequests().size() == 0 + && pickupStopTask.getDropoffRequests().size() == 0; + boolean removeDropoff = dropoffStopTask.getPickupRequests().size() == 0 + && dropoffStopTask.getDropoffRequests().size() == 0; + + Replacement pickupReplacement = removePickup ? findReplacement(vEntry, pickupStop) : null; + Replacement dropoffReplacement = removeDropoff ? findReplacement(vEntry, dropoffStop) : null; + + if (pickupReplacement != null && dropoffReplacement != null) { + if (pickupReplacement.endTask.getTaskIdx() >= dropoffReplacement.startTask.getTaskIdx()) { + // we have an overlap + pickupReplacement = new Replacement(pickupReplacement.startTask, dropoffReplacement.endTask, + vehicle.getSchedule()); + dropoffReplacement = null; + } + } + + if (pickupReplacement != null) { + unschedule(now, vEntry, pickupReplacement); + } + + if (dropoffReplacement != null) { + unschedule(now, vEntry, dropoffReplacement); + } + } + + private Replacement findReplacement(VehicleEntry vEntry, Waypoint.Stop stop) { + int stopIndex = vEntry.stops.indexOf(stop); + + final Task startTask; + if (stopIndex == 0) { + startTask = vEntry.vehicle.getSchedule().getCurrentTask(); + } else { + startTask = vEntry.stops.get(stopIndex - 1).task; + } + + final Task endTask; + if (stopIndex == vEntry.stops.size() - 1) { + endTask = Schedules.getLastTask(vEntry.vehicle.getSchedule()); + } else { + endTask = vEntry.stops.get(stopIndex + 1).task; + } + + return new Replacement(startTask, endTask, vEntry.vehicle.getSchedule()); + } + + private void unschedule(double now, VehicleEntry vEntry, Replacement replacement) { + Schedule schedule = vEntry.vehicle.getSchedule(); + + if (replacement.startTask instanceof DrtStayTask) { + replacement.startTask.setEndTime(now); + } + + // special case: we remove everything until the end (and replace the stay task) + boolean removeUntilEnd = replacement.endTask == Schedules.getLastTask(schedule); + if (removeUntilEnd) { + Verify.verify(replacement.endTask instanceof DrtStayTask); + final Link stayLink; + + if (replacement.startTask instanceof StayTask) { + stayLink = ((StayTask) replacement.startTask).getLink(); + } else { + Verify.verify(replacement.startTask.getStatus().equals(TaskStatus.STARTED)); + DriveTask driveTask = (DriveTask) replacement.startTask; + + OnlineDriveTaskTracker tracker = (OnlineDriveTaskTracker) driveTask.getTaskTracker(); + tracker.divertPath(VrpPaths.createZeroLengthPathForDiversion(tracker.getDiversionPoint())); + + stayLink = driveTask.getPath().getToLink(); + } + + double initialEndTime = replacement.endTask.getEndTime(); + + while (!(replacement.startTask == Schedules.getLastTask(schedule))) { + schedule.removeLastTask(); + } + + schedule.addTask(taskFactory.createStayTask(vEntry.vehicle, replacement.startTask.getEndTime(), + Math.max(replacement.startTask.getEndTime(), initialEndTime), stayLink)); + + return; // done + } + + // remove everything between the two indicated tasks + while (replacement.startTask.getTaskIdx() + 1 != replacement.endTask.getTaskIdx()) { + Task removeTask = schedule.getTasks().get(replacement.startTask.getTaskIdx() + 1); + schedule.removeTask(removeTask); + } + + // if destination is not the schedule end, it must be another stop + Verify.verify(replacement.endTask instanceof DrtStopTask); + Link endLink = ((StayTask) replacement.endTask).getLink(); + double endArrivalTime = replacement.endTask.getBeginTime(); + + final Task lastInsertedTask; + if (replacement.startTask instanceof DriveTask) { // special case: start task is driving + Verify.verify(replacement.startTask.getStatus().equals(TaskStatus.STARTED)); + + DriveTask driveTask = (DriveTask) replacement.startTask; + OnlineDriveTaskTracker tracker = (OnlineDriveTaskTracker) driveTask.getTaskTracker(); + LinkTimePair diversion = tracker.getDiversionPoint(); + + VrpPathWithTravelData vrpPath = VrpPaths.calcAndCreatePathForDiversion(diversion, endLink, router, + travelTime); + + if (vrpPath.getArrivalTime() < endArrivalTime && scheduleWaitBeforeDrive) { + tracker.divertPath(VrpPaths.createZeroLengthPathForDiversion(diversion)); + lastInsertedTask = insertDriveWithWait(vEntry.vehicle, replacement.startTask, vrpPath, endArrivalTime); + } else { + tracker.divertPath(vrpPath); + lastInsertedTask = insertWait(vEntry.vehicle, replacement.startTask, endArrivalTime); + } + } else { // normal case + StayTask startStayTask = (StayTask) replacement.startTask; + Link startLink = startStayTask.getLink(); + + if (startLink == endLink) { // no need to move, maybe just wait + if (startStayTask.getEndTime() < endArrivalTime) { + lastInsertedTask = insertWait(vEntry.vehicle, startStayTask, endArrivalTime); + } else { + lastInsertedTask = startStayTask; // nothing inserted + } + } else { + VrpPathWithTravelData vrpPath = VrpPaths.calcAndCreatePath(startLink, endLink, + startStayTask.getEndTime(), router, travelTime); + + lastInsertedTask = insertDriveWithWait(vEntry.vehicle, startStayTask, vrpPath, endArrivalTime); + } + } + + timingUpdater.updateTimingsStartingFromTaskIdx(vEntry.vehicle, lastInsertedTask.getTaskIdx() + 1, + lastInsertedTask.getEndTime()); + } + + /* + * Copy & paste from DefaultRequestInsertionScheduler + */ + private Task insertWait(DvrpVehicle vehicle, Task departureTask, double earliestNextStartTime) { + Schedule schedule = vehicle.getSchedule(); + + final Link waitLink; + if (departureTask instanceof StayTask) { + waitLink = ((StayTask) departureTask).getLink(); + } else if (departureTask instanceof DriveTask) { + waitLink = ((DriveTask) departureTask).getPath().getToLink(); + } else { + throw new IllegalStateException(); + } + + if (departureTask.getEndTime() < earliestNextStartTime) { + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, departureTask.getEndTime(), + earliestNextStartTime, waitLink); + schedule.addTask(departureTask.getTaskIdx() + 1, waitTask); + return waitTask; + } + + return departureTask; + } + + /* + * Copy & paste from DefaultRequestInsertionScheduler + */ + private Task insertDriveWithWait(DvrpVehicle vehicle, Task departureTask, VrpPathWithTravelData path, + double latestArrivalTime) { + Schedule schedule = vehicle.getSchedule(); + + Task leadingTask = departureTask; + + if (scheduleWaitBeforeDrive) { + double driveDepartureTime = latestArrivalTime - path.getTravelTime(); + + if (driveDepartureTime > departureTask.getEndTime()) { + // makes sense to insert a wait task before departure + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, departureTask.getEndTime(), + driveDepartureTime, path.getFromLink()); + schedule.addTask(departureTask.getTaskIdx() + 1, waitTask); + + path = path.withDepartureTime(driveDepartureTime); + leadingTask = waitTask; + } + } + + Task driveTask = taskFactory.createDriveTask(vehicle, path, DrtDriveTask.TYPE); + schedule.addTask(leadingTask.getTaskIdx() + 1, driveTask); + + if (driveTask.getEndTime() < latestArrivalTime) { + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, driveTask.getEndTime(), latestArrivalTime, + path.getToLink()); + schedule.addTask(driveTask.getTaskIdx() + 1, waitTask); + return waitTask; + } else { + return driveTask; + } + } + + private class Replacement { + final Task startTask; + final Task endTask; + + Replacement(Task startTask, Task endTask, Schedule schedule) { + boolean startIsOngoing = startTask.getStatus().equals(TaskStatus.STARTED); + boolean startIsStopTask = DrtTaskBaseType.STOP.isBaseTypeOf(startTask); + + Verify.verify(startIsOngoing || startIsStopTask); + this.startTask = startTask; + + boolean endIsLastStay = endTask instanceof DrtStayTask && Schedules.getLastTask(schedule) == endTask; + boolean endIsStopTask = DrtTaskBaseType.STOP.isBaseTypeOf(endTask); + + Verify.verify(endIsLastStay || endIsStopTask); + this.endTask = endTask; + } + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/RequestUnscheduler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/RequestUnscheduler.java new file mode 100644 index 00000000000..fde9b138e92 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/RequestUnscheduler.java @@ -0,0 +1,9 @@ +package org.matsim.contrib.drt.prebooking.unscheduler; + +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; + +public interface RequestUnscheduler { + void unscheduleRequest(double now, Id vehicleId, Id requestId); +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/SimpleRequestUnscheduler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/SimpleRequestUnscheduler.java new file mode 100644 index 00000000000..bc6f2f0fbcc --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/unscheduler/SimpleRequestUnscheduler.java @@ -0,0 +1,60 @@ +package org.matsim.contrib.drt.prebooking.unscheduler; + +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.schedule.Schedule; +import org.matsim.contrib.dvrp.schedule.Task; + +import com.google.common.base.Verify; + +/** + * This RequestUnscheduler searches for a request in a vehicle's schedule and + * removes the request from the relevant stop tasks. No other changes (wrt to + * rerouting the vehicle) are applied to the schedule. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class SimpleRequestUnscheduler implements RequestUnscheduler { + private final DvrpVehicleLookup vehicleLookup; + + public SimpleRequestUnscheduler(DvrpVehicleLookup vehicleLookup) { + this.vehicleLookup = vehicleLookup; + } + + @Override + public void unscheduleRequest(double now, Id vehicleId, Id requestId) { + DvrpVehicle vehicle = vehicleLookup.lookupVehicle(vehicleId); + Schedule schedule = vehicle.getSchedule(); + + DrtStopTask pickupTask = null; + DrtStopTask dropoffTask = null; + + int currentIndex = schedule.getCurrentTask().getTaskIdx(); + for (; currentIndex < schedule.getTaskCount() && dropoffTask == null; currentIndex++) { + Task currentTask = schedule.getTasks().get(currentIndex); + + if (currentTask instanceof DrtStopTask) { + DrtStopTask stopTask = (DrtStopTask) currentTask; + + if (stopTask.getPickupRequests().keySet().contains(requestId)) { + Verify.verify(pickupTask == null); + pickupTask = stopTask; + } + + if (stopTask.getDropoffRequests().keySet().contains(requestId)) { + Verify.verify(dropoffTask == null); + dropoffTask = stopTask; + } + } + } + + Verify.verifyNotNull(pickupTask); + Verify.verifyNotNull(dropoffTask); + + pickupTask.removePickupRequest(requestId); + dropoffTask.removeDropoffRequest(requestId); + } +} \ No newline at end of file diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifier.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifier.java index 549a4761a13..6fb846726ab 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifier.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifier.java @@ -31,7 +31,7 @@ import org.matsim.api.core.v01.population.PlanElement; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.router.MainModeIdentifier; import org.matsim.core.router.MainModeIdentifierImpl; import org.matsim.core.router.TripRouter; @@ -49,7 +49,7 @@ public MultiModeDrtMainModeIdentifier(MultiModeDrtConfigGroup drtCfg) { stageActivityTypeToDrtMode = drtCfg.getModalElements() .stream() .map(DrtConfigGroup::getMode) - .collect(Collectors.toMap(PlanCalcScoreConfigGroup::createStageActivityType, s -> s)); + .collect(Collectors.toMap(ScoringConfigGroup::createStageActivityType, s -> s)); // #deleteBeforeRelease : only used to retrofit plans created since the merge of fallback routing module (sep'-dec'19) fallbackModeToDrtMode = drtCfg.getModalElements() diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java index 1de8257baf3..03b9c551970 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigGroup.java @@ -34,16 +34,18 @@ import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryParams; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.extensive.ExtensiveInsertionSearchParams; +import org.matsim.contrib.drt.optimizer.insertion.repeatedselective.RepeatedSelectiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams; import org.matsim.contrib.drt.optimizer.rebalancing.mincostflow.MinCostFlowRebalancingStrategyParams; +import org.matsim.contrib.drt.prebooking.PrebookingParams; import org.matsim.contrib.drt.speedup.DrtSpeedUpParams; import org.matsim.contrib.dvrp.router.DvrpModeRoutingNetworkModule; import org.matsim.contrib.dvrp.run.Modal; import org.matsim.contrib.util.ReflectiveConfigGroupWithConfigurableParameterSets; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; import com.google.common.base.Preconditions; import com.google.common.base.Verify; @@ -80,7 +82,7 @@ public static DrtConfigGroup getSingleModeDrtConfig(Config config) { public boolean useModeFilteredSubnetwork = false; @Parameter - @Comment("Bus stop duration. Must be positive.") + @Comment("Minimum vehicle stop duration. Must be positive.") @Positive public double stopDuration = Double.NaN;// seconds @@ -129,6 +131,15 @@ public static DrtConfigGroup getSingleModeDrtConfig(Config config) { @Comment("Idle vehicles return to the nearest of all start links. See: DvrpVehicle.getStartLink()") public boolean idleVehiclesReturnToDepots = false; + @Parameter + @Comment("Specifies the duration (seconds) a vehicle needs to be idle in order to get send back to the depot." + + "Please be aware, that returnToDepotEvaluationInterval describes the minimal time a vehicle will be idle before it gets send back to depot.") + public double returnToDepotTimeout = 60; + + @Parameter + @Comment("Specifies the time interval (seconds) a vehicle gets evaluated to be send back to depot.") + public double returnToDepotEvaluationInterval = 60; + public enum OperationalScheme { stopbased, door2door, serviceAreaBased } @@ -181,9 +192,6 @@ public enum OperationalScheme { @Comment("Store planned unshared drt route as a link sequence") public boolean storeUnsharedPath = false; // If true, the planned unshared path is stored and exported in plans - @PositiveOrZero - public double advanceRequestPlanningHorizon = 0; // beta-feature; planning horizon for advance (prebooked) requests - @NotNull private DrtInsertionSearchParams drtInsertionSearchParams; @@ -199,6 +207,9 @@ public enum OperationalScheme { @Nullable private DrtSpeedUpParams drtSpeedUpParams; + @Nullable + private PrebookingParams prebookingParams; + @Nullable private DrtRequestInsertionRetryParams drtRequestInsertionRetryParams; @@ -216,13 +227,16 @@ private void initSingletonParameterSets() { addDefinition(DrtZonalSystemParams.SET_NAME, DrtZonalSystemParams::new, () -> zonalSystemParams, params -> zonalSystemParams = (DrtZonalSystemParams)params); - //insertion search params (one of: extensive, selective) + //insertion search params (one of: extensive, selective, repeated selective) addDefinition(ExtensiveInsertionSearchParams.SET_NAME, ExtensiveInsertionSearchParams::new, () -> drtInsertionSearchParams, params -> drtInsertionSearchParams = (ExtensiveInsertionSearchParams)params); addDefinition(SelectiveInsertionSearchParams.SET_NAME, SelectiveInsertionSearchParams::new, () -> drtInsertionSearchParams, params -> drtInsertionSearchParams = (SelectiveInsertionSearchParams)params); + addDefinition(RepeatedSelectiveInsertionSearchParams.SET_NAME, RepeatedSelectiveInsertionSearchParams::new, + () -> drtInsertionSearchParams, + params -> drtInsertionSearchParams = (RepeatedSelectiveInsertionSearchParams)params); //drt fare (optional) addDefinition(DrtFareParams.SET_NAME, DrtFareParams::new, () -> drtFareParams, @@ -236,6 +250,11 @@ private void initSingletonParameterSets() { addDefinition(DrtRequestInsertionRetryParams.SET_NAME, DrtRequestInsertionRetryParams::new, () -> drtRequestInsertionRetryParams, params -> drtRequestInsertionRetryParams = (DrtRequestInsertionRetryParams)params); + + //prebooking (optional) + addDefinition(PrebookingParams.SET_NAME, PrebookingParams::new, + () -> prebookingParams, + params -> prebookingParams = (PrebookingParams)params); } @Override @@ -269,6 +288,11 @@ protected void checkConsistency(Config config) { + " in order to speed up the DRT route update during the replanning phase."); } + if (this.idleVehiclesReturnToDepots && this.returnToDepotTimeout < this.returnToDepotEvaluationInterval) { + log.warn("idleVehiclesReturnToDepots is active and returnToDepotTimeout < returnToDepotEvaluationInterval. " + + "Vehicles will be send back to depot after {} seconds",returnToDepotEvaluationInterval); + } + Verify.verify(getParameterSets(MinCostFlowRebalancingStrategyParams.SET_NAME).size() <= 1, "More than one rebalancing parameter sets is specified"); @@ -306,8 +330,12 @@ public Optional getDrtRequestInsertionRetryParam return Optional.ofNullable(drtRequestInsertionRetryParams); } + public Optional getPrebookingParams() { + return Optional.ofNullable(prebookingParams); + } + /** - * Convenience method that brings syntax closer to syntax in, e.g., {@link PlansCalcRouteConfigGroup} or {@link PlanCalcScoreConfigGroup} + * Convenience method that brings syntax closer to syntax in, e.g., {@link RoutingConfigGroup} or {@link ScoringConfigGroup} */ public final void addDrtInsertionSearchParams(final DrtInsertionSearchParams pars) { addParameterSet(pars); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigs.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigs.java index 92d29a97d42..dccfb2d7449 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigs.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtConfigs.java @@ -22,8 +22,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup; /** * @author Michal Maciejewski (michalm) @@ -32,22 +32,22 @@ public class DrtConfigs { private static final Logger LOGGER = LogManager.getLogger(DrtControlerCreator.class); public static void adjustMultiModeDrtConfig(MultiModeDrtConfigGroup multiModeDrtCfg, - PlanCalcScoreConfigGroup planCalcScoreCfg, PlansCalcRouteConfigGroup plansCalcRouteCfg) { + ScoringConfigGroup planCalcScoreCfg, RoutingConfigGroup plansCalcRouteCfg) { for (DrtConfigGroup drtCfg : multiModeDrtCfg.getModalElements()) { DrtConfigs.adjustDrtConfig(drtCfg, planCalcScoreCfg, plansCalcRouteCfg); } } - public static void adjustDrtConfig(DrtConfigGroup drtCfg, PlanCalcScoreConfigGroup planCalcScoreCfg, - PlansCalcRouteConfigGroup plansCalcRouteCfg) { - String drtStageActivityType = PlanCalcScoreConfigGroup.createStageActivityType(drtCfg.getMode()); + public static void adjustDrtConfig(DrtConfigGroup drtCfg, ScoringConfigGroup planCalcScoreCfg, + RoutingConfigGroup plansCalcRouteCfg) { + String drtStageActivityType = ScoringConfigGroup.createStageActivityType(drtCfg.getMode()); if (planCalcScoreCfg.getActivityParams(drtStageActivityType) == null) { addDrtStageActivityParams(planCalcScoreCfg, drtStageActivityType); } } - private static void addDrtStageActivityParams(PlanCalcScoreConfigGroup planCalcScoreCfg, String stageActivityType) { - PlanCalcScoreConfigGroup.ActivityParams params = new PlanCalcScoreConfigGroup.ActivityParams(stageActivityType); + private static void addDrtStageActivityParams(ScoringConfigGroup planCalcScoreCfg, String stageActivityType) { + ScoringConfigGroup.ActivityParams params = new ScoringConfigGroup.ActivityParams(stageActivityType); params.setTypicalDuration(1); params.setScoringThisActivityAtAll(false); planCalcScoreCfg.getScoringParametersPerSubpopulation().values().forEach(k -> k.addActivityParams(params)); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtControlerCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtControlerCreator.java index 7c28080f46f..ab438acc805 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtControlerCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtControlerCreator.java @@ -61,7 +61,7 @@ public static Scenario createScenarioWithDrtRouteFactory(Config config) { */ public static Controler createControler(Config config, boolean otfvis) { MultiModeDrtConfigGroup multiModeDrtConfig = MultiModeDrtConfigGroup.get(config); - DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.planCalcScore(), config.plansCalcRoute()); + DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing()); Scenario scenario = createScenarioWithDrtRouteFactory(config); ScenarioUtils.loadScenario(scenario); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java index f2ee15674b0..677e5504c3c 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java @@ -23,11 +23,15 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.fare.DrtFareHandler; -import org.matsim.contrib.drt.optimizer.insertion.DefaultIncrementalStopDurationEstimator; -import org.matsim.contrib.drt.optimizer.insertion.IncrementalStopDurationEstimator; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingModule; -import org.matsim.contrib.drt.schedule.StopDurationEstimator; +import org.matsim.contrib.drt.prebooking.analysis.PrebookingModeAnalysisModule; import org.matsim.contrib.drt.speedup.DrtSpeedUp; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; +import org.matsim.contrib.drt.stops.MinimumStopDurationAdapter; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.PrebookingStopTimeCalculator; +import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.FleetModule; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.router.DvrpModeRoutingNetworkModule; @@ -35,11 +39,13 @@ import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.trafficmonitoring.DvrpTravelTimeModule; +import org.matsim.contrib.zone.skims.AdaptiveTravelTimeMatrixModule; import org.matsim.core.config.ConfigGroup; import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; import com.google.inject.Key; +import com.google.inject.Singleton; import com.google.inject.name.Names; /** @@ -76,15 +82,33 @@ public void install() { drtCfg.getDrtSpeedUpParams().ifPresent(drtSpeedUpParams -> { bindModal(DrtSpeedUp.class).toProvider(modalProvider( - getter -> new DrtSpeedUp(getMode(), drtSpeedUpParams, getConfig().controler(), + getter -> new DrtSpeedUp(getMode(), drtSpeedUpParams, getConfig().controller(), getter.get(Network.class), getter.getModal(FleetSpecification.class), getter.getModal(DrtEventSequenceCollector.class)))).asEagerSingleton(); addControlerListenerBinding().to(modalKey(DrtSpeedUp.class)); }); + + bindModal(PassengerStopDurationProvider.class).toProvider(modalProvider(getter -> { + return StaticPassengerStopDurationProvider.of(drtCfg.stopDuration, 0.0); + })); + + bindModal(DefaultStopTimeCalculator.class).toProvider(modalProvider(getter -> { + return new DefaultStopTimeCalculator(drtCfg.stopDuration); + })).in(Singleton.class); + + if (drtCfg.getPrebookingParams().isEmpty()) { + bindModal(StopTimeCalculator.class).toProvider(modalProvider(getter -> { + return new DefaultStopTimeCalculator(drtCfg.stopDuration); + })).in(Singleton.class); + } else { + bindModal(StopTimeCalculator.class).toProvider(modalProvider(getter -> { + PassengerStopDurationProvider provider = getter.getModal(PassengerStopDurationProvider.class); + return new MinimumStopDurationAdapter(new PrebookingStopTimeCalculator(provider), drtCfg.stopDuration); + })); + + install(new PrebookingModeAnalysisModule(getMode())); + } - bindModal(StopDurationEstimator.class).toInstance( - (vehicle, dropoffRequests, pickupRequests) -> drtCfg.stopDuration); - bindModal(IncrementalStopDurationEstimator.class).toInstance( - new DefaultIncrementalStopDurationEstimator(drtCfg.stopDuration)); + install(new AdaptiveTravelTimeMatrixModule(drtCfg.mode)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java index ffbe7f67537..526c75d8020 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java @@ -20,21 +20,29 @@ package org.matsim.contrib.drt.run; -import com.google.inject.Inject; -import com.google.inject.Provider; import org.matsim.contrib.drt.optimizer.DrtModeOptimizerQSimModule; import org.matsim.contrib.drt.passenger.DrtRequestCreator; +import org.matsim.contrib.drt.prebooking.PrebookingManager; +import org.matsim.contrib.drt.prebooking.PrebookingModeQSimModule; import org.matsim.contrib.drt.speedup.DrtSpeedUp; +import org.matsim.contrib.drt.vrpagent.DrtActionCreator; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.DefaultPassengerRequestValidator; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; import org.matsim.contrib.dvrp.passenger.TeleportingPassengerEngine.TeleportedRouteCalculator; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.vrpagent.VrpAgentSourceQSimModule; +import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.qsim.AbstractQSimModule; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + /** * @author Michal Maciejewski (michalm) */ @@ -55,7 +63,7 @@ public DrtModeQSimModule(DrtConfigGroup drtCfg, AbstractQSimModule optimizerQSim @Override protected void configureQSim() { boolean teleportDrtUsers = drtCfg.getDrtSpeedUpParams().isPresent() && DrtSpeedUp.isTeleportDrtUsers( - drtCfg.getDrtSpeedUpParams().get(), getConfig().controler(), getIterationNumber()); + drtCfg.getDrtSpeedUpParams().get(), getConfig().controller(), getIterationNumber()); if (teleportDrtUsers) { install(new PassengerEngineQSimModule(getMode(), PassengerEngineQSimModule.PassengerEngineType.TELEPORTING)); @@ -68,6 +76,13 @@ protected void configureQSim() { install(optimizerQSimModule); } + if (drtCfg.getPrebookingParams().isPresent()) { + install(new PrebookingModeQSimModule(getMode(), drtCfg.getPrebookingParams().get())); + bindModal(AdvanceRequestProvider.class).to(modalKey(PrebookingManager.class)); + } else { + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); + } + bindModal(PassengerRequestValidator.class).to(DefaultPassengerRequestValidator.class).asEagerSingleton(); bindModal(PassengerRequestCreator.class).toProvider(new Provider() { @@ -79,5 +94,14 @@ public DrtRequestCreator get() { return new DrtRequestCreator(getMode(), events); } }).asEagerSingleton(); + + // this is not the actual selection which DynActionCreator is used, see + // DrtModeOptimizerQSimModule + bindModal(DrtActionCreator.class) + .toProvider(modalProvider(getter -> new DrtActionCreator(getter.getModal(PassengerHandler.class), + getter.getModal(VrpLegFactory.class)))) + .in(Singleton.class); // Not an eager binding, as taxi contrib doesn't need this implementation, but + // would search for VrpLegFactory which is provided in the + // DrtModeOptimizerModule if bound eagerly } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunMultiModeDrtExample.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunMultiModeDrtExample.java index 53da0c974db..8452181b4c2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunMultiModeDrtExample.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunMultiModeDrtExample.java @@ -41,7 +41,7 @@ public class RunMultiModeDrtExample { public static void run(URL configUrl, boolean otfvis, int lastIteration) { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); Controler controler = DrtControlerCreator.createControler(config, otfvis); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunOneSharedTaxiExample.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunOneSharedTaxiExample.java index 9ca1f9f3452..3ce549d1ac7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunOneSharedTaxiExample.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunOneSharedTaxiExample.java @@ -35,8 +35,8 @@ public class RunOneSharedTaxiExample { public static void run(URL configUrl, boolean otfvis, int lastIteration) { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); - config.controler().setWriteEventsInterval(lastIteration); + config.controller().setLastIteration(lastIteration); + config.controller().setWriteEventsInterval(lastIteration); DrtControlerCreator.createControler(config, otfvis).run(); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DefaultDrtStopTask.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DefaultDrtStopTask.java index 21cb658a010..6efeb3fff96 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DefaultDrtStopTask.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DefaultDrtStopTask.java @@ -84,4 +84,14 @@ public String toString() { .add("super", super.toString()) .toString(); } + + @Override + public void removePickupRequest(Id requestId) { + pickupRequests.remove(requestId); + } + + @Override + public void removeDropoffRequest(Id requestId) { + dropoffRequests.remove(requestId); + } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStayTaskEndTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStayTaskEndTimeCalculator.java index 9a24dcbfc8c..147b8f621ad 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStayTaskEndTimeCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStayTaskEndTimeCalculator.java @@ -21,8 +21,8 @@ import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.getBaseTypeOrElseThrow; import static org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater.REMOVE_STAY_TASK; -import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; -import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; import org.matsim.contrib.dvrp.schedule.Schedules; @@ -30,10 +30,10 @@ public class DrtStayTaskEndTimeCalculator implements ScheduleTimingUpdater.StayTaskEndTimeCalculator { - private final StopDurationEstimator stopDurationEstimator; + private final StopTimeCalculator stopTimeCalculator; - public DrtStayTaskEndTimeCalculator(StopDurationEstimator stopDurationEstimator) { - this.stopDurationEstimator = stopDurationEstimator; + public DrtStayTaskEndTimeCalculator(StopTimeCalculator stopTimeCalculator) { + this.stopTimeCalculator = stopTimeCalculator; } @Override @@ -55,16 +55,7 @@ public double calcNewEndTime(DvrpVehicle vehicle, StayTask task, double newBegin } } case STOP: { - double maxEarliestPickupTime = ((DrtStopTask)task).getPickupRequests() - .values() - .stream() - .mapToDouble(AcceptedDrtRequest::getEarliestStartTime) - .max() - .orElse(Double.NEGATIVE_INFINITY); //TODO REMOVE_STAY_TASK ?? @michal - double stopDuration = stopDurationEstimator.calcDuration(vehicle, - ((DrtStopTask) task).getDropoffRequests().values(), - ((DrtStopTask) task).getPickupRequests().values()); - return Math.max(newBeginTime + stopDuration, maxEarliestPickupTime); + return stopTimeCalculator.shiftEndTime(vehicle, (DrtStopTask) task, newBeginTime); } default: diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStopTask.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStopTask.java index f4e25dd79fd..3c2b5dd8351 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStopTask.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/DrtStopTask.java @@ -42,4 +42,8 @@ public interface DrtStopTask extends StayTask { void addDropoffRequest(AcceptedDrtRequest request); void addPickupRequest(AcceptedDrtRequest request); + + void removePickupRequest(Id requestId); + + void removeDropoffRequest(Id requestId); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/StopDurationEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/StopDurationEstimator.java deleted file mode 100644 index 71a3742d4d2..00000000000 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/schedule/StopDurationEstimator.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.matsim.contrib.drt.schedule; - -import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; -import org.matsim.contrib.dvrp.fleet.DvrpVehicle; - -import java.util.Collection; - -public interface StopDurationEstimator { - - double calcDuration(DvrpVehicle vehicle, Collection dropoffRequests, - Collection pickupRequests); -} 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 a62abbdefbd..32929a39984 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 @@ -22,7 +22,6 @@ import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.DRIVE; import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; -import java.util.Collections; import java.util.List; import org.matsim.api.core.v01.network.Link; @@ -30,15 +29,17 @@ import org.matsim.contrib.drt.optimizer.Waypoint; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; -import org.matsim.contrib.drt.schedule.*; import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.schedule.DrtTaskBaseType; import org.matsim.contrib.drt.schedule.DrtTaskFactory; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; import org.matsim.contrib.dvrp.path.VrpPaths; +import org.matsim.contrib.dvrp.schedule.DriveTask; import org.matsim.contrib.dvrp.schedule.Schedule; import org.matsim.contrib.dvrp.schedule.Schedule.ScheduleStatus; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; @@ -53,6 +54,7 @@ /** * @author michalm + * @author Sebastian Hörl, IRT SystemX (sebhoerl) */ public class DefaultRequestInsertionScheduler implements RequestInsertionScheduler { @@ -60,16 +62,18 @@ public class DefaultRequestInsertionScheduler implements RequestInsertionSchedul private final TravelTime travelTime; private final ScheduleTimingUpdater scheduleTimingUpdater; private final DrtTaskFactory taskFactory; - private StopDurationEstimator stopDurationEstimator; + private final StopTimeCalculator stopTimeCalculator; + private final boolean scheduleWaitBeforeDrive; public DefaultRequestInsertionScheduler(Fleet fleet, MobsimTimer timer, TravelTime travelTime, ScheduleTimingUpdater scheduleTimingUpdater, DrtTaskFactory taskFactory, - StopDurationEstimator stopDurationEstimator) { + StopTimeCalculator stopTimeCalculator, boolean scheduleWaitBeforeDrive) { this.timer = timer; this.travelTime = travelTime; this.scheduleTimingUpdater = scheduleTimingUpdater; this.taskFactory = taskFactory; - this.stopDurationEstimator = stopDurationEstimator; + this.stopTimeCalculator = stopTimeCalculator; + this.scheduleWaitBeforeDrive = scheduleWaitBeforeDrive; initSchedules(fleet); } @@ -90,9 +94,72 @@ public PickupDropoffTaskPair scheduleRequest(AcceptedDrtRequest request, Inserti var dropoffTask = insertDropoff(request, insertion, pickupTask); verifyTimes("Inconsistent dropoff arrival time", insertion.detourTimeInfo.dropoffDetourInfo.arrivalTime, dropoffTask.getBeginTime()); - + + verifyTaskContinuity(insertion); + verifyConstraints(insertion); + verifyStructure(insertion.insertion.vehicleEntry.vehicle.getSchedule()); + return new PickupDropoffTaskPair(pickupTask, dropoffTask); } + + private void verifyTaskContinuity(InsertionWithDetourData insertion) { + /* + * During insertion of tasks the common sanity checks that happen when appending + * to the schedule (next start time = previous end time) are not evaluated. This + * method verifies that the timing remains intact after insertion. + */ + + Schedule schedule = insertion.insertion.vehicleEntry.vehicle.getSchedule(); + for (int i = 1; i < schedule.getTaskCount(); i++) { + Task first = schedule.getTasks().get(i - 1); + Task second = schedule.getTasks().get(i); + Verify.verify(first.getEndTime() == second.getBeginTime()); + } + } + + private final boolean verifyConstraints = false; + + private void verifyConstraints(InsertionWithDetourData insertion) { + /* + * Verifies that no request constraints are violated after scheduling. This + * check is only valid when simulating under free-flow conditions. + */ + if (verifyConstraints) { + Schedule schedule = insertion.insertion.vehicleEntry.vehicle.getSchedule(); + + for (Task task : schedule.getTasks()) { + if (task instanceof DrtStopTask) { + DrtStopTask stopTask = (DrtStopTask) task; + + for (AcceptedDrtRequest request : stopTask.getPickupRequests().values()) { + Verify.verify(stopTask.getEndTime() <= request.getLatestStartTime()); + } + + for (AcceptedDrtRequest request : stopTask.getDropoffRequests().values()) { + Verify.verify(stopTask.getBeginTime() <= request.getLatestArrivalTime()); + } + } + } + } + } + + private void verifyStructure(Schedule schedule) { + boolean previousDrive = false; + + int startIndex = schedule.getStatus().equals(ScheduleStatus.STARTED) ? schedule.getCurrentTask().getTaskIdx() + : 0; + + for (int index = startIndex; index < schedule.getTaskCount(); index++) { + Task task = schedule.getTasks().get(index); + + if (task instanceof DriveTask) { + Verify.verify(!previousDrive); + previousDrive = true; + } else { + previousDrive = false; + } + } + } private void verifyTimes(String messageStart, double timeFromInsertionData, double timeFromScheduler) { // The source of discrepancies is the first link travel time (as it should be taken into consideration @@ -105,6 +172,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(); @@ -123,27 +191,35 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour beforePickupTask = currentTask; VrpPathWithTravelData vrpPath = VrpPaths.createPath(vehicleEntry.start.link, request.getFromLink(), vehicleEntry.start.time, detourData.detourToPickup, travelTime); - ((OnlineDriveTaskTracker)beforePickupTask.getTaskTracker()).divertPath(vrpPath); + + if (vrpPath.getArrivalTime() < request.getEarliestStartTime() && scheduleWaitBeforeDrive) { + // prebooking case: we need to wait right now + ((OnlineDriveTaskTracker)beforePickupTask.getTaskTracker()).divertPath(VrpPaths.createZeroLengthPathForDiversion(diversion)); + beforePickupTask = insertDriveWithWait(vehicleEntry.vehicle, currentTask, vrpPath, request.getEarliestStartTime()); + } else { + ((OnlineDriveTaskTracker)beforePickupTask.getTaskTracker()).divertPath(vrpPath); + // prebooking: may want to wait after continuing to drive + beforePickupTask = insertWait(vehicleEntry.vehicle, beforePickupTask, request.getEarliestStartTime()); + } } else { // too late for diversion if (request.getFromLink() != vehicleEntry.start.link) { // add a new drive task VrpPathWithTravelData vrpPath = VrpPaths.createPath(vehicleEntry.start.link, request.getFromLink(), vehicleEntry.start.time, detourData.detourToPickup, travelTime); - beforePickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE); - schedule.addTask(currentTask.getTaskIdx() + 1, beforePickupTask); - } else { // no need for a new drive task - beforePickupTask = currentTask; + // prebooking: may want to wait before or after driving + beforePickupTask = insertDriveWithWait(vehicleEntry.vehicle, currentTask, vrpPath, request.getEarliestStartTime()); + } else { // no need for a new drive task, but may want to wait until start of stop + beforePickupTask = insertWait(vehicleEntry.vehicle, currentTask, dropoffIdx); } } } else { // insert pickup after an existing stop/stay task - DrtStayTask stayTask = null; + StayTask stayTask = null; DrtStopTask stopTask = null; if (pickupIdx == 0) { if (scheduleStatus == ScheduleStatus.PLANNED) {// PLANNED schedule - stayTask = (DrtStayTask)schedule.getTasks().get(0); + stayTask = (StayTask)schedule.getTasks().get(0); 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(); + stayTask = (StayTask)currentTask; // ongoing stay task if (stayTask.getEndTime() > now) { // stop stay task; a new stop/drive task can be inserted now stayTask.setEndTime(now); } @@ -153,29 +229,24 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour } else { stopTask = stops.get(pickupIdx - 1).task; // future stop task } - - if (stopTask != null && request.getFromLink() == stopTask.getLink()) { // no detour; no new stop task + + boolean canMergePickup = stopTask != null && request.getFromLink() == stopTask.getLink() + && stopTask.getEndTime() >= request.getEarliestStartTime(); + + if (canMergePickup) { // no detour; no new stop task // add pickup request to stop task stopTask.addPickupRequest(request); - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, stopTask.getDropoffRequests().values(), stopTask.getPickupRequests().values()); - stopTask.setEndTime(Math.max(stopTask.getBeginTime() + stopDuration, request.getEarliestStartTime())); + + // potentially extend task + stopTask.setEndTime(stopTimeCalculator.updateEndTimeForPickup(vehicleEntry.vehicle, stopTask, + now, request.getRequest())); - /// ADDED - //// TODO this is copied, but has not been updated !!!!!!!!!!!!!!! // add drive from pickup if (pickupIdx == dropoffIdx) { // remove drive i->i+1 (if there is one) if (pickupIdx < stops.size()) {// there is at least one following stop DrtStopTask nextStopTask = stops.get(pickupIdx).task; - if (stopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx()) {// there must a drive task in - // between - throw new RuntimeException(); - } - if (stopTask.getTaskIdx() + 2 == nextStopTask.getTaskIdx()) {// there must a drive task in - // between - int driveTaskIdx = stopTask.getTaskIdx() + 1; - schedule.removeTask(schedule.getTasks().get(driveTaskIdx)); - } + removeBetween(schedule, stopTask, nextStopTask); } Link toLink = request.getToLink(); // pickup->dropoff @@ -183,7 +254,7 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink, stopTask.getEndTime(), detourData.detourFromPickup, travelTime); Task driveFromPickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, - DrtDriveTask.TYPE); + DrtDriveTask.TYPE); // immediate drive to dropoff schedule.addTask(stopTask.getTaskIdx() + 1, driveFromPickupTask); // update timings @@ -191,6 +262,9 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, stopTask.getTaskIdx() + 2, driveFromPickupTask.getEndTime()); /////// + } else { + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, + stopTask.getTaskIdx() + 1, stopTask.getEndTime()); } return stopTask; @@ -200,59 +274,66 @@ private DrtStopTask insertPickup(AcceptedDrtRequest request, InsertionWithDetour // remove drive i->i+1 (if there is one) if (pickupIdx < stops.size()) {// there is at least one following stop DrtStopTask nextStopTask = stops.get(pickupIdx).task; - - // check: if there is at most one drive task in between - if (stayOrStopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx() // - && stayTask != null && stayTask.getTaskIdx() + 1 != nextStopTask.getTaskIdx()) { - throw new RuntimeException(); - } - if (stayOrStopTask.getTaskIdx() + 2 == nextStopTask.getTaskIdx()) { - // removing the drive task that is in between - int driveTaskIdx = stayOrStopTask.getTaskIdx() + 1; - schedule.removeTask(schedule.getTasks().get(driveTaskIdx)); - } + removeBetween(schedule, stayOrStopTask, nextStopTask); } - if (stayTask != null && request.getFromLink() == stayTask.getLink()) { + if (request.getFromLink() == stayOrStopTask.getLink()) { // the bus stays where it is - beforePickupTask = stayTask; + beforePickupTask = stayOrStopTask; + + // prebooking: but we may want to wait a bit if next stop is in a while + beforePickupTask = insertWait(vehicleEntry.vehicle, beforePickupTask, request.getEarliestStartTime()); } else {// add drive task to pickup location // insert drive i->pickup VrpPathWithTravelData vrpPath = VrpPaths.createPath(stayOrStopTask.getLink(), request.getFromLink(), stayOrStopTask.getEndTime(), detourData.detourToPickup, travelTime); - beforePickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE); - schedule.addTask(stayOrStopTask.getTaskIdx() + 1, beforePickupTask); + // we may want to wait before or after driving + beforePickupTask = insertDriveWithWait(vehicleEntry.vehicle, stayOrStopTask, vrpPath, request.getEarliestStartTime()); } } } // insert pickup stop task - double startTime = beforePickupTask.getEndTime(); int taskIdx = beforePickupTask.getTaskIdx() + 1; - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, Collections.emptySet(), Collections.singleton(request)); - DrtStopTask pickupStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, - Math.max(startTime + stopDuration, request.getEarliestStartTime()), request.getFromLink()); + double stopEndTime = stopTimeCalculator.initEndTimeForPickup(vehicleEntry.vehicle, beforePickupTask.getEndTime(), request.getRequest()); + DrtStopTask pickupStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, beforePickupTask.getEndTime(), + stopEndTime, request.getFromLink()); schedule.addTask(taskIdx, pickupStopTask); pickupStopTask.addPickupRequest(request); // add drive from pickup Link toLink = pickupIdx == dropoffIdx ? request.getToLink() // pickup->dropoff : stops.get(pickupIdx).task.getLink(); // pickup->i+1 + + double nextBeginTime = pickupIdx == dropoffIdx ? // + pickupStopTask.getEndTime() : // asap + stops.get(pickupIdx).task.getBeginTime(); // as planned + + if (request.getFromLink() == toLink) { + // prebooking case when we are already at the stop location, but next stop task happens in the future + Task afterPickupTask = insertWait(vehicleEntry.vehicle, pickupStopTask, nextBeginTime); + + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, + afterPickupTask.getEndTime()); + } else { + VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink, pickupStopTask.getEndTime(), + detourData.detourFromPickup, travelTime); + + // may want to wait now or before next stop task + Task afterPickupTask = insertDriveWithWait(vehicleEntry.vehicle, pickupStopTask, vrpPath, nextBeginTime); - VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getFromLink(), toLink, pickupStopTask.getEndTime(), - detourData.detourFromPickup, travelTime); - Task driveFromPickupTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE); - schedule.addTask(taskIdx + 1, driveFromPickupTask); - - // update timings - // TODO should be enough to update the timeline only till dropoffIdx... - scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, taskIdx + 2, - driveFromPickupTask.getEndTime()); + // update timings + // TODO should be enough to update the timeline only till dropoffIdx... + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, afterPickupTask.getTaskIdx() + 1, + afterPickupTask.getEndTime()); + } + return pickupStopTask; } 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(); @@ -261,7 +342,7 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou int dropoffIdx = insertion.dropoff.index; var detourData = insertionWithDetourData.detourData; - Task driveToDropoffTask; + final Task driveToDropoffTask; if (pickupIdx == dropoffIdx) { // no drive to dropoff int pickupTaskIdx = pickupTask.getTaskIdx(); driveToDropoffTask = schedule.getTasks().get(pickupTaskIdx + 1); @@ -270,24 +351,26 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou if (request.getToLink() == stopTask.getLink()) { // no detour; no new stop task // add dropoff request to stop task, and extend the stop task (when incremental stop task duration is used) stopTask.addDropoffRequest(request); - double updatedStopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, stopTask.getDropoffRequests().values(), stopTask.getPickupRequests().values()); - stopTask.setEndTime(stopTask.getBeginTime() + updatedStopDuration); + + // potentially extend task + stopTask.setEndTime(stopTimeCalculator.updateEndTimeForDropoff(vehicleEntry.vehicle, stopTask, + now, request.getRequest())); + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, + stopTask.getTaskIdx() + 1, stopTask.getEndTime()); + return stopTask; } else { // add drive task to dropoff location // remove drive j->j+1 (if j is not the last stop) if (dropoffIdx < stops.size()) { DrtStopTask nextStopTask = stops.get(dropoffIdx).task; - if (stopTask.getTaskIdx() + 2 != nextStopTask.getTaskIdx()) { - throw new IllegalStateException(); - } - int driveTaskIdx = stopTask.getTaskIdx() + 1; - schedule.removeTask(schedule.getTasks().get(driveTaskIdx)); + removeBetween(schedule, stopTask, nextStopTask); } // insert drive i->dropoff VrpPathWithTravelData vrpPath = VrpPaths.createPath(stopTask.getLink(), request.getToLink(), stopTask.getEndTime(), detourData.detourToDropoff, travelTime); + // direct drive to dropoff (no waiting) driveToDropoffTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE); schedule.addTask(stopTask.getTaskIdx() + 1, driveToDropoffTask); } @@ -296,9 +379,9 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou // insert dropoff stop task double startTime = driveToDropoffTask.getEndTime(); int taskIdx = driveToDropoffTask.getTaskIdx() + 1; - double stopDuration = stopDurationEstimator.calcDuration(vehicleEntry.vehicle, Collections.singleton(request), Collections.emptySet()); + double stopEndTime = stopTimeCalculator.initEndTimeForDropoff(vehicleEntry.vehicle, startTime, request.getRequest()); DrtStopTask dropoffStopTask = taskFactory.createStopTask(vehicleEntry.vehicle, startTime, - startTime + stopDuration, request.getToLink()); + stopEndTime, request.getToLink()); schedule.addTask(taskIdx, dropoffStopTask); dropoffStopTask.addDropoffRequest(request); @@ -319,15 +402,109 @@ private DrtStopTask insertDropoff(AcceptedDrtRequest request, InsertionWithDetou } else { Link toLink = stops.get(dropoffIdx).task.getLink(); // dropoff->j+1 - VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getToLink(), toLink, startTime + stopDuration, + VrpPathWithTravelData vrpPath = VrpPaths.createPath(request.getToLink(), toLink, dropoffStopTask.getEndTime(), detourData.detourFromDropoff, travelTime); - Task driveFromDropoffTask = taskFactory.createDriveTask(vehicleEntry.vehicle, vrpPath, DrtDriveTask.TYPE); - schedule.addTask(taskIdx + 1, driveFromDropoffTask); + + if (toLink == request.getToLink()) { + // prebooking case: we stay, but may add some wait time until the next stop + Task afterDropoffTask = insertWait(vehicleEntry.vehicle, dropoffStopTask, + stops.get(dropoffIdx).task.getBeginTime()); + + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, + afterDropoffTask.getTaskIdx() + 1, afterDropoffTask.getEndTime()); + } else { + // may want to wait here or after driving before starting next stop + Task afterDropoffTask = insertDriveWithWait(vehicleEntry.vehicle, dropoffStopTask, vrpPath, + stops.get(dropoffIdx).task.getBeginTime()); - // update timings - scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, taskIdx + 2, - driveFromDropoffTask.getEndTime()); + scheduleTimingUpdater.updateTimingsStartingFromTaskIdx(vehicleEntry.vehicle, + afterDropoffTask.getTaskIdx() + 1, afterDropoffTask.getEndTime()); + } } return dropoffStopTask; } + + /** + * Removes drive and wait tasks between the startTask and the endTask + */ + private void removeBetween(Schedule schedule, Task startTask, Task endTask) { + Verify.verify(endTask.getTaskIdx() >= startTask.getTaskIdx()); + Verify.verify(endTask.getTaskIdx() <= startTask.getTaskIdx() + 3); + + int waitCount = 0; + int driveCount = 0; + + int removeCount = endTask.getTaskIdx() - startTask.getTaskIdx() - 1; + + for (int k = 0; k < removeCount; k++) { + Task task = schedule.getTasks().get(startTask.getTaskIdx() + 1); + + if (DrtTaskBaseType.DRIVE.isBaseTypeOf(task.getTaskType())) { + driveCount++; + } else if (DrtStayTask.TYPE.equals(task.getTaskType())) { + waitCount++; + } else { + throw new IllegalStateException("Invalid schedule structure: expected WAIT or DRIVE task"); + } + + schedule.removeTask(task); + } + + Verify.verify(waitCount <= 1); + Verify.verify(driveCount <= 1); + } + + private Task insertWait(DvrpVehicle vehicle, Task departureTask, double earliestNextStartTime) { + Schedule schedule = vehicle.getSchedule(); + + final Link waitLink; + if (departureTask instanceof StayTask) { + waitLink = ((StayTask) departureTask).getLink(); + } else if (departureTask instanceof DriveTask) { + waitLink = ((DriveTask) departureTask).getPath().getToLink(); + } else { + throw new IllegalStateException(); + } + + if (departureTask.getEndTime() < earliestNextStartTime) { + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, departureTask.getEndTime(), + earliestNextStartTime, waitLink); + schedule.addTask(departureTask.getTaskIdx() + 1, waitTask); + return waitTask; + } + + return departureTask; + } + + private Task insertDriveWithWait(DvrpVehicle vehicle, Task departureTask, VrpPathWithTravelData path, double latestArrivalTime) { + Schedule schedule = vehicle.getSchedule(); + + Task leadingTask = departureTask; + + if (scheduleWaitBeforeDrive) { + double driveDepartureTime = latestArrivalTime - path.getTravelTime(); + + if (driveDepartureTime > departureTask.getEndTime()) { + // makes sense to insert a wait task before departure + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, departureTask.getEndTime(), + driveDepartureTime, path.getFromLink()); + schedule.addTask(departureTask.getTaskIdx() + 1, waitTask); + + path = path.withDepartureTime(driveDepartureTime); + leadingTask = waitTask; + } + } + + Task driveTask = taskFactory.createDriveTask(vehicle, path, DrtDriveTask.TYPE); + schedule.addTask(leadingTask.getTaskIdx() + 1, driveTask); + + if (driveTask.getEndTime() < latestArrivalTime) { + DrtStayTask waitTask = taskFactory.createStayTask(vehicle, driveTask.getEndTime(), latestArrivalTime, + path.getToLink()); + schedule.addTask(driveTask.getTaskIdx() + 1, waitTask); + return waitTask; + } else { + return driveTask; + } + } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/EmptyVehicleRelocator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/EmptyVehicleRelocator.java index 57567dd5634..98a1994e268 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/EmptyVehicleRelocator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/scheduler/EmptyVehicleRelocator.java @@ -41,6 +41,7 @@ */ public class EmptyVehicleRelocator { public static final DrtTaskType RELOCATE_VEHICLE_TASK_TYPE = new DrtTaskType("RELOCATE", DRIVE); + public static final DrtTaskType RELOCATE_VEHICLE_TO_DEPOT_TASK_TYPE = new DrtTaskType("RELOCATE_TO_DEPOT", DRIVE); private final TravelTime travelTime; private final MobsimTimer timer; @@ -55,7 +56,7 @@ public EmptyVehicleRelocator(Network network, TravelTime travelTime, TravelDisut router = new SpeedyALTFactory().createPathCalculator(network, travelDisutility, travelTime); } - public void relocateVehicle(DvrpVehicle vehicle, Link link) { + public void relocateVehicle(DvrpVehicle vehicle, Link link, DrtTaskType relocationTaskType) { DrtStayTask currentTask = (DrtStayTask)vehicle.getSchedule().getCurrentTask(); Link currentLink = currentTask.getLink(); @@ -63,12 +64,12 @@ public void relocateVehicle(DvrpVehicle vehicle, Link link) { VrpPathWithTravelData path = VrpPaths.calcAndCreatePath(currentLink, link, timer.getTimeOfDay(), router, travelTime); if (path.getArrivalTime() < vehicle.getServiceEndTime()) { - relocateVehicleImpl(vehicle, path); + relocateVehicleImpl(vehicle, path, relocationTaskType); } } } - private void relocateVehicleImpl(DvrpVehicle vehicle, VrpPathWithTravelData vrpPath) { + private void relocateVehicleImpl(DvrpVehicle vehicle, VrpPathWithTravelData vrpPath, DrtTaskType relocationTaskType) { Schedule schedule = vehicle.getSchedule(); DrtStayTask stayTask = (DrtStayTask)schedule.getCurrentTask(); if (stayTask.getTaskIdx() != schedule.getTaskCount() - 1) { @@ -76,7 +77,7 @@ private void relocateVehicleImpl(DvrpVehicle vehicle, VrpPathWithTravelData vrpP } stayTask.setEndTime(vrpPath.getDepartureTime()); // finish STAY - schedule.addTask(taskFactory.createDriveTask(vehicle, vrpPath, RELOCATE_VEHICLE_TASK_TYPE)); // add RELOCATE + schedule.addTask(taskFactory.createDriveTask(vehicle, vrpPath, relocationTaskType)); // add RELOCATE // append STAY schedule.addTask(taskFactory.createStayTask(vehicle, vrpPath.getArrivalTime(), vehicle.getServiceEndTime(), vrpPath.getToLink())); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java index 13da140a338..5ad8dd776af 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java @@ -27,14 +27,16 @@ import org.apache.commons.math3.stat.regression.SimpleRegression; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; 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.contrib.common.util.DistanceUtils; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; import org.matsim.contrib.drt.speedup.DrtSpeedUpParams.WaitingTimeUpdateDuringSpeedUp; import org.matsim.contrib.dvrp.fleet.FleetSpecification; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.controler.events.IterationEndsEvent; import org.matsim.core.controler.events.IterationStartsEvent; import org.matsim.core.controler.listener.IterationEndsListener; @@ -49,7 +51,7 @@ public final class DrtSpeedUp implements IterationStartsListener, IterationEndsListener { private static final Logger log = LogManager.getLogger(DrtSpeedUp.class); - public static boolean isTeleportDrtUsers(DrtSpeedUpParams drtSpeedUpParams, ControlerConfigGroup controlerConfig, + public static boolean isTeleportDrtUsers(DrtSpeedUpParams drtSpeedUpParams, ControllerConfigGroup controlerConfig, int iteration) { int lastIteration = controlerConfig.getLastIteration(); if (iteration < drtSpeedUpParams.fractionOfIterationsSwitchOn * lastIteration @@ -63,7 +65,7 @@ public static boolean isTeleportDrtUsers(DrtSpeedUpParams drtSpeedUpParams, Cont private final String mode; private final DrtSpeedUpParams drtSpeedUpParams; - private final ControlerConfigGroup controlerConfig; + private final ControllerConfigGroup controlerConfig; private final Network network; private final FleetSpecification fleetSpecification; private final DrtEventSequenceCollector drtEventSequenceCollector; @@ -76,7 +78,7 @@ public static boolean isTeleportDrtUsers(DrtSpeedUpParams drtSpeedUpParams, Cont private double currentAvgWaitingTime; private double currentAvgInVehicleBeelineSpeed; - public DrtSpeedUp(String mode, DrtSpeedUpParams drtSpeedUpParams, ControlerConfigGroup controlerConfig, + public DrtSpeedUp(String mode, DrtSpeedUpParams drtSpeedUpParams, ControllerConfigGroup controlerConfig, Network network, FleetSpecification fleetSpecification, DrtEventSequenceCollector drtEventSequenceCollector) { this.mode = mode; @@ -192,18 +194,24 @@ private SimulatedTripStats computeSimulatedTripStats() { continue;//skip incomplete sequences } DrtRequestSubmittedEvent submittedEvent = sequence.getSubmitted(); - Link depLink = network.getLinks().get(submittedEvent.getFromLinkId()); Link arrLink = network.getLinks().get(submittedEvent.getToLinkId()); double beelineDistance = DistanceUtils.calculateDistance(depLink.getToNode(), arrLink.getToNode()); - double pickupTime = sequence.getPickedUp().get().getTime(); - double waitTime = pickupTime - sequence.getSubmitted().getTime(); - double rideTime = sequence.getDroppedOff().get().getTime() - pickupTime; - - //TODO I would map unshared_ride_time to rideTime -- should be more precise - meanInVehicleBeelineSpeed.increment(beelineDistance / rideTime); - meanWaitTime.increment(waitTime); + for (Id personId : submittedEvent.getPersonIds()) { + if(sequence.getPersonEvents().containsKey(personId)) { + DrtEventSequenceCollector.EventSequence.PersonEvents personEvents = sequence.getPersonEvents().get(personId); + if(personEvents.getPickedUp().isPresent() && personEvents.getDroppedOff().isPresent()) { + double pickupTime = personEvents.getPickedUp().get().getTime(); + double waitTime = pickupTime - sequence.getSubmitted().getTime(); + double rideTime = personEvents.getDroppedOff().get().getTime() - pickupTime; + + //TODO I would map unshared_ride_time to rideTime -- should be more precise + meanInVehicleBeelineSpeed.increment(beelineDistance / rideTime); + meanWaitTime.increment(waitTime); + } + } + } } int count = (int)meanWaitTime.getN(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java index 7935a2403e8..eca53f0c0d7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java @@ -40,6 +40,12 @@ public class DrtTeleportedRouteCalculator implements TeleportedRouteCalculator { this.averageInVehicleBeelineSpeed = averageInVehicleBeelineSpeed; } + // TODO: from discussion from michal and rakow + // speedup is currently using very simple and not exchangeable estimators + // it could be possible to integrate the drt estimators used by the informed mode-choice + // this router should probably not use the beeline distance but the direct travel route + // speed-up would still be significant (oct'23) + @Override public Route calculateRoute(PassengerRequest request) { Link startLink = request.getFromLink(); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CorrectedStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CorrectedStopTimeCalculator.java new file mode 100644 index 00000000000..9833211eeff --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CorrectedStopTimeCalculator.java @@ -0,0 +1,51 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +/** + * This calculator extend the simple logic of DefaultStopTimeCalculator by + * allowing tasks to be extended. This happens when an ongoing task is assigned + * a new pickup. + */ +public class CorrectedStopTimeCalculator implements StopTimeCalculator { + private final double stopDuration; + + public CorrectedStopTimeCalculator(double stopDuration) { + this.stopDuration = stopDuration; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends when pickup time has elapsed + return beginTime + stopDuration; + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // an additional stop may extend the stop duration + return Math.max(stop.getEndTime(), insertionTime + stopDuration); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends after stopDuration has elapsed (dropoff happens at beginning) + return beginTime + stopDuration; + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // adding a dropoff may extend the stop duration + return Math.max(stop.getEndTime(), insertionTime + stopDuration); + } + + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + // stop always has a fixed duration and ongoing tasks cannot be shifted (no need + // to cover that case) + return beginTime + stopDuration; + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CumulativeStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CumulativeStopTimeCalculator.java new file mode 100644 index 00000000000..588b16385c0 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/CumulativeStopTimeCalculator.java @@ -0,0 +1,53 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class CumulativeStopTimeCalculator implements StopTimeCalculator { + private final PassengerStopDurationProvider provider; + + public CumulativeStopTimeCalculator(PassengerStopDurationProvider provider) { + this.provider = provider; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // pickup takes the indicated duration + return beginTime + provider.calcPickupDuration(vehicle, request); + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // pickup extends the stop duration + return stop.getEndTime() + provider.calcPickupDuration(vehicle, request); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // dropoff takes the indicated duration + return beginTime + provider.calcDropoffDuration(vehicle, request); + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + double stopDuration = provider.calcDropoffDuration(vehicle, request); + + if (insertionTime > stop.getBeginTime()) { + // insertion has shifted the stop + double initialDuration = stop.getEndTime() - stop.getBeginTime(); + return insertionTime + initialDuration + stopDuration; + } else { + // no shift, we simply add the new duration + return stop.getEndTime() + stopDuration; + } + } + + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + // shifting the stop does not change the duration + return beginTime + (stop.getEndTime() - stop.getBeginTime()); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultPassengerStopDurationProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultPassengerStopDurationProvider.java new file mode 100644 index 00000000000..4de82f03561 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultPassengerStopDurationProvider.java @@ -0,0 +1,25 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class DefaultPassengerStopDurationProvider implements PassengerStopDurationProvider { + private final double stopDuration; + + public DefaultPassengerStopDurationProvider(double stopDuration) { + this.stopDuration = stopDuration; + } + + @Override + public double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request) { + // pickups happen after this time from the stop beginning + return stopDuration; + } + + @Override + public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { + // dropoffs happen at the beginning of the stop, but extend the stop duration by + // this amount + return stopDuration; + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultStopTimeCalculator.java new file mode 100644 index 00000000000..d66d9c6a5ae --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/DefaultStopTimeCalculator.java @@ -0,0 +1,57 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +/** + * Attention: This is a simple stop time calculator with quick calculations for + * *immediate* requests. The calculations in case of prebooking become more + * complicated, especially in the case of updating end times when dropoffs are + * added: Because the previously inserted pickup generates a time penalty in + * InsertionGenerator, not only the newly added dropoff needs to be taken into + * account here, but also the general shifting of the task. Then we need to + * evaluate the earliestDepartureTime of all assigned pickups. For that reason, + * we have a separate implementation that is also valid for prebooking requests, + * but we keep this one here as it requires less calculations. + */ +public class DefaultStopTimeCalculator implements StopTimeCalculator { + private final double stopDuration; + + public DefaultStopTimeCalculator(double stopDuration) { + this.stopDuration = stopDuration; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends when pickup time has elapsed + return beginTime + stopDuration; + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // an additional stop does not change the end time + return stop.getEndTime(); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends after stopDuration has elapsed (dropoff happens at beginning) + return beginTime + stopDuration; + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // adding a dropoff does not change the end time, but the whole task may be + // shifted due to a previous pickup insertion + return Math.max(stop.getEndTime(), insertionTime + stopDuration); + } + + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + // stop always has a fixed duration + return beginTime + stopDuration; + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/MinimumStopDurationAdapter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/MinimumStopDurationAdapter.java new file mode 100644 index 00000000000..0bbdc9f6c14 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/MinimumStopDurationAdapter.java @@ -0,0 +1,44 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class MinimumStopDurationAdapter implements StopTimeCalculator { + private final StopTimeCalculator delegate; + private final double minimumStopDuration; + + public MinimumStopDurationAdapter(StopTimeCalculator delegate, double minimumStopDuration) { + this.delegate = delegate; + this.minimumStopDuration = minimumStopDuration; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + return Math.max(beginTime + minimumStopDuration, delegate.initEndTimeForPickup(vehicle, beginTime, request)); + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + return Math.max(stop.getBeginTime() + minimumStopDuration, + delegate.updateEndTimeForPickup(vehicle, stop, insertionTime, request)); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + return Math.max(beginTime + minimumStopDuration, delegate.initEndTimeForDropoff(vehicle, beginTime, request)); + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + return Math.max(insertionTime + minimumStopDuration, + delegate.updateEndTimeForDropoff(vehicle, stop, insertionTime, request)); + } + + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + return Math.max(beginTime + minimumStopDuration, delegate.shiftEndTime(vehicle, stop, beginTime)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/ParallelStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/ParallelStopTimeCalculator.java new file mode 100644 index 00000000000..c0cd836cde7 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/ParallelStopTimeCalculator.java @@ -0,0 +1,52 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +/** + * This calculator is a more flexible implementation of + * CorrectedStopTimeCalculator. Instead of imposing a fixed stop duration, it + * makes use of a StopDurationProvider. Adding a pickup or a dropoff may extend + * the stop duration to accomodate for a request that needs more time to enter + * the vehicle than others or induce a longer time after dropoff. + */ +public class ParallelStopTimeCalculator implements StopTimeCalculator { + private final PassengerStopDurationProvider stopDurationProvider; + + public ParallelStopTimeCalculator(PassengerStopDurationProvider stopDurationProvider) { + this.stopDurationProvider = stopDurationProvider; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends when pickup time has elapsed + return beginTime + stopDurationProvider.calcPickupDuration(vehicle, request); + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // an additional stop may extend the stop duration + return Math.max(stop.getEndTime(), insertionTime + stopDurationProvider.calcPickupDuration(vehicle, request)); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + // stop ends after stopDuration has elapsed (dropoff happens at beginning) + return beginTime + stopDurationProvider.calcDropoffDuration(vehicle, request); + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + // adding a dropoff may extend the stop duration + return Math.max(stop.getEndTime(), insertionTime + stopDurationProvider.calcDropoffDuration(vehicle, request)); + } + + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + // shifting the stop does not change the duration + return beginTime + (stop.getEndTime() - stop.getBeginTime()); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PassengerStopDurationProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PassengerStopDurationProvider.java new file mode 100644 index 00000000000..374bca88e68 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PassengerStopDurationProvider.java @@ -0,0 +1,15 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +/** + * This class provides the stop duration of a request. Eventually, this could + * also become a simple attribute of a DrtRequest to simplify the code + * complexity. + */ +public interface PassengerStopDurationProvider { + double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request); + + double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request); +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java new file mode 100644 index 00000000000..1eee457066d --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/PrebookingStopTimeCalculator.java @@ -0,0 +1,82 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class PrebookingStopTimeCalculator implements StopTimeCalculator { + private final PassengerStopDurationProvider provider; + + public PrebookingStopTimeCalculator(PassengerStopDurationProvider provider) { + this.provider = provider; + } + + @Override + public double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + double stopDuration = provider.calcPickupDuration(vehicle, request); + + // pickup duration starts either at the earliest departure time or when stop + // beginning is planned + return Math.max(request.getEarliestStartTime(), beginTime) + stopDuration; + } + + @Override + public double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + double stopDuration = provider.calcPickupDuration(vehicle, request); + + // the pickup starts at the latest of the following events + // - the begin time of the existing stop (immediate request) + // - the given insertionTime (immediate request merged to an ongoing stop) + // - the requests's earliest departure time (prebooking) + + double earliestStartTime = stop.getBeginTime(); + earliestStartTime = Math.max(earliestStartTime, insertionTime); + earliestStartTime = Math.max(earliestStartTime, request.getEarliestStartTime()); + + // from that point on, we add the stop duration, and we never shrink the stop to + // account for other assigned requests + return Math.max(stop.getEndTime(), earliestStartTime + stopDuration); + } + + @Override + public double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request) { + double stopDuration = provider.calcDropoffDuration(vehicle, request); + + // dropoff duration starts at the planned stop begin time + return beginTime + stopDuration; + } + + @Override + public double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, + DrtRequest request) { + final double stopDuration = provider.calcDropoffDuration(vehicle, request); + + // we need to take into account the general shift of the task and adding the new + // stop + + // first, we apply shiftEndTime, TODO: can we do this without the complex + // calculation? + double shiftedEndTime = shiftEndTime(vehicle, stop, insertionTime); + + // second, we add the dropoff + return Math.max(stop.getEndTime(), Math.max(shiftedEndTime, insertionTime + stopDuration)); + } + + /** + * Calculate the expected end time of a stop when begin time is updated + */ + @Override + public double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime) { + double latestPickupEndTime = stop.getPickupRequests().values().stream().mapToDouble(request -> { + double departureTime = Math.max(beginTime, request.getEarliestStartTime()); + return departureTime + provider.calcPickupDuration(vehicle, request.getRequest()); + }).max().orElse(beginTime); + + double latestDropoffEndTime = stop.getDropoffRequests().values().stream().mapToDouble(request -> { + return beginTime + provider.calcDropoffDuration(vehicle, request.getRequest()); + }).max().orElse(beginTime); + + return Math.max(latestPickupEndTime, latestDropoffEndTime); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StaticPassengerStopDurationProvider.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StaticPassengerStopDurationProvider.java new file mode 100644 index 00000000000..504bf6f309c --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StaticPassengerStopDurationProvider.java @@ -0,0 +1,28 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +public class StaticPassengerStopDurationProvider implements PassengerStopDurationProvider { + private final double pickupDuration; + private final double dropoffDuration; + + public StaticPassengerStopDurationProvider(double pickupDuration, double dropoffDuration) { + this.pickupDuration = pickupDuration; + this.dropoffDuration = dropoffDuration; + } + + @Override + public double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request) { + return pickupDuration; + } + + @Override + public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { + return dropoffDuration; + } + + public static StaticPassengerStopDurationProvider of(double pickupDuration, double dropoffDuration) { + return new StaticPassengerStopDurationProvider(pickupDuration, dropoffDuration); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StopTimeCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StopTimeCalculator.java new file mode 100644 index 00000000000..05a63426129 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/stops/StopTimeCalculator.java @@ -0,0 +1,70 @@ +package org.matsim.contrib.drt.stops; + +import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; + +/** + * This interface is used to manage the logic of stop times in DRT. It is able + * to calculate: + * + *
    + *
  • The end time of a to-be-created stop that will be initially populated by + * a pickup
  • + *
  • The updated end time of a stop when a new pickup is added
  • + *
  • The end time of a to-be-created stop that will be initially populated by + * a dropoff
  • + *
  • The updated end time of a stop when a new dropoff is added
  • + *
  • The shifted end time of a stop when its begin time is shifted
  • + *
+ * + * The interface is mainly used by the insertion algorithm (InsertionGenerator, + * InsertionDetourTimeCalculator) in order to calculate how inserting new + * requests changes the stop sequence structure, and it is used by the + * RequestInsertionScheduler to obtain the correct timing of the created stop + * tasks. Finally, it is used by the StayTaskEndTimeCalculator to update the + * schedule timing when delays occur. + * + * Some thoughts need to be put into new implementations, especially in the + * *update* methods. Both for pickups and dropoffs, they have an insertionTime + * parameter that indicates when the pickup or dropoff can actually be inserted + * at earliest into the task. Usually, this is the beginTime of the task, but + * there are important exceptions. + * + * For pickups, this is mainly when a new request is added to an already ongoing + * stop task. In the traditional DRT implementation, a new pickup would just be + * added without changing the end time of the task. This then lead to some + * requests having wait times below the predefined stop duration, because the + * task would just end as planned. However, a more correct implementation is to + * consider the insertionTime, which indicates the "current time" in that case, + * and add the stop duration at this point. + * + * For dropoffs, this case cannot occur because before a dropoff is inserted a + * pickup needs to be inserted. So a dropoff can never be merged into an ongoing + * stop task. The difficulty for dropoffs is that during insertion first a + * pickup is inserted. This usually causes a shift of all following tasks to the + * future and later on DRT evaluates if this leads to any constraint (pickup + * time, dropoff time) violations. But this means that the current task that we + * want to update has a certain beginTime, but it is not valid anymore at the + * point of calculating the new end time. The insertionTime then indicates the + * updated start time of the stop task, including the time loss produced by the + * pickup. When calculating the new end time there are, hence, two contribution + * from adding a new request, but also from shifting the task, which both need + * to be taken into account. + */ +public interface StopTimeCalculator { + // a new stop with a pickup is created + double initEndTimeForPickup(DvrpVehicle vehicle, double beginTime, DrtRequest request); + + // a pickup is added to an existing stop + double updateEndTimeForPickup(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, DrtRequest request); + + // a new stop with a dropoff is created + double initEndTimeForDropoff(DvrpVehicle vehicle, double beginTime, DrtRequest request); + + // a dropoff is added to an existing stop + double updateEndTimeForDropoff(DvrpVehicle vehicle, DrtStopTask stop, double insertionTime, DrtRequest request); + + // the begin time of an existing stop is shifted + double shiftEndTime(DvrpVehicle vehicle, DrtStopTask stop, double beginTime); +} 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..bb87604c123 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 @@ -25,15 +25,12 @@ import org.matsim.contrib.drt.schedule.DrtStopTask; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.passenger.PassengerHandler; -import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.schedule.Task; -import org.matsim.contrib.dvrp.tracker.OnlineTrackerListener; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; import org.matsim.contrib.dvrp.vrpagent.VrpLegFactory; import org.matsim.contrib.dynagent.DynAction; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.contrib.dynagent.IdleDynActivity; -import org.matsim.core.mobsim.framework.MobsimTimer; /** * @author michalm @@ -44,11 +41,6 @@ public class DrtActionCreator implements VrpAgentLogic.DynActionCreator { private final PassengerHandler passengerHandler; private final VrpLegFactory legFactory; - public DrtActionCreator(PassengerHandler passengerHandler, MobsimTimer timer, DvrpConfigGroup dvrpCfg) { - this(passengerHandler, v -> VrpLegFactory.createWithOnlineTracker(dvrpCfg.mobsimMode, v, - OnlineTrackerListener.NO_LISTENER, timer)); - } - public DrtActionCreator(PassengerHandler passengerHandler, VrpLegFactory legFactory) { this.passengerHandler = passengerHandler; this.legFactory = legFactory; @@ -63,7 +55,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/fare/DrtFareHandlerTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java index 490eea446df..e586e1a9dda 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java @@ -31,6 +31,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; import org.matsim.core.events.ParallelEventsManager; +import java.util.List; + /** * @author jbischoff */ @@ -69,8 +71,8 @@ public void reset(int iteration) { var personId = Id.createPersonId("p1"); { var requestId = Id.create(0, Request.class); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, personId, Id.createLinkId("12"), - Id.createLinkId("23"), 240, 1000, 0.0, 0.0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("12"), + Id.createLinkId("23"), 240, 1000, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.flush(); @@ -80,8 +82,8 @@ public void reset(int iteration) { { // test minFarePerTrip var requestId = Id.create(1, Request.class); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, personId, Id.createLinkId("45"), - Id.createLinkId("56"), 24, 100, 0.0, 0.0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("45"), + Id.createLinkId("56"), 24, 100, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.finishProcessing(); 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..035367e15fc 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 @@ -22,11 +22,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.matsim.contrib.drt.optimizer.VehicleDataEntryFactoryImpl.computeSlackTimes; -import static org.matsim.contrib.drt.optimizer.Waypoint.Stop; + +import java.util.Arrays; +import java.util.List; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.drt.optimizer.Waypoint.Stop; import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; @@ -48,35 +51,76 @@ public class VehicleDataEntryFactoryImplTest { @Test public void computeSlackTimes_withStops() { + final List precedingStayTimes = Arrays.asList(0.0, 0.0); + //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, precedingStayTimes)).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, precedingStayTimes)).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, precedingStayTimes)).containsExactly(10, 10, 10, 10); } @Test public void computeSlackTimes_withoutStops() { + final List precedingStayTimes = Arrays.asList(); + //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, precedingStayTimes)).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, precedingStayTimes)).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, precedingStayTimes)).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, precedingStayTimes)).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, precedingStayTimes)).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, precedingStayTimes)).containsExactly(0, 0); + } + + @Test + public void computeSlackTimes_withStart() { + final List noPrecedingStayTimes = Arrays.asList(); + final List onePrecedingStayTime = Arrays.asList(0.0); + + //start without stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] {}, stop0, noPrecedingStayTimes)).containsExactly(30, 50); + + //start without stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] {}, stop1, noPrecedingStayTimes)).containsExactly(30, 50); + + //start with stop + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] { stop1 }, stop0, onePrecedingStayTime)).containsExactly(30, 30, 50); + } + + @Test + public void computeSlackTimes_withPrecedingStayTimes() { + final List precedingStayTimes = Arrays.asList( // + 0.0, // + 33.0 // second stop is a prebooked pickup, so slack for insertion after first stop is longer + ); + + // note that these examples are naively adapted from computeSlackTimes_withStops + // in practice the slack would never pass the service end time slack (ie the + // last value in the list) if the preceding insertions were done correctly and + // there was no congestion + + //final stay task not started - vehicle slack time is 50 + assertThat(computeSlackTimes(vehicle(500, 450), 100, new Stop[] { stop0, stop1 }, null, precedingStayTimes)).containsExactly(20, 20, 63, 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 }, null, precedingStayTimes)).containsExactly(20, 20, 58, 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 }, null, precedingStayTimes)).containsExactly(20, 20, 43, 10); } 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..32b44c82f2f 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, 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..5730c59ea46 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 @@ -23,12 +23,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.matsim.contrib.drt.optimizer.insertion.DefaultUnplannedRequestInserter.NO_INSERTION_FOUND_CAUSE; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; +import java.util.*; import org.apache.commons.lang3.mutable.MutableInt; import org.junit.Rule; @@ -45,6 +46,7 @@ import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler; import org.matsim.contrib.drt.scheduler.RequestInsertionScheduler.PickupDropoffTaskPair; +import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.Fleet; import org.matsim.contrib.dvrp.optimizer.Request; @@ -114,7 +116,7 @@ public void notScheduled_rejected() { PassengerRequestRejectedEvent.class); verify(eventsManager, times(1)).processEvent(captor.capture()); assertThat(captor.getValue()).isEqualToComparingFieldByField( - new PassengerRequestRejectedEvent(now, mode, request1.getId(), request1.getPassengerId(), + new PassengerRequestRejectedEvent(now, mode, request1.getId(), request1.getPassengerIds(), NO_INSERTION_FOUND_CAUSE)); } @@ -197,7 +199,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, null, 0); var createEntryCounter = new MutableInt(); VehicleEntry.EntryFactory entryFactory = (vehicle, currentTime) -> { //make sure the right arguments are passed @@ -243,7 +245,7 @@ public void acceptedRequest() { PassengerRequestScheduledEvent.class); verify(eventsManager, times(1)).processEvent(captor.capture()); assertThat(captor.getValue()).isEqualToComparingFieldByField( - new PassengerRequestScheduledEvent(now, mode, request1.getId(), request1.getPassengerId(), + new PassengerRequestScheduledEvent(now, mode, request1.getId(), request1.getPassengerIds(), vehicle1.getId(), pickupEndTime, dropoffBeginTime)); //vehicle entry was created twice: @@ -271,7 +273,7 @@ private DvrpVehicle vehicle(String vehicleId) { private DrtRequest request(String id, String fromLinkId, String toLinkId) { return DrtRequest.newBuilder() .id(Id.create(id, Request.class)) - .passengerId(Id.createPersonId(id)) + .passengerIds(List.of(Id.createPersonId(id))) .fromLink(link(fromLinkId)) .toLink(link(toLinkId)) .mode(mode) @@ -283,7 +285,7 @@ private DefaultUnplannedRequestInserter newInserter(Fleet fleet, double now, DrtInsertionSearch insertionSearch, RequestInsertionScheduler insertionScheduler) { return new DefaultUnplannedRequestInserter(mode, fleet, () -> now, eventsManager, insertionScheduler, vehicleEntryFactory, insertionRetryQueue, insertionSearch, DrtOfferAcceptor.DEFAULT_ACCEPTOR, - rule.forkJoinPool); + rule.forkJoinPool, StaticPassengerStopDurationProvider.of(10.0, 0.0)); } private Link link(String id) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DrtPoolingParameterTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DrtPoolingParameterTest.java index 236d2889a6a..c708e623de0 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DrtPoolingParameterTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DrtPoolingParameterTest.java @@ -4,7 +4,6 @@ import java.util.*; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; @@ -213,9 +212,9 @@ private PersonEnterDrtVehicleEventHandler setupAndRunScenario(double maxWaitTime new OTFVisConfigGroup()); config.plans().setInputFile(null); - config.controler() + config.controller() .setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); MultiModeDrtConfigGroup mm = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); mm.getModalElements().forEach(x -> { 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..563788c0f5c 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, null, 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 bdc0e150a8c..3489c5ee854 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 @@ -21,9 +21,8 @@ package org.matsim.contrib.drt.optimizer.insertion; import static org.assertj.core.api.Assertions.assertThat; -import static org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DropoffDetourInfo; -import static org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.PickupDetourInfo; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -32,12 +31,13 @@ import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.Waypoint; import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DetourTimeInfo; +import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DropoffDetourInfo; +import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.PickupDetourInfo; import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData.InsertionDetourData; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; -import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; import org.matsim.contrib.dvrp.path.OneToManyPathSearch.PathData; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.core.router.util.LeastCostPathCalculator.Path; @@ -76,7 +76,8 @@ public void detourTimeLoss_start_pickup_dropoff() { @Test public void detourTimeLoss_ongoingStopAsStart_pickup_dropoff() { //similar to detourTmeLoss_start_pickup_dropoff(), but the pickup is appended to the ongoing STOP task - Waypoint.Start start = start(new DefaultDrtStopTask(20, 20 + STOP_DURATION, fromLink), STOP_DURATION, fromLink); + // sh 03/08/23: Changed this test, according to VehicleDataEntryFactoryImpl the start time should be end time of stop task + Waypoint.Start start = start(new DefaultDrtStopTask(20, 20 + STOP_DURATION, fromLink), 20 + STOP_DURATION, fromLink); VehicleEntry entry = entry(start); var detour = detourData(0., 15., Double.NaN, 0.);//toPickup/Dropoff unused var insertion = insertion(entry, 0, 0, detour); @@ -196,7 +197,7 @@ public void replacedDriveTimeEstimator() { .put(stop0.getLink(), stop1.getLink(), dropoffDetourReplacedDriveEstimate) .build(); - var detourTimeCalculator = new InsertionDetourTimeCalculator(new DefaultIncrementalStopDurationEstimator(STOP_DURATION), + var detourTimeCalculator = new InsertionDetourTimeCalculator(new DefaultStopTimeCalculator(STOP_DURATION), (from, to, departureTime) -> replacedDriveTimeEstimates.get(from, to)); var actualDetourTimeInfo = detourTimeCalculator.calculateDetourTimeInfo(insertion.insertion, insertion.detourData, drtRequest); @@ -215,7 +216,7 @@ public void replacedDriveTimeEstimator() { } private void assertDetourTimeInfo(InsertionWithDetourData insertion, DetourTimeInfo expected) { - var detourTimeCalculator = new InsertionDetourTimeCalculator(new DefaultIncrementalStopDurationEstimator(STOP_DURATION), null); + var detourTimeCalculator = new InsertionDetourTimeCalculator(new DefaultStopTimeCalculator(STOP_DURATION), null); var detourTimeInfo = detourTimeCalculator.calculateDetourTimeInfo(insertion.insertion, insertion.detourData, drtRequest); assertThat(detourTimeInfo).usingRecursiveComparison().isEqualTo(expected); } @@ -233,7 +234,8 @@ 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); + List precedingStayTimes = Collections.nCopies(stops.length, 0.0); + return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null, precedingStayTimes, 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 67e0fc59cae..461a598d874 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 @@ -20,8 +20,11 @@ package org.matsim.contrib.drt.optimizer.insertion; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableTable; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.List; + import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -33,15 +36,17 @@ import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.stops.CumulativeStopTimeCalculator; +import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.path.OneToManyPathSearch; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.testcases.fakes.FakeLink; -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableTable; /** * @author Michal Maciejewski (michalm) @@ -57,29 +62,33 @@ public class InsertionDetourTimeCalculatorWithVariableDurationTest { private static final int STOP_DURATION_INITIAL = 10; private static final int STOP_DURATION_ADDED = 5; - public static final IncrementalStopDurationEstimator INCREMENTAL_STOP_DURATION_ESTIMATOR = new IncrementalStopDurationEstimator() { + + public static final PassengerStopDurationProvider STOP_DURATION_PROVIDER = new PassengerStopDurationProvider() { @Override - public double calcForPickup(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest pickupRequest) { - double time = 0; - if (pickupRequest.equals(drtRequestInitial)) { - time += STOP_DURATION_INITIAL; - } else if (pickupRequest.equals(drtRequestAdded)) { - time += STOP_DURATION_ADDED; + public double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request) { + if (request.equals(drtRequestInitial)) { + return STOP_DURATION_INITIAL; + } else if (request.equals(drtRequestAdded)) { + return STOP_DURATION_ADDED; } - return time; + + throw new IllegalStateException(); } @Override - public double calcForDropoff(DvrpVehicle vehicle, DrtStopTask stopTask, DrtRequest dropoffRequest) { - double time = 0; - if (dropoffRequest.equals(drtRequestInitial)) { + public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { + if (request.equals(drtRequestInitial)) { return STOP_DURATION_INITIAL; - } else if (dropoffRequest.equals(drtRequestAdded)) { + } else if (request.equals(drtRequestAdded)) { return STOP_DURATION_ADDED; } - return time; + + throw new IllegalStateException(); } }; + + public static final StopTimeCalculator STOP_TIME_CALCULATOR = + new CumulativeStopTimeCalculator(STOP_DURATION_PROVIDER); @Test public void detourTimeLoss_start_pickup_dropoff() { @@ -104,7 +113,8 @@ public void detourTimeLoss_ongoingStopAsStart_pickup_dropoff() { //similar to detourTmeLoss_start_pickup_dropoff(), but the pickup is appended to the ongoing STOP task DrtStopTask stopTask = new DefaultDrtStopTask(20, 20 + STOP_DURATION_INITIAL, fromLink); stopTask.addDropoffRequest(AcceptedDrtRequest.createFromOriginalRequest(drtRequestInitial)); - Waypoint.Start start = start(stopTask, STOP_DURATION_INITIAL, fromLink); + // sh 03/08/23: Updated this test, according to VehicleDataEntryFactoryImpl start time should be task end time + Waypoint.Start start = start(stopTask, 20 + STOP_DURATION_INITIAL, fromLink); VehicleEntry entry = entry(start); var detour = new Detour(0., 15., 0., 0.);//toPickup/Dropoff unused var insertion = insertion(entry, 0, 0, detour); @@ -222,7 +232,7 @@ public void replacedDriveTimeEstimator() { .put(stop0.getLink(), stop1.getLink(), dropoffDetourReplacedDriveEstimate) .build(); - var detourTimeCalculator = new InsertionDetourTimeCalculator(INCREMENTAL_STOP_DURATION_ESTIMATOR, + var detourTimeCalculator = new InsertionDetourTimeCalculator(STOP_TIME_CALCULATOR, new DetourTimeEstimator() { @Override public double estimateTime(Link from, Link to, double departureTime) { @@ -245,7 +255,7 @@ public double estimateTime(Link from, Link to, double departureTime) { } private void assertDetourTimeInfo(InsertionWithDetourData insertion, DetourTimeInfo expected) { - var detourTimeCalculator = new InsertionDetourTimeCalculator(INCREMENTAL_STOP_DURATION_ESTIMATOR, null); + var detourTimeCalculator = new InsertionDetourTimeCalculator(STOP_TIME_CALCULATOR, null); DetourTimeInfo detourTimeInfo = detourTimeCalculator.calculateDetourTimeInfo(insertion.insertion, insertion.detourData, drtRequestAdded); assertThat(detourTimeInfo.pickupDetourInfo).isEqualToComparingFieldByField(expected.pickupDetourInfo); assertThat(detourTimeInfo.dropoffDetourInfo).isEqualToComparingFieldByField(expected.dropoffDetourInfo); @@ -266,7 +276,8 @@ 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); + List precedingStayTimes = Collections.nCopies(stops.length, 0.0); + return new VehicleEntry(null, start, ImmutableList.copyOf(stops), null, precedingStayTimes, 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 9fc94299b29..18bb57661f2 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 @@ -20,31 +20,34 @@ package org.matsim.contrib.drt.optimizer.insertion; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.google.common.collect.Sets; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.contrib.drt.optimizer.VehicleEntry; import org.matsim.contrib.drt.optimizer.Waypoint; import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DetourTimeInfo; +import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DropoffDetourInfo; +import org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.PickupDetourInfo; import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; import org.matsim.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; -import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification; import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; import org.matsim.testcases.fakes.FakeLink; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.DropoffDetourInfo; -import static org.matsim.contrib.drt.optimizer.insertion.InsertionDetourTimeCalculator.PickupDetourInfo; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; /** * @author Michal Maciejewski (michalm) @@ -62,7 +65,23 @@ public class InsertionGeneratorTest { private final Link fromLink = link("from"); private final Link toLink = link("to"); - private final DrtRequest drtRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).build(); + private final DrtRequest drtRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds(List.of(Id.createPersonId("person"))).build(); + + private final DrtRequest drtRequest2Pax = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds( + List.of( + Id.createPersonId("person1"), + Id.createPersonId("person2") + )).build(); + + private final DrtRequest drtRequest5Pax = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds( + List.of( + Id.createPersonId("person1"), + Id.createPersonId("person2"), + Id.createPersonId("person3"), + Id.createPersonId("person4"), + Id.createPersonId("person5") + )).build(); + private final DrtRequest prebookedRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).earliestStartTime(100).build(); private final Link depotLink = link("depot"); private final DvrpVehicleSpecification vehicleSpecification = ImmutableDvrpVehicleSpecification.newBuilder() @@ -205,10 +224,13 @@ 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); + + List precedingStayTimes = Arrays.asList(0.0, 0.0); + + VehicleEntry entry = new VehicleEntry(vehicle, start, ImmutableList.of(stop0, stop1), slackTimes, precedingStayTimes, 0); var insertions = new ArrayList(); {//12 @@ -343,6 +365,85 @@ public void noDetourForDropoff_vehicleOutgoingFullAfterDropoff_insertionPossible new Insertion(drtRequest, entry, 2, 2)); } + @Test + public void startEmpty_prebookedRequest() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); + VehicleEntry entry = entry(start); + assertInsertionsOnly(prebookedRequest, entry, + new Insertion(prebookedRequest, entry, 0, 0)); + } + + @Test + public void startEmpty_onlineRequest_beforeAlreadyPrebookedOtherRequest() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); + Waypoint.Stop stop0 = stop(200, fromLink, 1); + Waypoint.Stop stop1 = stop(400, link("stop"), 0); + VehicleEntry entry = entry(start, stop0, stop1); + assertInsertionsOnly(drtRequest, entry, + new Insertion(drtRequest, entry, 0, 0), + new Insertion(drtRequest, entry, 0, 1), + new Insertion(drtRequest, entry, 0, 2), + new Insertion(drtRequest, entry, 1, 1), + new Insertion(drtRequest, entry, 1, 2), + new Insertion(drtRequest, entry, 2, 2) + ); + } + + @Test + public void startEmpty_prebookedRequest_inMiddleOfAlreadyPrebookedOtherRequest() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); + Waypoint.Stop stop0 = stop(50, fromLink, 1); + Waypoint.Stop stop1 = stop(300, link("stop"), 0); + VehicleEntry entry = entry(start, stop0, stop1); + assertInsertionsOnly(prebookedRequest, entry, + new Insertion(prebookedRequest, entry, 1, 1), + new Insertion(prebookedRequest, entry, 1, 2), + new Insertion(prebookedRequest, entry, 2, 2)); + } + + @Test + public void startEmpty_prebookedRequest_afterAlreadyPrebookedOtherRequest() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); + Waypoint.Stop stop0 = stop(20, fromLink, 1); + Waypoint.Stop stop1 = stop(70, link("stop"), 0); + VehicleEntry entry = entry(start, stop0, stop1); + assertInsertionsOnly(prebookedRequest, entry, + new Insertion(prebookedRequest, entry, 2, 2)); + } + + + @Test + public void startEmpty_smallGroup() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + VehicleEntry entry = entry(start); + assertInsertionsOnly(drtRequest2Pax, entry, + //pickup after start + new Insertion(drtRequest2Pax, entry, 0, 0)); + } + + @Test + public void startEmpty_groupExceedsCapacity() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + VehicleEntry entry = entry(start); + assertInsertionsOnly(drtRequest5Pax, entry + //no insertion possible + ); + } + + @Test + public void startEmpty_twoStops_groupExceedsCapacityAtFirstStop() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + Waypoint.Stop stop0 = stop(0, toLink, 3);//dropoff 1 pax + Waypoint.Stop stop1 = stop(0, link("stop1"), 0);//dropoff 1 pax + VehicleEntry entry = entry(start, stop0, stop1); + assertInsertionsOnly(drtRequest2Pax, entry, + //pickup after start: + new Insertion(drtRequest2Pax, entry, 0, 1), + //pickup after stop 1 + new Insertion(drtRequest2Pax, entry, 2, 2) + ); + } + private Link link(String id) { return new FakeLink(Id.createLinkId(id)); } @@ -373,7 +474,7 @@ private void assertInsertionsWithDetour(DrtRequest drtRequest, VehicleEntry entr return TIME_REPLACED_DRIVE; }; - var actualInsertions = new InsertionGenerator(new DefaultIncrementalStopDurationEstimator(STOP_DURATION), timeEstimator).generateInsertions(drtRequest, + var actualInsertions = new InsertionGenerator(new DefaultStopTimeCalculator(STOP_DURATION), timeEstimator).generateInsertions(drtRequest, entry); assertThat(actualInsertions).usingRecursiveFieldByFieldElementComparator() .containsExactlyElementsOf(expectedInsertions); @@ -386,7 +487,7 @@ private void assertInsertionsOnly(DrtRequest drtRequest, VehicleEntry entry, Ins DetourTimeEstimator timeEstimator = (from, to, departureTime) -> 0; - var actualInsertions = new InsertionGenerator(new DefaultIncrementalStopDurationEstimator(STOP_DURATION), timeEstimator).generateInsertions(drtRequest, + var actualInsertions = new InsertionGenerator(new DefaultStopTimeCalculator(STOP_DURATION), timeEstimator).generateInsertions(drtRequest, entry); assertThat(actualInsertions.stream().map(i -> i.insertion)).usingRecursiveFieldByFieldElementComparator() .containsExactly(expectedInsertions); @@ -397,8 +498,9 @@ 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); + List precedingStayTimes = Collections.nCopies(stops.length, 0.0); + return new VehicleEntry(vehicle, start, ImmutableList.copyOf(stops), slackTimes, precedingStayTimes, 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..af7c0c331f2 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, null, 0); } private Waypoint.Stop stop(Link link) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProviderTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProviderTest.java index 3463753331b..3e94e70dfd5 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProviderTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/extensive/ExtensiveInsertionProviderTest.java @@ -37,6 +37,7 @@ import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData.InsertionDetourData; import org.matsim.contrib.drt.passenger.DrtRequest; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; /** * @author Michal Maciejewski (michalm) @@ -47,7 +48,7 @@ public class ExtensiveInsertionProviderTest { @Test public void getInsertions_noInsertionsGenerated() { - var insertionProvider = new ExtensiveInsertionProvider(null, null, new InsertionGenerator(new DefaultIncrementalStopDurationEstimator(120), null), + var insertionProvider = new ExtensiveInsertionProvider(null, null, new InsertionGenerator(new DefaultStopTimeCalculator(120), null), rule.forkJoinPool); assertThat(insertionProvider.getInsertions(null, List.of())).isEmpty(); } 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..77fc80cf388 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, null, 0); } private List filterOneInsertionAtEnd(InsertionWithDetourData... insertions) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProviderTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProviderTest.java index 25553c038d0..a88df5ac539 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProviderTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/selective/SelectiveInsertionProviderTest.java @@ -34,12 +34,14 @@ import org.junit.Rule; import org.junit.Test; import org.matsim.contrib.drt.optimizer.VehicleEntry; -import org.matsim.contrib.drt.optimizer.insertion.*; +import org.matsim.contrib.drt.optimizer.insertion.BestInsertionFinder; +import org.matsim.contrib.drt.optimizer.insertion.ForkJoinPoolTestRule; +import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator; import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion; +import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData; import org.matsim.contrib.drt.optimizer.insertion.InsertionWithDetourData.InsertionDetourData; import org.matsim.contrib.drt.passenger.DrtRequest; -import org.matsim.contrib.drt.schedule.DrtStopTask; -import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; /** * @author Michal Maciejewski (michalm) @@ -53,7 +55,7 @@ public class SelectiveInsertionProviderTest { @Test public void getInsertions_noInsertionsGenerated() { var insertionProvider = new SelectiveInsertionProvider(initialInsertionFinder, - new InsertionGenerator(new DefaultIncrementalStopDurationEstimator(120), null), rule.forkJoinPool); + new InsertionGenerator(new DefaultStopTimeCalculator(120), null), rule.forkJoinPool); assertThat(insertionProvider.getInsertion(null, List.of())).isEmpty(); } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimatorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimatorTest.java similarity index 90% rename from contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimatorTest.java rename to contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimatorTest.java index ef1e59c0fdf..7a155267da4 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDRTDemandEstimatorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimatorTest.java @@ -39,7 +39,7 @@ /** * @author michalm (Michal Maciejewski) */ -public class PreviousIterationDRTDemandEstimatorTest { +public class PreviousIterationDrtDemandEstimatorTest { private static final int ESTIMATION_PERIOD = 1800; @@ -52,7 +52,7 @@ public class PreviousIterationDRTDemandEstimatorTest { @Test public void noDepartures() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); //no events in previous iterations estimator.reset(1); @@ -67,7 +67,7 @@ public void noDepartures() { @Test public void drtDepartures() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); //time bin 0-1800 estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); @@ -102,7 +102,7 @@ public void drtDepartures() { @Test public void nonDrtDepartures() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); estimator.handleEvent(departureEvent(100, link1, "mode X")); estimator.handleEvent(departureEvent(200, link2, TransportMode.car)); @@ -114,7 +114,7 @@ public void nonDrtDepartures() { @Test public void currentCountsAreCopiedToPreviousAfterReset() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); estimator.handleEvent(departureEvent(200, link2, TransportMode.drt)); @@ -130,7 +130,7 @@ public void currentCountsAreCopiedToPreviousAfterReset() { @Test public void timeBinsAreRespected() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); estimator.handleEvent(departureEvent(2200, link2, TransportMode.drt)); @@ -148,7 +148,7 @@ public void timeBinsAreRespected() { @Test public void noTimeLimitIsImposed() { - PreviousIterationDRTDemandEstimator estimator = createEstimator(); + PreviousIterationDrtDemandEstimator estimator = createEstimator(); estimator.handleEvent(departureEvent(10000000, link1, TransportMode.drt)); estimator.reset(1); @@ -156,21 +156,21 @@ public void noTimeLimitIsImposed() { assertDemand(estimator, 10000000, zone1, 1); } - private PreviousIterationDRTDemandEstimator createEstimator() { + private PreviousIterationDrtDemandEstimator createEstimator() { RebalancingParams rebalancingParams = new RebalancingParams(); rebalancingParams.interval = ESTIMATION_PERIOD; DrtConfigGroup drtConfigGroup = new DrtConfigGroup(); drtConfigGroup.addParameterSet(rebalancingParams); - return new PreviousIterationDRTDemandEstimator(zonalSystem, drtConfigGroup, ESTIMATION_PERIOD); + return new PreviousIterationDrtDemandEstimator(zonalSystem, drtConfigGroup, ESTIMATION_PERIOD); } private PersonDepartureEvent departureEvent(double time, Link link, String mode) { return new PersonDepartureEvent(time, null, link.getId(), mode, mode); } - private void assertDemand(PreviousIterationDRTDemandEstimator estimator, double fromTime, DrtZone zone, + private void assertDemand(PreviousIterationDrtDemandEstimator estimator, double fromTime, DrtZone zone, double expectedDemand) { assertThat(estimator.getExpectedDemand(fromTime, ESTIMATION_PERIOD).applyAsDouble(zone)).isEqualTo( expectedDemand); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/AbandonAndCancelTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/AbandonAndCancelTest.java new file mode 100644 index 00000000000..c3dc7f546b0 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/AbandonAndCancelTest.java @@ -0,0 +1,268 @@ +package org.matsim.contrib.drt.prebooking; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.contrib.drt.prebooking.PrebookingTestEnvironment.RequestInfo; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.PlanAgent; +import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.testcases.MatsimTestUtils; + +/** + * @author Sebastian Hörl (sebhoerl) / IRT SystemX + */ +public class AbandonAndCancelTest { + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void noAbandonTest() { + /* + * One person requests to depart at 2000 and also is there at 2000. Another + * person asks also to depart at 2000, but only arrives at 4000, i.e. the person + * has 1000s delay. The vehicle should wait accordingly. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .addRequest("personOk", 0, 0, 5, 5, 2000.0, 0.0, 2000.0) // + .addRequest("personLate", 0, 0, 5, 5, 4000.0, 0.0, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + PrebookingTest.installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personOk"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2061.0, requestInfo.pickupTime, 1e-3); + assertEquals(4271.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personLate"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(4060.0, requestInfo.pickupTime, 1e-3); + assertEquals(4271.0, requestInfo.dropoffTime, 1e-3); + } + } + + @Test + public void abandonTest() { + /* + * One person requests to depart at 2000 and also is there at 2000. Another + * person asks also to depart at 2000, but only arrives at 4000, i.e. the person + * has 1000s delay. + * + * We configure that the vehicle should leave without the passenger if it waits + * longer than 500s. The late request will be rejected! + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .addRequest("personOk", 0, 0, 5, 5, 2000.0, 0.0, 2000.0) // + .addRequest("personLate", 0, 0, 5, 5, 4000.0, 0.0, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + PrebookingParams parameters = PrebookingTest.installPrebooking(controller); + parameters.maximumPassengerDelay = 500.0; + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personOk"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2061.0, requestInfo.pickupTime, 1e-3); + assertEquals(2713.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personLate"); + assertEquals(0.0, requestInfo.submissionTimes.get(0), 1e-3); + // agent tries a non-prebooked request upon arrival + assertEquals(4000.0, requestInfo.submissionTimes.get(1), 1e-3); + assertTrue(requestInfo.rejected); + } + } + + @Test + public void abandonThenImmediateTest() { + /* + * One person requests to depart at 2000 and also is there at 2000. Another + * person asks also to depart at 2000, but only arrives at 4000, i.e. the person + * has 1000s delay. + * + * We configure that the vehicle should leave without the passenger if it waits + * longer than 500s. The person will, however, send a new request when arriving + * at the departure point and get an immediate vehicle. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .addVehicle("vehicle2", 1, 1) // + .addRequest("personOk", 0, 0, 5, 5, 2000.0, 0.0, 2000.0) // + .addRequest("personLate", 0, 0, 5, 5, 4000.0, 0.0, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + PrebookingParams parameters = PrebookingTest.installPrebooking(controller); + parameters.maximumPassengerDelay = 500.0; + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personOk"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2061.0, requestInfo.pickupTime, 1e-3); + assertEquals(2713.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personLate"); + assertEquals(0.0, requestInfo.submissionTimes.get(0), 1e-3); + // agent tries a non-prebooked request upon arrival + assertEquals(4000.0, requestInfo.submissionTimes.get(1), 1e-3); + assertEquals(4146.0, requestInfo.pickupTime, 1e-3); + assertEquals(4357.0, requestInfo.dropoffTime, 1e-3); + assertTrue(requestInfo.rejected); + } + } + + @Test + public void cancelEarlyTest() { + /* + * One person requests to depart at 2000 and also is there at 2000. Another + * person asks also to depart at 2000, but only arrives at 4000, i.e. the person + * has 1000s delay. + * + * In this test we manually cancel the second request at 500.0 (so before + * departure of any agent). + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .addRequest("personOk", 0, 0, 5, 5, 2000.0, 0.0, 2000.0) // + .addRequest("personLate", 0, 0, 5, 5, 4000.0, 0.0, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + PrebookingTest.installPrebooking(controller); + + controller.addOverridingQSimModule(new AbstractDvrpModeQSimModule("drt") { + @Override + protected void configureQSim() { + addModalQSimComponentBinding().toProvider(modalProvider(getter -> { + PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); + QSim qsim = getter.get(QSim.class); + + return new MobsimBeforeSimStepListener() { + @Override + public void notifyMobsimBeforeSimStep(MobsimBeforeSimStepEvent e) { + if (e.getSimulationTime() == 500.0) { + PlanAgent planAgent = (PlanAgent) qsim.getAgents() + .get(Id.createPersonId("personLate")); + + Leg leg = TripStructureUtils.getLegs(planAgent.getCurrentPlan()).get(1); + + prebookingManager.cancel(leg); + } + } + }; + })); + } + }); + + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personOk"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2061.0, requestInfo.pickupTime, 1e-3); + assertEquals(2272.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personLate"); + assertEquals(0.0, requestInfo.submissionTimes.get(0), 1e-3); + // agent tries a non-prebooked request upon arrival + assertEquals(4000.0, requestInfo.submissionTimes.get(1), 1e-3); + assertTrue(requestInfo.rejected); + } + } + + @Test + public void cancelLateTest() { + /* + * One person requests to depart at 2000 and also is there at 2000. Another + * person asks also to depart at 2000, but only arrives at 4000, i.e. the person + * has 1000s delay. + * + * In this test we manually cancel the second request at 3000.0 (so after + * departure of the first agent). + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .addRequest("personOk", 0, 0, 5, 5, 2000.0, 0.0, 2000.0) // + .addRequest("personLate", 0, 0, 5, 5, 4000.0, 0.0, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + PrebookingTest.installPrebooking(controller); + + controller.addOverridingQSimModule(new AbstractDvrpModeQSimModule("drt") { + @Override + protected void configureQSim() { + addModalQSimComponentBinding().toProvider(modalProvider(getter -> { + PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); + QSim qsim = getter.get(QSim.class); + + return new MobsimBeforeSimStepListener() { + @Override + public void notifyMobsimBeforeSimStep(MobsimBeforeSimStepEvent e) { + if (e.getSimulationTime() == 3000.0) { + PlanAgent planAgent = (PlanAgent) qsim.getAgents() + .get(Id.createPersonId("personLate")); + + Leg leg = TripStructureUtils.getLegs(planAgent.getCurrentPlan()).get(1); + + prebookingManager.cancel(leg); + } + } + }; + })); + } + }); + + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personOk"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2061.0, requestInfo.pickupTime, 1e-3); + assertEquals(3212.0, requestInfo.dropoffTime, 1e-3); // still waited quite a bit + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("personLate"); + assertEquals(0.0, requestInfo.submissionTimes.get(0), 1e-3); + // agent tries a non-prebooked request upon arrival + assertEquals(4000.0, requestInfo.submissionTimes.get(1), 1e-3); + assertTrue(requestInfo.rejected); + } + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/ComplexUnschedulerTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/ComplexUnschedulerTest.java new file mode 100644 index 00000000000..84c3ada7791 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/ComplexUnschedulerTest.java @@ -0,0 +1,688 @@ +package org.matsim.contrib.drt.prebooking; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.junit.Test; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.NetworkFactory; +import org.matsim.api.core.v01.network.Node; +import org.matsim.contrib.drt.optimizer.VehicleDataEntryFactoryImpl; +import org.matsim.contrib.drt.optimizer.VehicleEntry; +import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; +import org.matsim.contrib.drt.prebooking.unscheduler.ComplexRequestUnscheduler; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.schedule.DefaultDrtStopTask; +import org.matsim.contrib.drt.schedule.DrtDriveTask; +import org.matsim.contrib.drt.schedule.DrtStayTask; +import org.matsim.contrib.drt.schedule.DrtStopTask; +import org.matsim.contrib.drt.schedule.DrtTaskFactory; +import org.matsim.contrib.drt.schedule.DrtTaskFactoryImpl; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification; +import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.path.DivertedVrpPath; +import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; +import org.matsim.contrib.dvrp.path.VrpPaths; +import org.matsim.contrib.dvrp.schedule.DriveTask; +import org.matsim.contrib.dvrp.schedule.Schedule; +import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; +import org.matsim.contrib.dvrp.schedule.StayTask; +import org.matsim.contrib.dvrp.schedule.Task; +import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker; +import org.matsim.contrib.dvrp.util.LinkTimePair; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.router.DijkstraFactory; +import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility; +import org.matsim.core.router.util.LeastCostPathCalculator; +import org.matsim.core.router.util.TravelTime; +import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime; +import org.mockito.Mockito; + +/** + * @author Sebastian Hörl (sebhoerl) / IRT SystemX + */ +public class ComplexUnschedulerTest { + @Test + public void testDirectDropoffAfterPickup() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest1); + fixture.addDrive("f20"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest2); + fixture.addDrive("f30"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); + fixture.addDrive("f50"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addDropoffRequest(otherRequest2); + fixture.addDrive("f60"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest1); + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(100.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(13, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 10001.0), // 0 + new ReferenceTask(DrtStayTask.class, 10001.0, 10301.0), // 1 + new ReferenceTask(DefaultDrtStopTask.class, 10301.0, 10361.0), // 2 + new ReferenceTask(DrtDriveTask.class, 10361.0, 20362.0), // 3 + new ReferenceTask(DrtStayTask.class, 20362.0, 20662.0), // 4 + new ReferenceTask(DefaultDrtStopTask.class, 20662.0, 20722.0), // 5 + new ReferenceTask(DrtDriveTask.class, 20722.0, 50723.0), // 6 + new ReferenceTask(DrtStayTask.class, 50723.0, 51745.0), // 7 + new ReferenceTask(DefaultDrtStopTask.class, 51745.0, 51805.0), // 8 + new ReferenceTask(DrtDriveTask.class, 51805.0, 61806.0), // 9 + new ReferenceTask(DrtStayTask.class, 61806.0, 62106.0), // 10 + new ReferenceTask(DefaultDrtStopTask.class, 62106.0, 62166.0), // 11 + new ReferenceTask(DrtStayTask.class, 62166.0, 30.0 * 3600.0) // 12 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(6); + assertEquals("f20", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f50", insertedDriveTask.getPath().getToLink().getId().toString()); + } + + @Test + public void testStandardSituation() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest1); // f10 + fixture.addDrive("f20"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f20 + fixture.addDrive("f30"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addPickupRequest(otherRequest2); // f30 + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest2); // f40 + fixture.addDrive("f50"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addDrive("f60"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addDropoffRequest(otherRequest1); // f60 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(100.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(13, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 10001.0), // 0 + new ReferenceTask(DrtStayTask.class, 10001.0, 10301.0), // 1 + new ReferenceTask(DefaultDrtStopTask.class, 10301.0, 10361.0), // 2 + new ReferenceTask(DrtDriveTask.class, 10361.0, 30362.0), // 3 + new ReferenceTask(DrtStayTask.class, 30362.0, 31023.0), // 4 + new ReferenceTask(DefaultDrtStopTask.class, 31023.0, 31083.0), // 5 + new ReferenceTask(DrtDriveTask.class, 31083.0, 41084.0), // 6 + new ReferenceTask(DrtStayTask.class, 41084.0, 41384.0), // 7 + new ReferenceTask(DefaultDrtStopTask.class, 41384.0, 41444.0), // 8 + new ReferenceTask(DrtDriveTask.class, 41444.0, 61445.0), // 9 + new ReferenceTask(DrtStayTask.class, 61445.0, 62106.0), // 10 + new ReferenceTask(DefaultDrtStopTask.class, 62106.0, 62166.0), // 11 + new ReferenceTask(DrtStayTask.class, 62166.0, 30.0 * 3600.0) // 12 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(3); + assertEquals("f10", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f30", insertedDriveTask.getPath().getToLink().getId().toString()); + + DrtDriveTask insertedDriveTask2 = (DrtDriveTask) schedule.getTasks().get(9); + assertEquals("f40", insertedDriveTask2.getPath().getFromLink().getId().toString()); + assertEquals("f60", insertedDriveTask2.getPath().getToLink().getId().toString()); + } + + @Test + public void testRemoveAtEnd() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest1); // f10 + fixture.addDrive("f20"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f20 + fixture.addDrive("f30"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addPickupRequest(otherRequest2); // f30 + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest2); // f40 + fixture.addDrive("f50"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest1); // f50 + fixture.addDrive("f60"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f60 // replace end + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(100.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 10001.0), // 0 + new ReferenceTask(DrtStayTask.class, 10001.0, 10301.0), // 1 + new ReferenceTask(DefaultDrtStopTask.class, 10301.0, 10361.0), // 2 + new ReferenceTask(DrtDriveTask.class, 10361.0, 30362.0), // 3 + new ReferenceTask(DrtStayTask.class, 30362.0, 31023.0), // 4 + new ReferenceTask(DefaultDrtStopTask.class, 31023.0, 31083.0), // 5 + new ReferenceTask(DrtDriveTask.class, 31083.0, 41084.0), // 6 + new ReferenceTask(DrtStayTask.class, 41084.0, 41384.0), // 7 + new ReferenceTask(DefaultDrtStopTask.class, 41384.0, 41444.0), // 8 + new ReferenceTask(DrtDriveTask.class, 41444.0, 51445.0), // 9 + new ReferenceTask(DrtStayTask.class, 51445.0, 51745.0), // 10 + new ReferenceTask(DefaultDrtStopTask.class, 51745.0, 51805.0), // 11 + new ReferenceTask(DrtStayTask.class, 51805.0, 30.0 * 3600.0) // 12 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(3); + assertEquals("f10", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f30", insertedDriveTask.getPath().getToLink().getId().toString()); + + DrtDriveTask insertedDriveTask2 = (DrtDriveTask) schedule.getTasks().get(9); + assertEquals("f40", insertedDriveTask2.getPath().getFromLink().getId().toString()); + assertEquals("f50", insertedDriveTask2.getPath().getToLink().getId().toString()); + + DrtStayTask stayTask = (DrtStayTask) schedule.getTasks().get(12); + assertEquals("f50", stayTask.getLink().getId().toString()); + } + + @Test + public void testRemoveAtBeginningWithWaitSecond() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); // replace start + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f10 + fixture.addDrive("f20"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addPickupRequest(otherRequest1); // f20 + fixture.addDrive("f30"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest2); // f30 + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest2); // f40 + fixture.addDrive("f50"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addDrive("f60"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addDropoffRequest(otherRequest1); // f60 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(10100.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(15, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 10001.0), // 0 + new ReferenceTask(DrtStayTask.class, 10001.0, 10100.0), // 1 + new ReferenceTask(DrtDriveTask.class, 10100.0, 20101.0), // 2 + new ReferenceTask(DrtStayTask.class, 20101.0, 20662.0), // 3 + new ReferenceTask(DefaultDrtStopTask.class, 20662.0, 20722.0), // 4 + new ReferenceTask(DrtDriveTask.class, 20722.0, 30723.0), // 5 + new ReferenceTask(DrtStayTask.class, 30723.0, 31023.0), // 6 + new ReferenceTask(DefaultDrtStopTask.class, 31023.0, 31083.0), // 7 + new ReferenceTask(DrtDriveTask.class, 31083.0, 41084.0), // 8 + new ReferenceTask(DrtStayTask.class, 41084.0, 41384.0), // 9 + new ReferenceTask(DefaultDrtStopTask.class, 41384.0, 41444.0), // 10 + new ReferenceTask(DrtDriveTask.class, 41444.0, 61445.0), // 11 + new ReferenceTask(DrtStayTask.class, 61445.0, 62106.0), // 12 + new ReferenceTask(DefaultDrtStopTask.class, 62106.0, 62166.0), // 13 + new ReferenceTask(DrtStayTask.class, 62166.0, 30.0 * 3600.0) // 14 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(2); + assertEquals("f10", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f20", insertedDriveTask.getPath().getToLink().getId().toString()); + + DrtDriveTask insertedDriveTask2 = (DrtDriveTask) schedule.getTasks().get(11); + assertEquals("f40", insertedDriveTask2.getPath().getFromLink().getId().toString()); + assertEquals("f60", insertedDriveTask2.getPath().getToLink().getId().toString()); + } + + @Test + public void testRemoveAtBeginningWithWaitFirst() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addWait(300.0); // replace start + fixture.addDrive("f10"); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f10 + fixture.addDrive("f20"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addPickupRequest(otherRequest1); // f20 + fixture.addDrive("f30"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest2); // f30 + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest2); // f40 + fixture.addDrive("f50"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addDrive("f60"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addDropoffRequest(otherRequest1); // f60 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(500.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(14, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtStayTask.class, 0.0, 500.0), // 0 + new ReferenceTask(DrtDriveTask.class, 500.0, 20501.0), // 1 + new ReferenceTask(DrtStayTask.class, 20501.0, 20662.0), // 2 + new ReferenceTask(DefaultDrtStopTask.class, 20662.0, 20722.0), // 3 + new ReferenceTask(DrtDriveTask.class, 20722.0, 30723.0), // 4 + new ReferenceTask(DrtStayTask.class, 30723.0, 31023.0), // 5 + new ReferenceTask(DefaultDrtStopTask.class, 31023.0, 31083.0), // 6 + new ReferenceTask(DrtDriveTask.class, 31083.0, 41084.0), // 7 + new ReferenceTask(DrtStayTask.class, 41084.0, 41384.0), // 8 + new ReferenceTask(DefaultDrtStopTask.class, 41384.0, 41444.0), // 9 + new ReferenceTask(DrtDriveTask.class, 41444.0, 61445.0), // 10 + new ReferenceTask(DrtStayTask.class, 61445.0, 62106.0), // 11 + new ReferenceTask(DefaultDrtStopTask.class, 62106.0, 62166.0), // 12 + new ReferenceTask(DrtStayTask.class, 62166.0, 30.0 * 3600.0) // 13 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(1); + assertEquals("f0", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f20", insertedDriveTask.getPath().getToLink().getId().toString()); + + DrtDriveTask insertedDriveTask2 = (DrtDriveTask) schedule.getTasks().get(10); + assertEquals("f40", insertedDriveTask2.getPath().getFromLink().getId().toString()); + assertEquals("f60", insertedDriveTask2.getPath().getToLink().getId().toString()); + } + + @Test + public void testRemoveAtBeginningWithDriveDiversion() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest otherRequest1 = fixture.createRequest(); + AcceptedDrtRequest otherRequest2 = fixture.createRequest(); + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); // replace start + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f10 + fixture.addDrive("f20"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addPickupRequest(otherRequest1); // f20 + fixture.addDrive("f30"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(otherRequest2); // f30 + fixture.addDrive("f40"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(otherRequest2); // f40 + fixture.addDrive("f50"); // replace start + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addDrive("f60"); + fixture.addWait(300.0); // replace end + fixture.addStop(60.0).addDropoffRequest(otherRequest1); // f60 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + + OnlineDriveTaskTracker tracker = Mockito.mock(OnlineDriveTaskTracker.class); + schedule.getTasks().get(0).initTaskTracker(tracker); + + LinkTimePair diversionPoint = new LinkTimePair(fixture.network.getLinks().get(Id.createLinkId("f5")), 20.0); + Mockito.when(tracker.getDiversionPoint()).thenReturn(diversionPoint); + + Mockito.doAnswer(invocation -> { + VrpPathWithTravelData path = invocation.getArgument(0); + DriveTask task = (DriveTask) schedule.getTasks().get(0); + DivertedVrpPath divertedPath = new DivertedVrpPath(task.getPath(), path, 5); + task.pathDiverted(divertedPath, path.getArrivalTime()); + return null; + }).when(tracker).divertPath(Mockito.any()); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(500.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(13, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 15019.0), // 0 + new ReferenceTask(DrtStayTask.class, 15019.0, 20662.0), // 1 + new ReferenceTask(DefaultDrtStopTask.class, 20662.0, 20722.0), // 2 + new ReferenceTask(DrtDriveTask.class, 20722.0, 30723.0), // 3 + new ReferenceTask(DrtStayTask.class, 30723.0, 31023.0), // 4 + new ReferenceTask(DefaultDrtStopTask.class, 31023.0, 31083.0), // 5 + new ReferenceTask(DrtDriveTask.class, 31083.0, 41084.0), // 6 + new ReferenceTask(DrtStayTask.class, 41084.0, 41384.0), // 7 + new ReferenceTask(DefaultDrtStopTask.class, 41384.0, 41444.0), // 8 + new ReferenceTask(DrtDriveTask.class, 41444.0, 61445.0), // 9 + new ReferenceTask(DrtStayTask.class, 61445.0, 62106.0), // 10 + new ReferenceTask(DefaultDrtStopTask.class, 62106.0, 62166.0), // 11 + new ReferenceTask(DrtStayTask.class, 62166.0, 30.0 * 3600.0) // 12 + )); + + DrtDriveTask insertedDriveTask = (DrtDriveTask) schedule.getTasks().get(0); + assertEquals("f0", insertedDriveTask.getPath().getFromLink().getId().toString()); + assertEquals("f20", insertedDriveTask.getPath().getToLink().getId().toString()); + + DrtDriveTask insertedDriveTask2 = (DrtDriveTask) schedule.getTasks().get(9); + assertEquals("f40", insertedDriveTask2.getPath().getFromLink().getId().toString()); + assertEquals("f60", insertedDriveTask2.getPath().getToLink().getId().toString()); + } + + @Test + public void testRemoveAllStartWithWait() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addWait(300.0); + fixture.addDrive("f10"); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f10 + fixture.addDrive("f20"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(0.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(2, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtStayTask.class, 0.0, 0.0), // 0 + new ReferenceTask(DrtStayTask.class, 0.0, 30.0 * 3600.0) // 1 + )); + + assertEquals("f0", ((StayTask) schedule.getTasks().get(0)).getLink().getId().toString()); + assertEquals("f0", ((StayTask) schedule.getTasks().get(1)).getLink().getId().toString()); + } + + @Test + public void testRemoveAllStartWithDrive() { + Fixture fixture = new Fixture(); + Schedule schedule = fixture.schedule; + + AcceptedDrtRequest unscheduleRequest = fixture.createRequest(); + + fixture.addDrive("f10"); + fixture.addWait(300.0); + fixture.addStop(60.0).addPickupRequest(unscheduleRequest); // f10 + fixture.addDrive("f20"); + fixture.addWait(300.0); + fixture.addStop(60.0).addDropoffRequest(unscheduleRequest); // f50 + fixture.addFinalStay(30.0 * 3600.0); + + schedule.nextTask(); + + OnlineDriveTaskTracker tracker = Mockito.mock(OnlineDriveTaskTracker.class); + schedule.getTasks().get(0).initTaskTracker(tracker); + + LinkTimePair diversionPoint = new LinkTimePair(fixture.network.getLinks().get(Id.createLinkId("f5")), 20.0); + Mockito.when(tracker.getDiversionPoint()).thenReturn(diversionPoint); + + Mockito.doAnswer(invocation -> { + VrpPathWithTravelData path = invocation.getArgument(0); + DriveTask task = (DriveTask) schedule.getTasks().get(0); + DivertedVrpPath divertedPath = new DivertedVrpPath(task.getPath(), path, 5); + task.pathDiverted(divertedPath, path.getArrivalTime()); + return null; + }).when(tracker).divertPath(Mockito.any()); + + ComplexRequestUnscheduler unscheduler = new ComplexRequestUnscheduler(fixture.lookup, fixture.entryFactory, + fixture.taskFactory, fixture.router, fixture.travelTime, fixture.timingUpdater, false); + + unscheduler.unscheduleRequest(0.0, fixture.vehicle.getId(), unscheduleRequest.getId()); + + assertEquals(2, schedule.getTaskCount()); + + assertTasks(schedule, Arrays.asList( // + new ReferenceTask(DrtDriveTask.class, 0.0, 19.0), // 0 + new ReferenceTask(DrtStayTask.class, 19.0, 30.0 * 3600.0) // 1 + )); + + DrtDriveTask driveTask = (DrtDriveTask) schedule.getTasks().get(0); + assertEquals("f0", driveTask.getPath().getFromLink().getId().toString()); + assertEquals("f5", driveTask.getPath().getToLink().getId().toString()); + + assertEquals("f5", ((StayTask) schedule.getTasks().get(1)).getLink().getId().toString()); + } + + record ReferenceTask(Class taskType, double startTime, double endTime) { + } + + private static void assertTasks(Schedule schedule, List references) { + for (int i = 0; i < references.size(); i++) { + Task task = schedule.getTasks().get(i); + ReferenceTask reference = references.get(i); + + assertEquals("wrong type in task " + i, reference.taskType, task.getClass()); + assertEquals("wrong begin time in task " + i, reference.startTime, task.getBeginTime(), 1e-3); + assertEquals("wrong end time in task " + i, reference.endTime, task.getEndTime(), 1e-3); + + if (i > 0) { + assertEquals("wrong transition from " + (i - 1) + " to " + i, schedule.getTasks().get(i).getBeginTime(), + schedule.getTasks().get(i - 1).getEndTime(), 1e-3); + } + + assertTrue("invalid task " + i, task.getEndTime() >= task.getBeginTime()); + } + } + + private Network createNetwork() { + Network network = NetworkUtils.createNetwork(); + NetworkFactory networkFactory = network.getFactory(); + + List nodes = new LinkedList<>(); + + for (int i = 0; i < 100; i++) { + Node node = networkFactory.createNode(Id.createNodeId("n" + i), new Coord(0.0, i * 1000.0)); + network.addNode(node); + nodes.add(node); + } + + for (int i = 0; i < 99; i++) { + Link forwardLink = networkFactory.createLink(Id.createLinkId("f" + i), nodes.get(i), nodes.get(i + 1)); + network.addLink(forwardLink); + + Link backwardLink = networkFactory.createLink(Id.createLinkId("b" + i), nodes.get(i + 1), nodes.get(i)); + network.addLink(backwardLink); + } + + for (Link link : network.getLinks().values()) { + link.setAllowedModes(Collections.singleton("car")); + link.setLength(1000.0); + link.setFreespeed(1.0); + } + + return network; + } + + private class Fixture { + private final DvrpVehicle vehicle; + private final Schedule schedule; + private final Network network; + + private Link currentLink; + private double currentTime; + + private final DrtTaskFactory taskFactory = new DrtTaskFactoryImpl(); + private final LeastCostPathCalculator router; + private final TravelTime travelTime = new FreeSpeedTravelTime(); + + private final VehicleEntry.EntryFactory entryFactory; + private final ScheduleTimingUpdater timingUpdater; + private final DvrpVehicleLookup lookup; + + private int requestIndex = 0; + + Fixture() { + this.network = createNetwork(); + + Link depotLink = network.getLinks().get(Id.createLinkId("f0")); + + DvrpVehicleSpecification vehicleSpecification = ImmutableDvrpVehicleSpecification.newBuilder() // + .id(Id.create("vehicle", DvrpVehicle.class)) // + .capacity(4) // + .serviceBeginTime(0.0) // + .serviceEndTime(30.0 * 3600.0) // + .startLinkId(depotLink.getId()) // + .build(); + + this.vehicle = new DvrpVehicleImpl(vehicleSpecification, depotLink); + this.schedule = vehicle.getSchedule(); + this.currentLink = vehicle.getStartLink(); + this.currentTime = 0.0; + this.router = new DijkstraFactory().createPathCalculator(network, + new OnlyTimeDependentTravelDisutility(travelTime), travelTime); + + this.lookup = Mockito.mock(DvrpVehicleLookup.class); + Mockito.when(this.lookup.lookupVehicle(Mockito.any())).thenReturn(vehicle); + + DrtConfigGroup drtConfig = new DrtConfigGroup(); + drtConfig.stopDuration = 30.0; + drtConfig.maxWaitTime = 600.0; + + this.entryFactory = new VehicleDataEntryFactoryImpl(); + + this.timingUpdater = Mockito.mock(ScheduleTimingUpdater.class); + } + + AcceptedDrtRequest createRequest() { + AcceptedDrtRequest request = Mockito.mock(AcceptedDrtRequest.class); + Mockito.when(request.getId()).thenReturn(Id.create("req_" + requestIndex++, Request.class)); + return request; + } + + DrtStayTask addWait(double duration) { + DrtStayTask task = taskFactory.createStayTask(vehicle, currentTime, currentTime + duration, currentLink); + schedule.addTask(task); + + currentTime += duration; + return task; + } + + DrtStayTask addStay(double duration) { + DrtStayTask task = taskFactory.createStayTask(vehicle, currentTime, currentTime + duration, currentLink); + schedule.addTask(task); + + currentTime += duration; + return task; + } + + DrtStayTask addFinalStay(double until) { + DrtStayTask task = taskFactory.createStayTask(vehicle, currentTime, Math.max(currentTime, until), + currentLink); + schedule.addTask(task); + + currentTime = Math.max(currentTime, until); + return task; + } + + DrtStopTask addStop(double duration) { + DrtStopTask task = taskFactory.createStopTask(vehicle, currentTime, currentTime + duration, currentLink); + schedule.addTask(task); + currentTime += duration; + return task; + } + + DrtDriveTask addDrive(String destinationLinkId) { + Link destinationLink = network.getLinks().get(Id.createLinkId(destinationLinkId)); + + VrpPathWithTravelData path = VrpPaths.calcAndCreatePath(currentLink, destinationLink, currentTime, router, + travelTime); + DrtDriveTask driveTask = taskFactory.createDriveTask(vehicle, path, DrtDriveTask.TYPE); + schedule.addTask(driveTask); + + currentTime = driveTask.getEndTime(); + currentLink = destinationLink; + + return driveTask; + } + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java new file mode 100644 index 00000000000..11d3951b632 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java @@ -0,0 +1,227 @@ +package org.matsim.contrib.drt.prebooking; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.contrib.drt.prebooking.logic.ProbabilityBasedPrebookingLogic; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequest; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; +import org.matsim.testcases.MatsimTestUtils; + +/** + * @author Sebastian Hörl (sebhoerl) / IRT SystemX + */ +public class PersonStuckPrebookingTest { + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void baselineTest() { + /* + * Agent personA is performing three drt legs during the day. Agent personB does + * exactly the same in parallel, both prebook their requests. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(20000.0); + + Controler controller = environment.build(); + + implementPopulation(controller.getScenario().getPopulation()); + PrebookingTest.installPrebooking(controller, false); + ProbabilityBasedPrebookingLogic.install(controller, + DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()), 1.0, 20000.0); + + EventCounter eventCounterA = EventCounter.install(controller, Id.createPersonId("personA")); + EventCounter eventCounterB = EventCounter.install(controller, Id.createPersonId("personB")); + + controller.run(); + + assertEquals(3, eventCounterA.submittedCount); + assertEquals(3, eventCounterA.dropoffCount); + + assertEquals(3, eventCounterB.submittedCount); + assertEquals(3, eventCounterB.dropoffCount); + } + + @Test + public void cancelTest() { + /* + * Agent personA is performing three drt legs during the day. Agent personB does + * exactly the same in parallel, both prebook there requests. + * + * We cancel the first request of personA. We check that the other reservations + * are automatically rejected as soon as the person is stuck. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicle", 1, 1) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(20000.0); + + Controler controller = environment.build(); + + implementPopulation(controller.getScenario().getPopulation()); + PrebookingTest.installPrebooking(controller, false); + ProbabilityBasedPrebookingLogic.install(controller, + DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()), 1.0, 20000.0); + + EventCounter eventCounterA = EventCounter.install(controller, Id.createPersonId("personA")); + EventCounter eventCounterB = EventCounter.install(controller, Id.createPersonId("personB")); + + controller.addOverridingQSimModule(new AbstractDvrpModeQSimModule("drt") { + @Override + protected void configureQSim() { + addModalQSimComponentBinding().toProvider(modalProvider(getter -> { + PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); + + return new MobsimBeforeSimStepListener() { + @Override + public void notifyMobsimBeforeSimStep(MobsimBeforeSimStepEvent e) { + if (e.getSimulationTime() == 500.0) { + prebookingManager.cancel(Id.create("drt_prebooked_0", Request.class)); + } + } + }; + })); + + bindModal(PassengerRequestValidator.class).toProvider(modalProvider(getter -> { + return new PassengerRequestValidator() { + @Override + public Set validateRequest(PassengerRequest request) { + if (!request.getId().toString().contains("prebooked")) { + return Collections.singleton("anything"); + } + + return Collections.emptySet(); + } + }; + })); + } + }); + + controller.run(); + + assertEquals(4, eventCounterA.submittedCount); + assertEquals(4, eventCounterA.rejectedCount); + assertEquals(0, eventCounterA.dropoffCount); + + assertEquals(3, eventCounterB.submittedCount); + assertEquals(0, eventCounterB.rejectedCount); + assertEquals(3, eventCounterB.dropoffCount); + } + + private void implementPopulation(Population population) { + PopulationFactory populationFactory = population.getFactory(); + + for (String personId : Arrays.asList("personA", "personB")) { + Person person = populationFactory.createPerson(Id.createPersonId(personId)); + population.addPerson(person); + + Plan plan = populationFactory.createPlan(); + person.addPlan(plan); + + Activity firstActivity = populationFactory.createActivityFromLinkId("generic", Id.createLinkId("1:1-2:1")); + firstActivity.setEndTime(2000.0); + plan.addActivity(firstActivity); + + // departure at 2000 + Leg firstLeg = populationFactory.createLeg("drt"); + plan.addLeg(firstLeg); + + Activity secondActivity = populationFactory.createActivityFromLinkId("generic", Id.createLinkId("5:5-6:5")); + secondActivity.setEndTime(6000.0); + plan.addActivity(secondActivity); + + // departure at 6000 + Leg secondLeg = populationFactory.createLeg("drt"); + plan.addLeg(secondLeg); + + Activity thirdActivity = populationFactory.createActivityFromLinkId("generic", Id.createLinkId("1:1-2:1")); + thirdActivity.setEndTime(10000.0); + plan.addActivity(thirdActivity); + + // departure at 10000 + Leg thirdLeg = populationFactory.createLeg("drt"); + plan.addLeg(thirdLeg); + + Activity finalActivity = populationFactory.createActivityFromLinkId("generic", Id.createLinkId("5:5-6:5")); + plan.addActivity(finalActivity); + } + } + + static private class EventCounter implements PassengerDroppedOffEventHandler, PassengerRequestSubmittedEventHandler, + PassengerRequestRejectedEventHandler { + private final Id personId; + + private EventCounter(Id personId) { + this.personId = personId; + } + + int dropoffCount = 0; + int submittedCount = 0; + int rejectedCount = 0; + + @Override + public void handleEvent(PassengerDroppedOffEvent event) { + if (event.getPersonId().equals(personId)) { + dropoffCount++; + } + } + + @Override + public void handleEvent(PassengerRequestSubmittedEvent event) { + if (event.getPersonIds().contains(personId)) { + submittedCount++; + } + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (event.getPersonIds().contains(personId)) { + rejectedCount++; + } + } + + static EventCounter install(Controler controller, Id personId) { + EventCounter instance = new EventCounter(personId); + + controller.addOverridingModule(new AbstractModule() { + + @Override + public void install() { + addEventHandlerBinding().toInstance(instance); + } + }); + + return instance; + } + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java new file mode 100644 index 00000000000..35d9a0ef62b --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTest.java @@ -0,0 +1,482 @@ +package org.matsim.contrib.drt.prebooking; + +import static org.junit.Assert.assertEquals; + +import org.junit.Rule; +import org.junit.Test; +import org.matsim.contrib.drt.prebooking.PrebookingTestEnvironment.RequestInfo; +import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.core.controler.Controler; +import org.matsim.testcases.MatsimTestUtils; + +/** + * @author Sebastian Hörl (sebhoerl) / IRT SystemX + */ +public class PrebookingTest { + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void withoutPrebookedRequests() { + /*- + * Standard test running with prebooking but without any prebooked requests + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("personA", 0, 0, 5, 5, 2000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + controller.run(); + + RequestInfo requestInfo = environment.getRequestInfo().get("personA"); + assertEquals(2000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2146.0, requestInfo.pickupTime, 1e-3); + assertEquals(2357.0, requestInfo.dropoffTime, 1e-3); + + var taskInfo = environment.getTaskInfo().get("vehicleA"); + assertEquals("STAY", taskInfo.get(0).type); + assertEquals("DRIVE", taskInfo.get(1).type); + assertEquals("STOP", taskInfo.get(2).type); + assertEquals("DRIVE", taskInfo.get(3).type); + assertEquals("STOP", taskInfo.get(4).type); + + assertEquals(2001.0, taskInfo.get(1).startTime, 1e-3); + + assertEquals(2086.0, taskInfo.get(2).startTime, 1e-3); + assertEquals(2146.0, taskInfo.get(2).endTime, 1e-3); + } + + static PrebookingParams installPrebooking(Controler controller) { + return installPrebooking(controller, true); + } + + static PrebookingParams installPrebooking(Controler controller, boolean installLogic) { + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()); + drtConfig.addParameterSet(new PrebookingParams()); + + if (installLogic) { + AttributeBasedPrebookingLogic.install(controller, drtConfig); + } + + return drtConfig.getPrebookingParams().get(); + } + + @Test + public void oneRequestArrivingLate() { + /*- + * One request arriving after the requested departure time. Vehicle should wait + * and depart with appropriate delay (taking into account a fixed duration for + * the person to enter instead of a fixed stop duration). + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + // 1800 indicated but only departing 2000 + .addRequest("personA", 0, 0, 5, 5, 2000.0, 0.0, 2000.0 - 200.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + RequestInfo requestInfo = environment.getRequestInfo().get("personA"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2060.0, requestInfo.pickupTime, 1e-3); + assertEquals(2271.0, requestInfo.dropoffTime, 1e-3); + + var taskInfo = environment.getTaskInfo().get("vehicleA"); + assertEquals("STAY", taskInfo.get(0).type); + assertEquals("DRIVE", taskInfo.get(1).type); + assertEquals("STAY", taskInfo.get(2).type); + assertEquals("STOP", taskInfo.get(3).type); + assertEquals("DRIVE", taskInfo.get(4).type); + assertEquals("STOP", taskInfo.get(5).type); + + assertEquals(1.0, taskInfo.get(1).startTime, 1e-3); // Pickup drive + assertEquals(86.0, taskInfo.get(2).startTime, 1e-3); // Starting to wait + assertEquals(1800.0, taskInfo.get(3).startTime, 1e-3); // Starting stop + assertEquals(2060.0, taskInfo.get(3).endTime, 1e-3); // Ending stop (260s duration) + assertEquals(2060.0, taskInfo.get(4).startTime, 1e-3); // Starting drive (ending stop) + } + + @Test + public void oneRequestArrivingEarly() { + /*- + * One request arriving in advance before the requested departure time. Vehicle + * will pickup up agent and then depart after the duration to enter the vehicle. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + // 2200 indicated but already departing 2000 + .addRequest("personA", 0, 0, 5, 5, 2000.0, 0.0, 2000.0 + 200.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + RequestInfo requestInfo = environment.getRequestInfo().get("personA"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2260.0 + 1.0, requestInfo.pickupTime, 1e-3); // One second for notifying vehicle + assertEquals(2472.0, requestInfo.dropoffTime, 1e-3); + + var taskInfo = environment.getTaskInfo().get("vehicleA"); + assertEquals("STAY", taskInfo.get(0).type); + assertEquals("DRIVE", taskInfo.get(1).type); + assertEquals("STAY", taskInfo.get(2).type); + assertEquals("STOP", taskInfo.get(3).type); + assertEquals("DRIVE", taskInfo.get(4).type); + assertEquals("STOP", taskInfo.get(5).type); + + assertEquals(1.0, taskInfo.get(1).startTime, 1e-3); // Pickup drive + assertEquals(86.0, taskInfo.get(2).startTime, 1e-3); // Starting to wait + assertEquals(2200.0, taskInfo.get(3).startTime, 1e-3); // Starting stop + assertEquals(2261.0, taskInfo.get(3).endTime, 1e-3); // Ending stop (60s) + assertEquals(2261.0, taskInfo.get(4).startTime, 1e-3); // Starting drive (ending stop) + } + + @Test + public void twoSequentialRequests() { + /*- + * Two requests that are scheduled in advance. + * - First the early one is submitted, then the late one + * - First the early one is picked up and dropped off, then the late one. + */ + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("earlyRequest", 0, 0, 5, 5, 2000.0, 0.0) // + .addRequest("lateRequest", 2, 2, 3, 3, 4000.0, 1000.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("earlyRequest"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2272.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("lateRequest"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(4000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(4104.0, requestInfo.dropoffTime, 1e-3); + } + } + + @Test + public void twoSequentialRequests_inverseSubmission() { + /*- + * Two requests that are scheduled in advance. + * - First the late one is submitted, then the early one (inverse of above test). + * - First the early one is picked up and dropped off, then the late one. + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("earlyRequest", 0, 0, 5, 5, 2000.0, 1000.0) // + .addRequest("lateRequest", 2, 2, 3, 3, 4000.0, 0.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("earlyRequest"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2272.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("lateRequest"); + assertEquals(0.0, requestInfo.submissionTime, 1e-3); + assertEquals(4000.0 + 60.0, requestInfo.pickupTime, 1e-3); + assertEquals(4103.0, requestInfo.dropoffTime, 1e-3); + } + } + + @Test + public void sameTrip_differentDepartureTime() { + /*- + * Two requests with the same origin and destination, but distinct departure time. + * - First, early one is submitted, then late one. + * - Vehicle picks up and drops off early one, then late one. + * - In total four stops (P,D,P,D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("earlyRequest", 1, 1, 5, 5, 2000.0, 1000.0) // + .addRequest("lateRequest", 1, 1, 5, 5, 4000.0, 1100.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("earlyRequest"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2230.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("lateRequest"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + assertEquals(4000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(4230.0, requestInfo.dropoffTime, 1e-3); + } + + // Four stops, 2x pickup, 2x dropoff + assertEquals(4, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + public void sameTrip_sameDepartureTime() { + /*- + * Two requests with the same origin and destination, and same departure time. + * - First, A is submitted, then B. + * - Vehicle picks up and A and B, then drops off A and B. + * - In total two stops (P,D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 5, 5, 2000.0, 1000.0) // + .addRequest("requestB", 1, 1, 5, 5, 2000.0, 1100.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2230.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2230.0, requestInfo.dropoffTime, 1e-3); + } + + // Two stops, 1x pickup, 1x dropoff + assertEquals(2, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + public void sameTrip_extendPickupDuration() { + /*- + * Two requests with the same origin and destination, different departure times. + * - First, A is submitted with departure time 2000 + * - Then, B is submitted with departure time 2020 + * - Scheduling A then B would give a total duration of 60 + 60 = 120s stop duration + * - Scheduling A then merging in B gives a total duration of 60 + 20 = 80s stop duration + * - Expected behavior is to merge the requests + * - In total two stops (P,D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 5, 5, 2000.0, 1000.0) // + .addRequest("requestB", 1, 1, 5, 5, 2020.0, 1100.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + // We always add 1s for first pickup + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + // We have additional 19s because stop task is extended! + assertEquals(2230.0 + 19.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + // Second pickup does not have one second offset + assertEquals(2020.0 + 60.0 + 0.0, requestInfo.pickupTime, 1e-3); + assertEquals(2230.0 + 19.0, requestInfo.dropoffTime, 1e-3); + } + + // Two stops, 1x pickup, 1x dropoff + assertEquals(2, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + public void sameTrip_splitPickup() { + /*- + * Two requests with the same origin and destination, different departure times. + * - First, A is submitted with departure time 2000 + * - Then, B is submitted with departure time 2080 + * - Stop for A goes from 2000 to 2060 + * - Stop for B goes from 2080 to 20140 + * - The requests don't overlap, so we schedule individual stops (in a more extreme use case we could schedule things in between then) + * - In total three stops (P, P, D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 5, 5, 2000.0, 1000.0) // + .addRequest("requestB", 1, 1, 5, 5, 2080.0, 1100.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + // Pickup as planned + assertEquals(2000.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + + // Dropoff a bit later than before because we pickup another one on the way + assertEquals(2310.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + // We insert a later pickup + assertEquals(2080.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + + // Dropoff as planned + assertEquals(2310.0, requestInfo.dropoffTime, 1e-3); + } + + // Three stops, 2x pickup, 1x dropoff + assertEquals(3, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + public void sameTrip_inverseSubmission_noPrepending() { + /*- + * Two requests with the same origin and destination, different departure times. + * - First, A is submitted with departure time 2020 + * - Then, B is submitted with departure time 2000 + * + * - This is inverse of sameTrip_extendPickupDuration above, the request with the earlier departure + * time is submitted second. + * - We would expect the requests to be merged, but this is not so trivial with the current code base: + * It would be necessary to shift the stop task to the past and extend it. Theoretically, this is + * possible but it would be nice to embed this in a more robust way in the code base. + * - Plus, it may change the current DRT behavior because we have these situations also in non-prebooked + * simulations: We may submit an immediate request and find an insertion for a stop task that starts + * in 5 seconds. This case is treated in standard DRT as any other request (extending the task but + * keeping the start time fixed). + * - We follow this default behaviour here: Instead of *prepending* the task with the new request, we + * *append* it as usual. If there is at least one pickup in the stop task with the standard stop + * durations, there is no additional cost of adding the request, but the person experiences a wait + * delay that could be minimized if we were able to prepend the task. + * - (Then again, do we actually want to do this, it would reserve the vehicle a few seconds more than + * necessary for a stop that is potentially prebooked in a long time). + * + * - Expected behavior in current implementation: Merge new request with the later departing existing + * one, which generates wait time for the customer. + * + * - In total two stops (P, D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 5, 5, 2020.0, 1000.0) // + .addRequest("requestB", 1, 1, 5, 5, 2000.0, 1100.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2020.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2250.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + assertEquals(2000.0 + 20.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2250.0, requestInfo.dropoffTime, 1e-3); + } + + // Three stops, 1x pickup, 1x dropoff + assertEquals(2, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } + + @Test + public void sameTrip_inverseSubmission_splitPickup() { + /*- + * Two requests with the same origin and destination, different departure times. + * - First, A is submitted with departure time 2020 + * - Then, B is submitted with departure time 1770 + * - B's departure is 250s before A, so appending it to A would lead to more than 300s + * of wait time, which is not a valid insertion + * - Hence, the insertion *after* the pickup of A is not valid + * - But the insertion *before* A is possible (it is usually evaluated but dominated by the insertion after) + * - TODO: May think this through and eliminate these insertion upfront + * + * - Expectation: Standard scheduling a prebooked request before another one (as if they had not the + * same origin link). + * - In total three stops (P,P,D) + */ + + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .addRequest("requestA", 1, 1, 5, 5, 2020.0, 1000.0) // + .addRequest("requestB", 1, 1, 5, 5, 1470.0, 1100.0) // + .configure(300.0, 2.0, 1800.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1000.0, requestInfo.submissionTime, 1e-3); + assertEquals(2020.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2249.0 + 1.0, requestInfo.dropoffTime, 1e-3); + } + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestB"); + assertEquals(1100.0, requestInfo.submissionTime, 1e-3); + assertEquals(1470.0 + 60.0 + 1.0, requestInfo.pickupTime, 1e-3); + assertEquals(2249.0 + 1.0, requestInfo.dropoffTime, 1e-3); + } + + // Three stops, 2x pickup, 1x dropoff + assertEquals(3, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java new file mode 100644 index 00000000000..8e1f6394388 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java @@ -0,0 +1,445 @@ +package org.matsim.contrib.drt.prebooking; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.NetworkFactory; +import org.matsim.api.core.v01.network.Node; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearchParams; +import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; +import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; +import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEventHandler; +import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.routing.DrtRouteFactory; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.DrtConfigs; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtModule; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.FleetSpecification; +import org.matsim.contrib.dvrp.fleet.FleetSpecificationImpl; +import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModule; +import org.matsim.contrib.dvrp.run.DvrpQSimComponents; +import org.matsim.contrib.dvrp.vrpagent.TaskEndedEvent; +import org.matsim.contrib.dvrp.vrpagent.TaskEndedEventHandler; +import org.matsim.contrib.dvrp.vrpagent.TaskStartedEvent; +import org.matsim.contrib.dvrp.vrpagent.TaskStartedEventHandler; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.QSimConfigGroup.EndtimeInterpretation; +import org.matsim.core.config.groups.QSimConfigGroup.StarttimeInterpretation; +import org.matsim.core.config.groups.ScoringConfigGroup.ActivityParams; +import org.matsim.core.config.groups.ScoringConfigGroup.ModeParams; +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; +import org.matsim.testcases.MatsimTestUtils; + +public class PrebookingTestEnvironment { + private final MatsimTestUtils utils; + + private final int width = 10; + private final int height = 10; + + private final double edgeLength = 200.0; + private final double speed = 10.0; + + private int vehicleCapacity = 4; + + private double maximumWaitTime = 3600.0; + private double detourRelative = 1.3; + private double detourAbsolute = 300.0; + private double stopDuration = 60.0; + private double endTime = 30.0 * 3600.0; + + public PrebookingTestEnvironment(MatsimTestUtils utils) { + this.utils = utils; + } + + public PrebookingTestEnvironment configure(double maximumWaitTime, double detourRelative, double detourAbsolute, + double stopDuration) { + this.maximumWaitTime = maximumWaitTime; + this.detourRelative = detourRelative; + this.detourAbsolute = detourAbsolute; + this.stopDuration = stopDuration; + return this; + } + + public PrebookingTestEnvironment endTime(double endTime) { + this.endTime = endTime; + return this; + } + + private class Request { + String personId; + + Pair origin = null; + Pair destination = null; + + double departureTime; + double submissionTime; + double plannedDepartureTime; + } + + List requests = new LinkedList<>(); + + public PrebookingTestEnvironment addRequest(String personId, int originX, int originY, int destinationX, + int destinationY, double departureTime, double submissionTime, double plannedDepartureTime) { + Request request = new Request(); + request.personId = personId; + request.origin = Pair.of(originX, originY); + request.destination = Pair.of(destinationX, destinationY); + request.departureTime = departureTime; + request.submissionTime = submissionTime; + request.plannedDepartureTime = plannedDepartureTime; + requests.add(request); + return this; + } + + public PrebookingTestEnvironment addRequest(String personId, int originX, int originY, int destinationX, + int destinationY, double departureTime, double submissionTime) { + return addRequest(personId, originX, originY, destinationX, destinationY, departureTime, submissionTime, + Double.NaN); + } + + public PrebookingTestEnvironment addRequest(String personId, int originX, int originY, int destinationX, + int destinationY, double departureTime) { + return addRequest(personId, originX, originY, destinationX, destinationY, departureTime, Double.NaN, + Double.NaN); + } + + private class Vehicle { + String vehicleId; + Pair depot; + } + + private List vehicles = new LinkedList<>(); + + public PrebookingTestEnvironment addVehicle(String vehicleId, int depotX, int depotY) { + Vehicle vehicle = new Vehicle(); + vehicle.vehicleId = vehicleId; + vehicle.depot = Pair.of(depotX, depotY); + vehicles.add(vehicle); + return this; + } + + public PrebookingTestEnvironment setVehicleCapacity(int vehicleCapacity) { + this.vehicleCapacity = vehicleCapacity; + return this; + } + + public Controler build() { + Config config = ConfigUtils.createConfig(); + buildConfig(config); + + Scenario scenario = ScenarioUtils.createScenario(config); + + scenario.getPopulation().getFactory().getRouteFactories().setRouteFactory(DrtRoute.class, + new DrtRouteFactory()); + + buildNetwork(scenario); + buildPopulation(scenario); + + Controler controller = new Controler(scenario); + buildController(controller); + buildFleet(controller); + + configureRequestListener(controller); + configureVehicleListener(controller); + + return controller; + } + + private void buildFleet(Controler controller) { + FleetSpecification fleetSpecification = new FleetSpecificationImpl(); + + for (Vehicle vehicle : vehicles) { + fleetSpecification.addVehicleSpecification(ImmutableDvrpVehicleSpecification.newBuilder() // + .id(Id.create(vehicle.vehicleId, DvrpVehicle.class)) // + .capacity(vehicleCapacity) // + .serviceBeginTime(0.0) // + .serviceEndTime(endTime) // + .startLinkId(createLinkId(createNodeId(vehicle.depot.getLeft(), vehicle.depot.getRight()), + createNodeId(vehicle.depot.getLeft() + 1, vehicle.depot.getRight()))) // + .build()); + } + + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { + @Override + public void install() { + bindModal(FleetSpecification.class).toInstance(fleetSpecification); + } + }); + } + + private void buildController(Controler controller) { + controller.addOverridingModule(new DvrpModule()); + controller.addOverridingModule(new MultiModeDrtModule()); + + MultiModeDrtConfigGroup multiModeDrtConfigGroup = MultiModeDrtConfigGroup.get(controller.getConfig()); + controller.configureQSimComponents(DvrpQSimComponents.activateAllModes(multiModeDrtConfigGroup)); + } + + private void buildConfig(Config config) { + config.controller().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); + + config.qsim().setStartTime(0.0); + config.qsim().setSimStarttimeInterpretation(StarttimeInterpretation.onlyUseStarttime); + + config.qsim().setEndTime(endTime); + config.qsim().setSimEndtimeInterpretation(EndtimeInterpretation.onlyUseEndtime); + + ModeParams drtParams = new ModeParams("drt"); + config.scoring().addModeParams(drtParams); + + ActivityParams genericParams = new ActivityParams("generic"); + genericParams.setScoringThisActivityAtAll(false); + config.scoring().addActivityParams(genericParams); + + DvrpConfigGroup dvrpConfig = new DvrpConfigGroup(); + config.addModule(dvrpConfig); + + MultiModeDrtConfigGroup drtConfig = new MultiModeDrtConfigGroup(); + config.addModule(drtConfig); + + DrtConfigGroup modeConfig = new DrtConfigGroup(); + drtConfig.addParameterSet(modeConfig); + modeConfig.mode = "drt"; + modeConfig.maxWaitTime = maximumWaitTime; + modeConfig.maxTravelTimeAlpha = detourRelative; + modeConfig.maxTravelTimeBeta = detourAbsolute; + modeConfig.stopDuration = stopDuration; + modeConfig.idleVehiclesReturnToDepots = false; + modeConfig.vehiclesFile = null; + + DrtInsertionSearchParams searchParams = new SelectiveInsertionSearchParams(); + modeConfig.addDrtInsertionSearchParams(searchParams); + + DrtConfigs.adjustMultiModeDrtConfig(drtConfig, config.scoring(), config.routing()); + } + + private void buildPopulation(Scenario scenario) { + Population population = scenario.getPopulation(); + PopulationFactory factory = population.getFactory(); + + for (Request request : requests) { + Id personId = Id.createPersonId(request.personId); + + Id originLinkId = createLinkId(createNodeId(request.origin.getLeft(), request.origin.getRight()), + createNodeId(request.origin.getLeft() + 1, request.origin.getRight())); + Id destinationLinkId = createLinkId( + createNodeId(request.destination.getLeft(), request.destination.getRight()), + createNodeId(request.destination.getLeft() + 1, request.destination.getRight())); + + Person person = factory.createPerson(personId); + population.addPerson(person); + + Plan plan = factory.createPlan(); + person.addPlan(plan); + + Activity firstActivity = factory.createActivityFromLinkId("generic", originLinkId); + plan.addActivity(firstActivity); + firstActivity.setEndTime(request.departureTime); + + Leg firstLeg = factory.createLeg("drt"); + plan.addLeg(firstLeg); + + if (!Double.isNaN(request.submissionTime)) { + firstActivity.getAttributes().putAttribute(AttributeBasedPrebookingLogic.getSubmissionAttribute("drt"), + request.submissionTime); + } + + if (!Double.isNaN(request.plannedDepartureTime)) { + firstActivity.getAttributes().putAttribute( + AttributeBasedPrebookingLogic.getPlannedDepartureAttribute("drt"), + request.plannedDepartureTime); + } + + Activity secondActivity = factory.createActivityFromLinkId("generic", destinationLinkId); + plan.addActivity(secondActivity); + } + } + + private Id createNodeId(int i, int j) { + return Id.createNodeId(i + ":" + j); + } + + private Id createLinkId(Id startId, Id endId) { + return Id.createLinkId(startId.toString() + "-" + endId.toString()); + } + + private void buildNetwork(Scenario scenario) { + Network network = scenario.getNetwork(); + NetworkFactory factory = network.getFactory(); + + Node[][] nodes = new Node[width][height]; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + nodes[i][j] = factory.createNode(createNodeId(i, j), new Coord(i * edgeLength, j * edgeLength)); + network.addNode(nodes[i][j]); + } + } + + // Horizontal + for (int j = 0; j < height; j++) { + for (int i = 0; i < width - 1; i++) { + Node firstNode = nodes[i][j]; + Node secondNode = nodes[i + 1][j]; + + Id forwardId = createLinkId(firstNode.getId(), secondNode.getId()); + Id backwardId = createLinkId(secondNode.getId(), firstNode.getId()); + + Link forwardLink = factory.createLink(forwardId, firstNode, secondNode); + Link backwardLink = factory.createLink(backwardId, secondNode, firstNode); + + network.addLink(forwardLink); + network.addLink(backwardLink); + } + } + + // Vertical + for (int i = 0; i < width; i++) { + for (int j = 0; j < height - 1; j++) { + Node firstNode = nodes[i][j]; + Node secondNode = nodes[i][j + 1]; + + Id forwardId = createLinkId(firstNode.getId(), secondNode.getId()); + Id backwardId = createLinkId(secondNode.getId(), firstNode.getId()); + + Link forwardLink = factory.createLink(forwardId, firstNode, secondNode); + Link backwardLink = factory.createLink(backwardId, secondNode, firstNode); + + network.addLink(forwardLink); + network.addLink(backwardLink); + } + } + + // Defaults + for (Link link : network.getLinks().values()) { + link.setFreespeed(speed); + link.setCapacity(1e9); + link.setLength(edgeLength); + link.setNumberOfLanes(1.0); + } + } + + // ANALYSIS PART + + public class RequestInfo { + public boolean rejected = false; + + public double submissionTime = Double.NaN; + public double pickupTime = Double.NaN; + public double dropoffTime = Double.NaN; + + public List submissionTimes = new LinkedList<>(); + } + + private Map requestInfo = new HashMap<>(); + + public Map getRequestInfo() { + return requestInfo; + } + + private void configureRequestListener(Controler controller) { + controller.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(new RequestListener()); + } + }); + } + + private class RequestListener implements DrtRequestSubmittedEventHandler, PassengerPickedUpEventHandler, + PassengerDroppedOffEventHandler, PassengerRequestRejectedEventHandler { + @Override + public void handleEvent(DrtRequestSubmittedEvent event) { + String ids = event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")); + requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTime = event + .getTime(); + requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTimes + .add(event.getTime()); + + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + requestInfo.computeIfAbsent(event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")), id -> new RequestInfo()).rejected = true; + } + + @Override + public void handleEvent(PassengerPickedUpEvent event) { + requestInfo.computeIfAbsent(event.getPersonId().toString(), id -> new RequestInfo()).pickupTime = event + .getTime(); + } + + @Override + public void handleEvent(PassengerDroppedOffEvent event) { + requestInfo.computeIfAbsent(event.getPersonId().toString(), id -> new RequestInfo()).dropoffTime = event + .getTime(); + } + } + + public class TaskInfo { + public double startTime = Double.NaN; + public double endTime = Double.NaN; + public String type; + } + + private Map> taskInfo = new HashMap<>(); + + public Map> getTaskInfo() { + return taskInfo; + } + + private void configureVehicleListener(Controler controller) { + controller.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(new VehicleListener()); + } + }); + } + + private class VehicleListener implements TaskStartedEventHandler, TaskEndedEventHandler { + @Override + public void handleEvent(TaskStartedEvent event) { + taskInfo.computeIfAbsent(event.getDvrpVehicleId().toString(), id -> new LinkedList<>()).add(new TaskInfo()); + taskInfo.get(event.getDvrpVehicleId().toString()).getLast().startTime = event.getTime(); + taskInfo.get(event.getDvrpVehicleId().toString()).getLast().type = event.getTaskType().name(); + } + + @Override + public void handleEvent(TaskEndedEvent event) { + taskInfo.get(event.getDvrpVehicleId().toString()).getLast().endTime = event.getTime(); + } + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifierTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifierTest.java index 750f73e21d2..d9af16f9896 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifierTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/routing/MultiModeDrtMainModeIdentifierTest.java @@ -14,7 +14,7 @@ import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.population.PopulationUtils; import org.matsim.core.router.TripRouter; @@ -43,7 +43,7 @@ public void test() { Assert.assertEquals(drtMode, mmi.identifyMainMode(testElements)); } { - String drtStageActivityType = PlanCalcScoreConfigGroup.createStageActivityType(drtMode); + String drtStageActivityType = ScoringConfigGroup.createStageActivityType(drtMode); List testElements = new ArrayList<>(); // #deleteBeforeRelease : only used to retrofit plans created since the merge of fallback routing module (sep'-dec'19) 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 e01a6a32f4f..b95f6d8de61 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 @@ -20,6 +20,7 @@ package org.matsim.contrib.drt.run.examples; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import java.io.IOException; import java.net.URL; @@ -34,11 +35,29 @@ import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.contrib.drt.optimizer.DrtRequestInsertionRetryParams; +import org.matsim.contrib.drt.optimizer.insertion.repeatedselective.RepeatedSelectiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; +import org.matsim.contrib.drt.prebooking.PrebookingParams; +import org.matsim.contrib.drt.prebooking.logic.ProbabilityBasedPrebookingLogic; +import org.matsim.contrib.drt.run.DrtConfigGroup; +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.PassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; +import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; +import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; @@ -49,6 +68,7 @@ /** * @author jbischoff + * @author Sebastian Hörl, IRT SystemX (sebhoerl) */ public class RunDrtExampleIT { @@ -67,8 +87,8 @@ public void testRunDrtExampleWithNoRejections_ExtensiveSearch() { drtCfg.rejectRequestIfMaxWaitOrTravelTimeViolated = false; } - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); RunDrtExample.run(config, false); var expectedStats = Stats.newBuilder() @@ -101,8 +121,8 @@ public void testRunDrtExampleWithNoRejections_SelectiveSearch() { drtCfg.rejectRequestIfMaxWaitOrTravelTimeViolated = false; } - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); RunDrtExample.run(config, false); var expectedStats = Stats.newBuilder() @@ -116,6 +136,41 @@ public void testRunDrtExampleWithNoRejections_SelectiveSearch() { verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); } + @Test + public void testRunDrtExampleWithNoRejections_RepeatedSelectiveSearch() { + Id.resetCaches(); + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), "mielec_drt_config.xml"); + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup()); + + for (var drtCfg : MultiModeDrtConfigGroup.get(config).getModalElements()) { + //replace extensive with repeated selective search + drtCfg.removeParameterSet(drtCfg.getDrtInsertionSearchParams()); + var repeatedSelectiveInsertionSearchParams = new RepeatedSelectiveInsertionSearchParams(); + // using adaptive travel time matrix + repeatedSelectiveInsertionSearchParams.retryInsertion = 5; + drtCfg.addParameterSet(repeatedSelectiveInsertionSearchParams); + + //disable rejections + drtCfg.rejectRequestIfMaxWaitOrTravelTimeViolated = false; + } + + config.controller().setLastIteration(3); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + RunDrtExample.run(config, false); + + var expectedStats = Stats.newBuilder() + .rejectionRate(0.0) + .rejections(0) + .waitAverage(261.57) + .inVehicleTravelTimeMean(382.74) + .totalTravelTimeMean(644.32) + .build(); + + verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); + } + @Test public void testRunDrtExampleWithRequestRetry() { Id.resetCaches(); @@ -130,8 +185,8 @@ public void testRunDrtExampleWithRequestRetry() { drtCfg.addParameterSet(drtRequestInsertionRetryParams); } - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); RunDrtExample.run(config, false); var expectedStats = Stats.newBuilder() @@ -153,8 +208,8 @@ public void testRunDrtStopbasedExample() { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); RunDrtExample.run(config, false); var expectedStats = Stats.newBuilder() @@ -168,6 +223,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.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().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() { Id.resetCaches(); @@ -176,8 +266,8 @@ public void testRunServiceAreabasedExampleWithSpeedUp() { Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); RunDrtExample.run(config, false); var expectedStats = Stats.newBuilder() @@ -191,6 +281,84 @@ public void testRunServiceAreabasedExampleWithSpeedUp() { verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); } + @Test + public void testRunDrtExampleWithIncrementalStopDuration() { + Id.resetCaches(); + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), "mielec_drt_config.xml"); + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup()); + + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + Controler controller = DrtControlerCreator.createControler(config, false); + + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { + @Override + public void install() { + PassengerStopDurationProvider stopDurationProvider = StaticPassengerStopDurationProvider.of(60.0, 5.0); + StopTimeCalculator stopTimeCalculator = new CumulativeStopTimeCalculator(stopDurationProvider); + stopTimeCalculator = new MinimumStopDurationAdapter(stopTimeCalculator, 60.0); + bindModal(StopTimeCalculator.class).toInstance(stopTimeCalculator); + } + }); + + controller.run(); + + // sh, 11/08/2023: updated after introducing prebookg, basically we generate a + // new feasible insertion (see InsertionGenerator) that previously did not + // exist, but has the same cost (pickup loss + drop-off loss) as the original + // one + var expectedStats = Stats.newBuilder() + .rejectionRate(0.04) + .rejections(16) + .waitAverage(278.76) + .inVehicleTravelTimeMean(384.93) + .totalTravelTimeMean(663.68) + .build(); + + verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); + } + + @Test + public void testRunDrtWithPrebooking() { + Id.resetCaches(); + URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("mielec"), + "mielec_drt_config.xml"); + + Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(), + new OTFVisConfigGroup()); + + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(config); + drtConfig.addParameterSet(new PrebookingParams()); + + Controler controller = DrtControlerCreator.createControler(config, false); + ProbabilityBasedPrebookingLogic.install(controller, drtConfig, 0.5, 4.0 * 3600.0); + + PrebookingTracker tracker = new PrebookingTracker(); + tracker.install(controller); + + controller.run(); + + assertEquals(157, tracker.immediateScheduled); + assertEquals(205, tracker.prebookedScheduled); + assertEquals(26, tracker.immediateRejected); + assertEquals(0, tracker.prebookedRejected); + + var expectedStats = Stats.newBuilder() + .rejectionRate(0.07) + .rejections(26) + .waitAverage(232.76) + .inVehicleTravelTimeMean(389.09) + .totalTravelTimeMean(621.85) + .build(); + + verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats); + } + /** * Early warning system: if customer stats vary more than the defined percentage above or below the expected values * then the following unit tests will fail. This is meant to serve as a red flag. @@ -299,4 +467,40 @@ public Stats build() { } } } + + static private class PrebookingTracker implements PassengerRequestRejectedEventHandler, PassengerRequestScheduledEventHandler { + int immediateScheduled = 0; + int prebookedScheduled = 0; + int immediateRejected = 0; + int prebookedRejected = 0; + + @Override + public void handleEvent(PassengerRequestScheduledEvent event) { + if (event.getRequestId().toString().contains("prebooked")) { + prebookedScheduled++; + } else { + immediateScheduled++; + } + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (event.getRequestId().toString().contains("prebooked")) { + prebookedRejected++; + } else { + immediateRejected++; + } + } + + void install(Controler controller) { + PrebookingTracker thisTracker = this; + + controller.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(thisTracker); + } + }); + } + } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java index d2c002a68a9..af92306b69e 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java @@ -27,7 +27,9 @@ import static org.mockito.Mockito.when; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.Test; import org.matsim.api.core.v01.Coord; @@ -38,6 +40,7 @@ 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.contrib.common.util.DistanceUtils; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector.EventSequence; @@ -54,7 +57,7 @@ import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.controler.events.IterationEndsEvent; import org.matsim.core.network.NetworkUtils; @@ -66,7 +69,7 @@ */ public class DrtSpeedUpTest { private final DrtSpeedUpParams drtSpeedUpParams = new DrtSpeedUpParams(); - private final ControlerConfigGroup controlerConfig = new ControlerConfigGroup(); + private final ControllerConfigGroup controlerConfig = new ControllerConfigGroup(); @Test public final void test_computeMovingAverage() { @@ -270,8 +273,10 @@ private void updateRequestAnalyser(EventSequence... eventSequences) { private EventSequence eventSequence(String id, double submittedTime, double waitTime, double inVehicleSpeed) { var requestId = Id.create(id, Request.class); - var submittedEvent = new DrtRequestSubmittedEvent(submittedTime, MODE, requestId, null, linkAB.getId(), - linkBC.getId(), Double.NaN, Double.NaN, Double.NaN, Double.NaN); + var personId = Id.create(id, Person.class); + + var submittedEvent = new DrtRequestSubmittedEvent(submittedTime, MODE, requestId, List.of(personId), linkAB.getId(), + linkBC.getId(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); var pickupEvent = new PassengerPickedUpEvent(submittedTime + waitTime, MODE, requestId, null, null); double rideTime = DistanceUtils.calculateDistance(linkBC, linkAB) / inVehicleSpeed; var dropoffEvent = new PassengerDroppedOffEvent(submittedTime + waitTime + rideTime, MODE, requestId, null, @@ -280,7 +285,8 @@ private EventSequence eventSequence(String id, double submittedTime, double wait MODE, requestId.toString()); var departureEvent = mock(PersonDepartureEvent.class); - return new EventSequence(departureEvent, submittedEvent, mock(PassengerRequestScheduledEvent.class), + + return new EventSequence(Id.createPersonId("r1"), departureEvent, submittedEvent, mock(PassengerRequestScheduledEvent.class), pickupEvent, dropoffEvent, List.of(drtFare)); } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java index b34b5af05ad..27fcefc3b52 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java @@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -46,7 +47,7 @@ import org.matsim.contrib.dvrp.vrpagent.TaskStartedEvent; import org.matsim.contrib.dvrp.vrpagent.TaskStartedEventHandler; import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.events.EventsUtils; import org.matsim.core.events.algorithms.EventWriterXML; @@ -64,7 +65,7 @@ public class DrtEventsReadersTest { //standard dvrp events are tested in DvrpEventsReadersTest private final List drtEvents = List.of( - new DrtRequestSubmittedEvent(0, mode, request, person, link1, link2, 111, 222, 412.0, 512.0),// + new DrtRequestSubmittedEvent(0, mode, request, List.of(person), link1, link2, 111, 222, 0.0, 412.0, 512.0),// taskStarted(10, DrtDriveTask.TYPE, 0, link1),// taskEnded(30, DefaultDrtStopTask.TYPE, 1, link2), // taskStarted(50, DrtStayTask.TYPE, 2, link1),// @@ -83,7 +84,7 @@ public void testReader() { eventsManager.initProcessing(); DrtEventsReaders.createEventsReader(eventsManager) .readStream(new ByteArrayInputStream(outputStream.toByteArray()), - ControlerConfigGroup.EventsFileFormat.xml); + ControllerConfigGroup.EventsFileFormat.xml); eventsManager.finishProcessing(); assertThat(handler.handledEvents).usingRecursiveFieldByFieldElementComparator() diff --git a/contribs/dvrp/pom.xml b/contribs/dvrp/pom.xml index ee859960c28..b16770af5e8 100644 --- a/contribs/dvrp/pom.xml +++ b/contribs/dvrp/pom.xml @@ -43,7 +43,7 @@ one.util streamex - 0.8.1 + 0.8.2 org.assertj diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileCalculator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileCalculator.java index 510965aec6a..16a8136492c 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileCalculator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileCalculator.java @@ -164,10 +164,9 @@ public TimeDiscretizer getTimeDiscretizer() { private void increment(VehicleState state, double endTime) { Verify.verify(state.taskType != null); Verify.verify(state.occupancy >= 0); + Verify.verify(state.occupancy <= maxCapacity); - boolean servingPassengers = passengerServingTaskTypes.contains(state.taskType); - Verify.verify(servingPassengers || state.occupancy == 0, - "Vehicles not serving passengers must not be occupied"); + boolean servingPassengers = passengerServingTaskTypes.contains(state.taskType) || state.occupancy > 0; double[] profile = servingPassengers ? vehicleOccupancyProfiles.get(state.occupancy) : @@ -182,7 +181,7 @@ private void increment(double[] values, double beginTime, double endTime) { } endTime = Math.min(endTime, analysisEndTime); - int timeInterval = timeDiscretizer.getTimeInterval(); + double timeInterval = timeDiscretizer.getTimeInterval(); int fromIdx = timeDiscretizer.getIdx(beginTime); int toIdx = timeDiscretizer.getIdx(endTime); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileView.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileView.java index 10a225fc680..09de7a82379 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileView.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleOccupancyProfileView.java @@ -70,7 +70,7 @@ public Map seriesPaints() { } @Override - public int[] times() { + public double[] times() { return calculator.getTimeDiscretizer().getTimes(); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileCalculator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileCalculator.java index f9be9fb5c62..673eae38188 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileCalculator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileCalculator.java @@ -127,7 +127,7 @@ private void increment(double[] values, double beginTime, double endTime) { } endTime = Math.min(endTime, analysisEndTime); - int timeInterval = timeDiscretizer.getTimeInterval(); + double timeInterval = timeDiscretizer.getTimeInterval(); int fromIdx = timeDiscretizer.getIdx(beginTime); int toIdx = timeDiscretizer.getIdx(endTime); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileView.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileView.java index 8b8e5aadba8..7bea199d9f6 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileView.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/analysis/VehicleTaskProfileView.java @@ -64,7 +64,7 @@ public Map seriesPaints() { } @Override - public int[] times() { + public double[] times() { return calculator.getTimeDiscretizer().getTimes(); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java index 1bbe061f8d7..ab5916c967e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java @@ -25,6 +25,7 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.contrib.dvrp.fleet.FleetModule; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.DefaultPassengerRequestValidator; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule.PassengerEngineType; @@ -87,6 +88,9 @@ protected void configureQSim() { // converts scheduled tasks into simulated actions (legs and activities) bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class).in(Singleton.class); + + // no advance request + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); } }); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java index fb23bac474d..ee5f38811fe 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java @@ -133,7 +133,7 @@ public void requestSubmitted(Request request) { eventsManager.processEvent( new PassengerRequestScheduledEvent(timer.getTimeOfDay(), TransportMode.taxi, request.getId(), - req.getPassengerId(), vehicle.getId(), t1, t4)); + req.getPassengerIds(), vehicle.getId(), t1, t4)); } @Override diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java index a1d86022e2a..de9f9f0936b 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java @@ -28,6 +28,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequest; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; +import java.util.*; + /** * @author michalm */ @@ -36,18 +38,18 @@ public final class OneTaxiRequest implements PassengerRequest { private final double submissionTime; private final double earliestStartTime; - private final Id passengerId; + private final List> passengerIds = new ArrayList<>(); private final String mode; private final Link fromLink; private final Link toLink; - public OneTaxiRequest(Id id, Id passengerId, String mode, Link fromLink, Link toLink, - double departureTime, double submissionTime) { + public OneTaxiRequest(Id id, Collection> passengerIds, String mode, Link fromLink, Link toLink, + double departureTime, double submissionTime) { this.id = id; this.submissionTime = submissionTime; this.earliestStartTime = departureTime; - this.passengerId = passengerId; + this.passengerIds.addAll(passengerIds); this.mode = mode; this.fromLink = fromLink; this.toLink = toLink; @@ -79,8 +81,8 @@ public Link getToLink() { } @Override - public Id getPassengerId() { - return passengerId; + public List> getPassengerIds() { + return List.copyOf(passengerIds); } @Override @@ -88,11 +90,16 @@ public String getMode() { return mode; } + @Override + public int getPassengerCount() { + return passengerIds.size(); + } + public static final class OneTaxiRequestCreator implements PassengerRequestCreator { @Override - public OneTaxiRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, + public OneTaxiRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, double departureTime, double submissionTime) { - return new OneTaxiRequest(id, passengerId, TransportMode.taxi, fromLink, toLink, departureTime, + return new OneTaxiRequest(id, passengerIds, TransportMode.taxi, fromLink, toLink, departureTime, submissionTime); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiExample.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiExample.java index 686ff8d9713..e151f68821c 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiExample.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiExample.java @@ -42,7 +42,7 @@ public final class RunOneTaxiExample { public static void run(URL configUrl, String taxisFile, boolean otfvis, int lastIteration) { // load config Config config = ConfigUtils.loadConfig(configUrl, new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); // load scenario Scenario scenario = ScenarioUtils.loadScenario(config); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxionetruck/RunOneTaxiOneTruckExample.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxionetruck/RunOneTaxiOneTruckExample.java index 9c3298cde14..4c4232b15b5 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxionetruck/RunOneTaxiOneTruckExample.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxionetruck/RunOneTaxiOneTruckExample.java @@ -45,7 +45,7 @@ public class RunOneTaxiOneTruckExample { public static void run(URL configUrl, String taxisFile, String trucksFile, boolean otfvis, int lastIteration) { // load config Config config = ConfigUtils.loadConfig(configUrl, new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); // load scenario Scenario scenario = ScenarioUtils.loadScenario(config); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetruck/RunOneTruckExample.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetruck/RunOneTruckExample.java index 5f555ce90f7..2bb198672a7 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetruck/RunOneTruckExample.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetruck/RunOneTruckExample.java @@ -41,7 +41,7 @@ public final class RunOneTruckExample { public static void run(URL configUrl, String trucksFile, boolean otfvis, int lastIteration) { // load config Config config = ConfigUtils.loadConfig(configUrl, new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(lastIteration); + config.controller().setLastIteration(lastIteration); // load scenario Scenario scenario = ScenarioUtils.loadScenario(config); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/fleet/FleetReader.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/fleet/FleetReader.java index 38db6d9b1e8..42151f3f956 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/fleet/FleetReader.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/fleet/FleetReader.java @@ -38,6 +38,7 @@ public class FleetReader extends MatsimXmlParser { private final FleetSpecification fleet; public FleetReader(FleetSpecification fleet) { + super(ValidationType.DTD_ONLY); this.fleet = fleet; } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java index e7d8df4a56c..529f2bfc5ba 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java @@ -20,13 +20,15 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; -import org.matsim.api.core.v01.events.HasPersonId; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * This class is designed for inheritance without overriding. @@ -35,19 +37,20 @@ * * @author Michal Maciejewski (michalm) */ -public abstract class AbstractPassengerRequestEvent extends Event implements HasPersonId { +public abstract class AbstractPassengerRequestEvent extends Event { public static final String ATTRIBUTE_MODE = "mode"; public static final String ATTRIBUTE_REQUEST = "request"; + public static final String ATTRIBUTE_PERSON = "person"; private final String mode; private final Id requestId; - private final Id personId; + private final List> personIds; - AbstractPassengerRequestEvent(double time, String mode, Id requestId, Id personId) { + protected AbstractPassengerRequestEvent(double time, String mode, Id requestId, List> personIds) { super(time); this.mode = mode; this.requestId = requestId; - this.personId = personId; + this.personIds = personIds; } public final String getMode() { @@ -62,11 +65,10 @@ public final Id getRequestId() { } /** - * @return id of the passenger (person) + * @return ids of the passengers (persons) */ - @Override - public final Id getPersonId() { - return personId; + public final List> getPersonIds() { + return List.copyOf(personIds); } @Override @@ -74,6 +76,7 @@ public Map getAttributes() { Map attr = super.getAttributes(); attr.put(ATTRIBUTE_MODE, mode); attr.put(ATTRIBUTE_REQUEST, requestId + ""); + attr.put(ATTRIBUTE_PERSON, personIds.stream().map(Object::toString).collect(Collectors.joining(","))); return attr; } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java new file mode 100644 index 00000000000..56426adb184 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java @@ -0,0 +1,13 @@ +package org.matsim.contrib.dvrp.passenger; + +import javax.annotation.Nullable; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.core.mobsim.framework.MobsimAgent; + +public interface AdvanceRequestProvider { + static public AdvanceRequestProvider NONE = (MobsimAgent agent, Leg leg) -> null; + + @Nullable + PassengerRequest retrieveRequest(MobsimAgent agent, Leg leg); +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java index 12600006799..6a19b93d470 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java @@ -19,14 +19,10 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; -import jakarta.inject.Inject; -import jakarta.inject.Provider; - +import com.google.common.base.Verify; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -45,7 +41,9 @@ import org.matsim.core.modal.ModalProviders; import com.google.common.base.Preconditions; -import com.google.common.base.Verify; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; /** * @author Michal Maciejewski (michalm) @@ -54,6 +52,7 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR private final String mode; private final MobsimTimer mobsimTimer; + private final EventsManager eventsManager; private final PassengerRequestCreator requestCreator; private final VrpOptimizer optimizer; @@ -61,24 +60,29 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR private final PassengerRequestValidator requestValidator; private final InternalPassengerHandling internalPassengerHandling; + private final AdvanceRequestProvider advanceRequestProvider; private InternalInterface internalInterface; //accessed in doSimStep() and handleDeparture() (no need to sync) - private final Map, MobsimPassengerAgent> activePassengers = new HashMap<>(); + private final Map, List> activePassengers = new HashMap<>(); + + // holds vehicle stop activities for requests that have not arrived at departure point yet + private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); //accessed in doSimStep() and handleEvent() (potential data races) private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); - DefaultPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, - PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, - PassengerRequestValidator requestValidator) { + DefaultPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, + VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, AdvanceRequestProvider advanceRequestProvider) { this.mode = mode; this.mobsimTimer = mobsimTimer; this.requestCreator = requestCreator; this.optimizer = optimizer; this.network = network; this.requestValidator = requestValidator; + this.eventsManager = eventsManager; + this.advanceRequestProvider = advanceRequestProvider; internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); } @@ -95,12 +99,32 @@ public void onPrepareSim() { @Override public void doSimStep(double time) { - while (!rejectedRequestsEvents.isEmpty()) { - MobsimPassengerAgent passenger = activePassengers.remove(rejectedRequestsEvents.poll().getRequestId()); - //not much else can be done for immediate requests - //set the passenger agent to abort - the event will be thrown by the QSim - passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); - internalInterface.arrangeNextAgentState(passenger); + // If prebooked requests are rejected (by the optimizer, through an + // event) after submission, but before departure, the PassengerEngine does not + // know this agent yet. Hence, we wait with setting the state to abort until the + // agent has arrived here (if ever). + + Iterator iterator = rejectedRequestsEvents.iterator(); + + while (iterator.hasNext()) { + PassengerRequestRejectedEvent event = iterator.next(); + if (event.getTime() == time) { + // There is a potential race condition wrt processing rejection events between doSimStep() and handleEvent(). + // To ensure a deterministic behaviour, we only process events from the previous time step. + break; + } + + List passengers = activePassengers.remove(event.getRequestId()); + + if (passengers != null) { + // not much else can be done for immediate requests + // set the passenger agent to abort - the event will be thrown by the QSim + for (MobsimPassengerAgent passenger: passengers) { + passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); + internalInterface.arrangeNextAgentState(passenger); + } + iterator.remove(); + } } } @@ -119,15 +143,36 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI Id toLinkId = passenger.getDestinationLinkId(); - Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); - PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - passenger.getId(), route, getLink(fromLinkId), getLink(toLinkId), now, now); - validateAndSubmitRequest(passenger, request, now); + // try to find a prebooked requests that is associated to this leg + Leg leg = (Leg)((PlanAgent)passenger).getCurrentPlanElement(); + PassengerRequest request = advanceRequestProvider.retrieveRequest(agent, leg); + + if (request == null) { // immediate request + Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); + request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), + List.of(passenger.getId()), route, getLink(fromLinkId), getLink(toLinkId), now, now); + + // must come before validateAndSubmitRequest (to come before rejection event) + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); + activePassengers.put(request.getId(), List.of(passenger)); + + validateAndSubmitRequest(List.of(passenger), request, now); + } else { // advance request + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); + activePassengers.put(request.getId(), List.of(passenger)); + + PassengerPickupActivity pickupActivity = waitingForPassenger.remove(request.getId()); + if (pickupActivity != null) { + // the vehicle is already waiting for the request, notify it + pickupActivity.notifyPassengersAreReadyForDeparture(List.of(passenger), now); + } + } + return true; } - private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerRequest request, double now) { - activePassengers.put(request.getId(), passenger); + private void validateAndSubmitRequest(List passengers, PassengerRequest request, double now) { + activePassengers.put(request.getId(), passengers); if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { //need to synchronise to address cases where requestSubmitted() may: // - be called from outside DepartureHandlers @@ -143,22 +188,49 @@ private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerR private Link getLink(Id linkId) { return Preconditions.checkNotNull(network.getLinks().get(linkId), - "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", - linkId, mode); + "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", linkId, mode); + } + + /** + * There are two ways of interacting with the PassengerEngine: + *

+ * - (1) The stop activity tries to pick up a passenger and receives whether the + * pickup succeeded or not (see tryPickUpPassenger). In the classic + * implementation, the vehicle only calls tryPickUpPassenger at the time when it + * actually wants to pick up the person (at the end of the activity). It may + * happen that the person is not present yet. In that case, the pickup request + * is saved and notifyPassengerReady is called on the stop activity upen + * departure of the agent. + *

+ * - (2) If pickup and dropoff times are handled more flexibly by the stop + * activity, it might want to detect whether an agent is ready to be picked up, + * then start an "interaction time" and only after perform the actual pickup. + * For that purpose, we have queryPickUpPassenger, which indicates whether the + * agent is already there, and, if not, makes sure that the stop activity is + * notified once the agent arrives for departure. + */ + @Override + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + if (!activePassengers.containsKey(requestId)) { + waitingForPassenger.put(requestId, pickupActivity); + return false; + } + + return true; } @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { - boolean pickedUp = internalPassengerHandling.tryPickUpPassenger(driver, activePassengers.get(requestId), + boolean pickedUp = internalPassengerHandling.tryPickUpPassengers(driver, activePassengers.get(requestId), requestId, now); Verify.verify(pickedUp, "Not possible without prebooking"); return pickedUp; } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassenger(driver, activePassengers.remove(requestId), requestId, now); + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, activePassengers.remove(requestId), requestId, now); } @Override @@ -178,9 +250,9 @@ public static Provider createProvider(String mode) { @Override public DefaultPassengerEngine get() { - return new DefaultPassengerEngine(getMode(), eventsManager, mobsimTimer, - getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), - getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class)); + return new DefaultPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), + getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), + getModalInstance(AdvanceRequestProvider.class)); } }; } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java new file mode 100644 index 00000000000..a5c2f9e9c84 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java @@ -0,0 +1,231 @@ +package org.matsim.contrib.dvrp.passenger; + +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Route; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.mobsim.framework.*; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.modal.ModalProviders; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; + +public class GroupPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler { + + private final String mode; + private final MobsimTimer mobsimTimer; + + private final EventsManager eventsManager; + + private final PassengerRequestCreator requestCreator; + private final VrpOptimizer optimizer; + private final Network network; + private final PassengerRequestValidator requestValidator; + + private final InternalPassengerHandling internalPassengerHandling; + + private InternalInterface internalInterface; + + + //accessed in doSimStep() and handleDeparture() (no need to sync) + private final Map, List> activePassengers = new HashMap<>(); + + // holds vehicle stop activities for requests that have not arrived at departure point yet + private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); + + //accessed in doSimStep() and handleEvent() (potential data races) + private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); + + private final Set currentDepartures = new LinkedHashSet<>(); + + private final PassengerGroupIdentifier groupIdentifier; + + GroupPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, PassengerGroupIdentifier groupIdentifier) { + this.mode = mode; + this.eventsManager = eventsManager; + this.mobsimTimer = mobsimTimer; + this.requestCreator = requestCreator; + this.optimizer = optimizer; + this.network = network; + this.requestValidator = requestValidator; + this.groupIdentifier = groupIdentifier; + + internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); + } + + @Override + public void setInternalInterface(InternalInterface internalInterface) { + this.internalInterface = internalInterface; + internalPassengerHandling.setInternalInterface(internalInterface); + } + + @Override + public void onPrepareSim() { + } + + @Override + public void doSimStep(double time) { + handleDepartures(time); + while (!rejectedRequestsEvents.isEmpty()) { + List passengers = activePassengers.remove(rejectedRequestsEvents.poll().getRequestId()); + //not much else can be done for immediate requests + //set the passenger agent to abort - the event will be thrown by the QSim + for (MobsimPassengerAgent passenger : passengers) { + passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); + internalInterface.arrangeNextAgentState(passenger); + } + } + } + + @Override + public void afterSim() { + } + + @Override + public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { + + if (!agent.getMode().equals(mode)) { + return false; + } + + MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; + internalInterface.registerAdditionalAgentOnLink(passenger); + currentDepartures.add(passenger); + return true; + } + + private void handleDepartures(double now) { + Map, List> agentGroups = + currentDepartures.stream().collect( + Collectors.groupingBy( + groupIdentifier, + Collectors.collectingAndThen(Collectors.toList(), list -> { + list.sort(Comparator.comparing(MobsimPassengerAgent::getId)); + return list; + }))); + + for (List group : agentGroups.values()) { + + Iterator iterator = group.iterator(); + MobsimAgent firstAgent = iterator.next(); + MobsimPassengerAgent passenger = (MobsimPassengerAgent) firstAgent; + + Id toLinkId = firstAgent.getDestinationLinkId(); + + while (iterator.hasNext()) { + MobsimAgent next = iterator.next(); + Gbl.assertIf(firstAgent.getCurrentLinkId().equals(next.getCurrentLinkId())); + Gbl.assertIf(toLinkId.equals(next.getDestinationLinkId())); + } + + Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); + + PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), + group.stream().map(Identifiable::getId).toList(), route, + getLink(firstAgent.getCurrentLinkId()), getLink(toLinkId), + now, now); + + + // must come before validateAndSubmitRequest (to come before rejection event) + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), group.stream().map(Identifiable::getId).toList())); + + validateAndSubmitRequest(group, request, mobsimTimer.getTimeOfDay()); + } + currentDepartures.clear(); + } + + private void validateAndSubmitRequest(List passengers, PassengerRequest request, double now) { + activePassengers.put(request.getId(), passengers); + if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { + //need to synchronise to address cases where requestSubmitted() may: + // - be called from outside DepartureHandlers + // - interfere with VrpOptimizer.nextTask() + // - impact VrpAgentLogic.computeNextAction() + synchronized (optimizer) { + //optimizer can also reject request if cannot handle it + // (async operation, notification comes via the events channel) + optimizer.requestSubmitted(request); + } + } + } + + private Link getLink(Id linkId) { + return Preconditions.checkNotNull(network.getLinks().get(linkId), "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", linkId, mode); + } + + /** + * There are two ways of interacting with the PassengerEngine: + *

+ * - (1) The stop activity tries to pick up a passenger and receives whether the + * pickup succeeded or not (see tryPickUpPassenger). In the classic + * implementation, the vehicle only calls tryPickUpPassenger at the time when it + * actually wants to pick up the person (at the end of the activity). It may + * happen that the person is not present yet. In that case, the pickup request + * is saved and notifyPassengerReady is called on the stop activity upen + * departure of the agent. + *

+ * - (2) If pickup and dropoff times are handled more flexibly by the stop + * activity, it might want to detect whether an agent is ready to be picked up, + * then start an "interaction time" and only after perform the actual pickup. + * For that purpose, we have queryPickUpPassenger, which indicates whether the + * agent is already there, and, if not, makes sure that the stop activity is + * notified once the agent arrives for departure. + */ + @Override + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + + if (!activePassengers.containsKey(requestId)) { + waitingForPassenger.put(requestId, pickupActivity); + return false; + } + + return true; + } + + @Override + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { + boolean pickedUp = internalPassengerHandling.tryPickUpPassengers(driver, activePassengers.get(requestId), requestId, now); + Verify.verify(pickedUp, "Not possible without prebooking"); + return pickedUp; + } + + @Override + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, activePassengers.remove(requestId), requestId, now); + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (event.getMode().equals(mode)) { + rejectedRequestsEvents.add(event); + } + } + + public static Provider createProvider(String mode) { + return new ModalProviders.AbstractProvider<>(mode, DvrpModes::mode) { + @Inject + private EventsManager eventsManager; + + @Inject + private MobsimTimer mobsimTimer; + + @Override + public GroupPassengerEngine get() { + return new GroupPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), getModalInstance(PassengerGroupIdentifier.class)); + } + }; + } +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java index 67da85c61a7..c36393d16f4 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java @@ -22,6 +22,7 @@ import static java.lang.String.format; +import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -70,39 +71,50 @@ boolean validateRequest(PassengerRequest request, PassengerRequestValidator requ LOGGER.warn(format("Request: %s of mode: %s will not be served. Agent will get stuck. Cause: %s", request.getId(), mode, cause)); eventsManager.processEvent( - new PassengerRequestRejectedEvent(now, mode, request.getId(), request.getPassengerId(), cause)); + new PassengerRequestRejectedEvent(now, mode, request.getId(), request.getPassengerIds(), cause)); } return violations.isEmpty(); } - boolean tryPickUpPassenger(MobsimDriverAgent driver, MobsimPassengerAgent passenger, Id requestId, + boolean tryPickUpPassengers(MobsimDriverAgent driver, List passengers, Id requestId, double now) { - if (internalInterface.unregisterAdditionalAgentOnLink(passenger.getId(), driver.getCurrentLinkId()) == null) { - //only possible with prebooking - return false; + + //ensure for every passenger first + for (MobsimPassengerAgent passenger : passengers) { + if (internalInterface.unregisterAdditionalAgentOnLink(passenger.getId(), driver.getCurrentLinkId()) == null) { + //only possible with prebooking + return false; + } } - MobsimVehicle mobVehicle = driver.getVehicle(); - mobVehicle.addPassenger(passenger); - passenger.setVehicle(mobVehicle); + for (MobsimPassengerAgent passenger : passengers) { + + MobsimVehicle mobVehicle = driver.getVehicle(); + mobVehicle.addPassenger(passenger); + passenger.setVehicle(mobVehicle); + + Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); + eventsManager.processEvent(new PersonEntersVehicleEvent(now, passenger.getId(), mobVehicle.getId())); + eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, requestId, passenger.getId(), vehicleId)); + } - Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); - eventsManager.processEvent(new PersonEntersVehicleEvent(now, passenger.getId(), mobVehicle.getId())); - eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, requestId, passenger.getId(), vehicleId)); return true; } - void dropOffPassenger(MobsimDriverAgent driver, MobsimPassengerAgent passenger, Id requestId, double now) { + void dropOffPassengers(MobsimDriverAgent driver, List passengers, Id requestId, double now) { MobsimVehicle mobVehicle = driver.getVehicle(); - mobVehicle.removePassenger(passenger); - passenger.setVehicle(null); - Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); - eventsManager.processEvent(new PassengerDroppedOffEvent(now, mode, requestId, passenger.getId(), vehicleId)); - eventsManager.processEvent(new PersonLeavesVehicleEvent(now, passenger.getId(), mobVehicle.getId())); - passenger.notifyArrivalOnLinkByNonNetworkMode(passenger.getDestinationLinkId()); - passenger.endLegAndComputeNextState(now); - internalInterface.arrangeNextAgentState(passenger); + for (MobsimPassengerAgent passenger : passengers) { + mobVehicle.removePassenger(passenger); + passenger.setVehicle(null); + + eventsManager.processEvent(new PassengerDroppedOffEvent(now, mode, requestId, passenger.getId(), vehicleId)); + eventsManager.processEvent(new PersonLeavesVehicleEvent(now, passenger.getId(), mobVehicle.getId())); + + passenger.notifyArrivalOnLinkByNonNetworkMode(passenger.getDestinationLinkId()); + passenger.endLegAndComputeNextState(now); + internalInterface.arrangeNextAgentState(passenger); + } } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java index e93a08f840b..7d157e4c798 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java @@ -54,7 +54,7 @@ protected boolean isLastStep(double now) { protected void afterLastStep(double now) { // dropoff at the end of stop activity for (PassengerRequest request : requests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java index 8d7bcb2da96..badc060b37e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java @@ -19,9 +19,13 @@ package org.matsim.contrib.dvrp.passenger; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.schedule.StayTask; @@ -35,7 +39,7 @@ public class MultiPassengerPickupActivity extends FirstLastSimStepDynActivity im private final Map, ? extends PassengerRequest> requests; private final double expectedEndTime; - private int passengersPickedUp = 0; + private int requestsPickedUp = 0; public MultiPassengerPickupActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask pickupTask, Map, ? extends PassengerRequest> requests, String activityType) { @@ -49,32 +53,32 @@ public MultiPassengerPickupActivity(PassengerHandler passengerHandler, DynAgent @Override protected boolean isLastStep(double now) { - return passengersPickedUp == requests.size() && now >= expectedEndTime; + return requestsPickedUp == requests.size() && now >= expectedEndTime; } @Override protected void beforeFirstStep(double now) { for (PassengerRequest request : requests.values()) { - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { - passengersPickedUp++; + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { + requestsPickedUp++; } } } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - PassengerRequest request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { - passengersPickedUp++; + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + PassengerRequest request = getRequestForPassenger(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { + requestsPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } - private PassengerRequest getRequestForPassenger(Id passengerId) { + private PassengerRequest getRequestForPassenger(List> passengerIds) { return requests.values() .stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java index 6f9517c5153..e9d806af179 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java @@ -10,7 +10,7 @@ */ public class PassengerEngineQSimModule extends AbstractDvrpModeQSimModule { public enum PassengerEngineType { - DEFAULT, WITH_PREBOOKING, TELEPORTING + DEFAULT, WITH_PREBOOKING, TELEPORTING, WITH_GROUPS } private final PassengerEngineType type; @@ -44,6 +44,10 @@ protected void configureQSim() { addModalComponent( PassengerEngine.class, TeleportingPassengerEngine.createProvider( getMode() ) ); return; } + case WITH_GROUPS -> { + addModalComponent( PassengerEngine.class, GroupPassengerEngine.createProvider( getMode() ) ); + return; + } default -> throw new IllegalStateException( "Type: " + type + " is not supported" ); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java index 3e6fb064d00..dc7939b34df 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java @@ -19,12 +19,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; @@ -62,6 +57,7 @@ public final class PassengerEngineWithPrebooking private final String mode; private final MobsimTimer mobsimTimer; + private final EventsManager eventsManager; private final PreplanningEngine preplanningEngine; private final PassengerRequestCreator requestCreator; @@ -87,6 +83,7 @@ public final class PassengerEngineWithPrebooking this.optimizer = optimizer; this.network = network; this.requestValidator = requestValidator; + this.eventsManager = eventsManager; internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); } @@ -137,11 +134,12 @@ public void bookTrip(MobsimPassengerAgent passenger, TripInfoWithRequiredBooking double now = mobsimTimer.getTimeOfDay(); //TODO have a separate request creator for prebooking (accept TripInfo instead of Route) PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - passenger.getId(), tripInfo.getOriginalRequest().getPlannedRoute(), + List.of(passenger.getId()), tripInfo.getOriginalRequest().getPlannedRoute(), getLink(tripInfo.getPickupLocation().getLinkId()), getLink(tripInfo.getDropoffLocation().getLinkId()), tripInfo.getExpectedBoardingTime(), now); validateAndSubmitRequest(passenger, request, tripInfo.getOriginalRequest(), now); - advanceRequests.put(request.getPassengerId(), request); + // hard assumption that with this engine, passenger ids is always a singleton. nkuehnel oct '23 + advanceRequests.put(request.getPassengerIds().stream().findFirst().orElseThrow(), request); } private Link getLink(Id linkId) { return Preconditions.checkNotNull(network.getLinks().get(linkId), @@ -164,10 +162,14 @@ private Link getLink(Id linkId) { //TODO what if it was already rejected while prebooking?? PassengerRequest prebookedRequest = prebookedRequests.get(0); + + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, prebookedRequest.getId(), prebookedRequest.getPassengerIds())); + PassengerPickupActivity awaitingPickup = awaitingPickups.remove(prebookedRequest.getId()); if (awaitingPickup != null) { - awaitingPickup.notifyPassengerIsReadyForDeparture(passenger, now); + awaitingPickup.notifyPassengersAreReadyForDeparture(List.of(passenger), now); } + return true; } @@ -197,7 +199,7 @@ private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerR // ================ PICKUP / DROPOFF @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { Id linkId = driver.getCurrentLinkId(); RequestEntry requestEntry = activeRequests.get(requestId); @@ -210,7 +212,7 @@ public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, Mobsim return false;// wait for the passenger } - if (!internalPassengerHandling.tryPickUpPassenger(driver, passenger, requestId, now)) { + if (!internalPassengerHandling.tryPickUpPassengers(driver, List.of(passenger), requestId, now)) { // the passenger has already been picked up and is on another taxi trip // seems there have been at least 2 requests made by this passenger for this location awaitingPickups.put(requestId, pickupActivity); @@ -221,8 +223,8 @@ public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, Mobsim } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassenger(driver, activeRequests.remove(requestId).passenger, requestId, now); + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, List.of(activeRequests.remove(requestId).passenger), requestId, now); } // ================ REJECTED/SCHEDULED EVENTS @@ -294,4 +296,25 @@ public static Provider createProvider(String mode) { } }; } + + /* + * This method has been retrofitted with the new prebooking implementation in + * Nov 2023. Not sure if this is the right way to do it, this class doesn't seem + * to be tested anywhere. /sebhoerl + */ + @Override + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + Id linkId = driver.getCurrentLinkId(); + RequestEntry requestEntry = activeRequests.get(requestId); + MobsimPassengerAgent passenger = requestEntry.passenger; + + if (passenger.getCurrentLinkId() != linkId + || passenger.getState() != MobsimAgent.State.LEG + || !passenger.getMode().equals(mode)) { + awaitingPickups.put(requestId, pickupActivity); + return false;// wait for the passenger + } + + return true; // passenger present? + } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java new file mode 100644 index 00000000000..8dba2bdac52 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java @@ -0,0 +1,21 @@ +package org.matsim.contrib.dvrp.passenger; + +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimPassengerAgent; + +import java.util.function.Function; + +/** + * Provides a method to identify the passenger group id of an agent. + * @author nkuehnel / MOIA + */ +public interface PassengerGroupIdentifier extends Function> { + + class PassengerGroup { + private PassengerGroup(){} + } + + @Override + Id apply(MobsimPassengerAgent agent); + +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java index 1a9e313731c..06912f29ae2 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java @@ -30,8 +30,10 @@ * This looks quite general. But as of now is a dvrp thing. kai, apr'23 */ public interface PassengerHandler { - boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, + boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId); + + boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now); - void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now); + void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java index a083ee9ce18..b8aa5cd611e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java @@ -22,6 +22,9 @@ import org.matsim.contrib.dynagent.DynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; +import java.util.Set; + public interface PassengerPickupActivity extends DynActivity { - void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now); + void notifyPassengersAreReadyForDeparture(List passengers, double now); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java index b3378b28f14..e6cbca39641 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java @@ -24,6 +24,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import java.util.List; + public interface PassengerRequest extends Request { /** * @return beginning of the time window (inclusive) - earliest time when the passenger can be picked up @@ -41,7 +43,9 @@ default double getLatestStartTime() { Link getToLink(); - Id getPassengerId(); + List> getPassengerIds(); String getMode(); + + int getPassengerCount(); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java index 9423cfd113b..15d4fe0ddde 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java @@ -25,6 +25,8 @@ import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; +import java.util.List; + /** * @author michalm */ @@ -34,7 +36,7 @@ public interface PassengerRequestCreator { * Prefer stateless implementation, otherwise provide other ways to achieve thread-safety. * * @param id request ID - * @param passengerId passenger ID + * @param passengerIds list of unique passenger IDs * @param route planned route (the required route type depends on the optimizer) * @param fromLink start location * @param toLink end location @@ -42,6 +44,6 @@ public interface PassengerRequestCreator { * @param submissionTime time at which request was submitted * @return */ - PassengerRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, Link toLink, - double departureTime, double submissionTime); + PassengerRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, + double departureTime, double submissionTime); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java index fbe61390f34..0d17138700d 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java @@ -20,14 +20,15 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -38,9 +39,9 @@ public class PassengerRequestRejectedEvent extends AbstractPassengerRequestEvent private final String cause; - public PassengerRequestRejectedEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestRejectedEvent(double time, String mode, Id requestId, List> personIds, String cause) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.cause = cause; } @@ -65,8 +66,11 @@ public static PassengerRequestRejectedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); - String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); - return new PassengerRequestRejectedEvent(time, mode, requestId, personId, cause); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); + return new PassengerRequestRejectedEvent(time, mode, requestId, personIds, cause); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java index 9de0b19b344..413b668db89 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java @@ -20,8 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; @@ -29,6 +28,8 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -46,9 +47,9 @@ public class PassengerRequestScheduledEvent extends AbstractPassengerRequestEven /** * An event processed upon request submission. */ - public PassengerRequestScheduledEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestScheduledEvent(double time, String mode, Id requestId, List> personIds, Id vehicleId, double pickupTime, double dropoffTime) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.vehicleId = vehicleId; this.pickupTime = pickupTime; this.dropoffTime = dropoffTime; @@ -94,10 +95,14 @@ public static PassengerRequestScheduledEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } Id vehicleId = Id.create(attributes.get(ATTRIBUTE_VEHICLE), DvrpVehicle.class); double pickupTime = Double.parseDouble(attributes.get(ATTRIBUTE_PICKUP_TIME)); double dropoffTime = Double.parseDouble(attributes.get(ATTRIBUTE_DROPOFF_TIME)); - return new PassengerRequestScheduledEvent(time, mode, requestId, personId, vehicleId, pickupTime, dropoffTime); + return new PassengerRequestScheduledEvent(time, mode, requestId, personIds, vehicleId, pickupTime, dropoffTime); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java index 73dd882740c..0b7981bd634 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java @@ -20,8 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; @@ -29,6 +28,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -41,9 +42,9 @@ public class PassengerRequestSubmittedEvent extends AbstractPassengerRequestEven private final Id fromLinkId; private final Id toLinkId; - public PassengerRequestSubmittedEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestSubmittedEvent(double time, String mode, Id requestId, List> personIds, Id fromLinkId, Id toLinkId) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.fromLinkId = fromLinkId; this.toLinkId = toLinkId; } @@ -80,9 +81,14 @@ public static PassengerRequestSubmittedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } + Id fromLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_FROM_LINK)); Id toLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_TO_LINK)); - return new PassengerRequestSubmittedEvent(time, mode, requestId, personId, fromLinkId, toLinkId); + return new PassengerRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java new file mode 100644 index 00000000000..db9a477954e --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java @@ -0,0 +1,59 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2017 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.dvrp.passenger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.GenericEvent; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.optimizer.Request; + +/** + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class PassengerWaitingEvent extends AbstractPassengerRequestEvent { + public static final String EVENT_TYPE = "passenger waiting"; + + public PassengerWaitingEvent(double time, String mode, Id requestId, List> personIds) { + super(time, mode, requestId, personIds); + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + public static PassengerWaitingEvent convert(GenericEvent event) { + Map attributes = event.getAttributes(); + double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); + String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); + Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } + return new PassengerWaitingEvent(time, mode, requestId, personIds); + } +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEventHandler.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEventHandler.java new file mode 100644 index 00000000000..1ca52e48d2f --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEventHandler.java @@ -0,0 +1,29 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2017 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.dvrp.passenger; + +import org.matsim.core.events.handler.EventHandler; + +/** + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public interface PassengerWaitingEventHandler extends EventHandler { + void handleEvent(final PassengerWaitingEvent event); +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/RequestQueue.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/RequestQueue.java deleted file mode 100644 index 14859041fe1..00000000000 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/RequestQueue.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * *********************************************************************** * - * project: org.matsim.* - * *********************************************************************** * - * * - * copyright : (C) 2020 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.dvrp.passenger; - -import java.util.Collection; -import java.util.Comparator; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.TreeSet; - -import org.matsim.contrib.dvrp.optimizer.Request; - -/** - * @author Michal Maciejewski (michalm) - */ -public final class RequestQueue { - public static RequestQueue withLimitedAdvanceRequestPlanningHorizon( - double planningHorizon) { - //all immediate and selected advance (i.e. starting within the planning horizon) requests are scheduled - return new RequestQueue<>(planningHorizon); - } - - public static RequestQueue withInfiniteAdvanceRequestPlanningHorizon() { - return new RequestQueue<>(Double.POSITIVE_INFINITY);//all immediate and advance requests are scheduled - } - - public static RequestQueue withNoAdvanceRequestPlanningHorizon() { - return new RequestQueue<>(0);//immediate requests only - } - - private static final Comparator ABSOLUTE_COMPARATOR = Comparator.comparing( - PassengerRequest::getEarliestStartTime) - .thenComparing(PassengerRequest::getLatestStartTime) - .thenComparing(Request::getSubmissionTime) - .thenComparing(Request::getId); - - //all requests in the planning horizon (also includes old requests: never scheduled or unscheduled) - private final Collection schedulableRequests = new TreeSet<>(ABSOLUTE_COMPARATOR); - - //advance requests that are not in the planning horizon - private final Queue postponedRequests = new PriorityQueue<>(ABSOLUTE_COMPARATOR); - - private final double planningHorizon; - - private double lastTimeStep = -Double.MAX_VALUE; - - private RequestQueue(double planningHorizon) { - this.planningHorizon = planningHorizon; - } - - public void updateQueuesOnNextTimeSteps(double currentTime) { - lastTimeStep = currentTime; - while (!postponedRequests.isEmpty() && isSchedulable(postponedRequests.peek())) { - schedulableRequests.add(postponedRequests.poll()); - } - } - - public void addRequest(R request) { - (isSchedulable(request) ? schedulableRequests : postponedRequests).add(request); - } - - private boolean isSchedulable(R request) { - return request.getEarliestStartTime() <= lastTimeStep + planningHorizon; - } - - /** - * Assumes external code can modify schedulableRequests (e.g. remove scheduled requests and add unscheduled ones) - * - * @return requests to be inserted into vehicle schedules - */ - public Collection getSchedulableRequests() { - return schedulableRequests; - } -} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java index 5d74a5ed024..afab824bd96 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java @@ -48,6 +48,6 @@ protected boolean isLastStep(double now) { @Override protected void afterLastStep(double now) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java index 33883acfcd6..3f0ee34bb62 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java @@ -19,18 +19,23 @@ package org.matsim.contrib.dvrp.passenger; +import org.matsim.api.core.v01.Identifiable; import org.matsim.contrib.dvrp.schedule.StayTask; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + public class SinglePassengerPickupActivity extends FirstLastSimStepDynActivity implements PassengerPickupActivity { private final PassengerHandler passengerHandler; private final DynAgent driver; private final PassengerRequest request; private final double expectedEndTime; - private boolean passengerAboard = false; + private boolean passengersAboard = false; public SinglePassengerPickupActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask pickupTask, PassengerRequest request, String activityType) { @@ -44,22 +49,22 @@ public SinglePassengerPickupActivity(PassengerHandler passengerHandler, DynAgent @Override protected boolean isLastStep(double now) { - return passengerAboard && now >= expectedEndTime; + return passengersAboard && now >= expectedEndTime; } @Override protected void beforeFirstStep(double now) { - passengerAboard = passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now); + passengersAboard = passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now); } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - if (passenger.getId().equals(request.getPassengerId())) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + if (request.getPassengerIds().containsAll(passengers.stream().map(Identifiable::getId).toList())) { throw new IllegalArgumentException("I am waiting for a different passenger!"); } - passengerAboard = passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now); - if (!passengerAboard) { + passengersAboard = passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now); + if (!passengersAboard) { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java index 0906bed9e71..1f773a77043 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java @@ -20,10 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Collection; -import java.util.Comparator; -import java.util.PriorityQueue; -import java.util.Queue; +import java.util.*; import jakarta.inject.Inject; import jakarta.inject.Provider; @@ -35,6 +32,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.run.DvrpModes; @@ -120,8 +118,10 @@ public void doSimStep(double time) { //first process passenger dropoff events while (!teleportedRequests.isEmpty() && teleportedRequests.peek().getLeft() <= time) { PassengerRequest request = teleportedRequests.poll().getRight(); - eventsManager.processEvent( - new PassengerDroppedOffEvent(time, mode, request.getId(), request.getPassengerId(), null)); + for (Id passenger : request.getPassengerIds()) { + eventsManager.processEvent( + new PassengerDroppedOffEvent(time, mode, request.getId(), passenger, null)); + } } //then end teleported rides @@ -143,10 +143,12 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI Id toLinkId = passenger.getDestinationLinkId(); Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - passenger.getId(), route, getLink(fromLinkId), getLink(toLinkId), now, now); + List.of(passenger.getId()), route, getLink(fromLinkId), getLink(toLinkId), now, now); + + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerIds())); if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { - Route teleportedRoute = adaptLegRouteForTeleportation(passenger, request, now); + Route teleportedRoute = adaptLegRouteForTeleportation(List.of(passenger), request, now); eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, request.getId(), passenger.getId(), null)); teleportationEngine.handleDeparture(now, passenger, fromLinkId); teleportedRequests.add(ImmutablePair.of(now + teleportedRoute.getTravelTime().seconds(), request)); @@ -160,20 +162,22 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI return true; } - private Route adaptLegRouteForTeleportation(MobsimPassengerAgent passenger, PassengerRequest request, double now) { + private Route adaptLegRouteForTeleportation(List passengers, PassengerRequest request, double now) { Route teleportedRoute = teleportedRouteCalculator.calculateRoute(request); - Leg leg = (Leg)WithinDayAgentUtils.getCurrentPlanElement(passenger);//side effect: makes the plan modifiable - Route originalRoute = leg.getRoute(); - Verify.verify(originalRoute.getStartLinkId().equals(teleportedRoute.getStartLinkId())); - Verify.verify(originalRoute.getEndLinkId().equals(teleportedRoute.getEndLinkId())); - Verify.verify(teleportedRoute.getTravelTime().isDefined()); + for (MobsimPassengerAgent passenger : passengers) { + Leg leg = (Leg)WithinDayAgentUtils.getCurrentPlanElement(passenger);//side effect: makes the plan modifiable + Route originalRoute = leg.getRoute(); + Verify.verify(originalRoute.getStartLinkId().equals(teleportedRoute.getStartLinkId())); + Verify.verify(originalRoute.getEndLinkId().equals(teleportedRoute.getEndLinkId())); + Verify.verify(teleportedRoute.getTravelTime().isDefined()); - leg.getAttributes().putAttribute(ORIGINAL_ROUTE_ATTRIBUTE, originalRoute); - leg.setRoute(teleportedRoute); + leg.getAttributes().putAttribute(ORIGINAL_ROUTE_ATTRIBUTE, originalRoute); + leg.setRoute(teleportedRoute); + } eventsManager.processEvent(new PassengerRequestScheduledEvent(mobsimTimer.getTimeOfDay(), mode, request.getId(), - request.getPassengerId(), null, now, now + teleportedRoute.getTravelTime().seconds())); + request.getPassengerIds(), null, now, now + teleportedRoute.getTravelTime().seconds())); return teleportedRoute; } @@ -184,13 +188,19 @@ private Link getLink(Id linkId) { } @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + Id requestId) { + throw new UnsupportedOperationException("No notifying when teleporting"); + } + + @Override + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { throw new UnsupportedOperationException("No picking-up when teleporting"); } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { throw new UnsupportedOperationException("No dropping-off when teleporting"); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathCalculator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathCalculator.java index 56154d13198..6ce7088c2c8 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathCalculator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathCalculator.java @@ -24,27 +24,32 @@ import static org.matsim.contrib.dvrp.path.LeastCostPathTreeStopCriteria.allEndNodesReached; import static org.matsim.contrib.dvrp.path.LeastCostPathTreeStopCriteria.withMaxTravelTime; import static org.matsim.contrib.dvrp.path.VrpPaths.FIRST_LINK_TT; -import static org.matsim.core.router.util.LeastCostPathCalculator.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Supplier; import javax.annotation.Nullable; +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.IdMap; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Node; import org.matsim.contrib.dvrp.path.OneToManyPathSearch.PathData; import org.matsim.core.router.speedy.LeastCostPathTree; +import org.matsim.core.router.util.LeastCostPathCalculator.Path; import org.matsim.core.router.util.TravelTime; import org.matsim.core.utils.misc.OptionalTime; /** * @author Michal Maciejewski (michalm) + * @author Sebastian Hörl, IRT SystemX (sebhoerl) */ class OneToManyPathCalculator { private final IdMap nodeMap; @@ -62,6 +67,8 @@ class OneToManyPathCalculator { this.forwardSearch = forwardSearch; this.fromLink = fromLink; this.startTime = startTime; + + verifyParallelLinks(); } void calculateDijkstraTree(Collection toLinks) { @@ -158,6 +165,7 @@ private List constructLinkSequence(List nodes) { for (Link link : prevNode.getOutLinks().values()) { //FIXME this method will not work properly if there are many prevNode -> nextNode links //TODO save link idx in tree OR pre-check: at most 1 arc per each node pair OR choose faster/better link + // sh, 26/07/2023, added a check further below to increase awareness if (link.getToNode() == nextNode) { links.add(link); break; @@ -182,4 +190,31 @@ private double getFirstAndLastLinkTT(Link fromLink, Link toLink, double pathTrav VrpPaths.getLastLinkTT(travelTime, fromLink, time); return FIRST_LINK_TT + lastLinkTT; } + + private final static Logger logger = LogManager.getLogger(OneToManyPathCalculator.class); + private static int parallelLinksWarningCount = 0; + + private void verifyParallelLinks() { + if (parallelLinksWarningCount < 20) { + for (Node prevNode : nodeMap.values()) { + Set candidates = new HashSet<>(); + + for (Link link : prevNode.getOutLinks().values()) { + if (!candidates.add(link.getToNode().getId().index())) { + logger.warn( + "Found parallel links between nodes {} and {}. This may lead to problems in path calculation.", + prevNode.getId().toString(), link.getToNode().getId().toString()); + + if (parallelLinksWarningCount > 20) { + logger.warn("Consider using NetworkSegmentDoubleLinks.run on your network"); + logger.warn("Only showing 20 of these warnings ..."); + return; + } + + parallelLinksWarningCount++; + } + } + } + } + } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathSearch.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathSearch.java index cdcf3c879b8..41859e240cc 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathSearch.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathSearch.java @@ -61,7 +61,7 @@ private PathData(double travelTime, double travelCost) { public PathData(Path path, double firstAndLastLinkTT) { this.pathSupplier = null; - this.path = new Path(null, ImmutableList.copyOf(path.links), path.travelTime, path.travelCost); + this.path = new Path(path.nodes!= null ? ImmutableList.copyOf(path.nodes) : null, ImmutableList.copyOf(path.links), path.travelTime, path.travelCost); this.travelTime = path.travelTime + firstAndLastLinkTT; } @@ -75,7 +75,7 @@ public double getTravelTime() { } //package visibility only (path.nodes is null) - Path getPath() { + public Path getPath() { if (path == null) { path = pathSupplier.get(); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java index cc4b609e6cc..6d0b68cb400 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/run/DvrpConfigGroup.java @@ -143,7 +143,7 @@ protected void checkConsistency(Config config) { if (config.qsim().isRemoveStuckVehicles()) { throw new RuntimeException("Stuck DynAgents cannot be removed from simulation"); } - if (!config.parallelEventHandling().getSynchronizeOnSimSteps()) { + if (!config.eventsManager().getSynchronizeOnSimSteps()) { throw new RuntimeException("Synchronization on sim steps is required"); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java index 252c5f872b1..33b22634347 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimeEstimator.java @@ -35,6 +35,7 @@ import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent; import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener; import org.matsim.core.router.util.TravelTime; +import org.matsim.core.trafficmonitoring.TimeBinUtils; import org.matsim.vehicles.Vehicle; import com.google.inject.Inject; @@ -57,7 +58,7 @@ public class DvrpOfflineTravelTimeEstimator private final TimeDiscretizer timeDiscretizer; private final int intervalCount; - private final int timeInterval; + private final double timeInterval; private final double[][] linkTravelTimes; private final double alpha; @@ -103,15 +104,7 @@ public double getLinkTravelTime(Link link, double time, Person person, Vehicle v } private int getIdx(double time) { - //handle negative times (e.g. in backward shortest path search) - if (time < 0) { - return 0; - } - int idx = (int)time / timeInterval;// rounding down - if (idx < intervalCount) { - return idx; - } - return intervalCount - 1; + return TimeBinUtils.getTimeBinIndex(time, timeInterval, intervalCount); } @Override diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimes.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimes.java index 78fa39ed528..67882267db2 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimes.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimes.java @@ -58,7 +58,7 @@ public static void saveLinkTravelTimes(TimeDiscretizer timeDiscretizer, double[] //header row writer.append("linkId" + DELIMITER); for (int i = 0; i < intervalCount; i++) { - int time = i * timeDiscretizer.getTimeInterval(); + double time = i * timeDiscretizer.getTimeInterval(); writer.append(time + DELIMITER); } writer.append('\n'); @@ -118,7 +118,7 @@ public static double[][] loadLinkTravelTimes(TimeDiscretizer timeDiscretizer, Bu String[] headerLine = reader.readLine().split(";"); verify(timeDiscretizer.getIntervalCount() == headerLine.length - 1); verify(headerLine[0].equals("linkId")); - timeDiscretizer.forEach((bin, time) -> verify(Integer.parseInt(headerLine[bin + 1]) == time)); + timeDiscretizer.forEach((bin, time) -> verify(Double.parseDouble(headerLine[bin + 1]) == time)); //regular rows reader.lines().map(line -> line.split(DELIMITER)).forEach(cells -> { diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/util/DvrpEventsReaders.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/util/DvrpEventsReaders.java index 2df41bed134..093071c7f08 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/util/DvrpEventsReaders.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/util/DvrpEventsReaders.java @@ -28,6 +28,7 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent; +import org.matsim.contrib.dvrp.passenger.PassengerWaitingEvent; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.dvrp.vrpagent.TaskEndedEvent; import org.matsim.contrib.dvrp.vrpagent.TaskStartedEvent; @@ -41,6 +42,7 @@ public static Map createCustomEven return Map.of(PassengerRequestSubmittedEvent.EVENT_TYPE, PassengerRequestSubmittedEvent::convert,// PassengerRequestScheduledEvent.EVENT_TYPE, PassengerRequestScheduledEvent::convert,// PassengerRequestRejectedEvent.EVENT_TYPE, PassengerRequestRejectedEvent::convert,// + PassengerWaitingEvent.EVENT_TYPE, PassengerWaitingEvent::convert,// PassengerPickedUpEvent.EVENT_TYPE, PassengerPickedUpEvent::convert, // PassengerDroppedOffEvent.EVENT_TYPE, PassengerDroppedOffEvent::convert,// TaskStartedEvent.EVENT_TYPE, e -> TaskStartedEvent.convert(e, stringToTaskTypeConverter), diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java index abdcc4521b3..68d6e327ed5 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; @@ -158,6 +159,36 @@ public String getType() { public void setType(String type) { throw new UnsupportedOperationException(); } + + @Override + public int getIterationCreated() { + throw new UnsupportedOperationException(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public Id getId() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } @Override public void setScore(Double score) { diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dynagent/examples/random/RunRandomDynAgentExample.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dynagent/examples/random/RunRandomDynAgentExample.java index 3bb9d6b7231..0952558534d 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dynagent/examples/random/RunRandomDynAgentExample.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dynagent/examples/random/RunRandomDynAgentExample.java @@ -47,9 +47,9 @@ public static void run(URL context, String networkFile, boolean otfvis) { config.qsim().setSimStarttimeInterpretation(StarttimeInterpretation.onlyUseStarttime); config.qsim().setSnapshotStyle(SnapshotStyle.queue); config.network().setInputFile(networkFile); - config.controler().setOutputDirectory("./test/output/random_dyn_agent/"); - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(0); + config.controller().setOutputDirectory("./test/output/random_dyn_agent/"); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); config.addConfigConsistencyChecker(new DynQSimConfigConsistencyChecker()); Scenario scenario = ScenarioUtils.loadScenario(config); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/Zones.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/Zones.java index 4a4be662672..34321d05068 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/Zones.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/Zones.java @@ -20,27 +20,14 @@ package org.matsim.contrib.zone; import java.io.File; +import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.URL; -import java.util.List; import java.util.Map; -import org.geotools.geometry.jts.JTS; -import org.geotools.referencing.CRS; -import org.locationtech.jts.geom.MultiPolygon; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.util.PolygonExtracter; import org.matsim.api.core.v01.Id; import org.matsim.contrib.zone.io.ZoneShpReader; -import org.matsim.contrib.zone.io.ZoneShpWriter; import org.matsim.contrib.zone.io.ZoneXmlReader; -import org.matsim.contrib.zone.io.ZoneXmlWriter; -import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.core.utils.io.UncheckedIOException; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.TransformException; public class Zones { public static Map, Zone> readZones(String zonesXmlFile, String zonesShpFile) { diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneXmlReader.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneXmlReader.java index 795dd4bf6ca..5be277bb3f7 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneXmlReader.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/io/ZoneXmlReader.java @@ -35,6 +35,10 @@ public Map, Zone> getZones() { return zones; } + public ZoneXmlReader() { + super(ValidationType.DTD_ONLY); + } + @Override public void startTag(String name, Attributes atts, Stack context) { if (ZONE.equals(name)) { diff --git a/matsim/src/main/java/org/matsim/core/trafficmonitoring/TravelTimeDataFactory.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrix.java similarity index 76% rename from matsim/src/main/java/org/matsim/core/trafficmonitoring/TravelTimeDataFactory.java rename to contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrix.java index fbe4aa0d726..1004bf2cf36 100644 --- a/matsim/src/main/java/org/matsim/core/trafficmonitoring/TravelTimeDataFactory.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrix.java @@ -1,10 +1,9 @@ /* *********************************************************************** * * project: org.matsim.* - * TravelTimeDataFactory.java * * * *********************************************************************** * * * - * copyright : (C) 2009 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 * * * @@ -18,14 +17,15 @@ * * * *********************************************************************** */ -package org.matsim.core.trafficmonitoring; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.network.Link; -import org.matsim.core.api.internal.MatsimFactory; +package org.matsim.contrib.zone.skims; -interface TravelTimeDataFactory extends MatsimFactory { - - public TravelTimeData createTravelTimeData(final Id linkId); +import org.matsim.api.core.v01.network.Node; +/** + * @author steffenaxer + */ +public interface AdaptiveTravelTimeMatrix { + double getTravelTime(Node fromNode, Node toNode, double departureTime); + void setTravelTime(Node fromNode, Node toNode, double travelTime, double departureTime); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixImpl.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixImpl.java new file mode 100644 index 00000000000..987f29ea47a --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixImpl.java @@ -0,0 +1,148 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.zone.skims; + +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.contrib.common.util.DistanceUtils; +import org.matsim.contrib.zone.SquareGridSystem; +import org.matsim.contrib.zone.ZonalSystems; +import org.matsim.contrib.zone.Zone; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.IntStream; + +/** + * @author steffenaxer + */ +public class AdaptiveTravelTimeMatrixImpl implements AdaptiveTravelTimeMatrix { + private final double TIME_INTERVAL = 3600.; + private final List timeDependentMatrix; + private final SquareGridSystem gridSystem; + private final double alpha; + private final Map centralNodes; + private final int numberOfBins; + private final DvrpTravelTimeMatrixParams params; + private final Map sparseTravelTimeCache = new ConcurrentHashMap<>(); + + public AdaptiveTravelTimeMatrixImpl(double maxTime, Network dvrpNetwork, DvrpTravelTimeMatrixParams params, + TravelTimeMatrix freeSpeedMatrix, double alpha) { + this.alpha = alpha; + this.numberOfBins = numberOfBins(maxTime); + this.gridSystem = new SquareGridSystem(dvrpNetwork.getNodes().values(), params.cellSize); + this.centralNodes = ZonalSystems.computeMostCentralNodes(dvrpNetwork.getNodes().values(), this.gridSystem); + this.timeDependentMatrix = IntStream.range(0, numberOfBins).mapToObj(i -> new Matrix(centralNodes.keySet())) + .toList(); + this.params = params; + this.initializeRegularMatrix(freeSpeedMatrix); + this.initializeSparseTravelTimeCache(freeSpeedMatrix); + } + + record SparseTravelTimeKey(Node fromNode, Node toNode, double timeBin) { + } + + int numberOfBins(double maxTime) { + return (int) (maxTime / TIME_INTERVAL); + } + + // SparseTravelTimeCache is a poor man's version of the SparseMatrix + // Much less code, but probably not so memory efficient? + void initializeSparseTravelTimeCache(TravelTimeMatrix freeSpeedMatrix) { + for (int i = 0; i < numberOfBins; i++) { + int bin = i; + centralNodes.entrySet().stream().parallel().forEach(originZoneEntry -> { + for (Entry destinationZoneEntry : centralNodes.entrySet()) { + Node originNode = originZoneEntry.getValue(); + Node destinationNode = destinationZoneEntry.getValue(); + if (DistanceUtils.calculateSquaredDistance(originNode.getCoord(), + destinationNode.getCoord()) < (params.maxNeighborDistance * params.maxNeighborDistance)) { + SparseTravelTimeKey key = getSparseTravelTimeKey(originNode, destinationNode, bin); + double freeSpeedTravelTime = freeSpeedMatrix.getTravelTime(originNode, destinationNode, Double.NaN); + this.sparseTravelTimeCache.computeIfAbsent(key, k -> freeSpeedTravelTime); + } + } + }); + } + } + + SparseTravelTimeKey getSparseTravelTimeKey(Node originNode, Node destinationNode, int bin) { + return new SparseTravelTimeKey(originNode, destinationNode, bin); + } + + // Matrix needs to be filled otherwise we have -1 exceptions + // We fill the matrix with already calculated free speed travel times + void initializeRegularMatrix(TravelTimeMatrix freeSpeedMatrix) { + for (int i = 0; i < numberOfBins; i++) { + int bin = i; + centralNodes.entrySet().stream().parallel().forEach(originZoneEntry -> { + for (Entry destinationZoneEntry : centralNodes.entrySet()) { + Node originNode = originZoneEntry.getValue(); + Node destinationNode = destinationZoneEntry.getValue(); + double freeSpeedTravelTime = freeSpeedMatrix.getTravelTime(originNode, destinationNode, Double.NaN); + this.timeDependentMatrix.get(bin).set(originZoneEntry.getKey(), destinationZoneEntry.getKey(), + freeSpeedTravelTime); + } + }); + } + } + + @Override + public double getTravelTime(Node fromNode, Node toNode, double departureTime) { + int bin = this.getBin(departureTime); + Double sparseValue = this.sparseTravelTimeCache.get(this.getSparseTravelTimeKey(fromNode, toNode, bin)); + if (sparseValue != null) { + return sparseValue; + } + return this.timeDependentMatrix.get(bin).get(this.gridSystem.getZone(fromNode), this.gridSystem.getZone(toNode)); + } + + int getBin(double departureTime) { + return Math.min((int) (departureTime / TIME_INTERVAL), this.numberOfBins-1); + } + + @Override + public void setTravelTime(Node fromNode, Node toNode, double routeEstimate, double departureTime) { + int bin = this.getBin(departureTime); + SparseTravelTimeKey key = this.getSparseTravelTimeKey(fromNode, toNode, bin); + Double sparseValue = this.sparseTravelTimeCache.get(this.getSparseTravelTimeKey(fromNode, toNode, bin)); + + // Update sparseTravelTimeCache + if (sparseValue != null) { + double value = getUpdatedValue(sparseValue, routeEstimate, this.alpha); + this.sparseTravelTimeCache.put(key, value); + + // Update regular matrix for long distances + } else { + double currentTravelTimeEstimate = this.getTravelTime(fromNode, toNode, departureTime); + double value = getUpdatedValue(currentTravelTimeEstimate, routeEstimate, this.alpha); + this.timeDependentMatrix.get(bin).set(this.gridSystem.getZone(fromNode), this.gridSystem.getZone(toNode), + value); + } + + } + + static double getUpdatedValue(double currentValue, double newValue, double alpha) { + return currentValue * (1 - alpha) + alpha * newValue; + } + +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixModule.java new file mode 100644 index 00000000000..a546f83a429 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/AdaptiveTravelTimeMatrixModule.java @@ -0,0 +1,55 @@ +/* *********************************************************************** * + * 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 org.matsim.contrib.zone.skims; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.core.config.groups.QSimConfigGroup; + +/** + * @author steffenaxer + */ +public class AdaptiveTravelTimeMatrixModule extends AbstractDvrpModeModule { + private final double ALTERNATIVE_ENDTIME = 30*3600; + private final static double SMOOTHING_ALPHA = 0.75; // High weight to new values + + @Inject + private QSimConfigGroup qsimConfig; + + @Inject + private DvrpConfigGroup dvrpConfigGroup; + + public AdaptiveTravelTimeMatrixModule(String mode) { + super(mode); + } + + @Override + public void install() { + bindModal(AdaptiveTravelTimeMatrix.class).toProvider(modalProvider( + getter -> new AdaptiveTravelTimeMatrixImpl(qsimConfig.getEndTime().orElse(ALTERNATIVE_ENDTIME), + getter.getModal(Network.class), + dvrpConfigGroup.getTravelTimeMatrixParams(), + getter.getModal(TravelTimeMatrix.class),SMOOTHING_ALPHA))) + .in(Singleton.class); + } +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/FreeSpeedTravelTimeMatrix.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/FreeSpeedTravelTimeMatrix.java index 6ee572bb03f..0adab8701df 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/FreeSpeedTravelTimeMatrix.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/FreeSpeedTravelTimeMatrix.java @@ -33,7 +33,7 @@ */ public class FreeSpeedTravelTimeMatrix implements TravelTimeMatrix { public static FreeSpeedTravelTimeMatrix createFreeSpeedMatrix(Network dvrpNetwork, DvrpTravelTimeMatrixParams params, int numberOfThreads, - double qSimTimeStepSize) { + double qSimTimeStepSize) { return new FreeSpeedTravelTimeMatrix(dvrpNetwork, params, numberOfThreads, new QSimFreeSpeedTravelTime(qSimTimeStepSize)); } @@ -48,7 +48,7 @@ public FreeSpeedTravelTimeMatrix(Network dvrpNetwork, DvrpTravelTimeMatrixParams var routingParams = new TravelTimeMatrices.RoutingParams(dvrpNetwork, travelTime, travelDisutility, numberOfThreads); freeSpeedTravelTimeMatrix = TravelTimeMatrices.calculateTravelTimeMatrix(routingParams, centralNodes, 0); freeSpeedTravelTimeSparseMatrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix(routingParams, params.maxNeighborDistance, - params.maxNeighborTravelTime, 0); + params.maxNeighborTravelTime, 0).orElse(null); } @Override @@ -56,9 +56,11 @@ public int getTravelTime(Node fromNode, Node toNode, double departureTime) { if (fromNode == toNode) { return 0; } - int time = freeSpeedTravelTimeSparseMatrix.get(fromNode, toNode); - if (time >= 0) {// value is present - return time; + if (freeSpeedTravelTimeSparseMatrix != null) { + int time = freeSpeedTravelTimeSparseMatrix.get(fromNode, toNode); + if (time >= 0) {// value is present + return time; + } } return freeSpeedTravelTimeMatrix.get(gridSystem.getZone(fromNode), gridSystem.getZone(toNode)); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/TravelTimeMatrices.java b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/TravelTimeMatrices.java index 38e5709b173..dcea254ac6c 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/TravelTimeMatrices.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/zone/skims/TravelTimeMatrices.java @@ -1,13 +1,29 @@ -/* - * Copyright (C) Schweizerische Bundesbahnen SBB, 2018. - */ - +/* *********************************************************************** * + * 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 org.matsim.contrib.zone.skims; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.IntStream; import org.matsim.api.core.v01.network.Network; @@ -41,7 +57,7 @@ public static Matrix calculateTravelTimeMatrix(RoutingParams params, Map centralNodes, double departureTime, Matrix travelTimeMatrix, - LeastCostPathTree lcpTree) { + LeastCostPathTree lcpTree) { Node fromNode = centralNodes.get(fromZone); lcpTree.calculate(fromNode.getId().index(), departureTime, null, null); @@ -50,26 +66,31 @@ private static void computeForDepartureZone(Zone fromZone, Map centr int nodeIndex = toNode.getId().index(); OptionalTime currOptionalTime = lcpTree.getTime(nodeIndex); double currTime = currOptionalTime.orElseThrow(() -> new RuntimeException( - "Undefined Time. Reason could be that the dvrp network is not fully connected. Please check and/or clean.")); + "Undefined Time. Reason could be that the dvrp network is not fully connected. Please check and/or clean.")); double tt = currTime - departureTime; travelTimeMatrix.set(fromZone, toZone, tt); } } - public static SparseMatrix calculateTravelTimeSparseMatrix(RoutingParams params, double maxDistance, double maxTravelTime, double departureTime) { + public static Optional calculateTravelTimeSparseMatrix(RoutingParams params, double maxDistance, double maxTravelTime, + double departureTime) { SparseMatrix travelTimeMatrix = new SparseMatrix(); + if (maxDistance == 0 && maxTravelTime == 0) { + return Optional.empty(); + } + var nodes = params.routingNetwork.getNodes().values(); var counter = "DVRP free-speed TT sparse matrix: node "; Calculation calculation = (lcpTree, n) -> computeForDepartureNode(n, nodes, departureTime, travelTimeMatrix, lcpTree, maxDistance, - maxTravelTime); + maxTravelTime); calculate(params, nodes, calculation, counter); - return travelTimeMatrix; + return Optional.of(travelTimeMatrix); } private static void computeForDepartureNode(Node fromNode, Collection nodes, double departureTime, SparseMatrix sparseMatrix, - LeastCostPathTree lcpTree, double maxDistance, double maxTravelTime) { + LeastCostPathTree lcpTree, double maxDistance, double maxTravelTime) { lcpTree.calculate(fromNode.getId().index(), departureTime, null, null, - (nodeIndex, arrivalTime, travelCost, distance, departTime) -> distance >= maxDistance && arrivalTime >= departTime + maxTravelTime); + (nodeIndex, arrivalTime, travelCost, distance, departTime) -> distance >= maxDistance && arrivalTime >= departTime + maxTravelTime); List neighborNodes = new ArrayList<>(); for (Node toNode : nodes) { @@ -94,8 +115,8 @@ private interface Calculation { private static void calculate(RoutingParams params, Collection elements, Calculation calculation, String counterPrefix) { var trees = IntStream.range(0, params.numberOfThreads) - .mapToObj(i -> new LeastCostPathTree(new SpeedyGraph(params.routingNetwork), params.travelTime, params.travelDisutility)) - .toList(); + .mapToObj(i -> new LeastCostPathTree(new SpeedyGraph(params.routingNetwork), params.travelTime, params.travelDisutility)) + .toList(); var executorService = new ExecutorServiceWithResource<>(trees); var counter = new Counter(counterPrefix, " / " + elements.size()); diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java index 02917236596..b6c7e2ed828 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java @@ -78,9 +78,9 @@ public void testRun() { URL configUrl = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("dvrp-grid"), "generic_dvrp_one_taxi_config.xml"); Config config = ConfigUtils.loadConfig(configUrl, new DvrpConfigGroup(), new OTFVisConfigGroup()); - config.controler().setLastIteration(0); + config.controller().setLastIteration(0); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); { QSimComponentsConfigGroup qsimComponentsConfig = ConfigUtils.addOrGetModule(config, QSimComponentsConfigGroup.class); @@ -142,7 +142,7 @@ public void install() { if (event instanceof AgentWakeupEvent) { wakeupEvents.put(((AgentWakeupEvent)event).getPersonId(), (AgentWakeupEvent)event); } else if (event instanceof PassengerRequestScheduledEvent) { - requestScheduledEvents.put(((PassengerRequestScheduledEvent)event).getPersonId(), + requestScheduledEvents.put(((PassengerRequestScheduledEvent)event).getPersonIds().stream().findFirst().orElseThrow(), (PassengerRequestScheduledEvent)event); } else if (event instanceof ActivityEndEvent && ((ActivityEndEvent)event).getActType() .equals("dummy")) { @@ -239,7 +239,7 @@ private static void assertRequestScheduledEvent(Map, PassengerRequest PassengerRequestScheduledEvent event = events.get(Id.createPersonId(personId)); assertThat(event.getVehicleId().toString()).isEqualTo("taxi_one"); assertThat(event.getPickupTime()).isCloseTo(pickupTime, Offset.offset(0.01)); - assertThat(event.getPersonId().toString()).isEqualTo(personId); + assertThat(event.getPersonIds().get(0).toString()).isEqualTo(personId); assertThat(event.getRequestId().toString()).isEqualTo(requestId); } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java index 6032c73484a..db4efe8d602 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java @@ -22,6 +22,8 @@ import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; +import java.util.Collections; +import java.util.List; import java.util.Set; import org.junit.Test; @@ -60,7 +62,6 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; -import com.google.inject.name.Names; /** * @author Michal Maciejewski (michalm) @@ -81,7 +82,7 @@ public class DefaultPassengerEngineTest { @Test public void test_valid_served() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of();//valid createQSim(requestValidator, OneTaxiOptimizer.class).run(); @@ -98,9 +99,11 @@ public void test_valid_served() { var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, VEHICLE_ID, 0, + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), VEHICLE_ID, 0, scheduledDropoffTime), new PersonEntersVehicleEvent(pickupStartTime, fixture.PERSON_ID, Id.createVehicleId(VEHICLE_ID)), new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, fixture.PERSON_ID, VEHICLE_ID), @@ -113,31 +116,37 @@ public void test_valid_served() { @Test public void test_invalid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of("invalid"); createQSim(requestValidator, OneTaxiOptimizer.class).run(); var requestId = Id.create("taxi_0", Request.class); - fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), + fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), + new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "invalid"), - new PersonStuckEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(0, MODE, requestId, List.of(fixture.PERSON_ID), "invalid"), + new PersonStuckEvent(1, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } @Test public void test_valid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of(); createQSim(requestValidator, RejectingOneTaxiOptimizer.class).run(); var requestId = Id.create("taxi_0", Request.class); - fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), + fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), + new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "rejecting_all_requests"), - new PersonStuckEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(0, MODE, requestId, List.of(fixture.PERSON_ID), "rejecting_all_requests"), + new PersonStuckEvent(1, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } private static class RejectingOneTaxiOptimizer implements VrpOptimizer { @@ -151,7 +160,7 @@ private static class RejectingOneTaxiOptimizer implements VrpOptimizer { public void requestSubmitted(Request request) { PassengerRequest passengerRequest = (PassengerRequest)request; eventsManager.processEvent(new PassengerRequestRejectedEvent(timer.getTimeOfDay(), MODE, request.getId(), - passengerRequest.getPassengerId(), "rejecting_all_requests")); + passengerRequest.getPassengerIds(), "rejecting_all_requests")); } @Override @@ -175,6 +184,7 @@ protected void configureQSim() { bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) .asEagerSingleton(); bindModal(PassengerRequestValidator.class).toInstance(requestValidator); + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); //supply addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class); diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java new file mode 100644 index 00000000000..2f34014ca2f --- /dev/null +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java @@ -0,0 +1,133 @@ +package org.matsim.contrib.dvrp.passenger; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.compress.utils.Sets; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiActionCreator; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiOptimizer; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; +import org.matsim.contrib.dvrp.fleet.Fleet; +import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.contrib.dvrp.run.MobsimTimerProvider; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentSourceQSimModule; +import org.matsim.contrib.dynagent.run.DynActivityEngine; +import org.matsim.core.events.MobsimScopeEventHandlingModule; +import org.matsim.core.mobsim.framework.MobsimTimer; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.QSimBuilder; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import java.util.List; +import java.util.Set; + +import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; + +public class GroupPassengerEngineTest { + + private final PassengerEngineTestFixture fixture = new PassengerEngineTestFixture(); + + private final Id VEHICLE_ID = Id.create("taxi1", DvrpVehicle.class); + private final DvrpVehicle oneTaxi = new DvrpVehicleImpl(ImmutableDvrpVehicleSpecification.newBuilder() + .id(VEHICLE_ID) + .serviceBeginTime(0) + .serviceEndTime(3600) + .startLinkId(fixture.linkAB.getId()) + .capacity(1) + .build(), fixture.linkAB); + private final Fleet fleet = () -> ImmutableMap.of(oneTaxi.getId(), oneTaxi); + + @Test + public void test_group() { + double departureTime = 0; + Id person1 = Id.createPersonId("1"); + Id person2 = Id.createPersonId("2"); + + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person1); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person2); + + PassengerRequestValidator requestValidator = request -> Set.of();//valid + createQSim(requestValidator, OneTaxiOptimizer.class).run(); + + double pickupStartTime = 1; + double pickupEndTime = pickupStartTime + OneTaxiOptimizer.PICKUP_DURATION; + double taxiDepartureTime = pickupEndTime + 1; + double taxiEntersLinkBATime = taxiDepartureTime + 1; + double taxiArrivalTime = taxiEntersLinkBATime + (fixture.linkBA.getLength() / fixture.linkBA.getFreespeed()); + double dropoffEndTime = taxiArrivalTime + OneTaxiOptimizer.DROPOFF_DURATION; + + //1 second delay between pickupEndTime and taxiDepartureTime is not considered in schedules + double scheduledDropoffTime = dropoffEndTime - pickupStartTime - 1; + + var requestId = Id.create("taxi_0", Request.class); + fixture.assertPassengerEvents( + List.of(person1, person2), + new ActivityEndEvent(departureTime, person2, fixture.linkAB.getId(), null, START_ACTIVITY), + new PersonDepartureEvent(departureTime, person2, fixture.linkAB.getId(), MODE, MODE), + new ActivityEndEvent(departureTime, person1, fixture.linkAB.getId(), null, START_ACTIVITY), + new PersonDepartureEvent(departureTime, person1, fixture.linkAB.getId(), MODE, MODE), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(person1, person2)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(person1, person2), VEHICLE_ID, 0, + scheduledDropoffTime), + new PersonEntersVehicleEvent(pickupStartTime, person1, Id.createVehicleId(VEHICLE_ID)), + new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person1, VEHICLE_ID), + new PersonEntersVehicleEvent(pickupStartTime, person2, Id.createVehicleId(VEHICLE_ID)), + new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person2, VEHICLE_ID), + new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person1, VEHICLE_ID), + new PersonLeavesVehicleEvent(dropoffEndTime, person1, Id.createVehicleId(VEHICLE_ID)), + new PersonArrivalEvent(dropoffEndTime, person1, fixture.linkBA.getId(), MODE), + new ActivityStartEvent(dropoffEndTime, person1, fixture.linkBA.getId(), null, END_ACTIVITY), + new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person2, VEHICLE_ID), + new PersonLeavesVehicleEvent(dropoffEndTime, person2, Id.createVehicleId(VEHICLE_ID)), + new PersonArrivalEvent(dropoffEndTime, person2, fixture.linkBA.getId(), MODE), + new ActivityStartEvent(dropoffEndTime, person2, fixture.linkBA.getId(), null, END_ACTIVITY) + ); + } + + + private QSim createQSim(PassengerRequestValidator requestValidator, Class optimizerClass) { + return new QSimBuilder(fixture.config).useDefaults() + .addOverridingModule(new MobsimScopeEventHandlingModule()) + .addQSimModule(new PassengerEngineQSimModule(MODE, PassengerEngineQSimModule.PassengerEngineType.WITH_GROUPS)) + .addQSimModule(new VrpAgentSourceQSimModule(MODE)) + .addQSimModule(new AbstractDvrpModeQSimModule(MODE) { + @Override + protected void configureQSim() { + bindModal(Network.class).toInstance(fixture.network); + bind(MobsimTimer.class).toProvider(MobsimTimerProvider.class).asEagerSingleton(); + + //requests + bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) + .asEagerSingleton(); + bindModal(PassengerRequestValidator.class).toInstance(requestValidator); + + //supply + addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class); + bindModal(Fleet.class).toInstance(fleet); + bindModal(VehicleType.class).toInstance(VehicleUtils.getDefaultVehicleType()); + bindModal(VrpOptimizer.class).to(optimizerClass).asEagerSingleton(); + bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class) + .asEagerSingleton(); + + //groups + bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Id.create("group1", PassengerGroupIdentifier.PassengerGroup.class)); + } + }) + .configureQSimComponents(components -> { + components.addComponent(DvrpModes.mode(MODE)); + components.addNamedComponent(DynActivityEngine.COMPONENT_NAME); + }) + .build(fixture.scenario, fixture.eventsManager); + } +} diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java index 84ec6202a63..1f83607037f 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java @@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.matsim.api.core.v01.Coord; @@ -81,7 +82,7 @@ public PassengerEngineTestFixture() { eventsManager.initProcessing(); } - void addPersonWithLeg(Link fromLink, Link toLink, double departureTime) { + void addPersonWithLeg(Link fromLink, Link toLink, double departureTime, Id person_id) { PopulationFactory factory = scenario.getPopulation().getFactory(); Plan plan = factory.createPlan(); @@ -99,15 +100,18 @@ void addPersonWithLeg(Link fromLink, Link toLink, double departureTime) { plan.addActivity(factory.createActivityFromLinkId(END_ACTIVITY, toLink.getId())); - Person person = factory.createPerson(PERSON_ID); + Person person = factory.createPerson(person_id); person.addPlan(plan); scenario.getPopulation().addPerson(person); } - void assertPassengerEvents(Event... events) { + void assertPassengerEvents(Collection> personIds, Event... events) { assertThat(recordedEvents.size()).isGreaterThanOrEqualTo(events.length); var recordedPassengerEvents = recordedEvents.stream() - .filter(e -> e instanceof HasPersonId && ((HasPersonId)e).getPersonId().equals(PERSON_ID)); + .filter(e -> + e instanceof HasPersonId && personIds.contains(((HasPersonId)e).getPersonId()) || + e instanceof AbstractPassengerRequestEvent + ); assertThat(recordedPassengerEvents).usingRecursiveFieldByFieldElementComparator().containsExactly(events); } } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java index d88211dc547..6d1c3edd7d1 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java @@ -20,17 +20,9 @@ package org.matsim.contrib.dvrp.passenger; -import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; - -import java.util.Set; - import org.junit.Test; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.ActivityEndEvent; -import org.matsim.api.core.v01.events.ActivityStartEvent; -import org.matsim.api.core.v01.events.PersonArrivalEvent; -import org.matsim.api.core.v01.events.PersonDepartureEvent; -import org.matsim.api.core.v01.events.PersonStuckEvent; +import org.matsim.api.core.v01.events.*; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; @@ -45,6 +37,12 @@ import org.matsim.core.mobsim.qsim.QSimBuilder; import org.matsim.core.population.routes.GenericRouteImpl; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; + /** * @author Michal Maciejewski (michalm) */ @@ -54,7 +52,7 @@ public class TeleportingPassengerEngineTest { @Test public void test_valid_teleported() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); double travelTime = 999; double travelDistance = 555; @@ -70,9 +68,11 @@ public void test_valid_teleported() { double arrivalTime = departureTime + travelTime; var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null, departureTime, + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), null, departureTime, arrivalTime), new PassengerPickedUpEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null), new PassengerDroppedOffEvent(arrivalTime, MODE, requestId, fixture.PERSON_ID, null), new TeleportationArrivalEvent(arrivalTime, fixture.PERSON_ID, travelDistance, MODE), @@ -83,7 +83,7 @@ public void test_valid_teleported() { @Test public void test_invalid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); TeleportedRouteCalculator teleportedRouteCalculator = request -> null; // unused PassengerRequestValidator requestValidator = request -> Set.of("invalid"); @@ -91,9 +91,11 @@ public void test_invalid_rejected() { var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerRequestRejectedEvent(departureTime, MODE, requestId, fixture.PERSON_ID, "invalid"), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), "invalid"), new PersonStuckEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/router/DiversionTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/router/DiversionTest.java index 5f158a0480a..adf44ba57ac 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/router/DiversionTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/router/DiversionTest.java @@ -126,8 +126,8 @@ public void testRepeatedSameDestinationDiversions() { { /* Create some necessary configuration for the test */ - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(0); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); config.qsim().setStartTime(0.0); config.qsim().setSimStarttimeInterpretation(StarttimeInterpretation.onlyUseStarttime); @@ -453,8 +453,8 @@ public void testRepeatedDiversionToDifferentDestinationRightBeforeLastLink() { { /* Create some necessary configuration for the test */ - config.controler().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setLastIteration(0); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setLastIteration(0); config.qsim().setStartTime(0.0); config.qsim().setSimStarttimeInterpretation(StarttimeInterpretation.onlyUseStarttime); diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimesTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimesTest.java index f4b7e0d1eee..efaa83bac7b 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimesTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/trafficmonitoring/DvrpOfflineTravelTimesTest.java @@ -64,14 +64,14 @@ public void saveLinkTravelTimes() throws IOException { var lines = stringWriter.toString().split("\n"); assertThat(lines).hasSize(3); - assertThat(lines[0].split(";")).containsExactly("linkId", "0", "900", "1800", "2700", "3600"); + assertThat(lines[0].split(";")).containsExactly("linkId", "0.0", "900.0", "1800.0", "2700.0", "3600.0"); assertThat(lines[1].split(";")).containsExactly("A", "1", "2", "3", "4", "5"); assertThat(lines[2].split(";")).containsExactly("B", "6", "7", "8", "9", "10"); } @Test public void loadLinkTravelTimes() throws IOException { - var line0 = String.join(";", "linkId", "0", "900", "1800", "2700", "3600"); + var line0 = String.join(";", "linkId", "0.0", "900.0", "1800.0", "2700.0", "3600.0"); var line1 = String.join(";", "A", 0.1 + "", 1.1 + "", 2.2 + "", 3.3 + "", 4.4 + ""); var line2 = String.join(";", "B", 5.5 + "", 6.6 + "", 7.7 + "", 8.8 + "", 9.9 + ""); var lines = String.join("\n", line0, line1, line2); diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java index fec0ba99afc..b9452d7c495 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java @@ -21,11 +21,12 @@ package org.matsim.contrib.dvrp.util; import static org.assertj.core.api.Assertions.assertThat; -import static org.matsim.core.config.groups.ControlerConfigGroup.EventsFileFormat; +import static org.matsim.core.config.groups.ControllerConfigGroup.EventsFileFormat; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -71,9 +72,9 @@ private enum TestTaskType implements Task.TaskType { } private final List dvrpEvents = List.of( - new PassengerRequestSubmittedEvent(0, mode, request, person, fromLink, toLink), - new PassengerRequestScheduledEvent(1, mode, request, person, vehicle, 100, 200), - new PassengerRequestRejectedEvent(2, mode, request, person, "cause_1"), + new PassengerRequestSubmittedEvent(0, mode, request, List.of(person), fromLink, toLink), + new PassengerRequestScheduledEvent(1, mode, request, List.of(person), vehicle, 100, 200), + new PassengerRequestRejectedEvent(2, mode, request, List.of(person), "cause_1"), new PassengerPickedUpEvent(111, mode, request, person, vehicle), new PassengerDroppedOffEvent(222, mode, request, person, vehicle), new TaskStartedEvent(300, mode, vehicle, driver, TestTaskType.DRIVE_TASK, 0, fromLink), diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/zone/skims/TravelTimeMatricesTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/zone/skims/TravelTimeMatricesTest.java index 380ab35182c..d60f34f7fa2 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/zone/skims/TravelTimeMatricesTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/zone/skims/TravelTimeMatricesTest.java @@ -69,7 +69,7 @@ public void travelTimeSparseMatrix_maxDistance() { NetworkUtils.createAndAddLink(network, Id.createLinkId("CA"), nodeC, nodeA, 600, 15, 80, 1); double maxDistance = 300;// B->A->C and C->A->B are pruned by the limit - var matrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix(routingParams(network), maxDistance, 0, 0); + var matrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix(routingParams(network), maxDistance, 0, 0).orElseThrow(); assertThat(matrix.get(nodeA, nodeA)).isEqualTo(0); assertThat(matrix.get(nodeA, nodeB)).isEqualTo(10); @@ -97,7 +97,7 @@ public void travelTimeSparseMatrix_maxTravelTime() { // 20 s (max TT) corresponds to 300 m (max distance) in another test (see: travelTimeSparseMatrix_maxDistance()) double maxTravelTime = 20;// B->A->C and C->A->B are pruned by the limit - var matrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix(routingParams(network), 0, maxTravelTime, 0); + var matrix = TravelTimeMatrices.calculateTravelTimeSparseMatrix(routingParams(network), 0, maxTravelTime, 0).orElseThrow(); assertThat(matrix.get(nodeA, nodeA)).isEqualTo(0); assertThat(matrix.get(nodeA, nodeB)).isEqualTo(10); diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/PositionEmissionsModule.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/PositionEmissionsModule.java index 5d9974f9c92..4652c3d7339 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/PositionEmissionsModule.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/PositionEmissionsModule.java @@ -37,7 +37,7 @@ import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.api.core.v01.events.HasPersonId; import org.matsim.core.config.Config; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.events.MatsimEventsReader; @@ -68,7 +68,7 @@ private static void checkConsistency(Config config) { if (config.qsim().getSnapshotPeriod() > 1) { throw new RuntimeException("only snapshot periods of 1s are supported."); } - if (!config.controler().getSnapshotFormat().contains(ControlerConfigGroup.SnapshotFormat.positionevents)) { + if (!config.controller().getSnapshotFormat().contains(ControllerConfigGroup.SnapshotFormat.positionevents)) { throw new RuntimeException("config.controler.snapshotFormat must be set to 'positionevents'"); } if (isNotCorrectSnapshotStyle(config.qsim().getSnapshotStyle())) { diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/EmissionsOnLinkEventHandler.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/EmissionsOnLinkEventHandler.java index b6f0585ba3b..e3bded8bde2 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/EmissionsOnLinkEventHandler.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/EmissionsOnLinkEventHandler.java @@ -42,7 +42,6 @@ public class EmissionsOnLinkEventHandler implements WarmEmissionEventHandler, Co private final TimeBinMap, EmissionsByPollutant>> timeBins; private final Map, Map> link2pollutants = new HashMap<>(); - public EmissionsOnLinkEventHandler(double timeBinSizeInSeconds) { this.timeBins = new TimeBinMap<>(timeBinSizeInSeconds); } @@ -76,7 +75,6 @@ public void handleEvent(WarmEmissionEvent event) { @Override public void handleEvent(ColdEmissionEvent event) { - handleEmissionEvent(event.getTime(), event.getLinkId(), event.getColdEmissions()); } diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzer.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzer.java index 25d112adb65..0e407e1e3c8 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzer.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzer.java @@ -30,6 +30,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.analysis.time.TimeBinMap; import org.matsim.contrib.emissions.Pollutant; import org.matsim.core.utils.collections.Tuple; @@ -44,235 +45,307 @@ */ public abstract class FastEmissionGridAnalyzer { - private static final Logger logger = LogManager.getLogger(FastEmissionGridAnalyzer.class); - - /** - * Processes an events file with emissions and renders emissions in three steps: - *

- * 1. All emissions are summed up by link id if the link id was found within the supplied network. - * 2. The aggregated emissions for each link are rastered onto all the raster-cells covered by the link. - * 3. In the smoothing step the emissions are blurred onto the surrounding raster-cells. - *

- * The blurring algorithm is a gaussian blur https://en.wikipedia.org/wiki/Gaussian_blur - *

- * If only a certain area of the scenario is of interest for the analysis. The supplied network must be filtered beforehand. - * The resulting raster's size depends on the bounding box of the supplied network. - *

- * Note: The algorithm is not accurate at the edges of the raster. The kernel is cut of a the edges meaning that emissions - * are underestimated at the edges of the raster. I didn't bother to implement this correctly. Otherwise the overall - * amount of emissions doesn't change. - * - * @param eventsFile The events file which contains the emission events - * @param network The network those emissions occurred on. The size of the resulting rater depends on the bounding box of the network - * @param cellSize size of a cell. This determines how many 'pixels' the resulting raster will have. Smaller cellSize means - * higher pixel-density - * @param radius smoothing radius which determines the strength of the blur. The radius describes onto how many cells - * the emissions of a single cell are distributed in one direction. A radius of 0 means no blurring. A radius of ~20 - * is probably a good guess for real-world scenarios. - * The resulting smoothing kernel will have radius * 2 + 1 entries. - * @return A raster containing emission values for each (x,y)-cell within the bounds of the network - */ - public static Map processEventsFile(final String eventsFile, final Network network, final double cellSize, final int radius) { - - logger.info("Start parsing events file."); - - Map>> linkEmissionsByPollutant = new HashMap<>(); - - new RawEmissionEventsReader((time, linkId, vehicleId, pollutant, value) -> { - - var id = Id.createLinkId(linkId); - if (network.getLinks().containsKey(id)) { - - var linkMap = linkEmissionsByPollutant.computeIfAbsent(pollutant, key -> new TObjectDoubleHashMap<>()); - linkMap.adjustOrPutValue(id, value, value); - } - }).readFile(eventsFile); - - logger.info("Start smoothing pollution."); - return linkEmissionsByPollutant.entrySet().stream() - .map(entry -> { - logger.info("Smoothing of: " + entry.getKey()); - return Tuple.of(entry.getKey(), processLinkEmissions(entry.getValue(), network, cellSize, radius)); - }) - .collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond)); - } - - /** - * Works as {@link FastEmissionGridAnalyzer#processEventsFile(String, Network, double, int)} but without events parsing - * The emissions per link have to be supplied. - */ - public static Raster processLinkEmissions(final TObjectDoubleMap> emissions, final Network network, final double cellSize, final int radius) { - - var originalRaster = rasterizeNetwork(network, emissions, cellSize); - return blur(originalRaster, radius); - } - - /** - * Works as {@link FastEmissionGridAnalyzer#processEventsFile(String, Network, double, int)} but without events parsing - * The emissions per link have to be supplied. - */ - public static Raster processLinkEmissions(final Map, Double> emissions, final Network network, final double cellSize, final int radius) { - - var originalRaster = rasterizeNetwork(network, emissions, cellSize); - return blur(originalRaster, radius); - } - - static Raster blur(Raster raster, int radius) { - - logger.info("Creating Kernel with " + (radius * 2 + 1) + " taps"); - var kernel = createKernel(radius * 2 + 1); - - var result = new Raster(raster.getBounds(), raster.getCellSize()); - - var firstPassRaster = new Raster(raster.getBounds(), raster.getCellSize()); - - // smooth horizontally - firstPassRaster.setValueForEachIndex((x, y) -> - calculateBlurredValue(y, x, firstPassRaster.getXLength(), kernel, (yf, xv) -> raster.getValueByIndex(xv, yf)) - ); - - // smooth vertically - result.setValueForEachIndex((x, y) -> - calculateBlurredValue(x, y, result.getYLength(), kernel, firstPassRaster::getValueByIndex) - ); - - return result; - } - - private static double calculateBlurredValue(int fixedIndex, int volatileIndex, int volatileLength, double[] kernel, GetValue getValue) { - - var halfKernelLength = kernel.length / 2; - var value = 0.; - var startIndex = (volatileIndex - halfKernelLength < 0) ? halfKernelLength - volatileIndex : 0; - var endIndex = (volatileIndex + halfKernelLength >= volatileLength) ? volatileLength - 1 - volatileIndex + halfKernelLength : kernel.length; - - for (var ki = startIndex; ki < endIndex; ki++) { - var kernelValue = kernel[ki]; - var originalValue = getValue.forIndex(fixedIndex, volatileIndex + ki - halfKernelLength); - value += originalValue * kernelValue; - } - return value; - } - - static Raster rasterizeNetwork(final Network network, final TObjectDoubleMap> emissions, final double cellSize) { - - var coords = network.getNodes().values().stream() - .map(BasicLocation::getCoord) - .collect(Collectors.toSet()); - - var bounds = new Raster.Bounds(coords); - var raster = new Raster(bounds, cellSize); - var cellArea = cellSize * cellSize; // assume square cells at the moment - - emissions.forEachEntry((linkId, value) -> { - var link = network.getLinks().get(linkId); - var numberOfCells = rasterizeLink(link, 0, raster); - rasterizeLink(link, value / numberOfCells / cellArea, raster); - return true; - }); - return raster; - } - - static Raster rasterizeNetwork(Network network, Map, Double> emissions, double cellSize) { - - var coords = network.getNodes().values().stream() - .map(BasicLocation::getCoord) - .collect(Collectors.toSet()); - - var bounds = new Raster.Bounds(coords); - var raster = new Raster(bounds, cellSize); - var cellArea = cellSize * cellSize; // assume square cells at the moment - - // rasterize network - for (var entry : emissions.entrySet()) { - - var link = network.getLinks().get(entry.getKey()); - var value = entry.getValue(); - // first count number of cells - var numberOfCells = rasterizeLink(link, 0, raster); - // second pass for actually writing the emission values - rasterizeLink(link, value / numberOfCells / cellArea, raster); - } - return raster; - } - - /** - * Rasterizes links into squares. Uses Bresenham's line drawing algorithm, which is supposed to be fast - * Maybe the result is too chunky, but it'll do as a first try - * - * @param link Matsim network link - * @return number of cells the link is rastered to - */ - private static int rasterizeLink(Link link, double value, Raster raster) { - - - int x0 = raster.getXIndex(link.getFromNode().getCoord().getX()); - int x1 = raster.getXIndex(link.getToNode().getCoord().getX()); - int y0 = raster.getYIndex(link.getFromNode().getCoord().getY()); - int y1 = raster.getYIndex(link.getToNode().getCoord().getY()); - int dx = Math.abs(x1 - x0); - int dy = -Math.abs(y1 - y0); - int err = dx + dy, e2; - - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; - - int result = 0; - - if (dx == 0 && dy == 0) { - // the algorithm doesn't really support lines shorter than the cell size. - // do avoid complicated computation within the loop, catch this case here - raster.adjustValueForIndex(x0, y0, value); - return 1; - } - - do { - raster.adjustValueForIndex(x0, y0, value); - result++; - - e2 = err + err; - if (e2 >= dy) { - err += dy; - x0 += sx; - } - if (e2 <= dx) { - err += dx; - y0 += sy; - } - // have this condition in separate method because we want to get one more cell than the original algorithm - // but then the direction of the line requires different conditions - } while (keepRasterizing(x0, x1, sx) && keepRasterizing(y0, y1, sy)); - - return result; - } - - private static boolean keepRasterizing(int value, int endCondition, int direction) { - - if (direction > 0) return value <= endCondition; - else return value >= endCondition; - } - - /** - * It might make sense to cut the edges of the distribution if we have a larger number of taps - * - * @param taps Length of the kernel - * @return Gaussian Kernel - */ - private static double[] createKernel(int taps) { - - var result = new double[taps]; - var binomialIndex = taps - 1; - var sum = Math.pow(2, binomialIndex); - - for (var i = 0; i < taps; i++) { - var coefficient = CombinatoricsUtils.binomialCoefficient(binomialIndex, i); - result[i] = coefficient / sum; - } - return result; - } - - @FunctionalInterface - private interface GetValue { - double forIndex(int fixedIndex, int volatileIndex); - } + private static final Logger logger = LogManager.getLogger(FastEmissionGridAnalyzer.class); + + /** + * Processes an events file with emissions and renders emissions in three steps: + *

+ * 1. All emissions are summed up by link id if the link id was found within the supplied network. + * 2. The aggregated emissions for each link are rastered onto all the raster-cells covered by the link. + * 3. In the smoothing step the emissions are blurred onto the surrounding raster-cells. + *

+ * The blurring algorithm is a gaussian blur https://en.wikipedia.org/wiki/Gaussian_blur + *

+ * If only a certain area of the scenario is of interest for the analysis. The supplied network must be filtered beforehand. + * The resulting raster's size depends on the bounding box of the supplied network. + *

+ * Note: The algorithm is not accurate at the edges of the raster. The kernel is cut of a the edges meaning that emissions + * are underestimated at the edges of the raster. I didn't bother to implement this correctly. Otherwise the overall + * amount of emissions doesn't change. + * + * @param eventsFile The events file which contains the emission events + * @param network The network those emissions occurred on. The size of the resulting rater depends on the bounding box of the network + * @param cellSize size of a cell. This determines how many 'pixels' the resulting raster will have. Smaller cellSize means + * higher pixel-density + * @param radius smoothing radius which determines the strength of the blur. The radius describes onto how many cells + * the emissions of a single cell are distributed in one direction. A radius of 0 means no blurring. A radius of ~20 + * is probably a good guess for real-world scenarios. + * The resulting smoothing kernel will have radius * 2 + 1 entries. + * @return A raster containing emission values for each (x,y)-cell within the bounds of the network + */ + public static Map processEventsFile(final String eventsFile, final Network network, final double cellSize, final int radius) { + + logger.info("Start parsing events file."); + + Map>> linkEmissionsByPollutant = new HashMap<>(); + + new RawEmissionEventsReader((time, linkId, vehicleId, pollutant, value) -> { + + var id = Id.createLinkId(linkId); + if (network.getLinks().containsKey(id)) { + + var linkMap = linkEmissionsByPollutant.computeIfAbsent(pollutant, key -> new TObjectDoubleHashMap<>()); + linkMap.adjustOrPutValue(id, value, value); + } + }).readFile(eventsFile); + + logger.info("Start smoothing pollution."); + return linkEmissionsByPollutant.entrySet().stream() + .map(entry -> { + logger.info("Smoothing of: " + entry.getKey()); + return Tuple.of(entry.getKey(), processLinkEmissions(entry.getValue(), network, cellSize, radius)); + }) + .collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond)); + } + + + /** + * Processes emissions that have been read by the {@link EmissionsOnLinkEventHandler}. + */ + public static Map processHandlerEmissions(Map, Map> link2pollutants, Network network, double cellSize, int radius) { + + Map>> linkEmissionsByPollutant = new HashMap<>(); + + // Transpose the map + for (Map.Entry, Map> perLink : link2pollutants.entrySet()) { + for (Map.Entry e : perLink.getValue().entrySet()) { + var linkMap = linkEmissionsByPollutant.computeIfAbsent(e.getKey(), key -> new TObjectDoubleHashMap<>()); + linkMap.put(perLink.getKey(), e.getValue()); + } + } + + return linkEmissionsByPollutant.entrySet().stream() + .map(entry -> { + logger.info("Smoothing of: " + entry.getKey()); + return Tuple.of(entry.getKey(), processLinkEmissions(entry.getValue(), network, cellSize, radius)); + }) + .collect(Collectors.toMap(Tuple::getFirst, Tuple::getSecond)); + } + + /** + * Processes emissions that have been read by the {@link EmissionsOnLinkEventHandler}. + */ + public static TimeBinMap> processHandlerEmissionsPerTimeBin(TimeBinMap, EmissionsByPollutant>> timeBinMap, Network network, double cellSize, int radius) { + + Map>>> linkEmissionsByPollutantAndTime = new HashMap<>(); + + for (TimeBinMap.TimeBin, EmissionsByPollutant>> perLink : timeBinMap.getTimeBins()) { + Double time = perLink.getStartTime(); + Map>> emissions = new HashMap<>(); + + for (Map.Entry, EmissionsByPollutant> emissionsByPollutantEntry : perLink.getValue().entrySet()) { + // Added linkID if not exists + Id linkId = emissionsByPollutantEntry.getKey(); + for (Map.Entry e : emissionsByPollutantEntry.getValue().getEmissions().entrySet()) { + + if (!emissions.containsKey(e.getKey())) + emissions.put(e.getKey(), new TObjectDoubleHashMap<>()); + + emissions.get(e.getKey()).put(linkId, e.getValue()); + } + } + + linkEmissionsByPollutantAndTime.put(time, emissions); + } + + TimeBinMap> result = new TimeBinMap<>(timeBinMap.getBinSize()); + + // Transpose the map + for (Map.Entry>>> timeSlice : linkEmissionsByPollutantAndTime.entrySet()) { + Map>> pollutants = timeSlice.getValue(); + Map rasterMap = new HashMap<>(); + + for (Map.Entry>> e : pollutants.entrySet()) { + + Pollutant pollutant = e.getKey(); + TObjectDoubleHashMap> emissions = e.getValue(); + + Raster raster = processLinkEmissions(emissions, network, cellSize, radius); + rasterMap.put(pollutant, raster); + } + + result.getTimeBin(timeSlice.getKey()).setValue(rasterMap); + } + + return result; + } + + /** + * Works as {@link FastEmissionGridAnalyzer#processEventsFile(String, Network, double, int)} but without events parsing + * The emissions per link have to be supplied. + */ + public static Raster processLinkEmissions(final TObjectDoubleMap> emissions, final Network network, final double cellSize, final int radius) { + + var originalRaster = rasterizeNetwork(network, emissions, cellSize); + return blur(originalRaster, radius); + } + + /** + * Works as {@link FastEmissionGridAnalyzer#processEventsFile(String, Network, double, int)} but without events parsing + * The emissions per link have to be supplied. + */ + public static Raster processLinkEmissions(final Map, Double> emissions, final Network network, final double cellSize, final int radius) { + + var originalRaster = rasterizeNetwork(network, emissions, cellSize); + return blur(originalRaster, radius); + } + + static Raster blur(Raster raster, int radius) { + + logger.info("Creating Kernel with " + (radius * 2 + 1) + " taps"); + var kernel = createKernel(radius * 2 + 1); + + var result = new Raster(raster.getBounds(), raster.getCellSize()); + + var firstPassRaster = new Raster(raster.getBounds(), raster.getCellSize()); + + // smooth horizontally + firstPassRaster.setValueForEachIndex((x, y) -> + calculateBlurredValue(y, x, firstPassRaster.getXLength(), kernel, (yf, xv) -> raster.getValueByIndex(xv, yf)) + ); + + // smooth vertically + result.setValueForEachIndex((x, y) -> + calculateBlurredValue(x, y, result.getYLength(), kernel, firstPassRaster::getValueByIndex) + ); + + return result; + } + + private static double calculateBlurredValue(int fixedIndex, int volatileIndex, int volatileLength, double[] kernel, GetValue getValue) { + + var halfKernelLength = kernel.length / 2; + var value = 0.; + var startIndex = (volatileIndex - halfKernelLength < 0) ? halfKernelLength - volatileIndex : 0; + var endIndex = (volatileIndex + halfKernelLength >= volatileLength) ? volatileLength - 1 - volatileIndex + halfKernelLength : kernel.length; + + for (var ki = startIndex; ki < endIndex; ki++) { + var kernelValue = kernel[ki]; + var originalValue = getValue.forIndex(fixedIndex, volatileIndex + ki - halfKernelLength); + value += originalValue * kernelValue; + } + return value; + } + + static Raster rasterizeNetwork(final Network network, final TObjectDoubleMap> emissions, final double cellSize) { + + var coords = network.getNodes().values().stream() + .map(BasicLocation::getCoord) + .collect(Collectors.toSet()); + + var bounds = new Raster.Bounds(coords); + var raster = new Raster(bounds, cellSize); + var cellArea = cellSize * cellSize; // assume square cells at the moment + + emissions.forEachEntry((linkId, value) -> { + var link = network.getLinks().get(linkId); + var numberOfCells = rasterizeLink(link, 0, raster); + rasterizeLink(link, value / numberOfCells / cellArea, raster); + return true; + }); + return raster; + } + + static Raster rasterizeNetwork(Network network, Map, Double> emissions, double cellSize) { + + var coords = network.getNodes().values().stream() + .map(BasicLocation::getCoord) + .collect(Collectors.toSet()); + + var bounds = new Raster.Bounds(coords); + var raster = new Raster(bounds, cellSize); + var cellArea = cellSize * cellSize; // assume square cells at the moment + + // rasterize network + for (var entry : emissions.entrySet()) { + + var link = network.getLinks().get(entry.getKey()); + var value = entry.getValue(); + // first count number of cells + var numberOfCells = rasterizeLink(link, 0, raster); + // second pass for actually writing the emission values + rasterizeLink(link, value / numberOfCells / cellArea, raster); + } + return raster; + } + + /** + * Rasterizes links into squares. Uses Bresenham's line drawing algorithm, which is supposed to be fast + * Maybe the result is too chunky, but it'll do as a first try + * + * @param link Matsim network link + * @return number of cells the link is rastered to + */ + private static int rasterizeLink(Link link, double value, Raster raster) { + + + int x0 = raster.getXIndex(link.getFromNode().getCoord().getX()); + int x1 = raster.getXIndex(link.getToNode().getCoord().getX()); + int y0 = raster.getYIndex(link.getFromNode().getCoord().getY()); + int y1 = raster.getYIndex(link.getToNode().getCoord().getY()); + int dx = Math.abs(x1 - x0); + int dy = -Math.abs(y1 - y0); + int err = dx + dy, e2; + + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + + int result = 0; + + if (dx == 0 && dy == 0) { + // the algorithm doesn't really support lines shorter than the cell size. + // do avoid complicated computation within the loop, catch this case here + raster.adjustValueForIndex(x0, y0, value); + return 1; + } + + do { + raster.adjustValueForIndex(x0, y0, value); + result++; + + e2 = err + err; + if (e2 >= dy) { + err += dy; + x0 += sx; + } + if (e2 <= dx) { + err += dx; + y0 += sy; + } + // have this condition in separate method because we want to get one more cell than the original algorithm + // but then the direction of the line requires different conditions + } while (keepRasterizing(x0, x1, sx) && keepRasterizing(y0, y1, sy)); + + return result; + } + + private static boolean keepRasterizing(int value, int endCondition, int direction) { + + if (direction > 0) return value <= endCondition; + else return value >= endCondition; + } + + /** + * It might make sense to cut the edges of the distribution if we have a larger number of taps + * + * @param taps Length of the kernel + * @return Gaussian Kernel + */ + private static double[] createKernel(int taps) { + + var result = new double[taps]; + var binomialIndex = taps - 1; + var sum = Math.pow(2, binomialIndex); + + for (var i = 0; i < taps; i++) { + var coefficient = CombinatoricsUtils.binomialCoefficient(binomialIndex, i); + result[i] = coefficient / sum; + } + return result; + } + + @FunctionalInterface + private interface GetValue { + double forIndex(int fixedIndex, int volatileIndex); + } } diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/RawEmissionEventsReader.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/RawEmissionEventsReader.java index d83cacfee44..ff962f67cff 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/RawEmissionEventsReader.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/analysis/RawEmissionEventsReader.java @@ -55,6 +55,7 @@ class RawEmissionEventsReader extends MatsimXmlParser { private final HandleEmissionEvent handler; RawEmissionEventsReader(HandleEmissionEvent handler) { + super(ValidationType.NO_VALIDATION); this.handler = handler; // events don't have dtd. Therefore validation is not possible this.setValidating(false); diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/CreateEmissionConfig.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/CreateEmissionConfig.java index 74f24e7321c..f50fc6a6d27 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/CreateEmissionConfig.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/CreateEmissionConfig.java @@ -23,19 +23,19 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigWriter; import org.matsim.core.config.groups.*; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; -import org.matsim.core.config.groups.StrategyConfigGroup.StrategySettings; +import org.matsim.core.config.groups.ScoringConfigGroup.ActivityParams; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; import org.matsim.core.controler.Controler; import org.matsim.core.controler.MatsimServices; /** - * - * Creates a config file + * + * Creates a config file * with necessary emission input files for the {@link EmissionsConfigGroup EmissionsConfigGroup}. - * + * * This config file is used by the {@link RunDetailedEmissionToolOfflineExample OfflineExample} and * the {@link RunDetailedEmissionToolOnlineExample OnlineExample} - * + * * @author benjamin, julia * * @deprecated -- has not been maintained and should in consequence be phased out. kai, nov'21 @@ -45,47 +45,47 @@ public final class CreateEmissionConfig { private static final String inputPath = "./test/input/org/matsim/contrib/emissions/"; - private static final String networkFile = //inputPath + + private static final String networkFile = //inputPath + "sample_network.xml"; - private static final String plansFile = //inputPath + + private static final String plansFile = //inputPath + "sample_population.xml"; - private static final String emissionVehicleFile = //inputPath + + private static final String emissionVehicleFile = //inputPath + "sample_emissionVehicles.xml"; - + private static final String roadTypeMappingFile = //inputPath + "sample_roadTypeMapping.txt"; - + private static final String averageFleetWarmEmissionFactorsFile = //inputPath + "sample_EFA_HOT_vehcat_2005average.txt"; private static final String averageFleetColdEmissionFactorsFile = //inputPath + "sample_EFA_ColdStart_vehcat_2005average.txt"; - + private static final boolean isUsingDetailedEmissionCalculation = true; private static final String detailedWarmEmissionFactorsFile = //inputPath + "sample_EFA_HOT_SubSegm_2005detailed.txt"; private static final String detailedColdEmissionFactorsFile = //inputPath + "sample_EFA_ColdStart_SubSegm_2005detailed.txt"; - + private static final String outputPath = "./test/output/"; private static final String configFilePath = inputPath + "config_v2.xml"; - + private static final int numberOfIterations = 6; - - + + public static void main(String[] args) { - + Config config = new Config(); config.addCoreModules(); MatsimServices controler = new Controler(config); - + // controlerConfigGroup - ControlerConfigGroup ccg = controler.getConfig().controler(); + ControllerConfigGroup ccg = controler.getConfig().controller(); ccg.setOutputDirectory(outputPath); ccg.setFirstIteration(0); ccg.setLastIteration(numberOfIterations-1); - + // planCalcScoreConfigGroup - PlanCalcScoreConfigGroup pcs = controler.getConfig().planCalcScore(); + ScoringConfigGroup pcs = controler.getConfig().scoring(); ActivityParams homeP = new ActivityParams("home"); homeP.setTypicalDuration(12 * 3600); pcs.addActivityParams(homeP); @@ -94,21 +94,21 @@ public static void main(String[] args) { pcs.addActivityParams(workP); // strategy - StrategyConfigGroup scg = controler.getConfig().strategy(); + ReplanningConfigGroup scg = controler.getConfig().replanning(); StrategySettings strategySettings = new StrategySettings(); strategySettings.setStrategyName("ChangeExpBeta"); strategySettings.setWeight(1.0); scg.addStrategySettings(strategySettings); - + // network NetworkConfigGroup ncg = controler.getConfig().network(); ncg.setInputFile(networkFile); - + // plans PlansConfigGroup pcg = controler.getConfig().plans(); pcg.setInputFile(plansFile); - - // define emission tool input files + + // define emission tool input files EmissionsConfigGroup ecg = new EmissionsConfigGroup() ; controler.getConfig().addModule(ecg); @@ -137,11 +137,11 @@ public static void main(String[] args) { // ecg.setEmissionCostMultiplicationFactor(1.0); // ecg.setConsideringCO2Costs(true); // ecg.setEmissionEfficiencyFactor(1.0); - - // write config + + // write config ConfigWriter cw = new ConfigWriter(config); cw.write(configFilePath); - + } diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExample.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExample.java index fd852ba1a61..bd1af3c2a90 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExample.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExample.java @@ -26,9 +26,7 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.Controler; import org.matsim.core.controler.Injector; -import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.events.EventsUtils; import org.matsim.core.events.MatsimEventsReader; import org.matsim.core.events.algorithms.EventWriterXML; @@ -39,7 +37,7 @@ /** * - * Use the config file as created by the + * Use the config file as created by the * {@link CreateEmissionConfig CreateEmissionConfig} to calculate * emissions based on the link leave events of an events file. Resulting emission events are written into an event file. * @@ -55,7 +53,7 @@ public final class RunAverageEmissionToolOfflineExample{ private Config config; - // ======================================================================================================= + // ======================================================================================================= public static void main (String[] args){ RunAverageEmissionToolOfflineExample emissionToolOfflineExampleV2 = new RunAverageEmissionToolOfflineExample(); @@ -105,7 +103,7 @@ public void install(){ EmissionModule emissionModule = injector.getInstance(EmissionModule.class); // OutputDirectoryHierarchy outputDirectoryHierarchy = injector.getInstance( OutputDirectoryHierarchy.class ); - final String outputDirectory = scenario.getConfig().controler().getOutputDirectory(); + final String outputDirectory = scenario.getConfig().controller().getOutputDirectory(); EventWriterXML emissionEventWriter = new EventWriterXML( outputDirectory + emissionEventsFilename ); emissionModule.getEmissionEventsManager().addHandler(emissionEventWriter); diff --git a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExample.java b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExample.java index a4818fcf794..191766556c2 100644 --- a/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExample.java +++ b/contribs/emissions/src/main/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExample.java @@ -29,7 +29,6 @@ import org.matsim.core.controler.Injector; import org.matsim.core.events.EventsUtils; import org.matsim.core.events.MatsimEventsReader; -import org.matsim.core.events.ParallelEventsManager; import org.matsim.core.events.algorithms.EventWriterXML; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; @@ -38,7 +37,7 @@ /** * - * Use the config file as created by the + * Use the config file as created by the * {@link CreateEmissionConfig CreateEmissionConfig} to calculate * emissions based on the link leave events of an events file. Resulting emission events are written into an event file. * @@ -54,7 +53,7 @@ public final class RunDetailedEmissionToolOfflineExample{ // private static final String emissionEventOutputFileName = "5.emission.events.offline.xml.gz"; private Config config; - // ======================================================================================================= + // ======================================================================================================= public static void main (String[] args){ RunDetailedEmissionToolOfflineExample emissionToolOfflineExampleV2Vehv1 = new RunDetailedEmissionToolOfflineExample(); @@ -96,7 +95,7 @@ public void install(){ EmissionModule emissionModule = injector.getInstance(EmissionModule.class); - final String outputDirectory = scenario.getConfig().controler().getOutputDirectory(); + final String outputDirectory = scenario.getConfig().controller().getOutputDirectory(); EventWriterXML emissionEventWriter = new EventWriterXML( outputDirectory + RunAverageEmissionToolOfflineExample.emissionEventsFilename ) ; emissionModule.getEmissionEventsManager().addHandler(emissionEventWriter); diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/TestPositionEmissionModule.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/TestPositionEmissionModule.java index 73ee5c208b5..c48f5254b39 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/TestPositionEmissionModule.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/TestPositionEmissionModule.java @@ -18,17 +18,16 @@ import org.matsim.contrib.emissions.utils.EmissionsConfigGroup; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.ControlerConfigGroup; +import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.config.groups.NetworkConfigGroup; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.events.EventsManagerImpl; import org.matsim.core.events.handler.BasicEventHandler; -import org.matsim.core.events.handler.EventHandler; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.routes.RouteUtils; @@ -36,7 +35,6 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; -import org.matsim.facilities.filters.Filter; import org.matsim.testcases.MatsimTestUtils; import org.matsim.vehicles.EngineInformation; import org.matsim.vehicles.Vehicle; @@ -69,11 +67,11 @@ public void simpleTest() { emissionConfig.setDetailedVsAverageLookupBehavior( EmissionsConfigGroup.DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable); //This is the previous behaviour var config = ConfigUtils.loadConfig(configFile, emissionConfig); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); config.qsim().setSnapshotPeriod(1); config.qsim().setSnapshotStyle(QSimConfigGroup.SnapshotStyle.queue); - config.controler().setWriteSnapshotsInterval(1); - config.controler().setSnapshotFormat(Set.of(ControlerConfigGroup.SnapshotFormat.positionevents)); + config.controller().setWriteSnapshotsInterval(1); + config.controller().setSnapshotFormat(Set.of(ControllerConfigGroup.SnapshotFormat.positionevents)); var scenario = ScenarioUtils.loadScenario(config); @@ -91,33 +89,33 @@ public void compareToOtherModule_singleVehicleSingleLink() { emissionConfig.setDetailedVsAverageLookupBehavior(EmissionsConfigGroup.DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable); var config = ConfigUtils.loadConfig(configFile, emissionConfig); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.controler().setOutputDirectory(testUtils.getOutputDirectory()); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOutputDirectory(testUtils.getOutputDirectory()); emissionConfig.setAverageColdEmissionFactorsFile("../sample_41_EFA_ColdStart_vehcat_2020average.csv"); emissionConfig.setAverageWarmEmissionFactorsFile( "../sample_41_EFA_HOT_vehcat_2020average.csv" ); emissionConfig.setHbefaTableConsistencyCheckingLevel( EmissionsConfigGroup.HbefaTableConsistencyCheckingLevel.consistent ); - final PlanCalcScoreConfigGroup.ActivityParams homeParams = new PlanCalcScoreConfigGroup.ActivityParams("home") + final ScoringConfigGroup.ActivityParams homeParams = new ScoringConfigGroup.ActivityParams("home") .setTypicalDuration(20); - config.planCalcScore().addActivityParams(homeParams); - final PlanCalcScoreConfigGroup.ActivityParams workParams = new PlanCalcScoreConfigGroup.ActivityParams("work") + config.scoring().addActivityParams(homeParams); + final ScoringConfigGroup.ActivityParams workParams = new ScoringConfigGroup.ActivityParams("work") .setTypicalDuration(20); - config.planCalcScore().addActivityParams(workParams); + config.scoring().addActivityParams(workParams); - var strategy = new StrategyConfigGroup.StrategySettings(); + var strategy = new ReplanningConfigGroup.StrategySettings(); strategy.setStrategyName("ChangeExpBeta"); strategy.setWeight(1.0); - config.strategy().addParameterSet(strategy); + config.replanning().addParameterSet(strategy); // activate snapshots config.qsim().setSnapshotPeriod(1); config.qsim().setSnapshotStyle(QSimConfigGroup.SnapshotStyle.queue); - config.controler().setWriteSnapshotsInterval(1); - config.controler().setSnapshotFormat(Set.of(ControlerConfigGroup.SnapshotFormat.positionevents)); - config.controler().setFirstIteration(0); - config.controler().setLastIteration(0); + config.controller().setWriteSnapshotsInterval(1); + config.controller().setSnapshotFormat(Set.of(ControllerConfigGroup.SnapshotFormat.positionevents)); + config.controller().setFirstIteration(0); + config.controller().setLastIteration(0); config.qsim().setVehiclesSource(QSimConfigGroup.VehiclesSource.fromVehiclesData); diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzerTest.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzerTest.java index a270e025c8c..ceb4b2a093d 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzerTest.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/analysis/FastEmissionGridAnalyzerTest.java @@ -199,4 +199,4 @@ public void processEventsFile() { // if we reach here, it means nothing has crashed, all the logic is tested elsewhere raster.forEachIndex((xi, yi, value) -> assertTrue(biggestExpectedValue >= value)); } -} \ No newline at end of file +} diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExampleIT.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExampleIT.java index fbe73cb8d24..5f231f96650 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExampleIT.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunAverageEmissionToolOfflineExampleIT.java @@ -30,7 +30,6 @@ import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; -import org.matsim.utils.eventsfilecomparison.EventsFileComparator; import org.matsim.utils.eventsfilecomparison.EventsFileComparator.Result; import java.net.URL; @@ -50,7 +49,7 @@ public final void testAverage_vehTypeV1() { URL scenarioUrl = ExamplesUtils.getTestScenarioURL( "emissions-sampleScenario/testv2_Vehv1" ); URL configUrl = IOUtils.extendUrl( scenarioUrl, "config_average.xml" ); Config config = offlineExample.prepareConfig( new String [] {configUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( HbefaVehicleDescriptionSource.fromVehicleTypeDescription ); @@ -71,7 +70,7 @@ public final void testAverage_vehTypeV2() { URL scenarioUrl = ExamplesUtils.getTestScenarioURL( "emissions-sampleScenario/testv2_Vehv2" ); URL configUrl = IOUtils.extendUrl( scenarioUrl, "config_average.xml" ); Config config = offlineExample.prepareConfig( new String [] {configUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( HbefaVehicleDescriptionSource.asEngineInformationAttributes ); @@ -98,7 +97,7 @@ public final void testAverage_vehTypeV2b() { URL scenarioUrl = ExamplesUtils.getTestScenarioURL( "emissions-sampleScenario/testv2_Vehv2" ); URL configUrl = IOUtils.extendUrl( scenarioUrl, "config_average.xml" ); Config config = offlineExample.prepareConfig( new String [] {configUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( HbefaVehicleDescriptionSource.fromVehicleTypeDescription ); @@ -120,7 +119,7 @@ public final void testAverage_vehTypeV2_HBEFA4() { URL scenarioUrl = ExamplesUtils.getTestScenarioURL( "emissions-sampleScenario/testv2_Vehv2" ); URL configUrl = IOUtils.extendUrl( scenarioUrl, "config_average.xml" ); Config config = offlineExample.prepareConfig( new String [] {configUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setAverageColdEmissionFactorsFile("../sample_41_EFA_ColdStart_vehcat_2020average.csv"); diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExampleIT.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExampleIT.java index 7c8c842941a..35735730695 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExampleIT.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOfflineExampleIT.java @@ -24,7 +24,6 @@ import org.matsim.contrib.emissions.utils.EmissionsConfigGroup; import org.matsim.contrib.emissions.utils.EmissionsConfigGroup.DetailedVsAverageLookupBehavior; import org.matsim.contrib.emissions.utils.EmissionsConfigGroup.HbefaVehicleDescriptionSource; -import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; @@ -59,7 +58,7 @@ public final void testDetailed_vehTypeV1() { EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( HbefaVehicleDescriptionSource.fromVehicleTypeDescription ); emissionsConfig.setDetailedVsAverageLookupBehavior( DetailedVsAverageLookupBehavior.onlyTryDetailedElseAbort ); - config.controler().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); offlineExample.run(); } catch (Exception ee ) { @@ -83,7 +82,7 @@ public final void testDetailed_vehTypeV2() { EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setDetailedVsAverageLookupBehavior( DetailedVsAverageLookupBehavior.onlyTryDetailedElseAbort ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); offlineExample.run(); } catch (Exception ee ) { @@ -110,7 +109,7 @@ public final void testDetailed_vehTypeV2_HBEFA4() { emissionsConfig.setDetailedColdEmissionFactorsFile("../sample_41_EFA_ColdStart_SubSegm_2020detailed.csv"); emissionsConfig.setDetailedWarmEmissionFactorsFile("../sample_41_EFA_HOT_SubSegm_2020detailed.csv"); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); offlineExample.run(); } catch (Exception ee ) { gotAnException = true ; @@ -135,7 +134,7 @@ public final void testDetailed_vehTypeV1_FallbackToAverage() { var cfUrl = IOUtils.extendUrl( scUrl, "config_detailed.xml" ); var config = offlineExample.prepareConfig( new String [] {cfUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setAverageColdEmissionFactorsFile("../sample_41_EFA_ColdStart_vehcat_2020average.csv"); @@ -156,7 +155,7 @@ public final void testDetailed_vehTypeV2_FallbackToAverage() { var cfUrl = IOUtils.extendUrl( scUrl, "config_detailed.xml" ); var config = offlineExample.prepareConfig( new String [] {cfUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setAverageColdEmissionFactorsFile("../sample_41_EFA_ColdStart_vehcat_2020average.csv"); @@ -176,7 +175,7 @@ public final void testDetailed_vehTypeV2_HBEFA4_FallbackToAverage() { var cfUrl = IOUtils.extendUrl( scUrl, "config_detailed.xml" ); var config = offlineExample.prepareConfig( new String [] {cfUrl.toString()} ); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controller().setOutputDirectory(utils.getOutputDirectory()); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setDetailedVsAverageLookupBehavior( DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable ); diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1.java index 0315a625693..a762a196a4a 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1.java @@ -55,8 +55,8 @@ public final void testDetailed_vehTypeV1() { try { RunDetailedEmissionToolOnlineExample onlineExample = new RunDetailedEmissionToolOnlineExample(); Config config = onlineExample.prepareConfig( new String[]{"./scenarios/sampleScenario/testv2_Vehv1/config_detailed.xml"} ) ; - config.controler().setOutputDirectory( utils.getOutputDirectory() ); - config.controler().setLastIteration( 1 ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setLastIteration( 1 ); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( EmissionsConfigGroup.HbefaVehicleDescriptionSource.fromVehicleTypeDescription ); emissionsConfig.setDetailedVsAverageLookupBehavior( EmissionsConfigGroup.DetailedVsAverageLookupBehavior.onlyTryDetailedElseAbort ); diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1FallbackToAverage.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1FallbackToAverage.java index 12eed58acea..a046d363371 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1FallbackToAverage.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV1FallbackToAverage.java @@ -55,8 +55,8 @@ public final void testDetailed_vehTypeV1_FallbackToAverage() { var configUrl = IOUtils.extendUrl( scenarioUrl, "config_detailed.xml" ); Config config = RunDetailedEmissionToolOnlineExample.prepareConfig( new String [] { configUrl.toString() } ); - config.controler().setOutputDirectory( utils.getOutputDirectory() ); - config.controler().setLastIteration( 1 ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setLastIteration( 1 ); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setHbefaVehicleDescriptionSource( EmissionsConfigGroup.HbefaVehicleDescriptionSource.fromVehicleTypeDescription ); emissionsConfig.setDetailedVsAverageLookupBehavior( diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2.java index 42caf0ed295..1a247d71217 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2.java @@ -49,8 +49,8 @@ public final void testDetailed_vehTypeV2() { try { RunDetailedEmissionToolOnlineExample onlineExample = new RunDetailedEmissionToolOnlineExample(); Config config = onlineExample.prepareConfig( new String[]{"./scenarios/sampleScenario/testv2_Vehv2/config_detailed.xml"} ) ; - config.controler().setOutputDirectory( utils.getOutputDirectory() ); - config.controler().setLastIteration( 1 ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setLastIteration( 1 ); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setDetailedVsAverageLookupBehavior( EmissionsConfigGroup.DetailedVsAverageLookupBehavior.onlyTryDetailedElseAbort ); Scenario scenario = onlineExample.prepareScenario( config ) ; diff --git a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2FallbackToAverage.java b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2FallbackToAverage.java index 5afbeb1337e..42f65e0010b 100644 --- a/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2FallbackToAverage.java +++ b/contribs/emissions/src/test/java/org/matsim/contrib/emissions/example/RunDetailedEmissionToolOnlineExampleIT_vehTypeV2FallbackToAverage.java @@ -51,8 +51,8 @@ public final void testDetailed_vehTypeV2_FallbackToAverage() { var configUrl = IOUtils.extendUrl( scenarioUrl, "config_detailed.xml" ); Config config = RunDetailedEmissionToolOnlineExample.prepareConfig( new String [] { configUrl.toString() } ); - config.controler().setOutputDirectory( utils.getOutputDirectory() ); - config.controler().setLastIteration( 1 ); + config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setLastIteration( 1 ); EmissionsConfigGroup emissionsConfig = ConfigUtils.addOrGetModule( config, EmissionsConfigGroup.class ); emissionsConfig.setDetailedVsAverageLookupBehavior( DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable ); //This is the previous behaviour -> Test only passes if falling back to average table :( 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/charging/VehicleChargingHandler.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/VehicleChargingHandler.java index d450afb6d24..4a2655a5ccf 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/VehicleChargingHandler.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/VehicleChargingHandler.java @@ -42,8 +42,8 @@ import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; -import org.matsim.contrib.ev.infrastructure.ChargingInfrastructures; -import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.contrib.ev.infrastructure.ChargingInfrastructureUtils; +import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.events.MobsimScopeEventHandler; import org.matsim.vehicles.Vehicle; @@ -60,7 +60,7 @@ public class VehicleChargingHandler ChargingEndEventHandler, MobsimScopeEventHandler { public static final String CHARGING_IDENTIFIER = " charging"; - public static final String CHARGING_INTERACTION = PlanCalcScoreConfigGroup.createStageActivityType( + public static final String CHARGING_INTERACTION = ScoringConfigGroup.createStageActivityType( CHARGING_IDENTIFIER); private final Map, Id> lastVehicleUsed = new HashMap<>(); private final Map, Id> vehiclesAtChargers = new HashMap<>(); @@ -73,7 +73,7 @@ public class VehicleChargingHandler VehicleChargingHandler(ChargingInfrastructure chargingInfrastructure, ElectricFleet electricFleet) { this.chargingInfrastructure = chargingInfrastructure; this.electricFleet = electricFleet; - chargersAtLinks = ChargingInfrastructures.getChargersAtLinks(chargingInfrastructure); + chargersAtLinks = ChargingInfrastructureUtils.getChargersAtLinks(chargingInfrastructure ); } /** diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DischargingModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DischargingModule.java index c36f95635a9..473def02fcf 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DischargingModule.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DischargingModule.java @@ -20,30 +20,32 @@ package org.matsim.contrib.ev.discharging; -import com.google.inject.Singleton; import org.matsim.contrib.ev.EvModule; import org.matsim.contrib.ev.temperature.TemperatureService; import org.matsim.core.controler.AbstractModule; import org.matsim.core.mobsim.qsim.AbstractQSimModule; +import com.google.inject.Singleton; + /** * @author Michal Maciejewski (michalm) */ -public class DischargingModule extends AbstractModule { +public final class DischargingModule extends AbstractModule { @Override public void install() { bind(DriveEnergyConsumption.Factory.class).toInstance(ev -> new OhdeSlaskiDriveEnergyConsumption()); bind(TemperatureService.class).toInstance(linkId -> 15);// XXX fixed temperature 15 oC - bind(AuxEnergyConsumption.Factory.class).to(OhdeSlaskiAuxEnergyConsumption.Factory.class).in( Singleton.class ); + bind(AuxEnergyConsumption.Factory.class).to(OhdeSlaskiAuxEnergyConsumption.Factory.class).in(Singleton.class); installQSimModule(new AbstractQSimModule() { @Override protected void configureQSim() { - this.bind(DriveDischargingHandler.class).in( Singleton.class ); + this.bind(DriveDischargingHandler.class).in(Singleton.class); addMobsimScopeEventHandlerBinding().to(DriveDischargingHandler.class); + this.addQSimComponentBinding(EvModule.EV_COMPONENT).to(DriveDischargingHandler.class); // event handlers are not qsim components - this.bind(IdleDischargingHandler.class).in( Singleton.class ); + this.bind(IdleDischargingHandler.class).in(Singleton.class); addMobsimScopeEventHandlerBinding().to(IdleDischargingHandler.class); this.addQSimComponentBinding(EvModule.EV_COMPONENT).to(IdleDischargingHandler.class); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java index eae4a115384..53b29390fac 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DriveDischargingHandler.java @@ -19,7 +19,9 @@ package org.matsim.contrib.ev.discharging; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.matsim.api.core.v01.Id; @@ -35,6 +37,8 @@ import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.events.MobsimScopeEventHandler; +import org.matsim.core.mobsim.framework.events.MobsimAfterSimStepEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimAfterSimStepListener; import org.matsim.vehicles.Vehicle; import com.google.inject.Inject; @@ -44,8 +48,10 @@ * calculating the drive-related energy consumption. However, the time spent on the first link is used by the time-based * idle discharge process (see {@link IdleDischargingHandler}). */ -public class DriveDischargingHandler - implements LinkLeaveEventHandler, VehicleEntersTrafficEventHandler, VehicleLeavesTrafficEventHandler, MobsimScopeEventHandler { +public final class DriveDischargingHandler + implements LinkLeaveEventHandler, VehicleEntersTrafficEventHandler, VehicleLeavesTrafficEventHandler, MobsimScopeEventHandler, + MobsimAfterSimStepListener { + private static class EvDrive { private final Id vehicleId; private final ElectricVehicle ev; @@ -67,6 +73,9 @@ private boolean isOnFirstLink() { private final Map, ? extends ElectricVehicle> eVehicles; private final Map, EvDrive> evDrives; + private final List linkLeaveEvents = new ArrayList<>(); + private final List trafficLeaveEvents = new ArrayList<>(); + @Inject DriveDischargingHandler(ElectricFleet data, Network network, EventsManager eventsManager) { this.network = network; @@ -86,24 +95,35 @@ public void handleEvent(VehicleEntersTrafficEvent event) { @Override public void handleEvent(LinkLeaveEvent event) { - EvDrive evDrive = dischargeVehicle(event.getVehicleId(), event.getLinkId(), event.getTime()); - if (evDrive != null) { - evDrive.movedOverNodeTime = event.getTime(); - } + linkLeaveEvents.add(event); } @Override public void handleEvent(VehicleLeavesTrafficEvent event) { - EvDrive evDrive = dischargeVehicle(event.getVehicleId(), event.getLinkId(), event.getTime()); - if (evDrive != null) { - evDrives.remove(evDrive.vehicleId); + trafficLeaveEvents.add(event); + } + + @Override + public void notifyMobsimAfterSimStep(MobsimAfterSimStepEvent e) { + // We want to process events in the main thread (instead of the event handling threads). + // This is to eliminate race conditions, where the battery is read/modified by many threads without proper synchronisation + for (var event : linkLeaveEvents) { + EvDrive evDrive = dischargeVehicle(event.getVehicleId(), event.getLinkId(), event.getTime()); + if (evDrive != null) { + evDrive.movedOverNodeTime = event.getTime(); + } + } + linkLeaveEvents.clear(); + + for (var event : trafficLeaveEvents) { + EvDrive evDrive = dischargeVehicle(event.getVehicleId(), event.getLinkId(), event.getTime()); + if (evDrive != null) { + evDrives.remove(evDrive.vehicleId); + } } + trafficLeaveEvents.clear(); } - //XXX The current implementation is thread-safe because no other EventHandler modifies battery charge - // (for instance, AUX discharging and battery charging modifies charge outside event handling - // (as MobsimAfterSimStepListeners) - //TODO In the long term, it will be safer to move the discharging procedure to a MobsimAfterSimStepListener private EvDrive dischargeVehicle(Id vehicleId, Id linkId, double eventTime) { EvDrive evDrive = evDrives.get(vehicleId); if (evDrive != null && !evDrive.isOnFirstLink()) {// handle only our EVs, except for the first link @@ -111,11 +131,11 @@ private EvDrive dischargeVehicle(Id vehicleId, Id linkId, double double tt = eventTime - evDrive.movedOverNodeTime; ElectricVehicle ev = evDrive.ev; double energy = ev.getDriveEnergyConsumption().calcEnergyConsumption(link, tt, eventTime - tt) + ev.getAuxEnergyConsumption() - .calcEnergyConsumption(eventTime - tt, tt, linkId); + .calcEnergyConsumption(eventTime - tt, tt, linkId); //Energy consumption may be negative on links with negative slope ev.getBattery() - .dischargeEnergy(energy, - missingEnergy -> eventsManager.processEvent(new MissingEnergyEvent(eventTime, ev.getId(), link.getId(), missingEnergy))); + .dischargeEnergy(energy, + missingEnergy -> eventsManager.processEvent(new MissingEnergyEvent(eventTime, ev.getId(), link.getId(), missingEnergy))); eventsManager.processEvent(new DrivingEnergyConsumptionEvent(eventTime, vehicleId, linkId, energy, ev.getBattery().getCharge())); } return evDrive; diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DrivingEnergyConsumptionEvent.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DrivingEnergyConsumptionEvent.java index f00009b3e7a..3289446a72e 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DrivingEnergyConsumptionEvent.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/DrivingEnergyConsumptionEvent.java @@ -27,7 +27,7 @@ /** * @author Michal Maciejewski (michalm) */ -public class DrivingEnergyConsumptionEvent extends AbstractEnergyConsumptionEvent { +public final class DrivingEnergyConsumptionEvent extends AbstractEnergyConsumptionEvent { public static final String EVENT_TYPE = "drivingEnergyConsumption"; public DrivingEnergyConsumptionEvent(double time, Id vehicleId, Id linkId, double energy, double endCharge) { diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdleDischargingHandler.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdleDischargingHandler.java index da6fab88948..20a90c08e13 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdleDischargingHandler.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdleDischargingHandler.java @@ -45,7 +45,7 @@ * VehicleProvider is responsible to decide if AUX discharging applies to a given vehicle based on information from * ActivityStartEvent. */ -public class IdleDischargingHandler +public final class IdleDischargingHandler implements MobsimAfterSimStepListener, ActivityStartEventHandler, ActivityEndEventHandler, MobsimScopeEventHandler { public interface VehicleProvider { /** diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdlingEnergyConsumptionEvent.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdlingEnergyConsumptionEvent.java index d992dc363f5..fcb625ad7b6 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdlingEnergyConsumptionEvent.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/IdlingEnergyConsumptionEvent.java @@ -27,7 +27,7 @@ /** * @author Michal Maciejewski (michalm) */ -public class IdlingEnergyConsumptionEvent extends AbstractEnergyConsumptionEvent { +public final class IdlingEnergyConsumptionEvent extends AbstractEnergyConsumptionEvent { public static final String EVENT_TYPE = "idlingEnergyConsumption"; public IdlingEnergyConsumptionEvent(double time, Id vehicleId, Id linkId, double energy, double endCharge) { diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/LTHDriveEnergyConsumption.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/LTHDriveEnergyConsumption.java index afc8d17d93a..ea20f84a484 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/LTHDriveEnergyConsumption.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/LTHDriveEnergyConsumption.java @@ -32,7 +32,7 @@ import com.google.common.primitives.Doubles; -public class LTHDriveEnergyConsumption implements DriveEnergyConsumption { +public final class LTHDriveEnergyConsumption implements DriveEnergyConsumption { private final PiecewiseBicubicSplineInterpolatingFunction function; diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/MissingEnergyEvent.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/MissingEnergyEvent.java index a85c043522d..ef120c09fa9 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/MissingEnergyEvent.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/MissingEnergyEvent.java @@ -26,7 +26,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.vehicles.Vehicle; -public class MissingEnergyEvent extends Event { +public final class MissingEnergyEvent extends Event { public static final String EVENT_TYPE = "missing_energy"; public static final String ATTRIBUTE_VEHICLE = "vehicle"; public static final String ATTRIBUTE_ENERGY = "energy"; diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiAuxEnergyConsumption.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiAuxEnergyConsumption.java index 39d93db41c0..b344474bf9d 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiAuxEnergyConsumption.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiAuxEnergyConsumption.java @@ -27,7 +27,7 @@ import com.google.common.base.Preconditions; import com.google.inject.Inject; -public class OhdeSlaskiAuxEnergyConsumption implements AuxEnergyConsumption { +public final class OhdeSlaskiAuxEnergyConsumption implements AuxEnergyConsumption { private static final double a = 1.3;// [W] private static final double b = -63.4;// [W] private static final double c = 1748.1;// [W] @@ -46,7 +46,7 @@ private static double calcPower(double temp) { private final TemperatureService temperatureService; - public OhdeSlaskiAuxEnergyConsumption(TemperatureService temperatureService) { + OhdeSlaskiAuxEnergyConsumption(TemperatureService temperatureService) { this.temperatureService = temperatureService; } @@ -59,7 +59,7 @@ public static class Factory implements AuxEnergyConsumption.Factory { private final TemperatureService temperatureService; @Inject - public Factory(TemperatureService temperatureService) { + Factory(TemperatureService temperatureService) { this.temperatureService = temperatureService; } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiDriveEnergyConsumption.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiDriveEnergyConsumption.java index dafad6d76d1..44b62145719 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiDriveEnergyConsumption.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/OhdeSlaskiDriveEnergyConsumption.java @@ -30,7 +30,7 @@ * https://www.researchgate.net/profile/Michal-Maciejewski-3/publication/312393169_Statistical_analysis_of_real-world_urban_driving_cycles_for_modelling_energy_consumption_of_electric_vehicles/links/59b7a17faca2722453a5fc7f/Statistical-analysis-of-real-world-urban-driving-cycles-for-modelling-energy-consumption-of-electric-vehicles.pdf * TODO Add (dis-)charging efficiency relative to SOC, temperature, etc... */ -public class OhdeSlaskiDriveEnergyConsumption implements DriveEnergyConsumption { +public final class OhdeSlaskiDriveEnergyConsumption implements DriveEnergyConsumption { private static final double g = 9.81; // g [m/s^2] private static final double m = 1525; // vehicle mass [kg] private static final double m_s = m + 100; // vehicle mass + extra mass [kg] diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/VehicleTypeSpecificDriveEnergyConsumptionFactory.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/VehicleTypeSpecificDriveEnergyConsumptionFactory.java index b905a6f881e..4310d786b80 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/VehicleTypeSpecificDriveEnergyConsumptionFactory.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/discharging/VehicleTypeSpecificDriveEnergyConsumptionFactory.java @@ -29,7 +29,7 @@ import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.vehicles.VehicleType; -public class VehicleTypeSpecificDriveEnergyConsumptionFactory implements DriveEnergyConsumption.Factory { +public final class VehicleTypeSpecificDriveEnergyConsumptionFactory implements DriveEnergyConsumption.Factory { private final Map, DriveEnergyConsumption.Factory> consumptionMap = new HashMap<>(); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExample.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExample.java index 698dfe32e5c..e4980391db6 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExample.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExample.java @@ -23,7 +23,6 @@ import java.io.File; import java.io.IOException; -import java.net.URL; import java.util.Arrays; import org.apache.logging.log4j.LogManager; @@ -32,14 +31,12 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.EvModule; -import org.matsim.contrib.ev.charging.VehicleChargingHandler; import org.matsim.contrib.ev.routing.EvNetworkRoutingProvider; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.mobsim.qsim.AbstractQSimModule; import org.matsim.core.scenario.ScenarioUtils; public class RunEvExample { @@ -66,7 +63,7 @@ public static void main(String[] args) throws IOException { public void run( String[] args ) { Config config = ConfigUtils.loadConfig(args, new EvConfigGroup()); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); Scenario scenario = ScenarioUtils.loadScenario(config); Controler controler = new Controler(scenario); controler.addOverridingModule( new AbstractModule(){ @@ -74,8 +71,11 @@ public void run( String[] args ) { install( new EvModule() ); addRoutingModuleBinding( TransportMode.car ).toProvider(new EvNetworkRoutingProvider(TransportMode.car) ); - // a router that inserts charging activities when the battery is run empty. there may be some other way to insert - // charging activities, based on the situation. kai, dec'22 + // a router that inserts charging activities INTO THE ROUTE when the battery is run empty. This assumes that a full + // charge at the start of the route is not sufficient to drive the route. There are other settings where the + // situation is different, e.g. urban, where there may be a CHAIN of activities, and charging in general is done in + // parallel with some of these activities. That second situation is adressed by some "ev" code in the vsp contrib. + // kai, dec'22 } } ); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModel.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModel.java index 86db1f4478a..024bc2e4c54 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModel.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithLTHConsumptionModel.java @@ -24,7 +24,6 @@ import java.io.File; import java.io.IOException; -import java.net.URL; import java.util.Arrays; import org.apache.logging.log4j.LogManager; @@ -34,7 +33,6 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.EvModule; -import org.matsim.contrib.ev.charging.VehicleChargingHandler; import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; import org.matsim.contrib.ev.discharging.VehicleTypeSpecificDriveEnergyConsumptionFactory; @@ -46,7 +44,6 @@ import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.mobsim.qsim.AbstractQSimModule; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.vehicles.VehicleType; @@ -79,7 +76,7 @@ public static void main(String[] args) throws IOException { public void run( String[] args ) { Config config = ConfigUtils.loadConfig(args, new EvConfigGroup()); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); // === @@ -92,7 +89,7 @@ public void run( String[] args ) { VehicleTypeSpecificDriveEnergyConsumptionFactory driveEnergyConsumptionFactory = new VehicleTypeSpecificDriveEnergyConsumptionFactory(); var vehicleType = Id.create( "EV_65.0kWh", VehicleType.class ); driveEnergyConsumptionFactory.addEnergyConsumptionModelFactory( vehicleType, - new LTHConsumptionModelReader( vehicleType ).readURL( ConfigGroup.getInputFileURL( config.getContext(), "MidCarMap.csv" ) ) ); + new LTHConsumptionModelReader().readURL( ConfigGroup.getInputFileURL( config.getContext(), "MidCarMap.csv" ) ) ); controler.addOverridingModule( new EvModule() ); controler.addOverridingModule( new AbstractModule(){ @@ -109,6 +106,7 @@ public void install(){ } ); } + controler.run(); } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithOwnConsumptionModel.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithOwnConsumptionModel.java new file mode 100644 index 00000000000..eacaba778ad --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/example/RunEvExampleWithOwnConsumptionModel.java @@ -0,0 +1,137 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2016 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.ev.example; +/* + * created by jbischoff, 19.03.2019 + */ + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.contrib.ev.EvConfigGroup; +import org.matsim.contrib.ev.EvModule; +import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; +import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; +import org.matsim.contrib.ev.discharging.LTHDriveEnergyConsumption; +import org.matsim.contrib.ev.fleet.ElectricVehicle; +import org.matsim.contrib.ev.infrastructure.LTHConsumptionModelReader; +import org.matsim.contrib.ev.routing.EvNetworkRoutingProvider; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigGroup; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.scenario.ScenarioUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +/** + * Runs a sample EV run using a vehicle consumption model designed at LTH in Lund which takes the speed and the slope of a link into account. + * Link slopes may be added using a double array on the network. + * The consumption maps are based on Domingues, Gabriel. / Modeling, Optimization and Analysis of Electromobility Systems. Lund : Department of Biomedical Engineering, Lund university, 2018. 169 p., PhD thesis + */ +public class RunEvExampleWithOwnConsumptionModel{ + static final String DEFAULT_CONFIG_FILE = "test/input/org/matsim/contrib/ev/example/RunEvExample/config.xml"; + private static final Logger log = LogManager.getLogger( RunEvExampleWithOwnConsumptionModel.class ); + + public static void main(String[] args) throws IOException { + if (args.length > 0) { + log.info("Starting simulation run with the following arguments:"); + log.info("args=" + Arrays.toString( args ) ); + } else { + File localConfigFile = new File(DEFAULT_CONFIG_FILE); + if (localConfigFile.exists()) { + log.info("Starting simulation run with the local example config file"); + args = new String[]{ DEFAULT_CONFIG_FILE }; + } else { + log.info("Starting simulation run with the example config file from GitHub repository"); + args = new String[]{"https://raw.githubusercontent.com/matsim-org/matsim/master/contribs/ev/" + + DEFAULT_CONFIG_FILE }; + } + } + new RunEvExampleWithOwnConsumptionModel().run(args ); + } + + public void run( String[] args ) { + Config config = ConfigUtils.loadConfig(args, new EvConfigGroup()); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + + // === + + Scenario scenario = ScenarioUtils.loadScenario(config); + + // === + + Controler controler = new Controler(scenario); + { + DriveEnergyConsumption.Factory driveEnergyConsumptionFactory = new DriveEnergyConsumption.Factory(){ + @Override public DriveEnergyConsumption create( ElectricVehicle electricVehicle ){ + DriveEnergyConsumption.Factory factory = new LTHConsumptionModelReader().readURL( ConfigGroup.getInputFileURL( config.getContext(), "MidCarMap.csv" ) ); + DriveEnergyConsumption delegate = factory.create( electricVehicle ); + + DriveEnergyConsumption consumption = new DriveEnergyConsumption(){ + @Override public double calcEnergyConsumption( Link link, double travelTime, double linkEnterTime ){ + + // discharge because the link must be driven: + double delta = delegate.calcEnergyConsumption( link, travelTime, linkEnterTime ); + + double desiredSocAtEndOfLink = (double) electricVehicle.getVehicleSpecification().getMatsimVehicle().getAttributes().getAttribute( "whatever" ); + + return electricVehicle.getBattery().getSoc() - desiredSocAtEndOfLink; + // * above will often be negative; this is the purpose: discharging is negative i.e. we are + // charging on the link. ((This is why I am in general against hiding the sign in the method + // name. kai)) + + // * above is in SOC space, needs to be translated into kWh space + + // * need to make sure that the above charging is physically possible + + // * need to make sure that we are not discharging beyond what is needed to drive the link + + } + }; + return consumption; + } + }; + + controler.addOverridingModule( new EvModule() ); + controler.addOverridingModule( new AbstractModule(){ + @Override + public void install(){ + bind( DriveEnergyConsumption.Factory.class ).toInstance( driveEnergyConsumptionFactory ); + bind( AuxEnergyConsumption.Factory.class ).toInstance( + electricVehicle -> ( beginTime, duration, linkId ) -> 0 ); //a dummy factory, as aux consumption is part of the drive consumption in the model + + addRoutingModuleBinding( TransportMode.car ).toProvider( new EvNetworkRoutingProvider( TransportMode.car ) ); + // a router that inserts charging activities when the battery is run empty. there may be some other way to insert + // charging activities, based on the situation. kai, dec'22 + } + } ); + } + + + controler.run(); + } +} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryDefaultImpl.java similarity index 94% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryDefaultImpl.java index b3be0ab8a02..a9629018dfc 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/BatteryDefaultImpl.java @@ -23,11 +23,11 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; -public class BatteryImpl implements Battery { +final class BatteryDefaultImpl implements Battery { private final double capacity; private double charge; - public BatteryImpl(double capacity, double charge) { + BatteryDefaultImpl( double capacity, double charge ) { this.capacity = capacity; this.charge = charge; } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ConvertInitialChargeToInitialSoc.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ConvertInitialChargeToInitialSoc.java deleted file mode 100644 index 2f1475f964e..00000000000 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ConvertInitialChargeToInitialSoc.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * *********************************************************************** * - * project: org.matsim.* - * *********************************************************************** * - * * - * copyright : (C) 2022 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.ev.fleet; - -import static org.matsim.contrib.ev.fleet.ElectricVehicleSpecificationImpl.INITIAL_SOC; - -import org.matsim.vehicles.MatsimVehicleReader; -import org.matsim.vehicles.MatsimVehicleWriter; -import org.matsim.vehicles.VehicleUtils; - -/** - * @author Michal Maciejewski (michalm) - */ -public class ConvertInitialChargeToInitialSoc { - private static final String INITIAL_ENERGY_kWh = "initialEnergyInKWh"; - - public static void run(String file) { - var vehicles = VehicleUtils.createVehiclesContainer(); - var reader = new MatsimVehicleReader(vehicles); - reader.readFile(file); - - for (var v : vehicles.getVehicles().values()) { - double battery_kWh = VehicleUtils.getEnergyCapacity(v.getType().getEngineInformation()); - double initial_kWh = (double)v.getAttributes().getAttribute(INITIAL_ENERGY_kWh); - double initial_soc = initial_kWh / battery_kWh; - v.getAttributes().removeAttribute(INITIAL_ENERGY_kWh); - v.getAttributes().putAttribute(INITIAL_SOC, initial_soc); - } - - var writer = new MatsimVehicleWriter(vehicles); - writer.writeFile(file); - } -} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetModule.java index fc6493ae3b8..179a6ec9116 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetModule.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetModule.java @@ -37,7 +37,7 @@ /** * @author Michal Maciejewski (michalm) */ -public class ElectricFleetModule extends AbstractModule { +public final class ElectricFleetModule extends AbstractModule { @Inject private EvConfigGroup evCfg; @@ -46,9 +46,9 @@ public void install() { bind(ElectricFleetSpecification.class).toProvider(new Provider<>() { @Inject private Vehicles vehicles; @Override public ElectricFleetSpecification get() { - ElectricFleetSpecification fleetSpecification = new ElectricFleetSpecificationImpl(); - ElectricVehicleSpecificationImpl.createAndAddVehicleSpecificationsFromMatsimVehicles(fleetSpecification, - vehicles.getVehicles().values()); + ElectricFleetSpecification fleetSpecification = new ElectricFleetSpecificationDefaultImpl(); + ElectricFleetUtils.createAndAddVehicleSpecificationsFromMatsimVehicles(fleetSpecification, + vehicles.getVehicles().values() ); return fleetSpecification; } }).asEagerSingleton(); @@ -64,8 +64,8 @@ protected void configureQSim() { @Override public ElectricFleet get() { - return ElectricFleets.createDefaultFleet(fleetSpecification, driveConsumptionFactory, auxConsumptionFactory, - chargingPowerFactory); + return ElectricFleetUtils.createDefaultFleet(fleetSpecification, driveConsumptionFactory, auxConsumptionFactory, + chargingPowerFactory ); } }).asEagerSingleton(); @@ -77,7 +77,7 @@ public ElectricFleet get() { for (var oldSpec : electricFleetSpecification.getVehicleSpecifications().values()) { var matsimVehicle = oldSpec.getMatsimVehicle(); double socAtEndOfCurrentIteration = electricFleet.getElectricVehicles().get(oldSpec.getId()).getBattery().getSoc(); - ElectricVehicleSpecifications.setInitialSoc(matsimVehicle, socAtEndOfCurrentIteration); + ElectricFleetUtils.setInitialSoc(matsimVehicle, socAtEndOfCurrentIteration ); } } }); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java similarity index 96% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java index 14a70682741..b24583feb13 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java @@ -30,7 +30,7 @@ /** * @author Michal Maciejewski (michalm) */ -public final class ElectricFleetSpecificationImpl implements ElectricFleetSpecification { +final class ElectricFleetSpecificationDefaultImpl implements ElectricFleetSpecification { private final Map, ElectricVehicleSpecification> specifications = new LinkedHashMap<>(); @Override diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetUtils.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetUtils.java new file mode 100644 index 00000000000..0e92b52759c --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetUtils.java @@ -0,0 +1,90 @@ +/* + * *********************************************************************** * + * project: org.matsim.* + * *********************************************************************** * + * * + * copyright : (C) 2022 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.ev.fleet; + +import java.util.Collection; +import java.util.Objects; + +import com.google.common.collect.ImmutableMap; +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.ev.charging.ChargingPower; +import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; +import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; +import org.matsim.vehicles.*; + +public final class ElectricFleetUtils { + public static final String EV_ENGINE_HBEFA_TECHNOLOGY = "electricity"; + public static final String INITIAL_SOC = "initialSoc";// in [0, 1] + public static final String CHARGER_TYPES = "chargerTypes"; + private static final String INITIAL_ENERGY_kWh = "initialEnergyInKWh"; + private ElectricFleetUtils(){} // do not instantiate + public static void setInitialSoc(Vehicle vehicle, double initialSoc) { + vehicle.getAttributes().putAttribute( INITIAL_SOC, initialSoc ); + } + + public static void setChargerTypes(EngineInformation engineInformation, Collection chargerTypes) { + engineInformation.getAttributes().putAttribute( CHARGER_TYPES, chargerTypes ); + } + public static void run(String file) { + var vehicles = VehicleUtils.createVehiclesContainer(); + var reader = new MatsimVehicleReader(vehicles); + reader.readFile(file); + + for (var v : vehicles.getVehicles().values()) { + double battery_kWh = VehicleUtils.getEnergyCapacity(v.getType().getEngineInformation()); + double initial_kWh = (double)v.getAttributes().getAttribute(INITIAL_ENERGY_kWh); + double initial_soc = initial_kWh / battery_kWh; + v.getAttributes().removeAttribute(INITIAL_ENERGY_kWh); + v.getAttributes().putAttribute(INITIAL_SOC, initial_soc); + } + + var writer = new MatsimVehicleWriter(vehicles); + writer.writeFile(file); + } + public static ElectricVehicle create( ElectricVehicleSpecification vehicleSpecification, + DriveEnergyConsumption.Factory driveFactory, AuxEnergyConsumption.Factory auxFactory, + ChargingPower.Factory chargingFactory ) { + ElectricVehicleDefaultImpl ev = new ElectricVehicleDefaultImpl(vehicleSpecification); + ev.driveEnergyConsumption = Objects.requireNonNull(driveFactory.create(ev ) ); + ev.auxEnergyConsumption = Objects.requireNonNull(auxFactory.create(ev)); + ev.chargingPower = Objects.requireNonNull(chargingFactory.create(ev)); + return ev; + } + public static void createAndAddVehicleSpecificationsFromMatsimVehicles(ElectricFleetSpecification fleetSpecification, Collection vehicles) { + vehicles.stream() + .filter(vehicle -> EV_ENGINE_HBEFA_TECHNOLOGY.equals(VehicleUtils.getHbefaTechnology(vehicle.getType().getEngineInformation()))) + .map( ElectricVehicleSpecificationDefaultImpl::new ) + .forEach(fleetSpecification::addVehicleSpecification); + } + public static ElectricVehicleSpecification createElectricVehicleSpecificationDefaultImpl( Vehicle matsimVehicle ){ + return new ElectricVehicleSpecificationDefaultImpl( matsimVehicle ); + } + public static ElectricFleet createDefaultFleet(ElectricFleetSpecification fleetSpecification, + DriveEnergyConsumption.Factory driveConsumptionFactory, AuxEnergyConsumption.Factory auxConsumptionFactory, + ChargingPower.Factory chargingFactory) { + ImmutableMap, ElectricVehicle> vehicles = fleetSpecification.getVehicleSpecifications() + .values() + .stream() + .map(s -> create(s, driveConsumptionFactory, auxConsumptionFactory, chargingFactory )) + .collect(ImmutableMap.toImmutableMap(ElectricVehicle::getId, v -> v)); + return () -> vehicles; + } +} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleets.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleets.java deleted file mode 100644 index fbe29db0ab3..00000000000 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleets.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * *********************************************************************** * - * project: org.matsim.* - * *********************************************************************** * - * * - * copyright : (C) 2019 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** * - */ - -package org.matsim.contrib.ev.fleet; - -import org.matsim.api.core.v01.Id; -import org.matsim.contrib.ev.charging.ChargingPower; -import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; -import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; -import org.matsim.vehicles.Vehicle; - -import com.google.common.collect.ImmutableMap; - -public class ElectricFleets { - public static ElectricFleet createDefaultFleet(ElectricFleetSpecification fleetSpecification, - DriveEnergyConsumption.Factory driveConsumptionFactory, AuxEnergyConsumption.Factory auxConsumptionFactory, - ChargingPower.Factory chargingFactory) { - ImmutableMap, ElectricVehicle> vehicles = fleetSpecification.getVehicleSpecifications() - .values() - .stream() - .map(s -> ElectricVehicleImpl.create(s, driveConsumptionFactory, auxConsumptionFactory, - chargingFactory)) - .collect(ImmutableMap.toImmutableMap(ElectricVehicle::getId, v -> v)); - return () -> vehicles; - } -} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleDefaultImpl.java similarity index 74% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleDefaultImpl.java index 91125b6500d..c4da3c65565 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleDefaultImpl.java @@ -20,8 +20,6 @@ package org.matsim.contrib.ev.fleet; -import java.util.Objects; - import org.matsim.api.core.v01.Id; import org.matsim.contrib.ev.charging.ChargingPower; import org.matsim.contrib.ev.discharging.AuxEnergyConsumption; @@ -31,27 +29,18 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; -public class ElectricVehicleImpl implements ElectricVehicle { - public static ElectricVehicle create(ElectricVehicleSpecification vehicleSpecification, - DriveEnergyConsumption.Factory driveFactory, AuxEnergyConsumption.Factory auxFactory, - ChargingPower.Factory chargingFactory) { - ElectricVehicleImpl ev = new ElectricVehicleImpl(vehicleSpecification); - ev.driveEnergyConsumption = Objects.requireNonNull(driveFactory.create(ev)); - ev.auxEnergyConsumption = Objects.requireNonNull(auxFactory.create(ev)); - ev.chargingPower = Objects.requireNonNull(chargingFactory.create(ev)); - return ev; - } +final class ElectricVehicleDefaultImpl implements ElectricVehicle { private final ElectricVehicleSpecification vehicleSpecification; private final Battery battery; - private DriveEnergyConsumption driveEnergyConsumption; - private AuxEnergyConsumption auxEnergyConsumption; - private ChargingPower chargingPower; + DriveEnergyConsumption driveEnergyConsumption; + AuxEnergyConsumption auxEnergyConsumption; + ChargingPower chargingPower; - private ElectricVehicleImpl(ElectricVehicleSpecification vehicleSpecification) { + ElectricVehicleDefaultImpl( ElectricVehicleSpecification vehicleSpecification ) { this.vehicleSpecification = vehicleSpecification; - battery = new BatteryImpl(vehicleSpecification.getBatteryCapacity(), vehicleSpecification.getInitialCharge()); + battery = new BatteryDefaultImpl(vehicleSpecification.getBatteryCapacity(), vehicleSpecification.getInitialCharge()); } @Override diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java similarity index 73% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java index 70d4c100f77..ef18ae6cce6 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java @@ -33,23 +33,11 @@ /** * @author Michal Maciejewski (michalm) */ -public class ElectricVehicleSpecificationImpl implements ElectricVehicleSpecification { - public static final String EV_ENGINE_HBEFA_TECHNOLOGY = "electricity"; - - public static final String INITIAL_SOC = "initialSoc";// in [0, 1] - public static final String CHARGER_TYPES = "chargerTypes"; - - public static void createAndAddVehicleSpecificationsFromMatsimVehicles(ElectricFleetSpecification fleetSpecification, - Collection vehicles) { - vehicles.stream() - .filter(vehicle -> EV_ENGINE_HBEFA_TECHNOLOGY.equals(VehicleUtils.getHbefaTechnology(vehicle.getType().getEngineInformation()))) - .map(ElectricVehicleSpecificationImpl::new) - .forEach(fleetSpecification::addVehicleSpecification); - } +final class ElectricVehicleSpecificationDefaultImpl implements ElectricVehicleSpecification { private final Vehicle matsimVehicle; - public ElectricVehicleSpecificationImpl(Vehicle matsimVehicle) { + ElectricVehicleSpecificationDefaultImpl( Vehicle matsimVehicle ) { this.matsimVehicle = matsimVehicle; //provided per vehicle type (in engine info) Preconditions.checkArgument(getInitialSoc() >= 0 && getInitialSoc() <= 1, "Invalid initialCharge or batteryCapacity of vehicle: %s", getId()); @@ -68,12 +56,12 @@ public Vehicle getMatsimVehicle() { @Override public ImmutableList getChargerTypes() { var engineInfo = matsimVehicle.getType().getEngineInformation(); - return ImmutableList.copyOf((Collection)engineInfo.getAttributes().getAttribute(CHARGER_TYPES)); + return ImmutableList.copyOf((Collection)engineInfo.getAttributes().getAttribute( ElectricFleetUtils.CHARGER_TYPES ) ); } @Override public double getInitialSoc() { - return (double)matsimVehicle.getAttributes().getAttribute(INITIAL_SOC); + return (double)matsimVehicle.getAttributes().getAttribute( ElectricFleetUtils.INITIAL_SOC ); } @Override diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecifications.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecifications.java deleted file mode 100644 index 225d5be689d..00000000000 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecifications.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * *********************************************************************** * - * project: org.matsim.* - * *********************************************************************** * - * * - * copyright : (C) 2022 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.ev.fleet; - -import java.util.Collection; - -import org.matsim.vehicles.EngineInformation; -import org.matsim.vehicles.Vehicle; - -public class ElectricVehicleSpecifications { - public static void setInitialSoc(Vehicle vehicle, double initialSoc) { - vehicle.getAttributes().putAttribute(ElectricVehicleSpecificationImpl.INITIAL_SOC, initialSoc); - } - - public static void setChargerTypes(EngineInformation engineInformation, Collection chargerTypes) { - engineInformation.getAttributes().putAttribute(ElectricVehicleSpecificationImpl.CHARGER_TYPES, chargerTypes); - } -} diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerDefaultImpl.java similarity index 95% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerDefaultImpl.java index a37cd181c9e..d2efb68578d 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerDefaultImpl.java @@ -29,13 +29,13 @@ import com.google.common.base.Preconditions; -public class ChargerImpl implements Charger { +class ChargerDefaultImpl implements Charger { private final ChargerSpecification specification; private final Link link; private final ChargingLogic logic; - public ChargerImpl(ChargerSpecification specification, Link link, ChargingLogic logic) { + ChargerDefaultImpl( ChargerSpecification specification, Link link, ChargingLogic logic ) { Preconditions.checkArgument(link.getId().equals(specification.getLinkId()), "link.id != specification.linkId"); this.specification = specification; this.link = link; diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerReader.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerReader.java index e69bbe48216..9864622b4cb 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerReader.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerReader.java @@ -28,12 +28,13 @@ import org.matsim.core.utils.io.MatsimXmlParser; import org.xml.sax.Attributes; -public class ChargerReader extends MatsimXmlParser { +public final class ChargerReader extends MatsimXmlParser { private final static String CHARGER = "charger"; private final ChargingInfrastructureSpecification chargingInfrastructure; public ChargerReader(ChargingInfrastructureSpecification chargingInfrastructure) { + super(ValidationType.DTD_ONLY); this.chargingInfrastructure = chargingInfrastructure; } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerWriter.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerWriter.java index bb03bc9f063..8955b266984 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerWriter.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargerWriter.java @@ -29,7 +29,7 @@ import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.io.MatsimXmlWriter; -public class ChargerWriter extends MatsimXmlWriter { +public final class ChargerWriter extends MatsimXmlWriter { private final Stream chargerSpecifications; public ChargerWriter(Stream chargerSpecifications) { diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureModule.java index ea2e2526afb..f44bee53f11 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureModule.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureModule.java @@ -36,7 +36,7 @@ /** * @author Michal Maciejewski (michalm) */ -public class ChargingInfrastructureModule extends AbstractModule { +public final class ChargingInfrastructureModule extends AbstractModule { public static final String CHARGERS = "chargers"; private final Key networkKey; @@ -56,7 +56,7 @@ public void install() { bind(Network.class).annotatedWith(Names.named(CHARGERS)).to(networkKey).asEagerSingleton(); bind(ChargingInfrastructureSpecification.class).toProvider(() -> { - ChargingInfrastructureSpecification chargingInfrastructureSpecification = new ChargingInfrastructureSpecificationImpl(); + ChargingInfrastructureSpecification chargingInfrastructureSpecification = new ChargingInfrastructureSpecificationDefaultImpl(); new ChargerReader(chargingInfrastructureSpecification).parse( ConfigGroup.getInputFileURL(getConfig().getContext(), evCfg.chargersFile)); return chargingInfrastructureSpecification; @@ -76,8 +76,8 @@ protected void configureQSim() { @Override public ChargingInfrastructure get() { - return ChargingInfrastructures.createChargingInfrastructure(chargingInfrastructureSpecification, - network.getLinks()::get, chargingLogicFactory); + return ChargingInfrastructureUtils.createChargingInfrastructure(chargingInfrastructureSpecification, + network.getLinks()::get, chargingLogicFactory ); } }).asEagerSingleton(); } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java similarity index 95% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationImpl.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java index ceeba42372f..48a27563616 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java @@ -28,7 +28,7 @@ /** * @author Michal Maciejewski (michalm) */ -public class ChargingInfrastructureSpecificationImpl implements ChargingInfrastructureSpecification { +final class ChargingInfrastructureSpecificationDefaultImpl implements ChargingInfrastructureSpecification { private final SpecificationContainer container = new SpecificationContainer<>(); @Override diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructures.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureUtils.java similarity index 90% rename from contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructures.java rename to contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureUtils.java index b86a140fcbb..e4e7ca5c4fa 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructures.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureUtils.java @@ -37,8 +37,10 @@ /** * @author Michal Maciejewski (michalm) */ -public class ChargingInfrastructures { - static final Logger log = LogManager.getLogger(ChargingInfrastructures.class); +public class ChargingInfrastructureUtils{ + static final Logger log = LogManager.getLogger( ChargingInfrastructureUtils.class ); + + private ChargingInfrastructureUtils(){} // do not instantiate public static ChargingInfrastructure createChargingInfrastructure( ChargingInfrastructureSpecification infrastructureSpecification, Function, Link> linkProvider, @@ -46,7 +48,7 @@ public static ChargingInfrastructure createChargingInfrastructure( var chargers = infrastructureSpecification.getChargerSpecifications() .values() .stream() - .map(s -> new ChargerImpl(s, linkProvider.apply(s.getLinkId()), chargingLogicFactory.create(s))) + .map(s -> new ChargerDefaultImpl(s, linkProvider.apply(s.getLinkId() ), chargingLogicFactory.create(s )) ) .collect(ImmutableMap.toImmutableMap(Charger::getId, c -> (Charger)c)); return () -> chargers; } @@ -72,7 +74,7 @@ public static ChargingInfrastructure createModalNetworkChargers(ChargingInfrastr var reachableLinks = network.getLinks(); var filteredChargers = infrastructure.getChargers().values().stream().map(c -> { var link = reachableLinks.get(c.getLink().getId()); - return link == null ? null : new ChargerImpl(c.getSpecification(), link, c.getLogic()); + return link == null ? null : new ChargerDefaultImpl(c.getSpecification(), link, c.getLogic()); }).filter(Objects::nonNull).collect(ImmutableMap.toImmutableMap(Charger::getId, c -> (Charger)c)); int chargerCount = infrastructure.getChargers().size(); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ImmutableChargerSpecification.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ImmutableChargerSpecification.java index aa08f5d6b85..a3d7192b793 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ImmutableChargerSpecification.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ImmutableChargerSpecification.java @@ -40,7 +40,7 @@ public class ImmutableChargerSpecification implements ChargerSpecification { private final double plugPower; private final int plugCount; - private ImmutableChargerSpecification(Builder builder) { + private ImmutableChargerSpecification( ChargerSpecificationBuilder builder ) { id = Objects.requireNonNull(builder.id); linkId = Objects.requireNonNull(builder.linkId); chargerType = Objects.requireNonNull(builder.chargerType); @@ -51,12 +51,12 @@ private ImmutableChargerSpecification(Builder builder) { Preconditions.checkArgument(plugCount >= 0, "Negative plugCount of charger: %s", id); } - public static Builder newBuilder() { - return new Builder(); + public static ChargerSpecificationBuilder newBuilder() { + return new ChargerSpecificationBuilder(); } - public static Builder newBuilder(ChargerSpecification copy) { - Builder builder = new Builder(); + public static ChargerSpecificationBuilder newBuilder( ChargerSpecification copy ) { + ChargerSpecificationBuilder builder = new ChargerSpecificationBuilder(); builder.id = copy.getId(); builder.linkId = copy.getLinkId(); builder.chargerType = copy.getChargerType(); @@ -101,37 +101,37 @@ public String toString() { .toString(); } - public static final class Builder { + public static final class ChargerSpecificationBuilder{ private Id id; private Id linkId; private String chargerType; private Double plugPower; private Integer plugCount; - private Builder() { + private ChargerSpecificationBuilder() { } - public Builder id(Id val) { + public ChargerSpecificationBuilder id( Id val ) { id = val; return this; } - public Builder linkId(Id val) { + public ChargerSpecificationBuilder linkId( Id val ) { linkId = val; return this; } - public Builder chargerType(String val) { + public ChargerSpecificationBuilder chargerType( String val ) { chargerType = val; return this; } - public Builder plugPower(double val) { + public ChargerSpecificationBuilder plugPower( double val ) { plugPower = val; return this; } - public Builder plugCount(int val) { + public ChargerSpecificationBuilder plugCount( int val ) { plugCount = val; return this; } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/LTHConsumptionModelReader.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/LTHConsumptionModelReader.java index 5cb4e90d129..0b8e732e85a 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/LTHConsumptionModelReader.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/LTHConsumptionModelReader.java @@ -28,6 +28,7 @@ import java.util.List; import org.matsim.api.core.v01.Id; +import org.matsim.contrib.ev.discharging.DriveEnergyConsumption; import org.matsim.contrib.ev.discharging.LTHDriveEnergyConsumption; import org.matsim.core.utils.io.tabularFileParser.TabularFileHandler; import org.matsim.core.utils.io.tabularFileParser.TabularFileParser; @@ -43,10 +44,10 @@ */ public class LTHConsumptionModelReader { - public LTHConsumptionModelReader(Id vehicleTypeId) { + public LTHConsumptionModelReader() { } - public LTHDriveEnergyConsumption.Factory readURL(URL fileUrl) { + public DriveEnergyConsumption.Factory readURL( URL fileUrl ) { List speeds = new ArrayList<>(); List slopes = new ArrayList<>(); TabularFileParserConfig tabularFileParserConfig = new TabularFileParserConfig(); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/package-info.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/package-info.java index b8acc39a3f7..1ae999170d8 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/package-info.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/package-info.java @@ -17,8 +17,6 @@ * * * *********************************************************************** */ -package org.matsim.contrib.ev; - /** * All values used in this package use SI base and derived units. In particular: *