diff --git a/contribs/application/pom.xml b/contribs/application/pom.xml index b2b75824e01..b427dfbfcf9 100644 --- a/contribs/application/pom.xml +++ b/contribs/application/pom.xml @@ -87,7 +87,7 @@ com.github.matsim-org gtfs2matsim - 47b0802a29 + 19f1676fc6 diff --git a/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java b/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java index ddbe0e7868a..5483167fcdf 100644 --- a/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java +++ b/contribs/application/src/main/java/org/matsim/application/prepare/pt/CreateTransitScheduleFromGtfs.java @@ -25,6 +25,7 @@ import org.matsim.core.utils.io.IOUtils; import org.matsim.pt.transitSchedule.api.*; import org.matsim.pt.utils.CreatePseudoNetwork; +import org.matsim.pt.utils.CreatePseudoNetworkWithLoopLinks; import org.matsim.pt.utils.TransitScheduleValidator; import org.matsim.vehicles.*; import picocli.CommandLine; @@ -85,8 +86,8 @@ public class CreateTransitScheduleFromGtfs implements MATSimAppCommand { @CommandLine.Option(names = "--transform-schedule", description = "Fully qualified class name to a Consumer to be executed after the schedule was created", arity = "0..*", split = ",") private List> transformSchedule; - @CommandLine.Option(names = "--merge-stops", description = "Whether stops should be merged by coordinate") - private boolean mergeStops; + @CommandLine.Option(names = "--merge-stops", description = "Whether stops should be merged by coordinate", defaultValue = "doNotMerge") + private GtfsConverter.MergeGtfsStops mergeStops; @CommandLine.Option(names = "--prefix", description = "Prefixes to add to the gtfs ids. Required if multiple inputs are used and ids are not unique.", split = ",") private List prefixes = new ArrayList<>(); @@ -100,10 +101,29 @@ public class CreateTransitScheduleFromGtfs implements MATSimAppCommand { @CommandLine.Option(names = "--shp-crs", description = "Overwrite coordinate system of the shape file") private String shpCrs; + @CommandLine.Option(names = "--pseudo-network", description = "Define how the pseudo network should be created", defaultValue = "singleLinkBetweenStops") + private PseudoNetwork pseudoNetwork; + + public static void main(String[] args) { System.exit(new CommandLine(new CreateTransitScheduleFromGtfs()).execute(args)); } + private static void addHbefaMapping(VehicleType vehicleType, HbefaVehicleCategory category) { + EngineInformation carEngineInformation = vehicleType.getEngineInformation(); + VehicleUtils.setHbefaVehicleCategory(carEngineInformation, String.valueOf(category)); + VehicleUtils.setHbefaTechnology(carEngineInformation, "average"); + VehicleUtils.setHbefaSizeClass(carEngineInformation, "average"); + VehicleUtils.setHbefaEmissionsConcept(carEngineInformation, "average"); + vehicleType.setNetworkMode(TransportMode.pt); + } + + private static void increaseLinkFreespeedIfLower(Link link, double newFreespeed) { + if (link.getFreespeed() < newFreespeed) { + link.setFreespeed(newFreespeed); + } + } + @Override public Integer call() throws Exception { @@ -183,6 +203,13 @@ public Integer call() throws Exception { Scenario ptScenario = getScenarioWithPseudoPtNetworkAndTransitVehicles(network, scenario.getTransitSchedule(), "pt_"); + for (TransitLine line : new ArrayList<>(scenario.getTransitSchedule().getTransitLines().values())) { + if (line.getRoutes().isEmpty()) { + log.warn("Line {} with no routes removed.", line.getId()); + scenario.getTransitSchedule().removeTransitLine(line); + } + } + if (validate) { //Check schedule and network TransitScheduleValidator.ValidationResult checkResult = TransitScheduleValidator.validateAll(ptScenario.getTransitSchedule(), ptScenario.getNetwork()); @@ -237,14 +264,19 @@ private Predicate createFilter(int i) throws Exception { /** * Creates the pt scenario and network. */ - private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network network, TransitSchedule schedule, String ptNetworkIdentifier) { + private Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network network, TransitSchedule schedule, String ptNetworkIdentifier) { ScenarioUtils.ScenarioBuilder builder = new ScenarioUtils.ScenarioBuilder(ConfigUtils.createConfig()); builder.setNetwork(network); builder.setTransitSchedule(schedule); Scenario scenario = builder.build(); // add pseudo network for pt - new CreatePseudoNetwork(scenario.getTransitSchedule(), scenario.getNetwork(), ptNetworkIdentifier, 0.1, 100000.0).createNetwork(); + switch (pseudoNetwork) { + case singleLinkBetweenStops -> + new CreatePseudoNetwork(scenario.getTransitSchedule(), scenario.getNetwork(), ptNetworkIdentifier, 0.1, 100000.0).createNetwork(); + case withLoopLinks -> + new CreatePseudoNetworkWithLoopLinks(scenario.getTransitSchedule(), scenario.getNetwork(), ptNetworkIdentifier, 0.1, 100000.0).createNetwork(); + } // create TransitVehicle types // see https://svn.vsp.tu-berlin.de/repos/public-svn/publications/vspwp/2014/14-24/ for veh capacities @@ -449,8 +481,12 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network // so we need to add time for passengers to board and alight double minStopTime = 30.0; + List> routeIds = new LinkedList<>(); + routeIds.add(route.getRoute().getStartLinkId()); + routeIds.addAll(route.getRoute().getLinkIds()); + routeIds.add(route.getRoute().getEndLinkId()); + for (int i = 1; i < routeStops.size(); i++) { - // TODO cater for loop link at first stop? Seems to just work without. TransitRouteStop routeStop = routeStops.get(i); // if there is no departure offset set (or infinity), it is the last stop of the line, // so we don't need to care about the stop duration @@ -462,8 +498,23 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network // Math.max to avoid negative values of travelTime double travelTime = Math.max(1, routeStop.getArrivalOffset().seconds() - lastDepartureOffset - 1.0 - (stopDuration >= minStopTime ? 0 : (minStopTime - stopDuration))); - Link link = network.getLinks().get(routeStop.getStopFacility().getLinkId()); - increaseLinkFreespeedIfLower(link, link.getLength() / travelTime); + + + Id stopLink = routeStop.getStopFacility().getLinkId(); + List> subRoute = new LinkedList<>(); + do { + Id linkId = routeIds.removeFirst(); + subRoute.add(linkId); + } while (!subRoute.contains(stopLink)); + + List links = subRoute.stream().map(scenario.getNetwork().getLinks()::get) + .toList(); + + double length = links.stream().mapToDouble(Link::getLength).sum(); + + for (Link link : links) { + increaseLinkFreespeedIfLower(link, length / travelTime); + } lastDepartureOffset = routeStop.getDepartureOffset().seconds(); } @@ -481,20 +532,15 @@ private static Scenario getScenarioWithPseudoPtNetworkAndTransitVehicles(Network return scenario; } - private static void addHbefaMapping(VehicleType vehicleType, HbefaVehicleCategory category) { - EngineInformation carEngineInformation = vehicleType.getEngineInformation(); - VehicleUtils.setHbefaVehicleCategory(carEngineInformation, String.valueOf(category)); - VehicleUtils.setHbefaTechnology(carEngineInformation, "average"); - VehicleUtils.setHbefaSizeClass(carEngineInformation, "average"); - VehicleUtils.setHbefaEmissionsConcept(carEngineInformation, "average"); - vehicleType.setNetworkMode(TransportMode.pt); - } - - private static void increaseLinkFreespeedIfLower(Link link, double newFreespeed) { - if (link.getFreespeed() < newFreespeed) { - link.setFreespeed(newFreespeed); - } + public enum PseudoNetwork { + /** + * Create links between all stops and possibly duplicate stops. + */ + singleLinkBetweenStops, + /** + * Create a pseudo network with loop links at each stop. + */ + withLoopLinks, } - } 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 00dabefc585..90773153294 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 @@ -59,6 +59,7 @@ 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.charging.ChargingStrategy; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructureUtils; import org.matsim.core.api.experimental.events.EventsManager; @@ -116,7 +117,8 @@ protected void configureQSim() { public EmptyVehicleChargingScheduler get() { var taskFactory = getModalInstance(DrtTaskFactory.class); var chargingInfrastructure = getModalInstance(ChargingInfrastructure.class); - return new EmptyVehicleChargingScheduler(timer, taskFactory, chargingInfrastructure); + ChargingStrategy.Factory chargingStrategyFactory = getModalInstance(ChargingStrategy.Factory.class); + return new EmptyVehicleChargingScheduler(timer, taskFactory, chargingInfrastructure, chargingStrategyFactory); } }).asEagerSingleton(); 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 741c4047786..915c5efa220 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 @@ -22,15 +22,17 @@ import java.net.URL; +import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory.EDrtVehicleDataEntryFactoryProvider; 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.DvrpConfigGroup; -import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory.EDrtVehicleDataEntryFactoryProvider; +import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.charging.ChargeUpToMaxSocStrategy; import org.matsim.contrib.ev.charging.ChargingLogic; import org.matsim.contrib.ev.charging.ChargingPower; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithQueueingAndAssignmentLogic; import org.matsim.contrib.ev.charging.FixedSpeedCharging; import org.matsim.contrib.ev.temperature.TemperatureService; @@ -40,6 +42,8 @@ import org.matsim.core.controler.Controler; import org.matsim.vis.otfvis.OTFVisConfigGroup; +import com.google.inject.Key; + /** * @author Michal Maciejewski (michalm) */ @@ -69,10 +73,13 @@ public void install() { controler.addOverridingModule(new AbstractModule() { @Override public void install() { - bind(ChargingLogic.Factory.class).toProvider(new ChargingWithQueueingAndAssignmentLogic.FactoryProvider( - charger -> new ChargeUpToMaxSocStrategy(charger, MAX_RELATIVE_SOC))); + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingAndAssignmentLogic.Factory.class); bind(ChargingPower.Factory.class).toInstance(ev -> new FixedSpeedCharging(ev, CHARGING_SPEED_FACTOR)); bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); + + for (DrtConfigGroup drtCfg : MultiModeDrtConfigGroup.get(config).getModalElements()) { + bind(Key.get(ChargingStrategy.Factory.class, DvrpModes.mode(drtCfg.mode))).toInstance(new ChargeUpToMaxSocStrategy.Factory(MAX_RELATIVE_SOC)); + } } }); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtChargingTask.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtChargingTask.java index e2dcfdba751..95b36f65889 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtChargingTask.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtChargingTask.java @@ -20,6 +20,7 @@ package org.matsim.contrib.drt.extension.edrt.schedule; import org.matsim.contrib.drt.schedule.DrtTaskType; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.evrp.ChargingTaskImpl; @@ -27,7 +28,7 @@ public class EDrtChargingTask extends ChargingTaskImpl { public static final DrtTaskType TYPE = new DrtTaskType("CHARGING"); - public EDrtChargingTask(double beginTime, double endTime, Charger charger, ElectricVehicle ev, double totalEnergy) { - super(TYPE, beginTime, endTime, charger, ev, totalEnergy); + public EDrtChargingTask(double beginTime, double endTime, Charger charger, ElectricVehicle ev, double totalEnergy, ChargingStrategy chargingStrategy) { + super(TYPE, beginTime, endTime, charger, ev, totalEnergy, chargingStrategy); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtTaskFactoryImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtTaskFactoryImpl.java index 4ec83dc7a15..d99fdc6fbfe 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtTaskFactoryImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/edrt/schedule/EDrtTaskFactoryImpl.java @@ -26,6 +26,7 @@ import org.matsim.contrib.dvrp.schedule.DefaultStayTask; import org.matsim.contrib.evrp.EvDvrpVehicle; import org.matsim.contrib.evrp.VrpPathEnergyConsumptions; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; @@ -59,8 +60,8 @@ public DefaultStayTask createInitialTask(DvrpVehicle vehicle, double beginTime, } public EDrtChargingTask createChargingTask(DvrpVehicle vehicle, double beginTime, double endTime, Charger charger, - double totalEnergy) { + double totalEnergy, ChargingStrategy chargingStrategy) { return new EDrtChargingTask(beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), - totalEnergy); + totalEnergy, chargingStrategy); } } 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 eea29745e80..2df2ed57028 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 @@ -47,10 +47,12 @@ public class EmptyVehicleChargingScheduler { private final MobsimTimer timer; private final EDrtTaskFactoryImpl taskFactory; private final Map, List> linkToChargersMap; + private final ChargingStrategy.Factory chargingStrategyFactory; public EmptyVehicleChargingScheduler(MobsimTimer timer, DrtTaskFactory taskFactory, - ChargingInfrastructure chargingInfrastructure) { + ChargingInfrastructure chargingInfrastructure, ChargingStrategy.Factory chargingStrategyFactory) { this.timer = timer; + this.chargingStrategyFactory = chargingStrategyFactory; this.taskFactory = (EDrtTaskFactoryImpl)taskFactory; linkToChargersMap = chargingInfrastructure.getChargers() .values() @@ -68,15 +70,16 @@ public void chargeVehicle(DvrpVehicle vehicle) { // 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); + ChargingStrategy strategy = chargingStrategyFactory.createStrategy(charger.getSpecification(), ev); + if (!strategy.isChargingCompleted()) { + chargeVehicleImpl(vehicle, ev, charger, strategy); } } } - private void chargeVehicleImpl(DvrpVehicle vehicle, Charger charger) { + private void chargeVehicleImpl(DvrpVehicle vehicle, ElectricVehicle ev, Charger charger, ChargingStrategy strategy) { Schedule schedule = vehicle.getSchedule(); DrtStayTask stayTask = (DrtStayTask)schedule.getCurrentTask(); if (stayTask.getTaskIdx() != schedule.getTaskCount() - 1) { @@ -86,19 +89,17 @@ private void chargeVehicleImpl(DvrpVehicle vehicle, Charger charger) { // add CHARGING TASK double beginTime = stayTask.getEndTime(); - ChargingStrategy strategy = charger.getLogic().getChargingStrategy(); - ElectricVehicle ev = ((EvDvrpVehicle)vehicle).getElectricVehicle(); - double totalEnergy = -strategy.calcRemainingEnergyToCharge(ev); + double totalEnergy = -strategy.calcRemainingEnergyToCharge(); - double chargingDuration = Math.min(strategy.calcRemainingTimeToCharge(ev), + double chargingDuration = Math.min(strategy.calcRemainingTimeToCharge(), vehicle.getServiceEndTime() - beginTime); if (chargingDuration <= 0) { return;// no charging } double endTime = beginTime + chargingDuration; - schedule.addTask(taskFactory.createChargingTask(vehicle, beginTime, endTime, charger, totalEnergy)); - ((ChargingWithAssignmentLogic)charger.getLogic()).assignVehicle(ev); + schedule.addTask(taskFactory.createChargingTask(vehicle, beginTime, endTime, charger, totalEnergy, strategy)); + ((ChargingWithAssignmentLogic)charger.getLogic()).assignVehicle(ev, strategy); // append STAY schedule.addTask(taskFactory.createStayTask(vehicle, endTime, vehicle.getServiceEndTime(), charger.getLink())); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/dispatcher/EDrtShiftDispatcherImpl.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/dispatcher/EDrtShiftDispatcherImpl.java index ace6e8d66e0..b3469edbc2e 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/dispatcher/EDrtShiftDispatcherImpl.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/dispatcher/EDrtShiftDispatcherImpl.java @@ -42,15 +42,18 @@ public class EDrtShiftDispatcherImpl implements DrtShiftDispatcher { private final Fleet fleet; + private final ChargingStrategy.Factory chargingStrategyFactory; + public EDrtShiftDispatcherImpl(EShiftTaskScheduler shiftTaskScheduler, ChargingInfrastructure chargingInfrastructure, ShiftsParams drtShiftParams, OperationFacilities operationFacilities, - DrtShiftDispatcher delegate, Fleet fleet) { + DrtShiftDispatcher delegate, Fleet fleet, ChargingStrategy.Factory chargingStrategyFactory) { this.shiftTaskScheduler = shiftTaskScheduler; this.chargingInfrastructure = chargingInfrastructure; this.drtShiftParams = drtShiftParams; this.operationFacilities = operationFacilities; this.delegate = delegate; this.fleet = fleet; + this.chargingStrategyFactory = chargingStrategyFactory; } @Override @@ -112,18 +115,18 @@ private void checkChargingAtHub(double timeStep) { if (selectedCharger.isPresent()) { Charger selectedChargerImpl = selectedCharger.get(); - ChargingStrategy chargingStrategy = selectedChargerImpl.getLogic().getChargingStrategy(); - if (!chargingStrategy.isChargingCompleted(electricVehicle)) { + ChargingStrategy chargingStrategy = chargingStrategyFactory.createStrategy(selectedChargerImpl.getSpecification(), electricVehicle); + if (!chargingStrategy.isChargingCompleted()) { final double waitTime = ChargingEstimations .estimateMaxWaitTimeForNextVehicle(selectedChargerImpl); final double chargingTime = chargingStrategy - .calcRemainingTimeToCharge(electricVehicle); + .calcRemainingTimeToCharge(); double energy = -chargingStrategy - .calcRemainingEnergyToCharge(electricVehicle); + .calcRemainingEnergyToCharge(); final double endTime = timeStep + waitTime + chargingTime; if (endTime < currentTask.getEndTime()) { shiftTaskScheduler.chargeAtHub((WaitForShiftTask) currentTask, eShiftVehicle, - electricVehicle, selectedChargerImpl, timeStep, endTime, energy); + electricVehicle, selectedChargerImpl, timeStep, endTime, energy, chargingStrategy); } } } 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 32bf96909bf..95b6feb85e3 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 @@ -34,6 +34,7 @@ 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.charging.ChargingStrategy; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimTimer; @@ -72,7 +73,7 @@ protected void configureQSim() { drtShiftParams, new EDrtShiftStartLogic(new DefaultShiftStartLogic()), new EDrtAssignShiftToVehicleLogic(new DefaultAssignShiftToVehicleLogic(drtShiftParams), drtShiftParams), getter.getModal(ShiftScheduler.class)), - getter.getModal(Fleet.class))) + getter.getModal(Fleet.class), getter.getModal(ChargingStrategy.Factory.class))) ).asEagerSingleton(); bindModal(VehicleEntry.EntryFactory.class).toProvider(modalProvider(getter -> @@ -87,7 +88,8 @@ drtShiftParams, new EDrtShiftStartLogic(new DefaultShiftStartLogic()), getter -> new EShiftTaskScheduler(getter.getModal(Network.class), getter.getModal(TravelTime.class), getter.getModal(TravelDisutilityFactory.class).createTravelDisutility(getter.getModal(TravelTime.class)), getter.get(MobsimTimer.class), getter.getModal(ShiftDrtTaskFactory.class), drtShiftParams, getter.getModal(ChargingInfrastructure.class), - getter.getModal(OperationFacilities.class), getter.getModal(Fleet.class)) + getter.getModal(OperationFacilities.class), getter.getModal(Fleet.class), + getter.getModal(ChargingStrategy.Factory.class)) )).asEagerSingleton(); // See EDrtModeOptimizerQSimModule 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 b7c2657f1bf..c74713d9f73 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 @@ -19,6 +19,7 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.path.VrpPathWithTravelData; import org.matsim.contrib.dvrp.schedule.DefaultStayTask; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.evrp.ChargingTask; import org.matsim.contrib.evrp.ChargingTaskImpl; @@ -99,21 +100,21 @@ public WaitForShiftTask createWaitForShiftStayTask(DvrpVehicle vehicle, double b public WaitForShiftTask createChargingWaitForShiftStayTask(DvrpVehicle vehicle, double beginTime, double endTime, Link link, OperationFacility facility, - double totalEnergy, Charger charger) { - ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy); + double totalEnergy, Charger charger, ChargingStrategy strategy) { + ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy, strategy); return new EDrtWaitForShiftTask(beginTime, endTime, link, totalEnergy, facility, chargingTask); } public EDrtShiftBreakTaskImpl createChargingShiftBreakTask(DvrpVehicle vehicle, double beginTime, double endTime, Link link, - DrtShiftBreak shiftBreak, Charger charger, double totalEnergy, OperationFacility facility) { - ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy); + DrtShiftBreak shiftBreak, Charger charger, double totalEnergy, OperationFacility facility, ChargingStrategy strategy) { + ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy, strategy); return new EDrtShiftBreakTaskImpl(beginTime, endTime, link, shiftBreak, totalEnergy, chargingTask, facility); } public ShiftChangeOverTask createChargingShiftChangeoverTask(DvrpVehicle vehicle, double beginTime, double endTime, Link link, Charger charger, double totalEnergy, - DrtShift shift, OperationFacility facility) { - ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy); + DrtShift shift, OperationFacility facility, ChargingStrategy strategy) { + ChargingTask chargingTask = new ChargingTaskImpl(EDrtChargingTask.TYPE, beginTime, endTime, charger, ((EvDvrpVehicle)vehicle).getElectricVehicle(), totalEnergy, strategy); return new EDrtShiftChangeoverTaskImpl(beginTime, endTime, link, shift, totalEnergy, chargingTask, facility); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/scheduler/EShiftTaskScheduler.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/scheduler/EShiftTaskScheduler.java index 9c5a7172282..2bc42cc1925 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/scheduler/EShiftTaskScheduler.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/scheduler/EShiftTaskScheduler.java @@ -1,5 +1,11 @@ package org.matsim.contrib.drt.extension.operations.eshifts.scheduler; +import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.DRIVE; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; @@ -12,7 +18,11 @@ import org.matsim.contrib.drt.extension.operations.operationFacilities.OperationFacility; 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.*; +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.ShiftSchedules; +import org.matsim.contrib.drt.extension.operations.shifts.schedule.WaitForShiftTask; import org.matsim.contrib.drt.extension.operations.shifts.scheduler.ShiftTaskScheduler; import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShift; import org.matsim.contrib.drt.extension.operations.shifts.shift.DrtShiftBreak; @@ -23,11 +33,16 @@ 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.*; +import org.matsim.contrib.dvrp.schedule.DriveTask; +import org.matsim.contrib.dvrp.schedule.Schedule; +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.tracker.OnlineDriveTaskTracker; import org.matsim.contrib.dvrp.util.LinkTimePair; import org.matsim.contrib.ev.charging.BatteryCharging; import org.matsim.contrib.ev.charging.ChargingEstimations; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; @@ -38,12 +53,7 @@ import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.TravelDisutility; import org.matsim.core.router.util.TravelTime; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.DRIVE; +import org.matsim.core.utils.collections.Tuple; /** * @author nkuehnel / MOIA @@ -61,10 +71,12 @@ public class EShiftTaskScheduler implements ShiftTaskScheduler { private final Network network; private final ChargingInfrastructure chargingInfrastructure; + private final ChargingStrategy.Factory chargingStrategyFactory; public EShiftTaskScheduler(Network network, TravelTime travelTime, TravelDisutility travelDisutility, MobsimTimer timer, ShiftDrtTaskFactory taskFactory, ShiftsParams shiftsParams, - ChargingInfrastructure chargingInfrastructure, OperationFacilities operationFacilities, Fleet fleet) { + ChargingInfrastructure chargingInfrastructure, OperationFacilities operationFacilities, Fleet fleet, + ChargingStrategy.Factory chargingStrategyFactory) { this.travelTime = travelTime; this.timer = timer; this.taskFactory = taskFactory; @@ -72,6 +84,7 @@ public EShiftTaskScheduler(Network network, TravelTime travelTime, TravelDisutil this.shiftsParams = shiftsParams; this.router = new SpeedyALTFactory().createPathCalculator(network, travelDisutility, travelTime); this.chargingInfrastructure = chargingInfrastructure; + this.chargingStrategyFactory = chargingStrategyFactory; } public void relocateForBreak(ShiftDvrpVehicle vehicle, OperationFacility breakFacility, DrtShift shift) { @@ -169,23 +182,25 @@ private void relocateForBreakImpl(ShiftDvrpVehicle vehicle, double startTime, do ShiftBreakTask dropoffStopTask; ElectricVehicle ev = ((EvDvrpVehicle) vehicle).getElectricVehicle(); - Optional charger = charge(breakFacility, ev); + Optional charger = charge(breakFacility, ev); if (charger.isPresent()) { - final Charger chargerImpl = charger.get(); + final ChargerWithStrategy chargerImpl = charger.get(); final double waitTime = ChargingEstimations - .estimateMaxWaitTimeForNextVehicle(chargerImpl); + .estimateMaxWaitTimeForNextVehicle(chargerImpl.charger); if (ev.getBattery().getCharge() / ev.getBattery().getCapacity() > shiftsParams.chargeDuringBreakThreshold || waitTime > 0) { dropoffStopTask = taskFactory.createShiftBreakTask(vehicle, startTime, endTime, link, shiftBreak, breakFacility); } else { - double energyCharge = ((BatteryCharging) ev.getChargingPower()).calcEnergyCharged(chargerImpl.getSpecification(), endTime - startTime); + ChargingStrategy strategy = chargingStrategyFactory.createStrategy(chargerImpl.charger.getSpecification(), ev); + + double energyCharge = ((BatteryCharging) ev.getChargingPower()).calcEnergyCharged(chargerImpl.charger.getSpecification(), endTime - startTime); double totalEnergy = -energyCharge; - ((ChargingWithAssignmentLogic) chargerImpl.getLogic()).assignVehicle(ev); + ((ChargingWithAssignmentLogic) chargerImpl.charger.getLogic()).assignVehicle(ev, strategy); dropoffStopTask = ((ShiftEDrtTaskFactoryImpl) taskFactory).createChargingShiftBreakTask(vehicle, - startTime, endTime, link, shiftBreak, chargerImpl, totalEnergy, breakFacility); + startTime, endTime, link, shiftBreak, chargerImpl.charger, totalEnergy, breakFacility, chargerImpl.strategy); } } else { dropoffStopTask = taskFactory.createShiftBreakTask(vehicle, startTime, @@ -202,7 +217,7 @@ private void relocateForBreakImpl(ShiftDvrpVehicle vehicle, double startTime, do shiftBreak.schedule(Math.min(latestDetourArrival, latestTimeConstraintArrival)); } - private Optional charge(OperationFacility breakFacility, ElectricVehicle electricVehicle) { + private Optional charge(OperationFacility breakFacility, ElectricVehicle electricVehicle) { if (chargingInfrastructure != null) { List> chargerIds = breakFacility.getChargers(); if (!chargerIds.isEmpty()) { @@ -218,16 +233,19 @@ private Optional charge(OperationFacility breakFacility, ElectricVehicl return Double.compare(waitTime, waitTime2); }); if (selectedCharger.isPresent()) { - if (selectedCharger.get().getLogic().getChargingStrategy().isChargingCompleted(electricVehicle)) { + ChargingStrategy strategy = chargingStrategyFactory.createStrategy(selectedCharger.get().getSpecification(), electricVehicle); + if (strategy.isChargingCompleted()) { return Optional.empty(); } + return Optional.of(new ChargerWithStrategy(selectedCharger.get(), strategy)); } - return selectedCharger; } } return Optional.empty(); } + private record ChargerWithStrategy(Charger charger, ChargingStrategy strategy) {} + public void relocateForShiftChange(DvrpVehicle vehicle, Link link, DrtShift shift, OperationFacility breakFacility) { final Schedule schedule = vehicle.getSchedule(); @@ -318,24 +336,24 @@ private void appendShiftChange(DvrpVehicle vehicle, DrtShift shift, OperationFac // append SHIFT_CHANGEOVER task ElectricVehicle ev = ((EvDvrpVehicle) vehicle).getElectricVehicle(); - Optional charger = charge(breakFacility, ev); + Optional charger = charge(breakFacility, ev); if (charger.isPresent()) { - Charger chargingImpl = charger.get(); - + ChargerWithStrategy chargingImpl = charger.get(); + final double waitTime = ChargingEstimations - .estimateMaxWaitTimeForNextVehicle(chargingImpl); + .estimateMaxWaitTimeForNextVehicle(chargingImpl.charger); if (ev.getBattery().getCharge() / ev.getBattery().getCapacity() < shiftsParams.chargeDuringBreakThreshold - || ((ChargingWithAssignmentLogic) chargingImpl.getLogic()).getAssignedVehicles().contains(ev) + || ((ChargingWithAssignmentLogic) chargingImpl.charger.getLogic()).isAssigned(ev) || waitTime > 0) { dropoffStopTask = taskFactory.createShiftChangeoverTask(vehicle, startTime, endTime, link, shift, breakFacility); } else { - double energyCharge = ((BatteryCharging) ev.getChargingPower()).calcEnergyCharged(chargingImpl.getSpecification(), endTime - startTime); + double energyCharge = ((BatteryCharging) ev.getChargingPower()).calcEnergyCharged(chargingImpl.charger.getSpecification(), endTime - startTime); double totalEnergy = -energyCharge; - ((ChargingWithAssignmentLogic) chargingImpl.getLogic()).assignVehicle(ev); + ((ChargingWithAssignmentLogic) chargingImpl.charger.getLogic()).assignVehicle(ev, chargingImpl.strategy); dropoffStopTask = ((ShiftEDrtTaskFactoryImpl) taskFactory).createChargingShiftChangeoverTask(vehicle, - startTime, endTime, link, chargingImpl, totalEnergy, shift, breakFacility); + startTime, endTime, link, chargingImpl.charger, totalEnergy, shift, breakFacility, chargingImpl.strategy); } } else { dropoffStopTask = taskFactory.createShiftChangeoverTask(vehicle, startTime, @@ -470,12 +488,12 @@ private void updateShiftChangeImpl(DvrpVehicle vehicle, VrpPathWithTravelData vr public void chargeAtHub(WaitForShiftTask currentTask, ShiftDvrpVehicle vehicle, ElectricVehicle electricVehicle, Charger charger, double beginTime, - double endTime, double energy) { + double endTime, double energy, ChargingStrategy strategy) { final double initialEndTime = currentTask.getEndTime(); currentTask.setEndTime(beginTime); - ((ChargingWithAssignmentLogic) charger.getLogic()).assignVehicle(electricVehicle); + ((ChargingWithAssignmentLogic) charger.getLogic()).assignVehicle(electricVehicle, strategy); final WaitForShiftTask chargingWaitForShiftTask = ((ShiftEDrtTaskFactoryImpl) taskFactory).createChargingWaitForShiftStayTask(vehicle, - beginTime, endTime, currentTask.getLink(), currentTask.getFacility(), energy, charger); + beginTime, endTime, currentTask.getLink(), currentTask.getFacility(), energy, charger, strategy); final WaitForShiftTask waitForShiftTask = taskFactory.createWaitForShiftStayTask(vehicle, endTime, initialEndTime, currentTask.getLink(), currentTask.getFacility()); 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 a137d932fe5..a65091c6102 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 @@ -17,6 +17,7 @@ import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.charging.*; import org.matsim.contrib.ev.temperature.TemperatureService; @@ -32,6 +33,8 @@ import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.examples.ExamplesUtils; +import com.google.inject.Key; + import java.util.HashSet; import java.util.Set; @@ -177,8 +180,8 @@ void test() { controler.addOverridingModule(new AbstractModule() { @Override public void install() { - bind(ChargingLogic.Factory.class).toProvider(new ChargingWithQueueingAndAssignmentLogic.FactoryProvider( - charger -> new ChargeUpToMaxSocStrategy(charger, MAX_RELATIVE_SOC))); + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingAndAssignmentLogic.Factory.class); + bind(Key.get(ChargingStrategy.Factory.class, DvrpModes.mode(drtConfigGroup.mode))).toInstance(new ChargeUpToMaxSocStrategy.Factory(MAX_RELATIVE_SOC)); bind(ChargingPower.Factory.class).toInstance(FastThenSlowCharging::new); bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); } diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunPrebookingShiftDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunPrebookingShiftDrtScenarioIT.java index 61e3a64a97a..e4286bed31c 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunPrebookingShiftDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunPrebookingShiftDrtScenarioIT.java @@ -280,8 +280,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(5000); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 1800.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 5000.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 1800.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 5000.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); @@ -295,8 +295,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(5000); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 900.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 5005.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 900.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 5005.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); @@ -310,8 +310,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(5000); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 4000.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 5000.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 4000.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 5000.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); @@ -325,8 +325,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(8000); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 4000.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 11000.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 4000.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 11000.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); @@ -340,8 +340,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(6000.); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 4000.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 6000.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 4000.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 6000.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); @@ -355,8 +355,8 @@ private void preparePopulation(Scenario scenario) { Plan plan = factory.createPlan(); Activity start = factory.createActivityFromLinkId("start", Id.createLinkId(1)); start.setEndTime(6500.); - start.getAttributes().putAttribute("prebooking:submissionTime" + "drt", 4000.); - start.getAttributes().putAttribute("prebooking:plannedDepartureTime" + "drt", 6500.); + AttributeBasedPrebookingLogic.setSubmissionTime("drt", start, 4000.0); + AttributeBasedPrebookingLogic.setPlannedDepartureTime("drt", start, 6500.0); plan.addActivity(start); plan.addLeg(factory.createLeg("drt")); plan.addActivity(factory.createActivityFromLinkId("end", Id.createLinkId(2))); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/services/RunEDrtWithServicesScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/services/RunEDrtWithServicesScenarioIT.java index b16dca56807..da8b28c6c82 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/services/RunEDrtWithServicesScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/services/RunEDrtWithServicesScenarioIT.java @@ -14,6 +14,7 @@ import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.ev.charging.*; import org.matsim.contrib.ev.temperature.TemperatureService; import org.matsim.core.config.Config; @@ -22,6 +23,8 @@ import org.matsim.core.controler.Controler; import org.matsim.testcases.MatsimTestUtils; +import com.google.inject.Key; + public class RunEDrtWithServicesScenarioIT { public static final double MINIMUM_RELATIVE_SOC = 0.2; public static final double MAX_SOC = 1.0; @@ -77,7 +80,8 @@ public void install() { controler.addOverridingModule(new AbstractModule() { @Override public void install() { - bind(ChargingLogic.Factory.class).toProvider(new ChargingWithQueueingAndAssignmentLogic.FactoryProvider(charger -> new ChargeUpToMaxSocStrategy(charger, MAX_SOC))); + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingAndAssignmentLogic.Factory.class); + bind(Key.get(ChargingStrategy.Factory.class, DvrpModes.mode(drtConfigGroup.mode))).toInstance(new ChargeUpToMaxSocStrategy.Factory(MAX_SOC)); bind(ChargingPower.Factory.class).toInstance(ev -> new FixedSpeedCharging(ev, RELATIVE_SPEED)); bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); } 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 90836e785cc..cee1304544d 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 @@ -159,7 +159,10 @@ public List getDrtFares() { } public boolean isCompleted() { - return submitted.getPersonIds().stream().allMatch(personId -> personEvents.get(personId).droppedOff != null); + return submitted.getPersonIds().stream().allMatch(personId -> { + var events = personEvents.get(personId); + return events != null && personEvents.get(personId).droppedOff != null; + }); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/constraints/DefaultDrtOptimizationConstraintsSet.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/constraints/DefaultDrtOptimizationConstraintsSet.java index 3ba0a7ce169..fd26a90256a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/constraints/DefaultDrtOptimizationConstraintsSet.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/constraints/DefaultDrtOptimizationConstraintsSet.java @@ -23,34 +23,35 @@ public class DefaultDrtOptimizationConstraintsSet extends DrtOptimizationConstra @Parameter @Comment( - "Defines the maximum allowed absolute detour in seconds. Note that the detour is computed from the latest promised pickup time. " + - "To enable the max detour constraint, maxAllowedPickupDelay has to be specified. maxAbsoluteDetour should not be smaller than 0, " + "Defines the maximum allowed absolute detour in seconds. maxAbsoluteDetour should not be smaller than 0, " + "and should be higher than the offset maxDetourBeta. By default, this limit is disabled (i.e. set to Inf)") @PositiveOrZero public double maxAbsoluteDetour = Double.POSITIVE_INFINITY;// [s] @Parameter @Comment( - "Defines the maximum allowed absolute detour based on the unsharedRideTime. Note that the detour is computed from the latest promised " - + "pickup time. To enable the max detour constraint, maxAllowedPickupDelay has to be specified. A linear combination similar to travel " + "Defines the maximum allowed absolute detour based on the unsharedRideTime. A linear combination similar to travel " + "time constrain is used. This is the ratio part. By default, this limit is disabled (i.e. set to Inf, together with maxDetourBeta).") @DecimalMin("1.0") public double maxDetourAlpha = Double.POSITIVE_INFINITY; @Parameter @Comment( - "Defines the maximum allowed absolute detour based on the unsharedRideTime. Note that the detour is computed from the latest promised " - + "pickup time. To enable the max detour constraint, maxAllowedPickupDelay has to be specified. A linear combination similar to travel " + "Defines the maximum allowed absolute detour based on the unsharedRideTime. A linear combination similar to travel " + "time constrain is used. This is the constant part. By default, this limit is disabled (i.e. set to Inf, together with maxDetourAlpha).") @PositiveOrZero public double maxDetourBeta = Double.POSITIVE_INFINITY;// [s] + @Parameter + @Comment( + "Defines the minimum allowed absolute detour in seconds. By default, this bound is disabled (i.e. set to 0.)") + @PositiveOrZero + public double minimumAllowedDetour = 0; + @Override protected void checkConsistency(Config config) { super.checkConsistency(config); - if ((maxDetourAlpha != Double.POSITIVE_INFINITY && maxDetourBeta != Double.POSITIVE_INFINITY) || maxAbsoluteDetour != Double.POSITIVE_INFINITY) { - Verify.verify(maxAllowedPickupDelay != Double.POSITIVE_INFINITY, "Detour constraints are activated, " + - "maxAllowedPickupDelay must be specified! A value between 0 and 240 seconds can be a good choice for maxAllowedPickupDelay."); - } + Verify.verify(maxAbsoluteDetour > minimumAllowedDetour, "The minimum allowed detour must" + + "be lower than the maximum allowed detour."); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategy.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategy.java index 4e06666ed05..f3892019345 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategy.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategy.java @@ -40,6 +40,9 @@ */ public class MinCostFlowRebalancingStrategy implements RebalancingStrategy { + public static final String REBALANCING_ZONAL_TARGET_ALPHA = "rebalalpha"; + public static final String REBALANCING_ZONAL_TARGET_BETA = "rebalbeta"; + private final RebalancingTargetCalculator rebalancingTargetCalculator; private final ZoneSystem zonalSystem; private final Fleet fleet; @@ -68,18 +71,37 @@ public List calcRelocations(Stream rebalancab return calculateMinCostRelocations(time, rebalancableVehiclesPerZone, soonIdleVehiclesPerZone); } - private List calculateMinCostRelocations(double time, + List calculateMinCostRelocations(double time, Map> rebalancableVehiclesPerZone, Map> soonIdleVehiclesPerZone) { ToDoubleFunction targetFunction = rebalancingTargetCalculator.calculate(time, rebalancableVehiclesPerZone); var minCostFlowRebalancingStrategyParams = (MinCostFlowRebalancingStrategyParams)params.getRebalancingStrategyParams(); - double alpha = minCostFlowRebalancingStrategyParams.targetAlpha; - double beta = minCostFlowRebalancingStrategyParams.targetBeta; - List vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> { + List vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> { + double alpha; + double beta; int rebalancable = rebalancableVehiclesPerZone.getOrDefault(z, List.of()).size(); int soonIdle = soonIdleVehiclesPerZone.getOrDefault(z, List.of()).size(); + + switch (minCostFlowRebalancingStrategyParams.targetCoefficientSource) { + case Static -> { + alpha = minCostFlowRebalancingStrategyParams.targetAlpha; + beta = minCostFlowRebalancingStrategyParams.targetBeta; + } + case FromZoneAttribute -> { + alpha = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA); + beta = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA); + } + case FromZoneAttributeOrStatic -> { + Object alphaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA); + alpha = alphaAttribute == null ? minCostFlowRebalancingStrategyParams.targetAlpha : (Double) alphaAttribute; + Object betaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA); + beta = betaAttribute == null ? minCostFlowRebalancingStrategyParams.targetBeta : (Double) betaAttribute; + } + default -> throw new IllegalStateException("Unknown target coefficient source " + minCostFlowRebalancingStrategyParams.targetCoefficientSource); + } + int target = (int)Math.floor(alpha * targetFunction.applyAsDouble(z) + beta); int surplus = Math.min(rebalancable + soonIdle - target, rebalancable); return new DrtZoneVehicleSurplus(z, surplus); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyParams.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyParams.java index 7c0a72e3a07..a09867d4785 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyParams.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyParams.java @@ -56,6 +56,19 @@ public enum RebalancingTargetCalculatorType { @NotNull public RebalancingTargetCalculatorType rebalancingTargetCalculatorType = RebalancingTargetCalculatorType.EstimatedDemand; + + public enum TargetCoefficientSource { + Static, FromZoneAttribute, FromZoneAttributeOrStatic + } + + @Parameter + @Comment("Defines whether the alpha and beta of the target function should be" + + " [Static] or [FromZoneAttribute] in which case alpha and beta can be provided per zone as an attribute." + + " [FromZoneAttributeOrStatic] will fall back to the static coefficients if no attribute is found for a given zone." + + " Use " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA + " and " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA + + " to set values accordingly.") + public TargetCoefficientSource targetCoefficientSource = TargetCoefficientSource.Static; + public enum ZonalDemandEstimatorType {PreviousIterationDemand, None} @Parameter 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 index a5dad2f114b..69aef1db822 100644 --- 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 @@ -399,22 +399,31 @@ private void processRejections(double now) { int index = WithinDayAgentUtils.getCurrentPlanElementIndex(agent); Plan plan = WithinDayAgentUtils.getModifiablePlan(agent); PlanElement planElement = plan.getPlanElements().get(index); - Activity activity; - if(planElement instanceof Activity currentActivity) { - activity = currentActivity; + + if (planElement instanceof Activity currentActivity) { + Activity activity = currentActivity; + activity.setEndTime(Double.POSITIVE_INFINITY); + activity.setMaximumDurationUndefined(); + + ((HasModifiablePlan) agent).resetCaches(); + internalInterface.getMobsim().rescheduleActivityEnd(agent); + eventsManager.processEvent(new PersonStuckEvent(now, agent.getId(), agent.getCurrentLinkId(), + this.mode)); + + internalInterface.getMobsim().getAgentCounter().incLost(); + internalInterface.getMobsim().getAgentCounter().decLiving(); } else { // If the current element is a leg, the agent is walking towards the pickup location // We make the agent stuck at the interaction activity - activity = (Activity) plan.getPlanElements().get(index+1); + while (index < plan.getPlanElements().size()) { + if (plan.getPlanElements().get(index) instanceof Activity activity) { + activity.setEndTime(Double.POSITIVE_INFINITY); + activity.setMaximumDurationUndefined(); + } + + index++; + } } - activity.setEndTime(Double.POSITIVE_INFINITY); - activity.setMaximumDurationUndefined(); - - ((HasModifiablePlan) agent).resetCaches(); - internalInterface.getMobsim().rescheduleActivityEnd(agent); - eventsManager.processEvent(new PersonStuckEvent(now, agent.getId(), agent.getCurrentLinkId(), - this.mode)); - internalInterface.getMobsim().getAgentCounter().incLost(); } } } 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 index a48cbe50e46..7908ca1378c 100644 --- 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 @@ -2,6 +2,7 @@ import java.util.Optional; +import org.matsim.api.core.v01.population.Activity; 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; @@ -35,23 +36,40 @@ * @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 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) { + static public String getSubmissionTimeAttribute(String mode) { return SUBMISSION_TIME_ATTRIBUTE_PREFIX + mode; } - static public String getPlannedDepartureAttribute(String mode) { + static public String getPlannedDepartureTimeAttribute(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))); + return Optional.ofNullable((Double) trip.getTripAttributes().getAttribute(getSubmissionTimeAttribute(mode))); } static public Optional getPlannedDepartureTime(String mode, Trip trip) { - return Optional.ofNullable((Double) trip.getTripAttributes().getAttribute(getPlannedDepartureAttribute(mode))); + return Optional + .ofNullable((Double) trip.getTripAttributes().getAttribute(getPlannedDepartureTimeAttribute(mode))); + } + + static public void setSubmissionTime(String mode, Trip trip, double submissionTime) { + trip.getTripAttributes().putAttribute(getSubmissionTimeAttribute(mode), submissionTime); + } + + static public void setPlannedDepartureTime(String mode, Trip trip, double plannedDepartureTime) { + trip.getTripAttributes().putAttribute(getPlannedDepartureTimeAttribute(mode), plannedDepartureTime); + } + + static public void setSubmissionTime(String mode, Activity originActivity, double submissionTime) { + originActivity.getAttributes().putAttribute(getSubmissionTimeAttribute(mode), submissionTime); + } + + static public void setPlannedDepartureTime(String mode, Activity originActivity, double plannedDepartureTime) { + originActivity.getAttributes().putAttribute(getPlannedDepartureTimeAttribute(mode), plannedDepartureTime); } private final PrebookingQueue prebookingQueue; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DefaultDrtRouteConstraintsCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DefaultDrtRouteConstraintsCalculator.java index f8e1f9b36eb..cfcaaa3e5c8 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DefaultDrtRouteConstraintsCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/routing/DefaultDrtRouteConstraintsCalculator.java @@ -25,9 +25,12 @@ public DefaultDrtRouteConstraintsCalculator(DrtConfigGroup drtCfg, ConstraintSet /** * Calculates the maximum travel time defined as: drtCfg.getMaxTravelTimeAlpha() * unsharedRideTime + drtCfg.getMaxTravelTimeBeta() - * - * Calculates the maximum ride time defined as: drtCfg.maxDetourAlpha * - * unsharedRideTime + drtCfg.maxDetourBeta + * + * Calculates the maximum ride time defined as: + * unsharedRideTime + min( + * maxAbsoluteDetour, + * max(minimumAllowedDetour, unsharedRideTime * (1-drtCfg.maxDetourAlpha) + drtCfg.maxDetourBeta) + * ) * * @return DrtRouteConstraints constraints */ @@ -40,8 +43,8 @@ public DrtRouteConstraints calculateRouteConstraints(double departureTime, Link if (constraintsSet instanceof DefaultDrtOptimizationConstraintsSet defaultSet) { double maxTravelTime = defaultSet.maxTravelTimeAlpha * unsharedRideTime + defaultSet.maxTravelTimeBeta; - double maxRideTime = Math.min(unsharedRideTime + defaultSet.maxAbsoluteDetour, - defaultSet.maxDetourAlpha * unsharedRideTime + defaultSet.maxDetourBeta); + double maxDetour = Math.max(defaultSet.minimumAllowedDetour, unsharedRideTime * (defaultSet.maxDetourAlpha -1) + defaultSet.maxDetourBeta); + double maxRideTime = unsharedRideTime + Math.min(defaultSet.maxAbsoluteDetour, maxDetour); double maxWaitTime = constraintsSet.maxWaitTime; return new DrtRouteConstraints(maxTravelTime, maxRideTime, maxWaitTime); 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 index 7f43fd42d8b..cca630fcf0c 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 @@ -199,9 +199,9 @@ static Network createNetwork() { network.addNode(b); Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b); - Link bc = network.getFactory().createLink(Id.createLinkId("link_2"), b, a); + Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a); network.addLink(ab); - network.addLink(bc); + network.addLink(ba); return network; } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyTest.java new file mode 100644 index 00000000000..8aba7eefc00 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyTest.java @@ -0,0 +1,279 @@ +package org.matsim.contrib.drt.optimizer.rebalancing.mincostflow; + +import com.google.common.collect.ImmutableMap; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.locationtech.jts.geom.prep.PreparedPolygon; +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.events.PersonDepartureEvent; +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.contrib.common.zones.Zone; +import org.matsim.contrib.common.zones.ZoneImpl; +import org.matsim.contrib.common.zones.ZoneSystem; +import org.matsim.contrib.common.zones.ZoneSystemImpl; +import org.matsim.contrib.drt.analysis.zonal.MostCentralDrtZoneTargetLinkSelector; +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.targetcalculator.DemandEstimatorAsTargetCalculator; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.fleet.*; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.utils.geometry.GeometryUtils; + +import java.util.*; + +public class MinCostFlowRebalancingStrategyTest { + + private static final int ESTIMATION_PERIOD = 1800; + + private final Network network = createNetwork(); + + private final Link link1 = network.getLinks().get(Id.createLinkId("link_1")); + private final Link link2 = network.getLinks().get(Id.createLinkId("link_2")); + + private final Zone zone1 = new ZoneImpl( + Id.create("zone_1", Zone.class), + new PreparedPolygon(GeometryUtils.createGeotoolsPolygon( + List.of( + new Coord(0, 0), + new Coord(0, 500), + new Coord(500, 500), + new Coord(500, 0), + new Coord(0, 0) + ))), "dummy"); + + private final Zone zone2 = new ZoneImpl( + Id.create("zone_2", Zone.class), + new PreparedPolygon(GeometryUtils.createGeotoolsPolygon( + List.of( + new Coord(500, 0), + new Coord(500, 500), + new Coord(1000, 500), + new Coord(1000, 0), + new Coord(500, 0) + ))), "dummy"); + + private final ZoneSystem zonalSystem = new ZoneSystemImpl(List.of(zone1, zone2), coord -> { + if (coord == link1.getToNode().getCoord()) { + return Optional.of(zone1); + } else if (coord == link2.getToNode().getCoord()) { + return Optional.of(zone2); + } else { + throw new RuntimeException(); + } + }, network); + + + @Test + void testEmptyDemandAndTarget() { + PreviousIterationDrtDemandEstimator estimator = createEstimator(); + DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD); + + RebalancingParams rebalancingParams = new RebalancingParams(); + MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams(); + minCostFlowRebalancingStrategyParams.targetAlpha = 1.; + minCostFlowRebalancingStrategyParams.targetBeta = 0.; + rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams); + + AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem)); + MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator, + zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams); + + Map> rebalanceableVehicles = new HashMap<>(); + List relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations.isEmpty()); + } + + @Test + void testDemandWithoutSurplus() { + PreviousIterationDrtDemandEstimator estimator = createEstimator(); + DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD); + + RebalancingParams rebalancingParams = new RebalancingParams(); + MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams(); + minCostFlowRebalancingStrategyParams.targetAlpha = 1.; + minCostFlowRebalancingStrategyParams.targetBeta = 0.; + rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams); + + AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem)); + MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator, + zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams); + + //time bin 0-1800 + estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(200, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(500, link2, TransportMode.drt)); + estimator.handleEvent(departureEvent(1500, link1, TransportMode.drt)); + estimator.reset(1); + + Map> rebalanceableVehicles = new HashMap<>(); + List relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations.isEmpty()); + } + + @Test + void testDemandWithSurplus() { + PreviousIterationDrtDemandEstimator estimator = createEstimator(); + DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD); + + RebalancingParams rebalancingParams = new RebalancingParams(); + MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams(); + minCostFlowRebalancingStrategyParams.targetAlpha = 1.; + minCostFlowRebalancingStrategyParams.targetBeta = 0.; + rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams); + + AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem)); + MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator, + zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams); + + // 3 expected trips in zone 1 + estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(200, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(300, link1, TransportMode.drt)); + // 1 expected trip in zone 2 + estimator.handleEvent(departureEvent(100, link2, TransportMode.drt)); + estimator.reset(1); + + Map> rebalanceableVehicles = new HashMap<>(); + + // 4 vehicles in zone 1 (surplus = 1) + List rebalanceableVehiclesZone1 = new ArrayList<>(); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1)); + rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1); + + List relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations.size()).isEqualTo(1); + Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId()); + + rebalanceableVehicles.clear(); + + // 5 vehicles in zone 1 (surplus = 2) + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1)); + rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1); + + //set alpha to 2 -> send two vehicles to zone 2 + minCostFlowRebalancingStrategyParams.targetAlpha = 2.; + List relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations2.size()).isEqualTo(2); + Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId()); + Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId()); + } + + @Test + void testDemandWithSurplusZoneBasedTargetRates() { + + // set attributes + zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 0.); + zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.); + zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 1.); + zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.); + + + PreviousIterationDrtDemandEstimator estimator = createEstimator(); + DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD); + + RebalancingParams rebalancingParams = new RebalancingParams(); + MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams(); + minCostFlowRebalancingStrategyParams.targetCoefficientSource = MinCostFlowRebalancingStrategyParams.TargetCoefficientSource.FromZoneAttribute; + rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams); + + AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem)); + MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator, + zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams); + + // 3 expected trips in zone 1 + estimator.handleEvent(departureEvent(100, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(200, link1, TransportMode.drt)); + estimator.handleEvent(departureEvent(300, link1, TransportMode.drt)); + // 1 expected trip in zone 2 + estimator.handleEvent(departureEvent(100, link2, TransportMode.drt)); + estimator.reset(1); + + Map> rebalanceableVehicles = new HashMap<>(); + + // 4 vehicles in zone 1 (surplus = 1) + List rebalanceableVehiclesZone1 = new ArrayList<>(); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1)); + rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1); + + List relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations.size()).isEqualTo(1); + Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId()); + + rebalanceableVehicles.clear(); + + // 5 vehicles in zone 1 (surplus = 2) + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1)); + rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1)); + rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1); + + //set alpha to 2 -> send two vehicles to zone 2 + zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 2.); + List relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap()); + Assertions.assertThat(relocations2.size()).isEqualTo(2); + Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId()); + Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId()); + } + + private DvrpVehicleImpl getDvrpVehicle(Id id, Link link) { + return new DvrpVehicleImpl( + ImmutableDvrpVehicleSpecification.newBuilder() + .id(id) + .capacity(0) + .serviceBeginTime(0) + .serviceEndTime(0) + .startLinkId(link.getId()) + .build(), link); + } + + private static Fleet createEmptyFleet() { + return () -> ImmutableMap., DvrpVehicle>builder().build(); + } + + + 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); + } + + private PersonDepartureEvent departureEvent(double time, Link link, String mode) { + return new PersonDepartureEvent(time, null, link.getId(), mode, mode); + } + + static Network createNetwork() { + Network network = NetworkUtils.createNetwork(); + Node a = network.getFactory().createNode(Id.createNodeId("a"), new Coord(0,0)); + Node b = network.getFactory().createNode(Id.createNodeId("b"), new Coord(500,0)); + network.addNode(a); + network.addNode(b); + + Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b); + Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a); + network.addLink(ab); + network.addLink(ba); + return network; + } +} 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 index 4809a205c8a..22f9bef8d06 100644 --- 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 @@ -4,6 +4,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.Coord; +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.contrib.drt.passenger.DrtRequest; import org.matsim.contrib.drt.prebooking.PrebookingTestEnvironment.RequestInfo; import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; @@ -13,6 +18,7 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.core.controler.Controler; +import org.matsim.core.population.PopulationUtils; import org.matsim.testcases.MatsimTestUtils; /** @@ -67,7 +73,8 @@ static PrebookingParams installPrebooking(Controler controller, boolean installL return installPrebooking(controller, installLogic, new PrebookingParams()); } - static PrebookingParams installPrebooking(Controler controller, boolean installLogic, PrebookingParams prebookingParams) { + static PrebookingParams installPrebooking(Controler controller, boolean installLogic, + PrebookingParams prebookingParams) { DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()); drtConfig.addParameterSet(prebookingParams); @@ -647,12 +654,12 @@ void intraStopTiming_pickupTooEarly() { .addRequest("requestA1", 1, 1, 8, 8, 2000.0, 1.0) // forward .addRequest("requestA2", 1, 1, 8, 8, 2000.0, 2.0) // forward .addRequest("requestB1", 8, 8, 1, 1, 2356.0, 3.0) // backward - .configure(300.0, 2.0, 1800.0, 60.0) // + .configure(300.0, 2.0, 1800.0, 60.0) // .endTime(12.0 * 3600.0); Controler controller = environment.build(); installPrebooking(controller); - + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { @Override public void install() { @@ -665,7 +672,7 @@ public double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request) { return 30.0; // shorter than the dropoff duration (see below) } } - + @Override public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { return 60.0; @@ -673,7 +680,7 @@ public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { }); } }); - + controller.run(); { @@ -693,13 +700,14 @@ public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { { RequestInfo requestInfo = environment.getRequestInfo().get("requestB1"); assertEquals(3.0, requestInfo.submissionTime, 1e-3); - assertEquals(2356.0 + 60.0, requestInfo.pickupTime, 1e-3); // NOT 30s because we need to wait for the dropoffs + assertEquals(2356.0 + 60.0, requestInfo.pickupTime, 1e-3); // NOT 30s because we need to wait for the + // dropoffs assertEquals(2753.0 + 60.0, requestInfo.dropoffTime, 1e-3); } assertEquals(3, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); } - + @Test void intraStopTiming_dropoffTooLate() { /*- @@ -715,12 +723,12 @@ void intraStopTiming_dropoffTooLate() { .addRequest("requestA", 1, 1, 8, 8, 2000.0, 1.0) // forward .addRequest("requestB1", 8, 8, 1, 1, 2356.0, 2.0) // backward .addRequest("requestB2", 8, 8, 1, 1, 2356.0, 3.0) // backward - .configure(300.0, 2.0, 1800.0, 60.0) // + .configure(300.0, 2.0, 1800.0, 60.0) // .endTime(12.0 * 3600.0); Controler controller = environment.build(); installPrebooking(controller); - + controller.addOverridingModule(new AbstractDvrpModeModule("drt") { @Override public void install() { @@ -729,7 +737,7 @@ public void install() { public double calcPickupDuration(DvrpVehicle vehicle, DrtRequest request) { return 60.0; } - + @Override public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { if (request.getPassengerIds().get(0).toString().equals("requestA")) { @@ -741,7 +749,7 @@ public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { }); } }); - + controller.run(); { @@ -755,9 +763,10 @@ public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { RequestInfo requestInfo = environment.getRequestInfo().get("requestB1"); assertEquals(2.0, requestInfo.submissionTime, 1e-3); assertEquals(2356.0 + 60.0, requestInfo.pickupTime, 1e-3); - assertEquals(2753.0 + 60.0 + 30.0, requestInfo.dropoffTime, 1e-3); // +30 because we wait for dropoff of A for B2 to enter + assertEquals(2753.0 + 60.0 + 30.0, requestInfo.dropoffTime, 1e-3); // +30 because we wait for dropoff of A + // for B2 to enter } - + { RequestInfo requestInfo = environment.getRequestInfo().get("requestB2"); assertEquals(3.0, requestInfo.submissionTime, 1e-3); @@ -767,4 +776,62 @@ public double calcDropoffDuration(DvrpVehicle vehicle, DrtRequest request) { assertEquals(3, environment.getTaskInfo().get("vehicleA").stream().filter(t -> t.type.equals("STOP")).count()); } + + @Test + void abortAfterRejection_onActivity() { + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .setVehicleCapacity(1) // + .addRequest("requestA", 1, 1, 8, 8, 2000.0, 1800.0) + .configure(10.0, 1.0, 0.0, 5.0) + .endTime(12.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1800.0, requestInfo.submissionTime); + assertEquals(Double.NaN, requestInfo.pickupTime, 1e-3); + assertEquals(1, requestInfo.submissionTimes.size()); + assertEquals(1, environment.getStuckInfo().size()); + } + } + + @Test + void abortAfterRejection_onLeg() { + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + .setVehicleCapacity(1) // + .addRequest("requestA", 1, 1, 8, 8, 2000.0, 1800.0) + .configure(10.0, 1.0, 0.0, 5.0) + .endTime(12.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + + // make sure the agent will be on a leg + for (Person person : controller.getScenario().getPopulation().getPersons().values()) { + Plan plan = person.getSelectedPlan(); + + Activity activity = PopulationUtils.createActivityFromCoord("generic", new Coord(-50000.0, -50000.0)); + activity.setEndTime(0.0); + Leg leg = PopulationUtils.createLeg("walk"); + + plan.getPlanElements().add(0, activity); + plan.getPlanElements().add(1, leg); + } + + controller.run(); + + { + RequestInfo requestInfo = environment.getRequestInfo().get("requestA"); + assertEquals(1800.0, requestInfo.submissionTime); + assertEquals(Double.NaN, requestInfo.pickupTime, 1e-3); + assertEquals(1, requestInfo.submissionTimes.size()); + assertEquals(1, environment.getStuckInfo().size()); + } + } } 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 index 60108070ab8..fcdb7ab7d7a 100644 --- 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 @@ -1,14 +1,26 @@ package org.matsim.contrib.drt.prebooking; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + 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.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.network.NetworkFactory; import org.matsim.api.core.v01.network.Node; -import org.matsim.api.core.v01.population.*; +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.common.zones.systems.grid.square.SquareGridZoneSystemParams; import org.matsim.contrib.drt.optimizer.constraints.DefaultDrtOptimizationConstraintsSet; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearchParams; @@ -26,7 +38,12 @@ 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.*; +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; @@ -48,11 +65,6 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.testcases.MatsimTestUtils; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - public class PrebookingTestEnvironment { private final MatsimTestUtils utils; @@ -164,6 +176,7 @@ public Controler build() { configureRequestListener(controller); configureVehicleListener(controller); + configureStuckListener(controller); return controller; } @@ -227,9 +240,9 @@ private void buildConfig(Config config) { DrtConfigGroup modeConfig = new DrtConfigGroup(); drtConfig.addParameterSet(modeConfig); modeConfig.mode = "drt"; - DefaultDrtOptimizationConstraintsSet defaultConstraintsSet = - (DefaultDrtOptimizationConstraintsSet) modeConfig.addOrGetDrtOptimizationConstraintsParams() - .addOrGetDefaultDrtOptimizationConstraintsSet(); + DefaultDrtOptimizationConstraintsSet defaultConstraintsSet = (DefaultDrtOptimizationConstraintsSet) modeConfig + .addOrGetDrtOptimizationConstraintsParams() + .addOrGetDefaultDrtOptimizationConstraintsSet(); defaultConstraintsSet.maxWaitTime = maximumWaitTime; defaultConstraintsSet.maxTravelTimeAlpha = detourRelative; defaultConstraintsSet.maxTravelTimeBeta = detourAbsolute; @@ -270,13 +283,13 @@ private void buildPopulation(Scenario scenario) { plan.addLeg(firstLeg); if (!Double.isNaN(request.submissionTime)) { - firstActivity.getAttributes().putAttribute(AttributeBasedPrebookingLogic.getSubmissionAttribute("drt"), + firstActivity.getAttributes().putAttribute(AttributeBasedPrebookingLogic.getSubmissionTimeAttribute("drt"), request.submissionTime); } if (!Double.isNaN(request.plannedDepartureTime)) { firstActivity.getAttributes().putAttribute( - AttributeBasedPrebookingLogic.getPlannedDepartureAttribute("drt"), + AttributeBasedPrebookingLogic.getPlannedDepartureTimeAttribute("drt"), request.plannedDepartureTime); } @@ -444,4 +457,26 @@ public void handleEvent(TaskEndedEvent event) { taskInfo.get(event.getDvrpVehicleId().toString()).getLast().endTime = event.getTime(); } } + + private final List stuckEvents = new LinkedList<>(); + + public List getStuckInfo() { + return stuckEvents; + } + + private void configureStuckListener(Controler controller) { + controller.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(new StuckListener()); + } + }); + } + + private class StuckListener implements PersonStuckEventHandler { + @Override + public void handleEvent(PersonStuckEvent event) { + stuckEvents.add(event); + } + } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingActivity.java index 05217bf940e..8c1cb0adee3 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingActivity.java @@ -21,6 +21,7 @@ package org.matsim.contrib.evrp; import org.matsim.contrib.dynagent.DynActivity; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; import org.matsim.contrib.ev.fleet.ElectricVehicle; @@ -64,7 +65,7 @@ private void initialize(double now) { ChargingWithAssignmentLogic logic = chargingTask.getChargingLogic(); ElectricVehicle ev = chargingTask.getElectricVehicle(); logic.unassignVehicle(ev); - logic.addVehicle(ev, new DvrpChargingListener(this), now); + logic.addVehicle(ev, chargingTask.getChargingStrategy(), new DvrpChargingListener(this), now); state = State.added; } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTask.java b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTask.java index 21ad8303ae5..7326e1bedd9 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTask.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTask.java @@ -20,6 +20,7 @@ package org.matsim.contrib.evrp; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; import org.matsim.contrib.ev.fleet.ElectricVehicle; @@ -29,6 +30,8 @@ public interface ChargingTask extends ETask { ChargingWithAssignmentLogic getChargingLogic(); + ChargingStrategy getChargingStrategy(); + ElectricVehicle getElectricVehicle(); double getChargingStartedTime(); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTaskImpl.java b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTaskImpl.java index 969fa50da00..8cbd525cbea 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTaskImpl.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/evrp/ChargingTaskImpl.java @@ -24,6 +24,7 @@ import com.google.common.base.Preconditions; import org.matsim.contrib.dvrp.schedule.DefaultStayTask; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; @@ -33,18 +34,20 @@ */ public class ChargingTaskImpl extends DefaultStayTask implements ChargingTask { private final ChargingWithAssignmentLogic chargingLogic; + private final ChargingStrategy chargingStrategy; private final ElectricVehicle ev; private Double chargingStartedTime; private final double totalEnergy; public ChargingTaskImpl(TaskType taskType, double beginTime, double endTime, Charger charger, ElectricVehicle ev, - double totalEnergy) { + double totalEnergy, ChargingStrategy chargingStrategy) { super(taskType, beginTime, endTime, charger.getLink()); Preconditions.checkArgument(totalEnergy < 0, "Total energy consumption is not negative: %s", totalEnergy); this.chargingLogic = (ChargingWithAssignmentLogic)charger.getLogic(); this.ev = ev; this.totalEnergy = totalEnergy; + this.chargingStrategy = chargingStrategy; } @Override @@ -57,6 +60,11 @@ public ChargingWithAssignmentLogic getChargingLogic() { return chargingLogic; } + @Override + public ChargingStrategy getChargingStrategy() { + return chargingStrategy; + } + @Override public ElectricVehicle getElectricVehicle() { return ev; @@ -76,6 +84,7 @@ public double getChargingStartedTime() { public String toString() { return MoreObjects.toStringHelper(this) .add("chargingLogic", chargingLogic) + .add("chargingStrategy", chargingStrategy) .add("ev", ev) .add("chargingStartedTime", chargingStartedTime) .add("totalEnergy", totalEnergy) diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargeUpToMaxSocStrategy.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargeUpToMaxSocStrategy.java index 85760aabefc..3703a7b0794 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargeUpToMaxSocStrategy.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargeUpToMaxSocStrategy.java @@ -23,30 +23,65 @@ import org.matsim.contrib.ev.fleet.Battery; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.ChargerSpecification; +import org.matsim.vehicles.Vehicle; /** * @author Michal Maciejewski (michalm) + * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public class ChargeUpToMaxSocStrategy implements ChargingStrategy { + static public final String MAXIMUM_SOC_VEHICLE_ATTRIBUTE = "maximumSoc"; + + private final ElectricVehicle ev; private final ChargerSpecification charger; private final double maxSoc; - public ChargeUpToMaxSocStrategy(ChargerSpecification charger, double maxSoc) { + public ChargeUpToMaxSocStrategy(ChargerSpecification charger, ElectricVehicle ev, double maxSoc) { if (maxSoc < 0 || maxSoc > 1) { throw new IllegalArgumentException(); } this.charger = charger; this.maxSoc = maxSoc; + this.ev = ev; } @Override - public double calcRemainingEnergyToCharge(ElectricVehicle ev) { + public double calcRemainingEnergyToCharge() { Battery battery = ev.getBattery(); return maxSoc * battery.getCapacity() - battery.getCharge(); } @Override - public double calcRemainingTimeToCharge(ElectricVehicle ev) { - return ((BatteryCharging)ev.getChargingPower()).calcChargingTime(charger, calcRemainingEnergyToCharge(ev)); + public double calcRemainingTimeToCharge() { + return ((BatteryCharging)ev.getChargingPower()).calcChargingTime(charger, calcRemainingEnergyToCharge()); + } + + @Override + public boolean isChargingCompleted() { + return calcRemainingEnergyToCharge() <= 0; + } + + static public class Factory implements ChargingStrategy.Factory { + private final double maxSoc; + + public Factory(double maxSoc) { + this.maxSoc = maxSoc; + } + + @Override + public ChargingStrategy createStrategy(ChargerSpecification charger, ElectricVehicle ev) { + double vehicleMaximumSoc = maxSoc; + + Vehicle vehicle = ev.getVehicleSpecification().getMatsimVehicle(); + if (vehicle != null) { + Double value = (Double) vehicle.getAttributes().getAttribute(MAXIMUM_SOC_VEHICLE_ATTRIBUTE); + + if (value != null) { + vehicleMaximumSoc = value; + } + } + + return new ChargeUpToMaxSocStrategy(charger, ev, vehicleMaximumSoc); + } } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingEstimations.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingEstimations.java index 5c0499f3bbf..502573d8bb2 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingEstimations.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingEstimations.java @@ -20,7 +20,7 @@ import java.util.stream.Stream; -import org.matsim.contrib.ev.fleet.ElectricVehicle; +import org.matsim.contrib.ev.charging.ChargingLogic.ChargingVehicle; import org.matsim.contrib.ev.infrastructure.Charger; /** @@ -38,20 +38,18 @@ public static double estimateMaxWaitTimeForNextVehicle(Charger charger) { } public static double estimateTotalTimeToCharge(ChargingLogic logic) { - return estimateTotalTimeToCharge(logic.getChargingStrategy(), - Stream.concat(logic.getPluggedVehicles().stream(), logic.getQueuedVehicles().stream())); + return estimateTotalTimeToCharge(Stream.concat(logic.getPluggedVehicles().stream(), logic.getQueuedVehicles().stream())); } public static double estimateTotalEnergyToCharge(ChargingLogic logic) { - return estimateTotalEnergyToCharge(logic.getChargingStrategy(), - Stream.concat(logic.getPluggedVehicles().stream(), logic.getQueuedVehicles().stream())); + return estimateTotalEnergyToCharge(Stream.concat(logic.getPluggedVehicles().stream(), logic.getQueuedVehicles().stream())); } - public static double estimateTotalTimeToCharge(ChargingStrategy strategy, Stream vehicles) { - return vehicles.mapToDouble(strategy::calcRemainingTimeToCharge).sum(); + public static double estimateTotalTimeToCharge(Stream vehicles) { + return vehicles.map(ChargingVehicle::strategy).mapToDouble(ChargingStrategy::calcRemainingTimeToCharge).sum(); } - public static double estimateTotalEnergyToCharge(ChargingStrategy strategy, Stream vehicles) { - return vehicles.mapToDouble(strategy::calcRemainingEnergyToCharge).sum(); + public static double estimateTotalEnergyToCharge(Stream vehicles) { + return vehicles.map(ChargingVehicle::strategy).mapToDouble(ChargingStrategy::calcRemainingEnergyToCharge).sum(); } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingLogic.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingLogic.java index 854947741e0..de274606326 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingLogic.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingLogic.java @@ -29,17 +29,17 @@ interface Factory { ChargingLogic create(ChargerSpecification charger); } - void addVehicle(ElectricVehicle ev, double now); + void addVehicle(ElectricVehicle ev, ChargingStrategy strategy, double now); - void addVehicle(ElectricVehicle ev, ChargingListener chargingListener, double now); + void addVehicle(ElectricVehicle ev, ChargingStrategy strategy, ChargingListener chargingListener, double now); void removeVehicle(ElectricVehicle ev, double now); void chargeVehicles(double chargePeriod, double now); - Collection getPluggedVehicles(); + Collection getPluggedVehicles(); - Collection getQueuedVehicles(); + Collection getQueuedVehicles(); - ChargingStrategy getChargingStrategy(); + record ChargingVehicle(ElectricVehicle ev, ChargingStrategy strategy) {} } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingModule.java index 7b42877507d..1f81bac495c 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingModule.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingModule.java @@ -20,14 +20,13 @@ package org.matsim.contrib.ev.charging; -import com.google.inject.Singleton; import org.matsim.contrib.ev.EvModule; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.controler.AbstractModule; import org.matsim.core.mobsim.qsim.AbstractQSimModule; -import com.google.inject.Inject; -import com.google.inject.Provider; +import com.google.inject.Provides; +import com.google.inject.Singleton; /** * @author Michal Maciejewski (michalm) @@ -35,13 +34,11 @@ public class ChargingModule extends AbstractModule { @Override public void install() { - // The following returns a charging logic for a given charger specification. Needs to be a provider, since the eventsManager needs to be inserted. - bind(ChargingLogic.Factory.class).toProvider(new Provider<>() { - @Inject private EventsManager eventsManager; - @Override public ChargingLogic.Factory get() { - return charger -> new ChargingWithQueueingLogic(charger, new ChargeUpToMaxSocStrategy(charger, 1.), eventsManager); - } - }); + // By default, charging logic with queue is used + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingLogic.Factory.class); + + // By default, charging strategy that chargers to 100% is used + bind(ChargingStrategy.Factory.class).toInstance(new ChargeUpToMaxSocStrategy.Factory(1.0)); // The following returns the charging power/speed for a vehicle: bind(ChargingPower.Factory.class).toInstance(ev -> new FixedSpeedCharging(ev, 1)); @@ -57,5 +54,18 @@ public void install() { // this.addMobsimListenerBinding().to( ChargingHandler.class ).in( Singleton.class ); // does not work since ChargingInfrastructure is not available. + + // standard charging priority for all chargers + bind(ChargingPriority.Factory.class).toInstance(ChargingPriority.FIFO); + } + + @Provides @Singleton + ChargingWithQueueingLogic.Factory provideChargingWithQueueingLogicFactory(EventsManager eventsManager, ChargingPriority.Factory chargingPriorityFactory) { + return new ChargingWithQueueingLogic.Factory(eventsManager, chargingPriorityFactory); + } + + @Provides @Singleton + ChargingWithQueueingAndAssignmentLogic.Factory provideChargingWithQueueingAndAssignmentLogicFactory(EventsManager eventsManager, ChargingPriority.Factory chargingPriorityFactory) { + return new ChargingWithQueueingAndAssignmentLogic.Factory(eventsManager, chargingPriorityFactory); } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingPriority.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingPriority.java new file mode 100644 index 00000000000..83087a37f79 --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingPriority.java @@ -0,0 +1,29 @@ +package org.matsim.contrib.ev.charging; + +import org.matsim.contrib.ev.charging.ChargingLogic.ChargingVehicle; +import org.matsim.contrib.ev.infrastructure.ChargerSpecification; + +/** + * This interface is supposed to decide if a vehicle can be plugged right now or + * if it needs to go to / remain in the queue. While the condition whether + * enough of empty plugs are available is *always* checked, the presented method + * allows to define a more complex logic beyond that. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public interface ChargingPriority { + /** + * The vehicle can start charging if the method returns true, otherwise it stays + * in the queue. + */ + boolean requestPlugNext(ChargingVehicle cv, double now); + + public interface Factory { + ChargingPriority create(ChargerSpecification charger); + } + + /** + * The default charging priority: first-in first-out. + */ + static public final Factory FIFO = charger -> (ev, now) -> true; +} \ No newline at end of file diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingStrategy.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingStrategy.java index 87b76bf1303..bb5717b3cc2 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingStrategy.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingStrategy.java @@ -19,17 +19,21 @@ package org.matsim.contrib.ev.charging; import org.matsim.contrib.ev.fleet.ElectricVehicle; +import org.matsim.contrib.ev.infrastructure.ChargerSpecification; /** * @author michalm + * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public interface ChargingStrategy { - default boolean isChargingCompleted(ElectricVehicle ev) { - return calcRemainingEnergyToCharge(ev) <= 0; - } + boolean isChargingCompleted(); - double calcRemainingEnergyToCharge(ElectricVehicle ev); + double calcRemainingEnergyToCharge(); //XXX should include potentially longer charging if AUX remains turned on - double calcRemainingTimeToCharge(ElectricVehicle ev); + double calcRemainingTimeToCharge(); + + static public interface Factory { + ChargingStrategy createStrategy(ChargerSpecification charger, ElectricVehicle ev); + } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithAssignmentLogic.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithAssignmentLogic.java index ceaabad797e..cd7333d9411 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithAssignmentLogic.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithAssignmentLogic.java @@ -5,9 +5,11 @@ import java.util.Collection; public interface ChargingWithAssignmentLogic extends ChargingLogic { - void assignVehicle(ElectricVehicle ev); + void assignVehicle(ElectricVehicle ev, ChargingStrategy strategy); void unassignVehicle(ElectricVehicle ev); - Collection getAssignedVehicles(); + Collection getAssignedVehicles(); + + boolean isAssigned(ElectricVehicle ev); } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingAndAssignmentLogic.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingAndAssignmentLogic.java index 5fb5cc0ae6d..1a0aa853af0 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingAndAssignmentLogic.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingAndAssignmentLogic.java @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.function.Function; import org.matsim.api.core.v01.Id; import org.matsim.contrib.ev.fleet.ElectricVehicle; @@ -31,21 +30,18 @@ import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.vehicles.Vehicle; -import com.google.inject.Inject; -import com.google.inject.Provider; - public class ChargingWithQueueingAndAssignmentLogic extends ChargingWithQueueingLogic implements ChargingWithAssignmentLogic { - private final Map, ElectricVehicle> assignedVehicles = new LinkedHashMap<>(); + private final Map, ChargingVehicle> assignedVehicles = new LinkedHashMap<>(); - public ChargingWithQueueingAndAssignmentLogic(ChargerSpecification charger, ChargingStrategy chargingStrategy, - EventsManager eventsManager) { - super(charger, chargingStrategy, eventsManager); + public ChargingWithQueueingAndAssignmentLogic(ChargerSpecification charger, EventsManager eventsManager, ChargingPriority priority) { + super(charger, eventsManager, priority); } @Override - public void assignVehicle(ElectricVehicle ev) { - if (assignedVehicles.put(ev.getId(), ev) != null) { + public void assignVehicle(ElectricVehicle ev, ChargingStrategy strategy) { + ChargingVehicle cv = new ChargingVehicle(ev, strategy); + if (assignedVehicles.put(ev.getId(), cv) != null) { throw new IllegalArgumentException("Vehicle is already assigned: " + ev.getId()); } } @@ -57,28 +53,31 @@ public void unassignVehicle(ElectricVehicle ev) { } } - private final Collection unmodifiableAssignedVehicles = Collections.unmodifiableCollection( + @Override + public boolean isAssigned(ElectricVehicle ev) { + return assignedVehicles.containsKey(ev.getId()); + } + + private final Collection unmodifiableAssignedVehicles = Collections.unmodifiableCollection( assignedVehicles.values()); @Override - public Collection getAssignedVehicles() { + public Collection getAssignedVehicles() { return unmodifiableAssignedVehicles; } - public static class FactoryProvider implements Provider { - @Inject - private EventsManager eventsManager; - - private final Function chargingStrategyCreator; + static public class Factory implements ChargingLogic.Factory { + private final EventsManager eventsManager; + private final ChargingPriority.Factory priorityFactory; - public FactoryProvider(Function chargingStrategyCreator) { - this.chargingStrategyCreator = chargingStrategyCreator; + public Factory(EventsManager eventsManager, ChargingPriority.Factory priorityFactory) { + this.eventsManager = eventsManager; + this.priorityFactory = priorityFactory; } @Override - public ChargingLogic.Factory get() { - return charger -> new ChargingWithQueueingAndAssignmentLogic(charger, - chargingStrategyCreator.apply(charger), eventsManager); + public ChargingLogic create(ChargerSpecification charger) { + return new ChargingWithQueueingAndAssignmentLogic(charger, eventsManager, priorityFactory.create(charger)); } } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingLogic.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingLogic.java index 8cf57733532..0e0de973ffe 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingLogic.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/charging/ChargingWithQueueingLogic.java @@ -19,78 +19,87 @@ package org.matsim.contrib.ev.charging; -import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + import org.matsim.api.core.v01.Id; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.ChargerSpecification; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.vehicles.Vehicle; -import java.util.*; -import java.util.concurrent.LinkedBlockingQueue; +import com.google.common.base.Preconditions; public class ChargingWithQueueingLogic implements ChargingLogic { protected final ChargerSpecification charger; - private final ChargingStrategy chargingStrategy; private final EventsManager eventsManager; + private final ChargingPriority priority; - private final Map, ElectricVehicle> pluggedVehicles = new LinkedHashMap<>(); - private final Queue queuedVehicles = new LinkedList<>(); - private final Queue arrivingVehicles = new LinkedBlockingQueue<>(); + private final Map, ChargingVehicle> pluggedVehicles = new LinkedHashMap<>(); + private final Queue queuedVehicles = new LinkedList<>(); + private final Queue arrivingVehicles = new LinkedBlockingQueue<>(); private final Map, ChargingListener> listeners = new LinkedHashMap<>(); - public ChargingWithQueueingLogic(ChargerSpecification charger, ChargingStrategy chargingStrategy, EventsManager eventsManager) { - this.chargingStrategy = Objects.requireNonNull(chargingStrategy); + public ChargingWithQueueingLogic(ChargerSpecification charger, EventsManager eventsManager, ChargingPriority priority) { this.charger = Objects.requireNonNull(charger); this.eventsManager = Objects.requireNonNull(eventsManager); + this.priority = priority; } @Override public void chargeVehicles(double chargePeriod, double now) { - Iterator evIter = pluggedVehicles.values().iterator(); - while (evIter.hasNext()) { - ElectricVehicle ev = evIter.next(); + Iterator cvIter = pluggedVehicles.values().iterator(); + while (cvIter.hasNext()) { + ChargingVehicle cv = cvIter.next(); // with fast charging, we charge around 4% of SOC per minute, // so when updating SOC every 10 seconds, SOC increases by less then 1% - double oldCharge = ev.getBattery().getCharge(); - double energy = ev.getChargingPower().calcChargingPower(charger) * chargePeriod; - double newCharge = Math.min(oldCharge + energy, ev.getBattery().getCapacity()); - ev.getBattery().setCharge(newCharge); - eventsManager.processEvent(new EnergyChargedEvent(now, charger.getId(), ev.getId(), newCharge - oldCharge, newCharge)); - - if (chargingStrategy.isChargingCompleted(ev)) { - evIter.remove(); - eventsManager.processEvent(new ChargingEndEvent(now, charger.getId(), ev.getId(), ev.getBattery().getCharge())); - listeners.remove(ev.getId()).notifyChargingEnded(ev, now); + double oldCharge = cv.ev().getBattery().getCharge(); + double energy = cv.ev().getChargingPower().calcChargingPower(charger) * chargePeriod; + double newCharge = Math.min(oldCharge + energy, cv.ev().getBattery().getCapacity()); + cv.ev().getBattery().setCharge(newCharge); + eventsManager.processEvent(new EnergyChargedEvent(now, charger.getId(), cv.ev().getId(), newCharge - oldCharge, newCharge)); + + if (cv.strategy().isChargingCompleted()) { + cvIter.remove(); + eventsManager.processEvent(new ChargingEndEvent(now, charger.getId(), cv.ev().getId(), cv.ev().getBattery().getCharge())); + listeners.remove(cv.ev().getId()).notifyChargingEnded(cv.ev(), now); } } - int queuedToPluggedCount = Math.min(queuedVehicles.size(), charger.getPlugCount() - pluggedVehicles.size()); - for (int i = 0; i < queuedToPluggedCount; i++) { - plugVehicle(queuedVehicles.poll(), now); + var queuedVehiclesIter = queuedVehicles.iterator(); + while (queuedVehiclesIter.hasNext() && pluggedVehicles.size() < charger.getPlugCount()) { + var cv = queuedVehiclesIter.next(); + if (plugVehicle(cv, now)) { + queuedVehiclesIter.remove(); + } } var arrivingVehiclesIter = arrivingVehicles.iterator(); while (arrivingVehiclesIter.hasNext()) { - var ev = arrivingVehiclesIter.next(); - if (pluggedVehicles.size() < charger.getPlugCount()) { - plugVehicle(ev, now); - } else { - queueVehicle(ev, now); + var cv = arrivingVehiclesIter.next(); + if (pluggedVehicles.size() >= charger.getPlugCount() || !plugVehicle(cv, now)) { + queueVehicle(cv, now); } - arrivingVehiclesIter.remove(); } + arrivingVehicles.clear(); } @Override - public void addVehicle(ElectricVehicle ev, double now) { - addVehicle(ev, new ChargingListener() { + public void addVehicle(ElectricVehicle ev, ChargingStrategy strategy, double now) { + addVehicle(ev, strategy, new ChargingListener() { }, now); } @Override - public void addVehicle(ElectricVehicle ev, ChargingListener chargingListener, double now) { - arrivingVehicles.add(ev); + public void addVehicle(ElectricVehicle ev, ChargingStrategy strategy, ChargingListener chargingListener, double now) { + arrivingVehicles.add(new ChargingVehicle(ev, strategy)); listeners.put(ev.getId(), chargingListener); } @@ -100,47 +109,79 @@ public void removeVehicle(ElectricVehicle ev, double now) { eventsManager.processEvent(new ChargingEndEvent(now, charger.getId(), ev.getId(), ev.getBattery().getCharge())); listeners.remove(ev.getId()).notifyChargingEnded(ev, now); - if (!queuedVehicles.isEmpty()) { - plugVehicle(queuedVehicles.poll(), now); + var queuedVehiclesIter = queuedVehicles.iterator(); + while (queuedVehiclesIter.hasNext()) { + var queuedVehicle = queuedVehiclesIter.next(); + if (plugVehicle(queuedVehicle, now)) { + queuedVehiclesIter.remove(); + break; + } } } else { - // make sure ev was in the queue - Preconditions.checkState(queuedVehicles.remove(ev), "Vehicle (%s) is neither queued nor plugged at charger (%s)", ev.getId(), - charger.getId()); - eventsManager.processEvent(new QuitQueueAtChargerEvent(now, charger.getId(), ev.getId())); + var queuedVehiclesIter = queuedVehicles.iterator(); + while (queuedVehiclesIter.hasNext()) { + var queuedVehicle = queuedVehiclesIter.next(); + + if (queuedVehicle.ev() == ev) { + queuedVehiclesIter.remove(); + eventsManager.processEvent(new QuitQueueAtChargerEvent(now, charger.getId(), ev.getId())); + return; // found the vehicle + } + } + + throw new IllegalStateException(String.format("Vehicle (%s) is neither queued nor plugged at charger (%s)", ev.getId(), + charger.getId())); } } - private void queueVehicle(ElectricVehicle ev, double now) { - queuedVehicles.add(ev); - eventsManager.processEvent(new QueuedAtChargerEvent(now, charger.getId(), ev.getId())); - listeners.get(ev.getId()).notifyVehicleQueued(ev, now); + private void queueVehicle(ChargingVehicle cv, double now) { + queuedVehicles.add(cv); + eventsManager.processEvent(new QueuedAtChargerEvent(now, charger.getId(), cv.ev().getId())); + listeners.get(cv.ev().getId()).notifyVehicleQueued(cv.ev(), now); } - private void plugVehicle(ElectricVehicle ev, double now) { - if (pluggedVehicles.put(ev.getId(), ev) != null) { + private boolean plugVehicle(ChargingVehicle cv, double now) { + assert pluggedVehicles.size() < charger.getPlugCount(); + + if (!priority.requestPlugNext(cv, now)) { + return false; + } + + if (pluggedVehicles.put(cv.ev().getId(), cv) != null) { throw new IllegalArgumentException(); } - eventsManager.processEvent(new ChargingStartEvent(now, charger.getId(), ev.getId(), ev.getBattery().getCharge())); - listeners.get(ev.getId()).notifyChargingStarted(ev, now); + eventsManager.processEvent(new ChargingStartEvent(now, charger.getId(), cv.ev().getId(), cv.ev().getBattery().getCharge())); + listeners.get(cv.ev().getId()).notifyChargingStarted(cv.ev(), now); + + return true; } - private final Collection unmodifiablePluggedVehicles = Collections.unmodifiableCollection(pluggedVehicles.values()); + private final Collection unmodifiablePluggedVehicles = Collections.unmodifiableCollection(pluggedVehicles.values()); @Override - public Collection getPluggedVehicles() { + public Collection getPluggedVehicles() { return unmodifiablePluggedVehicles; } - private final Collection unmodifiableQueuedVehicles = Collections.unmodifiableCollection(queuedVehicles); + private final Collection unmodifiableQueuedVehicles = Collections.unmodifiableCollection(queuedVehicles); @Override - public Collection getQueuedVehicles() { + public Collection getQueuedVehicles() { return unmodifiableQueuedVehicles; } - @Override - public ChargingStrategy getChargingStrategy() { - return chargingStrategy; + static public class Factory implements ChargingLogic.Factory { + private final EventsManager eventsManager; + private final ChargingPriority.Factory chargingPriorityFactory; + + public Factory(EventsManager eventsManager, ChargingPriority.Factory chargingPriorityFactory) { + this.eventsManager = eventsManager; + this.chargingPriorityFactory = chargingPriorityFactory; + } + + @Override + public ChargingLogic create(ChargerSpecification charger) { + return new ChargingWithQueueingLogic(charger, eventsManager, chargingPriorityFactory.create(charger)); + } } } 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 d206a50c875..15431cea1fe 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 @@ -86,11 +86,14 @@ public class VehicleChargingHandler private final ImmutableListMultimap, Charger> chargersAtLinks; private final EvConfigGroup evCfg; + private final ChargingStrategy.Factory strategyFactory; + @Inject - VehicleChargingHandler(ChargingInfrastructure chargingInfrastructure, ElectricFleet electricFleet, EvConfigGroup evConfigGroup) { + VehicleChargingHandler(ChargingInfrastructure chargingInfrastructure, ElectricFleet electricFleet, EvConfigGroup evConfigGroup, ChargingStrategy.Factory strategyFactory) { this.chargingInfrastructure = chargingInfrastructure; this.electricFleet = electricFleet; this.evCfg = evConfigGroup; + this.strategyFactory = strategyFactory; chargersAtLinks = ChargingInfrastructureUtils.getChargersAtLinks(chargingInfrastructure ); } @@ -112,7 +115,7 @@ public void handleEvent(ActivityStartEvent event) { .filter(ch -> ev.getChargerTypes().contains(ch.getChargerType())) .findAny() .get(); - c.getLogic().addVehicle(ev, event.getTime()); + c.getLogic().addVehicle(ev, strategyFactory.createStrategy(c.getSpecification(), ev), event.getTime()); vehiclesAtChargers.put(evId, c.getId()); } } diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java index b24583feb13..6b9b30e8cfd 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricFleetSpecificationDefaultImpl.java @@ -30,7 +30,7 @@ /** * @author Michal Maciejewski (michalm) */ -final class ElectricFleetSpecificationDefaultImpl implements ElectricFleetSpecification { +public 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/ElectricVehicleSpecificationDefaultImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java index ef18ae6cce6..e3a4caca03e 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/fleet/ElectricVehicleSpecificationDefaultImpl.java @@ -33,7 +33,7 @@ /** * @author Michal Maciejewski (michalm) */ -final class ElectricVehicleSpecificationDefaultImpl implements ElectricVehicleSpecification { +public final class ElectricVehicleSpecificationDefaultImpl implements ElectricVehicleSpecification { private final Vehicle matsimVehicle; diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java index 48a27563616..763a2cd70e1 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/infrastructure/ChargingInfrastructureSpecificationDefaultImpl.java @@ -28,7 +28,7 @@ /** * @author Michal Maciejewski (michalm) */ -final class ChargingInfrastructureSpecificationDefaultImpl implements ChargingInfrastructureSpecification { +public final class ChargingInfrastructureSpecificationDefaultImpl implements ChargingInfrastructureSpecification { private final SpecificationContainer container = new SpecificationContainer<>(); @Override diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationManager.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationManager.java new file mode 100644 index 00000000000..6e6fa1e77c9 --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationManager.java @@ -0,0 +1,106 @@ +package org.matsim.contrib.ev.reservation; + +import java.util.LinkedList; +import java.util.List; + +import org.matsim.api.core.v01.IdMap; +import org.matsim.contrib.ev.fleet.ElectricVehicle; +import org.matsim.contrib.ev.infrastructure.Charger; +import org.matsim.contrib.ev.infrastructure.ChargerSpecification; +import org.matsim.core.controler.events.IterationStartsEvent; +import org.matsim.core.controler.listener.IterationStartsListener; + +/** + * This class is a singleton service that keeps a list of reservations for + * chargers. It can be used in combination with the + * ReservationBasedChargingPriority to let vehicle pass on to charging only if + * they have a proper reservation. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class ChargerReservationManager implements IterationStartsListener { + private final IdMap> reservations = new IdMap<>(Charger.class); + + public boolean isAvailable(ChargerSpecification charger, ElectricVehicle vehicle, double startTile, + double endTime) { + if (charger.getPlugCount() == 0) { + return false; + } + + if (!reservations.containsKey(charger.getId())) { + return true; + } + + int remaining = charger.getPlugCount(); + for (Reservation reservation : reservations.get(charger.getId())) { + if (reservation.vehicle != vehicle && isOverlapping(reservation, startTile, endTime)) { + remaining--; + } + } + + return remaining > 0; + } + + private boolean isOverlapping(Reservation reservation, double startTime, double endTime) { + if (startTime >= reservation.startTime && startTime <= reservation.endTime) { + return true; // start time within existing range + } else if (endTime >= reservation.startTime && endTime <= reservation.endTime) { + return true; // end time within existing range + } else if (startTime <= reservation.startTime && endTime >= reservation.endTime) { + return true; // new range covers existing range + } else { + return false; + } + } + + public Reservation addReservation(ChargerSpecification charger, ElectricVehicle vehicle, double startTime, + double endTime) { + if (isAvailable(charger, vehicle, startTime, endTime)) { + List chargerReservations = reservations.get(charger.getId()); + + if (chargerReservations == null) { + chargerReservations = new LinkedList<>(); + reservations.put(charger.getId(), chargerReservations); + } + + Reservation reservation = new Reservation(charger, vehicle, startTime, endTime); + chargerReservations.add(reservation); + + return reservation; + } + + return null; + } + + public boolean removeReservation(Reservation reservation) { + List chargerReservations = reservations.get(reservation.charger.getId()); + + if (chargerReservations != null) { + return chargerReservations.remove(reservation); + } + + return false; + } + + public Reservation findReservation(ChargerSpecification charger, ElectricVehicle vehicle, double now) { + List chargerReservations = reservations.get(charger.getId()); + + if (chargerReservations != null) { + for (Reservation reservation : chargerReservations) { + if (reservation.vehicle == vehicle && now >= reservation.startTime && now <= reservation.endTime) { + return reservation; + } + } + } + + return null; + } + + public record Reservation(ChargerSpecification charger, ElectricVehicle vehicle, double startTime, double endTime) { + } + + @Override + public void notifyIterationStarts(IterationStartsEvent event) { + reservations.clear(); + } +} \ No newline at end of file diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationModule.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationModule.java new file mode 100644 index 00000000000..9a17f208055 --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ChargerReservationModule.java @@ -0,0 +1,35 @@ +package org.matsim.contrib.ev.reservation; + +import org.matsim.contrib.ev.charging.ChargingPriority; +import org.matsim.core.controler.AbstractModule; + +import com.google.inject.Provides; +import com.google.inject.Singleton; + +/** + * This module enables the reservation-based charging logic that requires + * vehicles to have or make a reservation when attempting to charge at a + * charger. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class ChargerReservationModule extends AbstractModule { + @Override + public void install() { + addControlerListenerBinding().to(ChargerReservationManager.class); + bind(ChargingPriority.Factory.class).to(ReservationBasedChargingPriority.Factory.class); + } + + @Provides + @Singleton + ReservationBasedChargingPriority.Factory provideReservationBasedChargingPriorityFactory( + ChargerReservationManager reservationManager) { + return new ReservationBasedChargingPriority.Factory(reservationManager); + } + + @Provides + @Singleton + ChargerReservationManager provideChargerReservationManager() { + return new ChargerReservationManager(); + } +} \ No newline at end of file diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ReservationBasedChargingPriority.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ReservationBasedChargingPriority.java new file mode 100644 index 00000000000..8ac5a79a8ad --- /dev/null +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/reservation/ReservationBasedChargingPriority.java @@ -0,0 +1,64 @@ +package org.matsim.contrib.ev.reservation; + +import org.matsim.contrib.ev.charging.ChargingLogic.ChargingVehicle; +import org.matsim.contrib.ev.charging.ChargingPriority; +import org.matsim.contrib.ev.infrastructure.ChargerSpecification; +import org.matsim.contrib.ev.reservation.ChargerReservationManager.Reservation; + +/** + * This is an implementation of a charging priority which is backed by the + * ReservationManager: When a vehicle arrives, it is checked whether a + * reservation for this vehicle exists for the respective charger and the + * current time. In that case, the vehicle can be plugged if it is on top of the + * queue. If not, the ChargingPriority will try to make a reservation and if it + * is successful, the vehicle can start charging. In all other cases, the + * vehicle will stay in the queue until it is removed manually or a successful + * reservation can be made at a future time. + * + * @author Sebastian Hörl (sebhoerl), IRT SystemX + */ +public class ReservationBasedChargingPriority implements ChargingPriority { + private final ChargerReservationManager manager; + private final ChargerSpecification charger; + + public ReservationBasedChargingPriority(ChargerReservationManager manager, ChargerSpecification charger) { + this.charger = charger; + this.manager = manager; + } + + @Override + public boolean requestPlugNext(ChargingVehicle cv, double now) { + Reservation reservation = manager.findReservation(charger, cv.ev(), now); + + if (reservation != null) { + // vehicle has a reservation, can be plugged right away, consume reservation + manager.removeReservation(reservation); + return true; + } + + double endTime = cv.strategy().calcRemainingTimeToCharge() + now; + reservation = manager.addReservation(charger, cv.ev(), now, endTime); + + if (reservation != null) { + // vehicle did not have a reservation, but managed to create one on the fly, + // consume it directly + manager.removeReservation(reservation); + return true; + } + + return false; + } + + static public class Factory implements ChargingPriority.Factory { + private final ChargerReservationManager reservationManager; + + public Factory(ChargerReservationManager reservationManager) { + this.reservationManager = reservationManager; + } + + @Override + public ChargingPriority create(ChargerSpecification charger) { + return new ReservationBasedChargingPriority(reservationManager, charger); + } + } +} \ No newline at end of file diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargingProceduresCSVWriter.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargingProceduresCSVWriter.java index ae1c9d0c013..4cef65da81b 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargingProceduresCSVWriter.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargingProceduresCSVWriter.java @@ -83,8 +83,12 @@ private void proccessChargingEventSequences(CSVPrinter csvPrinter, Collection() { - @Inject - private ChargerPowerTimeProfileCalculator calculator; - @Inject - private MatsimServices matsimServices; + }); + bind(ChargerPowerTimeProfileCalculator.class).asEagerSingleton(); + addEventHandlerBinding().to(ChargerPowerTimeProfileCalculator.class); + addControlerListenerBinding().toProvider(new Provider<>() { + @Inject + private ChargerPowerTimeProfileCalculator calculator; + @Inject + private MatsimServices matsimServices; - @Override - public ControlerListener get() { - var profileView = new ChargerPowerTimeProfileView(calculator); - return new ProfileWriter(matsimServices,"ev",profileView,"charger_power_time_profiles"); + @Override + public ControlerListener get() { + var profileView = new ChargerPowerTimeProfileView(calculator); + return new ProfileWriter(matsimServices, "ev", profileView, "charger_power_time_profiles"); - } - }); + } + }); + } } } diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiChargingTask.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiChargingTask.java index 8f7f9b018ca..afb1b839bd5 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiChargingTask.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiChargingTask.java @@ -20,6 +20,7 @@ package org.matsim.contrib.etaxi; import org.matsim.contrib.evrp.ChargingTaskImpl; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; import org.matsim.contrib.taxi.schedule.TaxiTaskType; @@ -28,7 +29,7 @@ public class ETaxiChargingTask extends ChargingTaskImpl { public static final TaxiTaskType TYPE = new TaxiTaskType("CHARGING"); public ETaxiChargingTask(double beginTime, double endTime, Charger charger, ElectricVehicle ev, - double totalEnergy) { - super(TYPE, beginTime, endTime, charger, ev, totalEnergy); + double totalEnergy, ChargingStrategy chargingStrategy) { + super(TYPE, beginTime, endTime, charger, ev, totalEnergy, chargingStrategy); } } diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiScheduler.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiScheduler.java index 79999d4b140..16850cf1004 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiScheduler.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/ETaxiScheduler.java @@ -34,6 +34,7 @@ import org.matsim.contrib.dvrp.schedule.Schedules; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.ev.charging.ChargingEstimations; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithAssignmentLogic; import org.matsim.contrib.ev.fleet.ElectricVehicle; import org.matsim.contrib.ev.infrastructure.Charger; @@ -50,10 +51,13 @@ public class ETaxiScheduler extends TaxiScheduler { public static final TaxiTaskType DRIVE_TO_CHARGER = new TaxiTaskType("DRIVE_TO_CHARGER", EMPTY_DRIVE); + private final ChargingStrategy.Factory chargingStrategyFactory; + public ETaxiScheduler(TaxiConfigGroup taxiCfg, Fleet fleet, TaxiScheduleInquiry taxiScheduleInquiry, TravelTime travelTime, Supplier routerCreator, EventsManager eventsManager, - MobsimTimer mobsimTimer) { + MobsimTimer mobsimTimer, ChargingStrategy.Factory chargingStrategyFactory) { super(taxiCfg, fleet, taxiScheduleInquiry, travelTime, routerCreator, eventsManager, mobsimTimer); + this.chargingStrategyFactory = chargingStrategyFactory; } // FIXME underestimated due to the ongoing AUX/drive consumption @@ -65,12 +69,13 @@ public void scheduleCharging(DvrpVehicle vehicle, ElectricVehicle ev, Charger ch divertOrAppendDrive(schedule, vrpPath, DRIVE_TO_CHARGER); ChargingWithAssignmentLogic logic = (ChargingWithAssignmentLogic)charger.getLogic(); + ChargingStrategy strategy = chargingStrategyFactory.createStrategy(charger.getSpecification(), ev); double chargingEndTime = vrpPath.getArrivalTime() + ChargingEstimations.estimateMaxWaitTimeForNextVehicle( charger)// TODO not precise!!! - + logic.getChargingStrategy().calcRemainingTimeToCharge(ev);// TODO not precise !!! (SOC will be lower) + + strategy.calcRemainingTimeToCharge();// TODO not precise !!! (SOC will be lower) schedule.addTask(new ETaxiChargingTask(vrpPath.getArrivalTime(), chargingEndTime, charger, ev, - -logic.getChargingStrategy().calcRemainingEnergyToCharge(ev)));// TODO not precise !!! (ditto) - logic.assignVehicle(ev); + -strategy.calcRemainingEnergyToCharge(), strategy));// TODO not precise !!! (ditto) + logic.assignVehicle(ev, strategy); appendStayTask(vehicle); } diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/ETaxiModeOptimizerQSimModule.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/ETaxiModeOptimizerQSimModule.java index 5860bea3890..a713f20796c 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/ETaxiModeOptimizerQSimModule.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/ETaxiModeOptimizerQSimModule.java @@ -34,6 +34,7 @@ import org.matsim.contrib.etaxi.ETaxiActionCreator; import org.matsim.contrib.etaxi.ETaxiScheduler; import org.matsim.contrib.etaxi.util.ETaxiStayTaskEndTimeCalculator; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructure; import org.matsim.contrib.ev.infrastructure.ChargingInfrastructureUtils; import org.matsim.contrib.taxi.analysis.TaxiEventSequenceCollector; @@ -113,8 +114,9 @@ public ETaxiScheduler get() { var speedyALTFactory = new SpeedyALTFactory(); Supplier routerCreator = () -> speedyALTFactory.createPathCalculator( network, travelDisutility, travelTime); + ChargingStrategy.Factory chargingStrategyFactory = getModalInstance(ChargingStrategy.Factory.class); return new ETaxiScheduler(taxiCfg, fleet, taxiScheduleInquiry, travelTime, routerCreator, - events, timer); + events, timer, chargingStrategyFactory); } }); diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/assignment/AssignmentChargerPlugData.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/assignment/AssignmentChargerPlugData.java index 6f3a1105fc3..3d3e4b720e8 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/assignment/AssignmentChargerPlugData.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/optimizer/assignment/AssignmentChargerPlugData.java @@ -71,7 +71,7 @@ static AssignmentDestinationData create(double currentTime, Iterabl } // does not include AUX+driving for assigned vehs - double assignedWorkload = ChargingEstimations.estimateTotalTimeToCharge(logic.getChargingStrategy(), + double assignedWorkload = ChargingEstimations.estimateTotalTimeToCharge( Streams.concat(logic.getPluggedVehicles().stream(), logic.getQueuedVehicles().stream(), logic.getAssignedVehicles().stream())); diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiBenchmark.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiBenchmark.java index 5e67a5e2105..68d20b5263b 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiBenchmark.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiBenchmark.java @@ -27,6 +27,7 @@ import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.run.DvrpQSimComponents; import org.matsim.contrib.ev.EvConfigGroup; import org.matsim.contrib.ev.EvModule; @@ -34,6 +35,7 @@ import org.matsim.contrib.ev.charging.ChargingEventSequenceCollector; import org.matsim.contrib.ev.charging.ChargingLogic; import org.matsim.contrib.ev.charging.ChargingPower; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithQueueingAndAssignmentLogic; import org.matsim.contrib.ev.charging.FixedSpeedCharging; import org.matsim.contrib.ev.discharging.IdleDischargingHandler; @@ -53,6 +55,8 @@ import org.matsim.core.mobsim.qsim.AbstractQSimModule; import org.matsim.core.scenario.ScenarioUtils; +import com.google.inject.Key; + /** * For a fair and consistent benchmarking of taxi dispatching algorithms we assume that link travel times are * deterministic. To simulate this property, we remove (1) all other traffic, and (2) link capacity constraints (e.g. by @@ -111,8 +115,8 @@ protected void configureQSim() { controler.addOverridingModule(new AbstractModule() { @Override public void install() { - bind(ChargingLogic.Factory.class).toProvider(new ChargingWithQueueingAndAssignmentLogic.FactoryProvider( - charger -> new ChargeUpToMaxSocStrategy(charger, MAX_SOC))); + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingAndAssignmentLogic.Factory.class); + bind(Key.get(ChargingStrategy.Factory.class, DvrpModes.mode(mode))).toInstance(new ChargeUpToMaxSocStrategy.Factory(MAX_SOC)); //TODO switch to VariableSpeedCharging for Nissan bind(ChargingPower.Factory.class).toInstance(ev -> new FixedSpeedCharging(ev, CHARGING_SPEED_FACTOR)); bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiScenario.java b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiScenario.java index f5598174103..bd266b4b0f2 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiScenario.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/etaxi/run/RunETaxiScenario.java @@ -23,6 +23,7 @@ import org.matsim.api.core.v01.Scenario; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModes; import org.matsim.contrib.dvrp.run.DvrpModule; import org.matsim.contrib.dvrp.run.DvrpQSimComponents; import org.matsim.contrib.ev.EvConfigGroup; @@ -30,12 +31,13 @@ import org.matsim.contrib.ev.charging.ChargeUpToMaxSocStrategy; import org.matsim.contrib.ev.charging.ChargingLogic; import org.matsim.contrib.ev.charging.ChargingPower; +import org.matsim.contrib.ev.charging.ChargingStrategy; import org.matsim.contrib.ev.charging.ChargingWithQueueingAndAssignmentLogic; import org.matsim.contrib.ev.charging.FixedSpeedCharging; import org.matsim.contrib.ev.discharging.IdleDischargingHandler; +import org.matsim.contrib.ev.temperature.TemperatureService; import org.matsim.contrib.evrp.EvDvrpFleetQSimModule; import org.matsim.contrib.evrp.OperatingVehicleProvider; -import org.matsim.contrib.ev.temperature.TemperatureService; import org.matsim.contrib.otfvis.OTFVisLiveModule; import org.matsim.contrib.taxi.run.MultiModeTaxiConfigGroup; import org.matsim.contrib.taxi.run.TaxiConfigGroup; @@ -48,6 +50,8 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.vis.otfvis.OTFVisConfigGroup; +import com.google.inject.Key; + public class RunETaxiScenario { private static final double CHARGING_SPEED_FACTOR = 1.5; // > 1 in this example private static final double MAX_SOC = 0.8; // charge up to 80% SOC @@ -91,11 +95,14 @@ protected void configureQSim() { controler.addOverridingModule(new AbstractModule() { @Override public void install() { - bind(ChargingLogic.Factory.class).toProvider(new ChargingWithQueueingAndAssignmentLogic.FactoryProvider( - charger -> new ChargeUpToMaxSocStrategy(charger, MAX_SOC))); + bind(ChargingLogic.Factory.class).to(ChargingWithQueueingAndAssignmentLogic.Factory.class); //TODO switch to VariableSpeedCharging for Nissan bind(ChargingPower.Factory.class).toInstance(ev -> new FixedSpeedCharging(ev, CHARGING_SPEED_FACTOR)); bind(TemperatureService.class).toInstance(linkId -> TEMPERATURE); + + for (TaxiConfigGroup taxiCfg : multiModeTaxiConfig.getModalElements()) { + bind(Key.get(ChargingStrategy.Factory.class, DvrpModes.mode(taxiCfg.getMode()))).toInstance(new ChargeUpToMaxSocStrategy.Factory(MAX_SOC)); + } } }); diff --git a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanVehicleChargingHandler.java b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanVehicleChargingHandler.java index 592722998fe..8e40eb18120 100644 --- a/contribs/vsp/src/main/java/playground/vsp/ev/UrbanVehicleChargingHandler.java +++ b/contribs/vsp/src/main/java/playground/vsp/ev/UrbanVehicleChargingHandler.java @@ -61,13 +61,16 @@ class UrbanVehicleChargingHandler private final ElectricFleet electricFleet; private final ImmutableListMultimap, Charger> chargersAtLinks; + private final ChargingStrategy.Factory chargingStrategyFactory; + private Map, Map, Tuple, Id>>> chargingProcedures = new HashMap<>(); @Inject - UrbanVehicleChargingHandler(ChargingInfrastructure chargingInfrastructure, ElectricFleet electricFleet) { + UrbanVehicleChargingHandler(ChargingInfrastructure chargingInfrastructure, ElectricFleet electricFleet, ChargingStrategy.Factory chargingStrategyFactory) { this.chargingInfrastructure = chargingInfrastructure; this.electricFleet = electricFleet; this.chargersAtLinks = ChargingInfrastructureUtils.getChargersAtLinks(chargingInfrastructure ); + this.chargingStrategyFactory = chargingStrategyFactory; } /** @@ -89,7 +92,8 @@ public void handleEvent(ActivityStartEvent event) { .findAny() .get(); - charger.getLogic().addVehicle(ev, event.getTime()); + ChargingStrategy strategy = chargingStrategyFactory.createStrategy(charger.getSpecification(), ev); + charger.getLogic().addVehicle(ev, strategy, event.getTime()); Map, Tuple, Id>> proceduresOnLink = this.chargingProcedures.get(event.getLinkId()); if(proceduresOnLink != null && proceduresOnLink.containsKey(event.getPersonId())){ throw new RuntimeException("person " + event.getPersonId() + " tries to charge 2 vehicles at the same time on link " + event.getLinkId() + diff --git a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java index d0532315137..02ea9efcf79 100644 --- a/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java +++ b/matsim/src/main/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinks.java @@ -20,7 +20,6 @@ package org.matsim.pt.utils; -import com.google.common.annotations.Beta; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; @@ -40,11 +39,8 @@ * is assigned to a loop link, located in a node with the same coordinates as the stop. * The stop facility ID is used for node and link IDs. * - * @author mrieser, davibicudo - * - * @implNote THis functionality might be merged with {@link CreatePseudoNetwork}. + * @author mrieser, davibicudo, rakow */ -@Beta public class CreatePseudoNetworkWithLoopLinks { private final TransitSchedule schedule; @@ -82,23 +78,29 @@ public void createNetwork() { List> toBeRemoved = new LinkedList<>(); for (TransitLine tLine : this.schedule.getTransitLines().values()) { for (TransitRoute tRoute : tLine.getRoutes().values()) { - ArrayList> routeLinks = new ArrayList<>(); + + if (tRoute.getStops().size() < 2) { + System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); + toBeRemoved.add(new Tuple<>(tLine, tRoute)); + continue; + } + + List> routeLinks = new ArrayList<>(); TransitRouteStop prevStop = null; + for (TransitRouteStop stop : tRoute.getStops()) { if (prevStop != null) { Link link = getNetworkLink(prevStop, stop); routeLinks.add(link.getId()); } + + // Add the loop links of all stops to the route + routeLinks.add(getLoopLink(stop.getStopFacility()).getId()); prevStop = stop; } - if (!routeLinks.isEmpty()) { - NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); - tRoute.setRoute(route); - } else { - System.err.println("Line " + tLine.getId() + " route " + tRoute.getId() + " has less than two stops. Removing this route from schedule."); - toBeRemoved.add(new Tuple<>(tLine, tRoute)); - } + NetworkRoute route = RouteUtils.createNetworkRoute(routeLinks); + tRoute.setRoute(route); } } @@ -114,6 +116,14 @@ private void createStopNodesAndLoopLinks() { this.nodes.put(stop, node); Link loopLink = this.network.getFactory().createLink(Id.createLinkId (this.prefix + stop.getId()), node, node); + // Loop links needs to have a length so that the travel time is not zero + loopLink.setLength(1); + loopLink.setFreespeed(linkFreeSpeed); + loopLink.setCapacity(linkCapacity); + // Ensure enough vehicles can be placed on the loop link + loopLink.setNumberOfLanes(linkCapacity); + loopLink.setAllowedModes(transitModes); + stop.setLinkId(loopLink.getId()); this.network.addLink(loopLink); Tuple connection = new Tuple<>(node, node); @@ -121,6 +131,15 @@ private void createStopNodesAndLoopLinks() { } } + /** + * Get the loop link for a stop facility. + */ + private Link getLoopLink(final TransitStopFacility stop) { + Node node = this.nodes.get(stop); + Tuple connection = new Tuple<>(node, node); + return this.links.get(connection); + } + private Link getNetworkLink(final TransitRouteStop fromStop, final TransitRouteStop toStop) { TransitStopFacility fromFacility = fromStop.getStopFacility(); TransitStopFacility toFacility = toStop.getStopFacility(); @@ -137,13 +156,15 @@ private Link createAndAddLink(Tuple connection) { Node fromNode = connection.getFirst(); Node toNode = connection.getSecond(); Link link; - link = this.network.getFactory().createLink(Id.createLinkId(this.prefix + fromNode.getId() + "-" + toNode.getId()), fromNode, - toNode); - link.setLength(CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord())); + link = this.network.getFactory().createLink(Id.createLinkId(fromNode.getId() + "-" + toNode.getId()), + fromNode, toNode); + double dist = CoordUtils.calcEuclideanDistance(fromNode.getCoord(), toNode.getCoord()); + link.setLength(dist); link.setFreespeed(linkFreeSpeed); link.setCapacity(linkCapacity); link.setNumberOfLanes(1); + this.network.addLink(link); link.setAllowedModes(this.transitModes); this.links.put(connection, link); diff --git a/matsim/src/test/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinksTest.java b/matsim/src/test/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinksTest.java new file mode 100644 index 00000000000..431c0c87bcd --- /dev/null +++ b/matsim/src/test/java/org/matsim/pt/utils/CreatePseudoNetworkWithLoopLinksTest.java @@ -0,0 +1,28 @@ +package org.matsim.pt.utils; + +import org.junit.jupiter.api.Test; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.scenario.ScenarioUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +class CreatePseudoNetworkWithLoopLinksTest { + + @Test + void createValidSchedule() { + + Scenario scenario = ScenarioUtils.loadScenario(ConfigUtils.loadConfig("test/scenarios/pt-tutorial/0.config.xml")); + + CreatePseudoNetworkWithLoopLinks creator = new CreatePseudoNetworkWithLoopLinks(scenario.getTransitSchedule(), scenario.getNetwork(), "pt_"); + creator.createNetwork(); + + + TransitScheduleValidator.ValidationResult result = TransitScheduleValidator.validateAll(scenario.getTransitSchedule(), scenario.getNetwork()); + + assertThat(result.getWarnings()).isEmpty(); + assertThat(result.getErrors()).isEmpty(); + assertThat(result.isValid()).isTrue(); + + } +}