From 5bbf22b366c162c73dd6c5d1a7a41d8369e591d3 Mon Sep 17 00:00:00 2001 From: u229187 Date: Mon, 16 Oct 2023 15:22:02 +0200 Subject: [PATCH 1/7] updates the license and warranty files to not include author names anymore. Also update the years. --- matsim/LICENSE | 13 ++++--------- matsim/WARRANTY | 9 ++------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/matsim/LICENSE b/matsim/LICENSE index e28a6ab76d8..c3676097f92 100755 --- a/matsim/LICENSE +++ b/matsim/LICENSE @@ -1,14 +1,9 @@ ########################################################################### -org.matsim java packages - -Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 by - Kay W. Axhausen, Michael Balmer, Christoph Dobler, Thibaut Dubernet, - Dominik Grether,Andreas Horni, Gregor Laemmel, Nicolas Lefebvre, - Fabrice Marchal, Konrad Meister, Kai Nagel, Andreas Neumann, - Marcel Rieser, David Strippgen, Rashid Waraich, Michael Zilske, - Technische Universitaet Berlin (TU-Berlin) and - Swiss Federal Institute of Technology Zurich (ETHZ) +org.matsim +and ch.sbb.matsim java packages +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 by + The MATSim Contributors This program is free software; you can redistribute it and/or modify diff --git a/matsim/WARRANTY b/matsim/WARRANTY index 450ba1b281c..ff03dcd5ce2 100755 --- a/matsim/WARRANTY +++ b/matsim/WARRANTY @@ -1,12 +1,7 @@ ########################################################################### -Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 by - Kay W. Axhausen, Michael Balmer, Christoph Dobler, Thibaut Dubernet, - Dominik Grether,Andreas Horni, Gregor Laemmel, Nicolas Lefebvre, - Fabrice Marchal, Konrad Meister, Kai Nagel, Andreas Neumann, - Marcel Rieser, David Strippgen, Rashid Waraich, Michael Zilske, - Technische Universitaet Berlin (TU-Berlin) and - Swiss Federal Institute of Technology Zurich (ETHZ) +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 by + The MATSim Contributors The org.matsim java packages are distributed in the hope that they will From e42801ef639c0714bcc50f1b154c2163646228d8 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 28 Nov 2023 12:21:17 +0100 Subject: [PATCH 2/7] Configurable initial DRT estimators (#2968) * add interface and implementation for initial drt estimates * remove left over toods --- .../estimator/DrtInitialEstimator.java | 9 ++ .../{ => impl}/BasicDrtEstimator.java | 30 ++---- .../estimator/impl/ConstantDrtEstimator.java | 50 ++++++++++ .../impl/PessimisticDrtEstimator.java | 40 ++++++++ .../run/DrtEstimatorConfigGroup.java | 13 +++ .../estimator/run/DrtEstimatorModule.java | 45 ++++++++- .../MultiModaFixedDrtLegEstimatorTest.java | 93 +++++++++++++++++++ 7 files changed, 256 insertions(+), 24 deletions(-) create mode 100644 contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtInitialEstimator.java rename contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/{ => impl}/BasicDrtEstimator.java (85%) create mode 100644 contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/ConstantDrtEstimator.java create mode 100644 contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/PessimisticDrtEstimator.java create mode 100644 contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModaFixedDrtLegEstimatorTest.java diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtInitialEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtInitialEstimator.java new file mode 100644 index 00000000000..a826a936e46 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtInitialEstimator.java @@ -0,0 +1,9 @@ +package org.matsim.contrib.drt.extension.estimator; + +/** + * This interface is used to provide an initial estimate for the drt service. + * Supposed to be used when no data is available from the simulation yet. + * The interface is exactly the same as {@link DrtEstimator}, but this class won't be called with update events. + */ +public interface DrtInitialEstimator extends DrtEstimator { +} 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/impl/BasicDrtEstimator.java similarity index 85% rename from contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java rename to contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/BasicDrtEstimator.java index d446d5f34bc..f1fd6f04c8f 100644 --- 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/impl/BasicDrtEstimator.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.extension.estimator; +package org.matsim.contrib.drt.extension.estimator.impl; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.commons.math3.stat.regression.RegressionResults; @@ -9,8 +9,9 @@ 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.DrtEstimator; +import org.matsim.contrib.drt.extension.estimator.DrtInitialEstimator; 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; @@ -32,6 +33,7 @@ public class BasicDrtEstimator implements DrtEstimator, IterationEndsListener { private final DrtEventSequenceCollector collector; private final DrtEstimatorConfigGroup config; private final DrtConfigGroup drtConfig; + private final DrtInitialEstimator initial; private final SplittableRandom rnd = new SplittableRandom(); /** @@ -40,10 +42,11 @@ public class BasicDrtEstimator implements DrtEstimator, IterationEndsListener { private GlobalEstimate currentEst; private RegressionResults fare; - public BasicDrtEstimator(DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config, - DrtConfigGroup drtConfig) { + public BasicDrtEstimator(DrtEventSequenceCollector collector, DrtInitialEstimator initial, + DrtEstimatorConfigGroup config, DrtConfigGroup drtConfig) { //zones = injector.getModal(DrtZonalSystem.class); this.collector = collector; + this.initial = initial; this.config = config; this.drtConfig = drtConfig; } @@ -115,23 +118,8 @@ public void notifyIterationEnds(IterationEndsEvent event) { 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); + // Same interface, just different binding + return initial.estimate(route, departureTime); } double fare = 0; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/ConstantDrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/ConstantDrtEstimator.java new file mode 100644 index 00000000000..d9a4d4be46f --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/ConstantDrtEstimator.java @@ -0,0 +1,50 @@ +package org.matsim.contrib.drt.extension.estimator.impl; + +import org.matsim.contrib.drt.extension.estimator.DrtInitialEstimator; +import org.matsim.contrib.drt.fare.DrtFareParams; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.core.utils.misc.OptionalTime; + +/** + * Estimates using a constant detour factor and waiting time. + */ +public class ConstantDrtEstimator implements DrtInitialEstimator { + + private final DrtConfigGroup drtConfig; + + /** + * Detour factor for the estimate. 1.0 means no detour, 2.0 means twice the distance. + */ + private final double detourFactor; + + /** + * Constant waiting time estimate in seconds. + */ + private final double waitingTime; + + public ConstantDrtEstimator(DrtConfigGroup drtConfig, double detourFactor, double waitingTime) { + this.drtConfig = drtConfig; + this.detourFactor = detourFactor; + this.waitingTime = waitingTime; + } + + @Override + public Estimate estimate(DrtRoute route, OptionalTime departureTime) { + + double distance = route.getDistance() * detourFactor; + double travelTime = route.getDirectRideTime() * detourFactor; + + double fare = 0; + if (drtConfig.getDrtFareParams().isPresent()) { + DrtFareParams fareParams = drtConfig.getDrtFareParams().get(); + fare = fareParams.distanceFare_m * distance + + fareParams.timeFare_h * travelTime / 3600.0 + + fareParams.baseFare; + + fare = Math.max(fare, fareParams.minFarePerTrip); + } + + return new Estimate(distance, travelTime, waitingTime, fare, 0); + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/PessimisticDrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/PessimisticDrtEstimator.java new file mode 100644 index 00000000000..d26a318c635 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/impl/PessimisticDrtEstimator.java @@ -0,0 +1,40 @@ +package org.matsim.contrib.drt.extension.estimator.impl; + +import org.matsim.contrib.drt.extension.estimator.DrtInitialEstimator; +import org.matsim.contrib.drt.fare.DrtFareParams; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.core.utils.misc.OptionalTime; + +/** + * Uses the upper bounds from config for the initial estimate. + */ +public class PessimisticDrtEstimator implements DrtInitialEstimator { + private final DrtConfigGroup drtConfig; + + public PessimisticDrtEstimator(DrtConfigGroup drtConfig) { + this.drtConfig = drtConfig; + } + + @Override + public Estimate estimate(DrtRoute route, OptionalTime departureTime) { + // 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); + } + +} 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 index 107aa2eb53a..985435bcfc3 100644 --- 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 @@ -16,6 +16,11 @@ public class DrtEstimatorConfigGroup extends ReflectiveConfigGroupWithConfigurab public enum EstimatorType { BASIC, + /** + * Will use the bound initial estimator, without any updates. + */ + INITIAL, + /** * Custom estimator, that needs to provided via binding. */ @@ -58,4 +63,12 @@ public String getMode() { return mode; } + /** + * Set estimator type and return same instance. + */ + public DrtEstimatorConfigGroup withEstimator(EstimatorType estimator) { + this.estimator = estimator; + return this; + } + } 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 index 14bc2ae330d..c5a8ed00e7c 100644 --- 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 @@ -3,9 +3,11 @@ 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.extension.estimator.DrtInitialEstimator; +import org.matsim.contrib.drt.extension.estimator.impl.BasicDrtEstimator; +import org.matsim.contrib.drt.extension.estimator.impl.PessimisticDrtEstimator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; @@ -14,13 +16,22 @@ import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.AbstractModule; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; /** * Main module that needs to be installed if any estimator is to be used. */ public class DrtEstimatorModule extends AbstractModule { + + /** + * Map of initial providers. + */ + private final Map> initial = new HashMap<>(); + @Override public void install() { @@ -35,7 +46,24 @@ public void install() { } } - static final class ModeModule extends AbstractDvrpModeModule { + /** + * Configure initial estimators for the given modes. + * + * @param getter modal getter, which can be used to use other modal components via guice + */ + public DrtEstimatorModule withInitialEstimator(Function getter, String... modes) { + + if (modes.length == 0) + throw new IllegalArgumentException("At least one mode needs to be provided."); + + for (String mode : modes) { + initial.put(mode, getter); + } + + return this; + } + + final class ModeModule extends AbstractDvrpModeModule { private final DrtConfigGroup cfg; private final DrtEstimatorConfigGroup group; @@ -52,10 +80,21 @@ 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) + getter -> new BasicDrtEstimator( + getter.getModal(DrtEventSequenceCollector.class), + getter.getModal(DrtInitialEstimator.class), + group, cfg + ) )).in(Singleton.class); + } else if (group.estimator == DrtEstimatorConfigGroup.EstimatorType.INITIAL) { + bindModal(DrtEstimator.class).to(modalKey(DrtInitialEstimator.class)); } + if (initial.containsKey(group.mode)) { + bindModal(DrtInitialEstimator.class).toProvider(() -> initial.get(group.mode).apply(cfg)).in(Singleton.class); + } else + bindModal(DrtInitialEstimator.class).toInstance(new PessimisticDrtEstimator(cfg)); + // DRT Estimators will be available as Map MapBinder.newMapBinder(this.binder(), DvrpMode.class, DrtEstimator.class) .addBinding(DvrpModes.mode(getMode())) diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModaFixedDrtLegEstimatorTest.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModaFixedDrtLegEstimatorTest.java new file mode 100644 index 00000000000..de1c98e62d1 --- /dev/null +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModaFixedDrtLegEstimatorTest.java @@ -0,0 +1,93 @@ +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.impl.ConstantDrtEstimator; +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 MultiModaFixedDrtLegEstimatorTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + private Controler controler; + + 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() + .withInitialEstimator(cfg -> new ConstantDrtEstimator(cfg, 1.05, 300), "drt", "av")); + } + + private static void prepare(Config config) { + + MultiModeDrtEstimatorConfigGroup estimators = ConfigUtils.addOrGetModule(config, MultiModeDrtEstimatorConfigGroup.class); + + estimators.addParameterSet(new DrtEstimatorConfigGroup("drt") + .withEstimator(DrtEstimatorConfigGroup.EstimatorType.INITIAL)); + 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)); + + } + + @Before + public void setUp() throws Exception { + + Config config = DrtTestScenario.loadConfig(utils); + + config.controller().setLastIteration(3); + + controler = MATSimApplication.prepare(new DrtTestScenario(MultiModaFixedDrtLegEstimatorTest::prepare, MultiModaFixedDrtLegEstimatorTest::prepare), config); + } + + @Test + public void run() { + + String out = utils.getOutputDirectory(); + + controler.run(); + + assertThat(new File(out, "kelheim-mini-drt.drt_estimates_drt.csv")) + .exists() + .isNotEmpty(); + + + } +} From d9c6011cb8d3b76f5cb02cb12c99f84da13dfc20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:57:12 +0000 Subject: [PATCH 3/7] build(deps): bump io.grpc:grpc-all from 1.59.0 to 1.59.1 Bumps [io.grpc:grpc-all](https://github.com/grpc/grpc-java) from 1.59.0 to 1.59.1. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.59.0...v1.59.1) --- updated-dependencies: - dependency-name: io.grpc:grpc-all dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- contribs/hybridsim/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index 52fb9013346..cc8bcea53c5 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -11,7 +11,7 @@ 3.25.1 - 1.59.0 + 1.59.1 From 38ad994e73ceff4f5913a6ea488474767c98c532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Thu, 30 Nov 2023 09:12:35 +0100 Subject: [PATCH 4/7] feat: customizable distance calculatorsfor drt constraints (#2971) --- .../insertion/DrtInsertionModule.java | 120 ++++++++++++++---- .../constraints/VehicleRangeConstraint.java | 5 +- .../distances/DistanceApproximator.java | 8 ++ .../distances/DistanceCalculator.java | 2 +- ...ava => EuclideanDistanceApproximator.java} | 6 +- .../InsertionDistanceCalculator.java | 8 +- .../distances/RoutingDistanceCalculator.java | 2 +- .../insertion/DrtInsertionExtensionIT.java | 84 ++++++++++++ 8 files changed, 199 insertions(+), 36 deletions(-) create mode 100644 contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceApproximator.java rename contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/{ApproximateDistanceCalculator.java => EuclideanDistanceApproximator.java} (66%) diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionModule.java index 9408dcfd067..23ad52f4e80 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionModule.java @@ -15,8 +15,9 @@ import org.matsim.contrib.drt.extension.insertion.constraints.SkillsConstraint.VehicleSkillsSupplier; import org.matsim.contrib.drt.extension.insertion.constraints.VehicleRangeConstraint; import org.matsim.contrib.drt.extension.insertion.constraints.VehicleRangeConstraint.VehicleRangeSupplier; -import org.matsim.contrib.drt.extension.insertion.distances.ApproximateDistanceCalculator; +import org.matsim.contrib.drt.extension.insertion.distances.DistanceApproximator; import org.matsim.contrib.drt.extension.insertion.distances.DistanceCalculator; +import org.matsim.contrib.drt.extension.insertion.distances.EuclideanDistanceApproximator; import org.matsim.contrib.drt.extension.insertion.distances.RoutingDistanceCalculator; import org.matsim.contrib.drt.extension.insertion.objectives.PassengerDelayObjective; import org.matsim.contrib.drt.extension.insertion.objectives.VehicleActiveTimeObjective; @@ -173,10 +174,93 @@ public DrtInsertionModule withConstraint(DrtInsertionConstraint constraint) { return this; } + // DISTANCES + + private DistanceCalculator distanceCalculator; + private DistanceApproximator distanceApproximator; + + private Class distanceCalculatorClass; + private Class distanceApproximatorClass; + + public DrtInsertionModule withDistanceCalculator(DistanceCalculator distanceCalculator) { + Preconditions.checkState(this.distanceCalculator == null && this.distanceCalculatorClass == null); + this.distanceCalculator = distanceCalculator; + return this; + } + + public DrtInsertionModule withDistanceCalculator(Class distanceCalculatorClass) { + Preconditions.checkState(this.distanceCalculator == null && this.distanceCalculatorClass == null); + this.distanceCalculatorClass = distanceCalculatorClass; + return this; + } + + public DrtInsertionModule withDistanceApproximator(DistanceApproximator distanceApproximator) { + Preconditions.checkState(this.distanceApproximator == null && this.distanceApproximatorClass == null); + this.distanceApproximator = distanceApproximator; + return this; + } + + public DrtInsertionModule withDistanceApproximator( + Class distanceApproximatorClass) { + Preconditions.checkState(this.distanceApproximator == null && this.distanceApproximatorClass == null); + this.distanceApproximatorClass = distanceApproximatorClass; + return this; + } + + public DrtInsertionModule withEuclideanDistanceApproximator(double euclideanDistanceFactor) { + Preconditions.checkState(this.distanceApproximator == null && this.distanceApproximatorClass == null); + this.distanceApproximator = new EuclideanDistanceApproximator(euclideanDistanceFactor); + return this; + } + + public DrtInsertionModule withEuclideanDistanceCalculator(double euclideanDistanceFactor) { + Preconditions.checkState(this.distanceCalculator == null && this.distanceCalculatorClass == null); + this.distanceCalculator = new EuclideanDistanceApproximator(euclideanDistanceFactor); + return this; + } + + private void configureDistances() { + bindModal(RoutingDistanceCalculator.class).toProvider(modalProvider(getter -> { + LeastCostPathCalculatorFactory factory = new SpeedyALTFactory(); + + TravelTime travelTime = getter.getModal(TravelTime.class); + TravelDisutility travelDisutility = new OnlyTimeDependentTravelDisutility(travelTime); + Network network = getter.getModal(Network.class); + + printDistanceWarning(); + + return new RoutingDistanceCalculator(factory.createPathCalculator(network, travelDisutility, travelTime), + travelTime); + })); + + if (distanceCalculator == null && distanceCalculatorClass == null) { + distanceCalculatorClass = RoutingDistanceCalculator.class; + } + + if (distanceApproximator == null && distanceApproximatorClass == null) { + distanceApproximator = DistanceApproximator.NULL; + } + + if (distanceCalculator != null) { + bindModal(DistanceCalculator.class).toInstance(distanceCalculator); + } + + if (distanceCalculatorClass != null) { + bindModal(DistanceCalculator.class).to(modalKey(distanceCalculatorClass)); + } + + if (distanceApproximator != null) { + bindModal(DistanceApproximator.class).toInstance(distanceApproximator); + } + + if (distanceApproximatorClass != null) { + bindModal(DistanceApproximator.class).to(modalKey(distanceApproximatorClass)); + } + } + // RANGE CONSTRAINT private boolean useRangeConstraint = false; - private double rangeEstimationFactor = Double.NaN; private VehicleRangeSupplier vehicleRangeSupplier; private Class vehicleRangeSupplierClass; @@ -200,11 +284,6 @@ public DrtInsertionModule withVehicleRange(Class return this; } - public DrtInsertionModule withRangeEstimationFactor(double rangeEstimationFactor) { - this.rangeEstimationFactor = rangeEstimationFactor; - return this; - } - private void configureRangeContraint(List> constraintBindings) { if (useRangeConstraint) { if (vehicleRangeSupplier != null) { @@ -216,10 +295,12 @@ private void configureRangeContraint(List> } bindModal(VehicleRangeConstraint.class).toProvider(modalProvider(getter -> { - DistanceCalculator distanceCalculator = getter.getModal(RoutingDistanceCalculator.class); - DistanceCalculator distanceApproximator = Double.isFinite(rangeEstimationFactor) - ? new ApproximateDistanceCalculator(rangeEstimationFactor) - : null; + DistanceCalculator distanceCalculator = getter.getModal(DistanceCalculator.class); + DistanceApproximator distanceApproximator = getter.getModal(DistanceApproximator.class); + + if (distanceApproximator == DistanceApproximator.NULL) { + distanceApproximator = null; + } return new VehicleRangeConstraint(vehicleRangeSupplier, distanceCalculator, distanceApproximator); })); @@ -362,7 +443,7 @@ private void configureVehicleDistanceObjective() { final DistanceCalculator distanceCalculator; if (Double.isFinite(distanceObjectiveEstimationFactor)) { - distanceCalculator = new ApproximateDistanceCalculator(distanceObjectiveEstimationFactor); + distanceCalculator = new EuclideanDistanceApproximator(distanceObjectiveEstimationFactor); } else { distanceCalculator = getter.getModal(RoutingDistanceCalculator.class); } @@ -373,6 +454,8 @@ private void configureVehicleDistanceObjective() { @Override protected void configureQSim() { + configureDistances(); + List> constraintBindings = new LinkedList<>(); configureExclusivityConstraint(constraintBindings); configureSingleRequestConstraint(constraintBindings); @@ -424,19 +507,6 @@ protected void configureQSim() { bindModal(CostCalculationStrategy.class).to(modalKey(ConfigurableCostCalculatorStrategy.class)); bindModal(DrtOfferAcceptor.class).to(modalKey(PromisedTimeWindowOfferAcceptor.class)); - - bindModal(RoutingDistanceCalculator.class).toProvider(modalProvider(getter -> { - LeastCostPathCalculatorFactory factory = new SpeedyALTFactory(); - - TravelTime travelTime = getter.getModal(TravelTime.class); - TravelDisutility travelDisutility = new OnlyTimeDependentTravelDisutility(travelTime); - Network network = getter.getModal(Network.class); - - printDistanceWarning(); - - return new RoutingDistanceCalculator(factory.createPathCalculator(network, travelDisutility, travelTime), - travelTime); - })); } private final static Logger logger = LogManager.getLogger(DrtInsertionModule.class); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/constraints/VehicleRangeConstraint.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/constraints/VehicleRangeConstraint.java index decf605d71a..649a68fd189 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/constraints/VehicleRangeConstraint.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/constraints/VehicleRangeConstraint.java @@ -3,6 +3,7 @@ import javax.annotation.Nullable; import org.matsim.contrib.drt.extension.insertion.DrtInsertionConstraint; +import org.matsim.contrib.drt.extension.insertion.distances.DistanceApproximator; import org.matsim.contrib.drt.extension.insertion.distances.DistanceCalculator; import org.matsim.contrib.drt.extension.insertion.distances.InsertionDistanceCalculator; import org.matsim.contrib.drt.extension.insertion.distances.InsertionDistanceCalculator.VehicleDistance; @@ -16,10 +17,10 @@ public class VehicleRangeConstraint implements DrtInsertionConstraint { private final VehicleRangeSupplier rangeSupplier; private final DistanceCalculator distanceCalculator; - private final DistanceCalculator distanceApproximator; + private final DistanceApproximator distanceApproximator; public VehicleRangeConstraint(VehicleRangeSupplier rangeSupplier, DistanceCalculator distanceEstimator, - @Nullable DistanceCalculator distanceApproximator) { + @Nullable DistanceApproximator distanceApproximator) { this.rangeSupplier = rangeSupplier; this.distanceCalculator = distanceEstimator; this.distanceApproximator = distanceApproximator; diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceApproximator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceApproximator.java new file mode 100644 index 00000000000..bae08f00040 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceApproximator.java @@ -0,0 +1,8 @@ +package org.matsim.contrib.drt.extension.insertion.distances; + +public interface DistanceApproximator extends DistanceCalculator { + static public DistanceApproximator NULL = (departureTime, fromLink, toLink) -> { + throw new IllegalStateException( + "The NULL DistanceApproximator is only used as a tag for not using any approximation."); + }; +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceCalculator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceCalculator.java index ff4943e6a4e..cc74978ac34 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceCalculator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/DistanceCalculator.java @@ -3,5 +3,5 @@ import org.matsim.api.core.v01.network.Link; public interface DistanceCalculator { - double estimateDistance(double departureTime, Link fromLink, Link toLink); + double calculateDistance(double departureTime, Link fromLink, Link toLink); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/ApproximateDistanceCalculator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/EuclideanDistanceApproximator.java similarity index 66% rename from contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/ApproximateDistanceCalculator.java rename to contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/EuclideanDistanceApproximator.java index 3d2893936d8..9f8c5c4556e 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/ApproximateDistanceCalculator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/EuclideanDistanceApproximator.java @@ -3,15 +3,15 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.core.utils.geometry.CoordUtils; -public class ApproximateDistanceCalculator implements DistanceCalculator { +public class EuclideanDistanceApproximator implements DistanceApproximator { private final double distanceEstimationFactor; - public ApproximateDistanceCalculator(double distanceEstimationFactor) { + public EuclideanDistanceApproximator(double distanceEstimationFactor) { this.distanceEstimationFactor = distanceEstimationFactor; } @Override - public double estimateDistance(double departureTime, Link fromLink, Link toLink) { + public double calculateDistance(double departureTime, Link fromLink, Link toLink) { return distanceEstimationFactor * CoordUtils.calcEuclideanDistance(fromLink.getFromNode().getCoord(), toLink.getFromNode().getCoord()); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/InsertionDistanceCalculator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/InsertionDistanceCalculator.java index 60e5642746e..01a6c91bf20 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/InsertionDistanceCalculator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/InsertionDistanceCalculator.java @@ -68,9 +68,9 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim int beforePickupOccupancy = insertion.pickup.previousWaypoint.getOutgoingOccupancy(); double beforePickupDistance = distanceEstimator - .estimateDistance(insertion.pickup.previousWaypoint.getDepartureTime(), pickupFromLink, pickupNewLink); + .calculateDistance(insertion.pickup.previousWaypoint.getDepartureTime(), pickupFromLink, pickupNewLink); - double afterPickupDistance = distanceEstimator.estimateDistance(detourTimeInfo.pickupDetourInfo.departureTime, + double afterPickupDistance = distanceEstimator.calculateDistance(detourTimeInfo.pickupDetourInfo.departureTime, pickupNewLink, pickupToLink); addedDistances.add(new DistanceEntry(beforePickupDistance, beforePickupOccupancy)); @@ -128,7 +128,7 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim final int beforeDropoffOccupancy; if (insertion.dropoff.index > insertion.pickup.index) { beforeDropoffOccupancy = insertion.dropoff.previousWaypoint.getOutgoingOccupancy() + 1; - double beforeDropoffDistance = distanceEstimator.estimateDistance( + double beforeDropoffDistance = distanceEstimator.calculateDistance( insertion.dropoff.previousWaypoint.getDepartureTime(), dropoffFromLink, dropoffNewLink); addedDistances.add(new DistanceEntry(beforeDropoffDistance, beforeDropoffOccupancy)); @@ -136,7 +136,7 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim beforeDropoffOccupancy = beforePickupOccupancy + 1; } - double afterDropoffDistance = distanceEstimator.estimateDistance(detourTimeInfo.dropoffDetourInfo.arrivalTime, + double afterDropoffDistance = distanceEstimator.calculateDistance(detourTimeInfo.dropoffDetourInfo.arrivalTime, dropoffNewLink, dropoffToLink); addedDistances.add(new DistanceEntry(afterDropoffDistance, beforeDropoffOccupancy - 1)); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/RoutingDistanceCalculator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/RoutingDistanceCalculator.java index 31ec3a55e06..cf74ff360a1 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/RoutingDistanceCalculator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/insertion/distances/RoutingDistanceCalculator.java @@ -19,7 +19,7 @@ public RoutingDistanceCalculator(LeastCostPathCalculator router, TravelTime trav // set this up in a more intelligent way, I'm not sure, but I guess that the // constraints are accesses in parallel by DRT. /sh nov'23 @Override - public synchronized double estimateDistance(double departureTime, Link fromLink, Link toLink) { + public synchronized double calculateDistance(double departureTime, Link fromLink, Link toLink) { VrpPathWithTravelData path = VrpPaths.calcAndCreatePath(fromLink, toLink, departureTime, router, travelTime); return VrpPaths.calcDistance(path); } diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java index 3740da65f35..001bae2a1e7 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/insertion/DrtInsertionExtensionIT.java @@ -16,7 +16,9 @@ import org.matsim.api.core.v01.IdSet; import org.matsim.api.core.v01.events.LinkEnterEvent; import org.matsim.api.core.v01.events.handler.LinkEnterEventHandler; +import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; +import org.matsim.contrib.drt.extension.insertion.distances.DistanceApproximator; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.run.DrtControlerCreator; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; @@ -33,6 +35,7 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEventHandler; import org.matsim.contrib.dvrp.passenger.PassengerWaitingEvent; import org.matsim.contrib.dvrp.passenger.PassengerWaitingEventHandler; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.vrpagent.TaskEndedEvent; import org.matsim.contrib.dvrp.vrpagent.TaskEndedEventHandler; @@ -41,6 +44,7 @@ import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting; +import org.matsim.core.utils.geometry.CoordUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; @@ -254,6 +258,86 @@ public void testRangeConstraint() { } } + @Test + public void testRangeConstraintWithCustomInstances() { + Controler controller = createController(); + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()); + + CustomCalculator distanceCalculator = new CustomCalculator(); + CustomCalculator distanceApproximator = new CustomCalculator(); + + DrtInsertionModule insertionModule = new DrtInsertionModule(drtConfig) // + .withVehicleRange(100.0 * 1e3) // + .withDistanceCalculator(distanceCalculator) // + .withDistanceApproximator(distanceApproximator); + + controller.addOverridingQSimModule(insertionModule); + + DistanceHandler handler = new DistanceHandler(controller.getScenario().getNetwork()); + handler.install(controller); + + controller.run(); + + for (var item : handler.distances.entrySet()) { + assertTrue(item.getValue() < 100.0 * 1e3); + } + + assertEquals(1470, distanceCalculator.calculatedDistances); + assertEquals(5288, distanceApproximator.calculatedDistances); + } + + @Test + public void testRangeConstraintWithCustomInjection() { + Controler controller = createController(); + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()); + + CustomDistanceCalculator distanceCalculator = new CustomDistanceCalculator(); + CustomDistanceApproximator distanceApproximator = new CustomDistanceApproximator(); + + DrtInsertionModule insertionModule = new DrtInsertionModule(drtConfig) // + .withVehicleRange(100.0 * 1e3) // + .withDistanceCalculator(CustomDistanceCalculator.class) // + .withDistanceApproximator(CustomDistanceApproximator.class); + + controller.addOverridingQSimModule(insertionModule); + + controller.addOverridingQSimModule(new AbstractDvrpModeQSimModule("drt") { + @Override + protected void configureQSim() { + bindModal(CustomDistanceCalculator.class).toInstance(distanceCalculator); + bindModal(CustomDistanceApproximator.class).toInstance(distanceApproximator); + } + }); + + DistanceHandler handler = new DistanceHandler(controller.getScenario().getNetwork()); + handler.install(controller); + + controller.run(); + + for (var item : handler.distances.entrySet()) { + assertTrue(item.getValue() < 100.0 * 1e3); + } + + assertEquals(1470, distanceCalculator.calculatedDistances); + assertEquals(5288, distanceApproximator.calculatedDistances); + } + + static class CustomDistanceCalculator extends CustomCalculator { + } + + static class CustomDistanceApproximator extends CustomCalculator { + } + + static class CustomCalculator implements DistanceApproximator { + int calculatedDistances = 0; + + @Override + public synchronized double calculateDistance(double departureTime, Link fromLink, Link toLink) { + calculatedDistances++; + return CoordUtils.calcEuclideanDistance(fromLink.getCoord(), toLink.getCoord()); + } + } + static public class DistanceHandler implements LinkEnterEventHandler { private final Network network; private final IdMap distances = new IdMap<>(Vehicle.class); From 12cd0e0dae01f4e4bcb8cbed9789c54227c072a8 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Thu, 30 Nov 2023 11:33:38 +0100 Subject: [PATCH 5/7] add tests for all parkingSearch strategies --- .../run/RunParkingSearchScenarioIT.java | 123 ++++++++++++++++-- .../output_events.xml.gz | Bin 0 -> 44970 bytes .../output_plans.xml.gz | Bin 0 -> 5208 bytes .../output_events.xml.gz | Bin 0 -> 35045 bytes .../output_plans.xml.gz | Bin 0 -> 4835 bytes 5 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingDistanceMemoryStrategy/output_events.xml.gz create mode 100644 contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingDistanceMemoryStrategy/output_plans.xml.gz create mode 100644 contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingNearestParkingSpotStrategy/output_events.xml.gz create mode 100644 contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingNearestParkingSpotStrategy/output_plans.xml.gz diff --git a/contribs/parking/src/test/java/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT.java b/contribs/parking/src/test/java/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT.java index eef6af3f609..ef761cedee2 100644 --- a/contribs/parking/src/test/java/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT.java +++ b/contribs/parking/src/test/java/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT.java @@ -17,38 +17,141 @@ * * * *********************************************************************** */ -/** - * - */ package org.matsim.contrib.parking.run; +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.population.Person; +import org.matsim.api.core.v01.population.Population; import org.matsim.contrib.parking.parkingsearch.ParkingSearchStrategy; import org.matsim.contrib.parking.parkingsearch.RunParkingSearchExample; import org.matsim.contrib.parking.parkingsearch.sim.ParkingSearchConfigGroup; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.population.PopulationUtils; import org.matsim.testcases.MatsimTestUtils; +import org.matsim.utils.eventsfilecomparison.EventsFileComparator; /** - * @author jbischoff - * + * @author jbischoff */ public class RunParkingSearchScenarioIT { - @Rule public MatsimTestUtils utils = new MatsimTestUtils() ; + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + @Test + public void testRunParkingBenesonStrategy() { + try { + String configFile = "./src/main/resources/parkingsearch/config.xml"; + Config config = ConfigUtils.loadConfig(configFile, new ParkingSearchConfigGroup()); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + ParkingSearchConfigGroup configGroup = (ParkingSearchConfigGroup) config.getModules().get(ParkingSearchConfigGroup.GROUP_NAME); + configGroup.setParkingSearchStrategy(ParkingSearchStrategy.Benenson); + + new RunParkingSearchExample().run(config, false); + + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("something went wrong"); + } + } @Test - public void testRunParking() { + public void testRunParkingRandomStrategy() { String configFile = "./src/main/resources/parkingsearch/config.xml"; Config config = ConfigUtils.loadConfig(configFile, new ParkingSearchConfigGroup()); config.controller().setLastIteration(0); - config.controller().setOutputDirectory( utils.getOutputDirectory() ); + config.controller().setOutputDirectory(utils.getOutputDirectory()); ParkingSearchConfigGroup configGroup = (ParkingSearchConfigGroup) config.getModules().get(ParkingSearchConfigGroup.GROUP_NAME); - configGroup.setParkingSearchStrategy(ParkingSearchStrategy.Benenson); + configGroup.setParkingSearchStrategy(ParkingSearchStrategy.Random); + + try { + new RunParkingSearchExample().run(config, false); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("something went wrong"); + } + } + + @Test + public void testRunParkingDistanceMemoryStrategy() { + try { + String configFile = "./src/main/resources/parkingsearch/config.xml"; + Config config = ConfigUtils.loadConfig(configFile, new ParkingSearchConfigGroup()); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + ParkingSearchConfigGroup configGroup = (ParkingSearchConfigGroup) config.getModules().get(ParkingSearchConfigGroup.GROUP_NAME); + configGroup.setParkingSearchStrategy(ParkingSearchStrategy.DistanceMemory); + + new RunParkingSearchExample().run(config, false); + { + Population expected = PopulationUtils.createPopulation(ConfigUtils.createConfig()); + PopulationUtils.readPopulation(expected, utils.getInputDirectory() + "/output_plans.xml.gz"); + + Population actual = PopulationUtils.createPopulation(ConfigUtils.createConfig()); + PopulationUtils.readPopulation(actual, utils.getOutputDirectory() + "/output_plans.xml.gz"); + + for (Id personId : expected.getPersons().keySet()) { + double scoreReference = expected.getPersons().get(personId).getSelectedPlan().getScore(); + double scoreCurrent = actual.getPersons().get(personId).getSelectedPlan().getScore(); + Assert.assertEquals("Scores of person=" + personId + " are different", scoreReference, scoreCurrent, MatsimTestUtils.EPSILON); + } + + } + { + String expected = utils.getInputDirectory() + "/output_events.xml.gz"; + String actual = utils.getOutputDirectory() + "/output_events.xml.gz"; + EventsFileComparator.Result result = EventsUtils.compareEventsFiles(expected, actual); + Assert.assertEquals(EventsFileComparator.Result.FILES_ARE_EQUAL, result); + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("something went wrong"); + } + } + + @Test + public void testRunParkingNearestParkingSpotStrategy() { + try { + String configFile = "./src/main/resources/parkingsearch/config.xml"; + Config config = ConfigUtils.loadConfig(configFile, new ParkingSearchConfigGroup()); + config.controller().setLastIteration(0); + config.controller().setOutputDirectory(utils.getOutputDirectory()); + + ParkingSearchConfigGroup configGroup = (ParkingSearchConfigGroup) config.getModules().get(ParkingSearchConfigGroup.GROUP_NAME); + configGroup.setParkingSearchStrategy(ParkingSearchStrategy.NearestParkingSpot); + + new RunParkingSearchExample().run(config, false); + { + Population expected = PopulationUtils.createPopulation(ConfigUtils.createConfig()); + PopulationUtils.readPopulation(expected, utils.getInputDirectory() + "/output_plans.xml.gz"); + + Population actual = PopulationUtils.createPopulation(ConfigUtils.createConfig()); + PopulationUtils.readPopulation(actual, utils.getOutputDirectory() + "/output_plans.xml.gz"); - new RunParkingSearchExample().run(config,false); + for (Id personId : expected.getPersons().keySet()) { + double scoreReference = expected.getPersons().get(personId).getSelectedPlan().getScore(); + double scoreCurrent = actual.getPersons().get(personId).getSelectedPlan().getScore(); + Assert.assertEquals("Scores of person=" + personId + " are different", scoreReference, scoreCurrent, MatsimTestUtils.EPSILON); + } + } + { + String expected = utils.getInputDirectory() + "/output_events.xml.gz"; + String actual = utils.getOutputDirectory() + "/output_events.xml.gz"; + EventsFileComparator.Result result = EventsUtils.compareEventsFiles(expected, actual); + Assert.assertEquals(EventsFileComparator.Result.FILES_ARE_EQUAL, result); + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("something went wrong"); + } } } diff --git a/contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingDistanceMemoryStrategy/output_events.xml.gz b/contribs/parking/test/input/org/matsim/contrib/parking/run/RunParkingSearchScenarioIT/testRunParkingDistanceMemoryStrategy/output_events.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..e80c449a03b7d66106265b3bd3ca28592c62f2f3 GIT binary patch literal 44970 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

ZlG^odod-ET41Os(*Hq>ma;dVkNl!7 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 literal 0 HcmV?d00001 From 76a5bdd0c232e173699053bd7b32030002b14548 Mon Sep 17 00:00:00 2001 From: "Dr. Steffen Axer" <26229392+steffenaxer@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:35:27 +0100 Subject: [PATCH 6/7] Bugfix edrt failIfNotStarted (#2972) * Fix edrt failIfNotStarted --- .../EDrtVehicleDataEntryFactory.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) 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 f552fb3ecae..76d84a09e32 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 @@ -71,16 +71,23 @@ public VehicleEntry create(DvrpVehicle vehicle, double currentTime) { Battery battery = ((EvDvrpVehicle)vehicle).getElectricVehicle().getBattery(); int nextTaskIdx; double chargeBeforeNextTask; - if (schedule.getStatus() == ScheduleStatus.PLANNED) { - nextTaskIdx = 0; - chargeBeforeNextTask = battery.getCharge(); - } else { // STARTED - Task currentTask = schedule.getCurrentTask(); - ETaskTracker eTracker = (ETaskTracker)currentTask.getTaskTracker(); - chargeBeforeNextTask = eTracker.predictChargeAtEnd(); - nextTaskIdx = currentTask.getTaskIdx() + 1; + + switch (schedule.getStatus()) { + case PLANNED: + nextTaskIdx = 0; + chargeBeforeNextTask = battery.getCharge(); + break; + case STARTED: + Task currentTask = schedule.getCurrentTask(); + ETaskTracker eTracker = (ETaskTracker) currentTask.getTaskTracker(); + chargeBeforeNextTask = eTracker.predictChargeAtEnd(); + nextTaskIdx = currentTask.getTaskIdx() + 1; + break; + default: + return null; } + List tasks = schedule.getTasks(); for (int i = nextTaskIdx; i < tasks.size() - 1; i++) { chargeBeforeNextTask -= ((ETask)tasks.get(i)).getTotalEnergy(); From 8ae1f60aab8cadf7340f32035f8c21beea41f190 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:04:57 +0000 Subject: [PATCH 7/7] build(deps): bump the github-actions group with 1 update Bumps the github-actions group with 1 update: [actions/setup-java](https://github.com/actions/setup-java). - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/code-coverage.yaml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/deploy-on-pr-merge.yaml | 2 +- .github/workflows/deploy-on-release-created.yaml | 2 +- .github/workflows/deploy-weekly.yaml | 2 +- .github/workflows/full-integration.yaml | 2 +- .github/workflows/verify-push.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml index 20ffe41971a..bdf0d7bfcde 100644 --- a/.github/workflows/code-coverage.yaml +++ b/.github/workflows/code-coverage.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 021bae1c67c..0f30527d41b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/deploy-on-pr-merge.yaml b/.github/workflows/deploy-on-pr-merge.yaml index 332a3dfe178..ef046e1bdb0 100644 --- a/.github/workflows/deploy-on-pr-merge.yaml +++ b/.github/workflows/deploy-on-pr-merge.yaml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/deploy-on-release-created.yaml b/.github/workflows/deploy-on-release-created.yaml index 7c37d3c6f7d..ace4f1881fb 100644 --- a/.github/workflows/deploy-on-release-created.yaml +++ b/.github/workflows/deploy-on-release-created.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/deploy-weekly.yaml b/.github/workflows/deploy-weekly.yaml index 0861a80ff91..88e183083b7 100644 --- a/.github/workflows/deploy-weekly.yaml +++ b/.github/workflows/deploy-weekly.yaml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/full-integration.yaml b/.github/workflows/full-integration.yaml index c831df5ec61..e63fc75985b 100644 --- a/.github/workflows/full-integration.yaml +++ b/.github/workflows/full-integration.yaml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml index f2a5b59d8ee..a4eb837efcb 100644 --- a/.github/workflows/verify-push.yaml +++ b/.github/workflows/verify-push.yaml @@ -84,7 +84,7 @@ jobs: - name: Setup Java if: ${{matrix.module != 'matsim' || steps.detect-changes.outputs.outside-contribs == 'true'}} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu'