diff --git a/src/main/java/org/matsim/freight/logistics/examples/multipleChains/ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll.java b/src/main/java/org/matsim/freight/logistics/examples/multipleChains/ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll.java
new file mode 100644
index 00000000..a95acb2b
--- /dev/null
+++ b/src/main/java/org/matsim/freight/logistics/examples/multipleChains/ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll.java
@@ -0,0 +1,479 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2024 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.multipleChains;
+
+import java.net.URL;
+import java.util.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.contrib.roadpricing.*;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.groups.ScoringConfigGroup;
+import org.matsim.core.config.groups.VspExperimentalConfigGroup;
+import org.matsim.core.controler.AbstractModule;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.replanning.GenericPlanStrategyImpl;
+import org.matsim.core.replanning.selectors.BestPlanSelector;
+import org.matsim.core.replanning.selectors.ExpBetaPlanSelector;
+import org.matsim.core.replanning.selectors.GenericWorstPlanForRemovalSelector;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.core.utils.misc.Time;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.controler.CarrierControlerUtils;
+import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory;
+import org.matsim.freight.carriers.controler.CarrierStrategyManager;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.examples.ExampleConstants;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LSPShipment;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/**
+ * This bases on {@link ExampleGroceryDeliveryMultipleChains}.
+ * Now it will include two different LSPs
+ *
+ */
+final class ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll {
+
+ private static final Logger log = LogManager.getLogger(ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll.class);
+ private static final Id HUB_LINK_ID_NEUKOELLN = Id.createLinkId("91085");
+ private static final double HUBCOSTS_FIX = 100;
+
+ private static final List TOLLED_LINKS = ExampleConstants.TOLLED_LINK_LIST_BERLIN;
+ private static final List TOLLED_VEHICLE_TYPES = List.of("heavy40t"); // Für welche Fahrzeugtypen soll das MautSchema gelten?
+ private static final double TOLL_VALUE = 1000;
+
+ private static final String CARRIER_PLAN_FILE = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/berlin/projects/freight/foodRetailing_wo_rangeConstraint/input/CarrierLEH_v2_withFleet_Shipment_OneTW_PickupTime_ICEVandBEV.xml";
+ private static final String VEHICLE_TYPE_FILE = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/berlin/projects/freight/foodRetailing_wo_rangeConstraint/input/vehicleTypesBVWP100_DC_noTax.xml";
+ private static final String EDEKA_SUPERMARKT_TROCKEN = "edeka_SUPERMARKT_TROCKEN";
+ private static final String KAUFLAND_VERBRAUCHERMARKT_TROCKEN = "kaufland_VERBRAUCHERMARKT_TROCKEN";
+
+ private static final String OUTPUT_DIRECTORY = "output/groceryDelivery_kmt_10_toll";
+
+
+ private ExampleTwoLspsGroceryDeliveryMultipleChainsWithToll() {}
+
+ public static void main(String[] args) {
+ log.info("Prepare config");
+ Config config = prepareConfig(args);
+
+ log.info("Prepare scenario");
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ CarrierVehicleTypes vehicleTypes = new CarrierVehicleTypes();
+ CarrierVehicleTypeReader vehicleTypeReader = new CarrierVehicleTypeReader(vehicleTypes);
+ vehicleTypeReader.readFile(VEHICLE_TYPE_FILE);
+
+ Carriers carriers = new Carriers();
+ CarrierPlanXmlReader carrierReader = new CarrierPlanXmlReader(carriers, vehicleTypes);
+ carrierReader.readFile(CARRIER_PLAN_FILE);
+
+ Carrier carrierEdeka = carriers.getCarriers().get(Id.create(EDEKA_SUPERMARKT_TROCKEN, CarrierImpl.class));
+ Carrier carrierKaufland = carriers.getCarriers().get(Id.create(KAUFLAND_VERBRAUCHERMARKT_TROCKEN, CarrierImpl.class));
+
+ log.info("Add LSP(s) to the scenario");
+ Collection lsps = new LinkedList<>();
+ lsps.add(createLspWithTwoChains(scenario, "Edeka", MultipleChainsUtils.createLSPShipmentsFromCarrierShipments(carrierEdeka), getDepotLinkFromVehicle(carrierEdeka), HUB_LINK_ID_NEUKOELLN, vehicleTypes, vehicleTypes, vehicleTypes));
+ lsps.add(createLspWithTwoChains(scenario, "Kaufland", MultipleChainsUtils.createLSPShipmentsFromCarrierShipments(carrierKaufland), getDepotLinkFromVehicle(carrierKaufland), HUB_LINK_ID_NEUKOELLN, vehicleTypes, vehicleTypes, vehicleTypes));
+ lsps.add(createLspWithDirectChain(scenario, "Edeka_DIRECT", MultipleChainsUtils.createLSPShipmentsFromCarrierShipments(carrierEdeka), getDepotLinkFromVehicle(carrierEdeka), vehicleTypes));
+ lsps.add(createLspWithDirectChain(scenario, "Kaufland_DIRECT", MultipleChainsUtils.createLSPShipmentsFromCarrierShipments(carrierKaufland), getDepotLinkFromVehicle(carrierKaufland), vehicleTypes));
+ LSPUtils.addLSPs(scenario, new LSPs(lsps));
+
+
+ RoadPricingSchemeUsingTollFactor rpScheme = setUpRoadpricing(config, scenario);
+
+
+ log.info("Prepare controler");
+ Controler controler = new Controler(scenario);
+ controler.addOverridingModule(
+ new AbstractModule() {
+ @Override
+ public void install() {
+ install(new LSPModule());
+ }
+ });
+
+ controler.addOverridingModule(
+ new AbstractModule() {
+ @Override
+ public void install() {
+ final EventBasedCarrierScorer4MultipleChains carrierScorer =
+ new EventBasedCarrierScorer4MultipleChains();
+ carrierScorer.setToll(TOLL_VALUE);
+ carrierScorer.setTolledVehicleTypes(TOLLED_VEHICLE_TYPES);
+ carrierScorer.setTolledLinks(TOLLED_LINKS);
+ bind(CarrierScoringFunctionFactory.class).toInstance(carrierScorer);
+ bind(LSPScorerFactory.class).toInstance(MyLSPScorer::new);
+ bind(CarrierStrategyManager.class)
+ .toProvider(
+ () -> {
+ CarrierStrategyManager strategyManager =
+ CarrierControlerUtils.createDefaultCarrierStrategyManager();
+ strategyManager.addStrategy(
+ new GenericPlanStrategyImpl<>(new BestPlanSelector<>()), null, 1);
+ return strategyManager;
+ });
+ bind(LSPStrategyManager.class)
+ .toProvider(
+ () -> {
+ LSPStrategyManager strategyManager = new LSPStrategyManagerImpl();
+ strategyManager.addStrategy(new GenericPlanStrategyImpl<>(new ExpBetaPlanSelector<>(new ScoringConfigGroup())), null, 1);
+ strategyManager.addStrategy(RandomShiftingStrategyFactory.createStrategy(), null, 4);
+ strategyManager.setMaxPlansPerAgent(5);
+ strategyManager.setPlanSelectorForRemoval(new GenericWorstPlanForRemovalSelector<>());
+ return strategyManager;
+ });
+ }
+ });
+ if (!rpScheme.getTolledLinkIds().isEmpty()) {
+ // RoadPricing.configure(controler);
+ controler.addOverridingModule( new RoadPricingModule(rpScheme) );
+ }
+
+ log.info("Run MATSim");
+
+ // The VSP default settings are designed for person transport simulation. After talking to Kai,
+ // they will be set to WARN here. Kai MT may'23
+ controler
+ .getConfig()
+ .vspExperimental()
+ .setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn);
+ controler.run();
+
+ log.info("Done.");
+ }
+
+ /*
+ * Set up roadpricing --- this is a copy paste from KMT laecture in GVSim --> need some adaptions
+ * TODO Adapt settings
+ */
+ private static RoadPricingSchemeUsingTollFactor setUpRoadpricing(Config config, Scenario scenario) {
+
+ //Create Rp Scheme from code.
+ RoadPricingSchemeImpl scheme = RoadPricingUtils.addOrGetMutableRoadPricingScheme(scenario );
+
+ /* Configure roadpricing scheme. */
+ RoadPricingUtils.setName(scheme, "MautFromcodeKMT");
+ RoadPricingUtils.setType(scheme, RoadPricingScheme.TOLL_TYPE_LINK);
+ RoadPricingUtils.setDescription(scheme, "Mautdaten erstellt aus Link-Liste.");
+
+ /* Add general toll. */
+ for (String linkIdString : TOLLED_LINKS) {
+ RoadPricingUtils.addLink(scheme, Id.createLinkId(linkIdString));
+ }
+
+ RoadPricingUtils.createAndAddGeneralCost(scheme,
+ Time.parseTime("00:00:00"),
+ Time.parseTime("72:00:00"),
+ TOLL_VALUE);
+ ///___ End creating from Code
+
+
+ // Roadpricing settings
+ RoadPricingConfigGroup roadPricingConfigGroup =
+ ConfigUtils.addOrGetModule(config, RoadPricingConfigGroup.class);
+
+ // Wenn FzgTypId in Liste, erfolgt die Bemautung mit dem Kostensatz (Faktor = 1),
+ // sonst mit 0 (Faktor = 0). ((MATSim seite)
+ TollFactor tollFactor =
+ new TollFactor() {
+ @Override
+ public double getTollFactor(Id personId, Id vehicleId, Id linkId, double time) {
+ var vehTypeId = VehicleUtils.findVehicle(vehicleId, scenario).getType().getId();
+ if (TOLLED_VEHICLE_TYPES.contains(vehTypeId.toString())) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+ return new RoadPricingSchemeUsingTollFactor(scheme, tollFactor);
+ }
+
+
+ private static Config prepareConfig(String[] args) {
+ Config config = ConfigUtils.createConfig();
+ if (args.length != 0) {
+ for (String arg : args) {
+ log.warn(arg);
+ }
+ ConfigUtils.applyCommandline(config, args);
+ } else {
+ config.controller().setOutputDirectory(OUTPUT_DIRECTORY);
+ config.controller().setLastIteration(1);
+ }
+
+ config.network().setInputFile(
+ "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/berlin/berlin-v5.5-10pct/input/berlin-v5.5-network.xml.gz");
+ config.global().setCoordinateSystem("EPSG:31468");
+ config.global().setRandomSeed(4177);
+ config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists);
+ config.controller().setWriteEventsInterval(1);
+
+ FreightCarriersConfigGroup freightConfig = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+
+ return config;
+ }
+
+ /**
+ * Creates an LSP with two chains:
+ * - direct delivery
+ * - 2-echelon delivery
+ *
+ * TODO: Es sollten dann am Besten direkt die zur Verfügung stehenden VehicleTypes übergeben werden und diese dann hier jeweils (alle) hinzugefügt werden.
+ * Aktuell erfolgt die Auswahl ja noch hier. Das sollte dann aber nicht mehr so sein, sondern bereits weiter upstream definiert werden.
+ * kmt Jul'24
+ *
+ * @param scenario the scenario, used e.g. for getting the network and register some stuff
+ * @param lspName String of LSP's Id
+ * @param lspShipments Collection of LSPShipments to be assigned to the LSP
+ * @param depotLinkId Id of the depot link
+ * @param hubLinkId location of the hub
+ * @param vehicleTypesMainRun vehicle types for the main run (2e-chain)
+ * @param vehicleTypesDistributionRun vehicle types for the distribution run (2e-chain)
+ * @param vehicleTypesDirect vehicle types for the direct run (direct chain)
+ * @return the LSP
+ */
+ private static LSP createLspWithTwoChains(Scenario scenario, String lspName, Collection lspShipments, Id depotLinkId, Id hubLinkId, CarrierVehicleTypes vehicleTypesMainRun, CarrierVehicleTypes vehicleTypesDistributionRun, CarrierVehicleTypes vehicleTypesDirect) {
+ log.info("create LSP");
+ //Chains
+ LogisticChain directChain = createDirectChain(scenario, lspName, depotLinkId, vehicleTypesDirect);
+ LogisticChain twoEchelonChain = createTwoEchelonChain(scenario, lspName, hubLinkId, depotLinkId, vehicleTypesMainRun, vehicleTypesDistributionRun);
+
+ LSPPlan multipleMixedEchelonChainsPlan =
+ LSPUtils.createLSPPlan()
+ .addLogisticChain(directChain)
+ .addLogisticChain(twoEchelonChain)
+ .setInitialShipmentAssigner(MultipleChainsUtils.createRandomLogisticChainShipmentAssigner());
+
+ List lspPlans = List.of(multipleMixedEchelonChainsPlan);
+
+ LSP lsp = LSPUtils.LSPBuilder.getInstance(Id.create(lspName, LSP.class))
+ .setInitialPlan(multipleMixedEchelonChainsPlan)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlans(lspPlans)))
+ .build();
+
+ log.info("assign the shipments to the LSP");
+ for (LSPShipment shipment : lspShipments) {
+ lsp.assignShipmentToLSP(shipment);
+ }
+
+ log.info("schedule the LSP with the shipments and according to the scheduler of the Resource");
+ lsp.scheduleLogisticChains();
+
+ return lsp;
+ }
+
+ private static Id getDepotLinkFromVehicle(Carrier carrier) {
+ return carrier
+ .getCarrierCapabilities()
+ .getCarrierVehicles()
+ .values()
+ .iterator()
+ .next()
+ .getLinkId();
+ }
+
+ /*
+ TODO: Es sollten dann am Besten direkt die zur Verfügung stehenden VehicleTypes übergeben werden und diese dann hier jeweils (alle) hinzugefügt werden.
+ Aktuell erfolgt die Auswahl ja noch hier. Das sollte dann aber nicht mehr so sein, sondern bereits weiter upstream definiert werden.
+ kmt Jul'24
+ */
+ private static LogisticChain createTwoEchelonChain(Scenario scenario, String lspName, Id hubLinkId, Id depotLinkFromVehicles, CarrierVehicleTypes vehicleTypesMainRun, CarrierVehicleTypes vehicleTypesDistributionRun) {
+ LogisticChain hubChain;
+ Carrier mainCarrier = CarriersUtils.createCarrier(Id.create(lspName +"_mainCarrier", Carrier.class));
+ mainCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ mainCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("mainTruck"),
+ depotLinkFromVehicles,
+ vehicleTypesMainRun.getVehicleTypes().get(Id.create("heavy40t", VehicleType.class))));
+ LSPResource mainCarrierResource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(
+ mainCarrier, scenario.getNetwork())
+ .setFromLinkId(depotLinkFromVehicles)
+ .setMainRunCarrierScheduler(
+ ResourceImplementationUtils.createDefaultMainRunCarrierScheduler())
+ .setToLinkId(hubLinkId)
+ .setVehicleReturn(ResourceImplementationUtils.VehicleReturn.returnToFromLink)
+ .build();
+
+ LogisticChainElement mainCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("mainCarrierElement", LogisticChainElement.class))
+ .setResource(mainCarrierResource)
+ .build();
+
+ LSPResourceScheduler hubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10)
+ .setCapacityNeedLinear(1)
+ .build();
+
+ LSPResource hubResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ Id.create(lspName +"_Hub", LSPResource.class), hubLinkId, scenario)
+ .setTransshipmentHubScheduler(hubScheduler)
+ .build();
+
+ LSPUtils.setFixedCost(hubResource, HUBCOSTS_FIX);
+
+ LogisticChainElement hubElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("HubElement", LogisticChainElement.class))
+ .setResource(hubResource)
+ .build();
+
+ Carrier distributionCarrier =
+ CarriersUtils.createCarrier(Id.create(lspName +"_distributionCarrier", Carrier.class));
+ distributionCarrier
+ .getCarrierCapabilities()
+ .setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ distributionCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("distributionTruck"),
+ hubLinkId,
+ vehicleTypesDistributionRun
+ .getVehicleTypes()
+ .get(Id.create("heavy40t_electro", VehicleType.class))));
+ LSPResource distributionCarrierResource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier, scenario.getNetwork())
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler())
+ .build();
+
+ LogisticChainElement distributionCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("distributionCarrierElement", LogisticChainElement.class))
+ .setResource(distributionCarrierResource)
+ .build();
+
+ mainCarrierElement.connectWithNextElement(hubElement);
+ hubElement.connectWithNextElement(distributionCarrierElement);
+
+ hubChain =
+ LSPUtils.LogisticChainBuilder.newInstance(Id.create("hubChain", LogisticChain.class))
+ .addLogisticChainElement(mainCarrierElement)
+ .addLogisticChainElement(hubElement)
+ .addLogisticChainElement(distributionCarrierElement)
+ .build();
+ return hubChain;
+ }
+
+ /**
+ * Creates an LSP with direct chains:
+ *
+ * @param scenario the scenario, used e.g. for getting the network and register some stuff
+ * @param lspName String of LSP's Id
+ * @param lspShipments Collection of LSPShipments to be assigned to the LSP
+ * @param depotLinkId Id of the depot link
+ * @param vehicleTypesDirect vehicle types for the direct run (direct chain)
+ * @return the LSP
+ */
+ private static LSP createLspWithDirectChain(Scenario scenario, String lspName, Collection lspShipments, Id depotLinkId, CarrierVehicleTypes vehicleTypesDirect) {
+ log.info("create LSP");
+
+ LSPPlan lspPlan = LSPUtils.createLSPPlan()
+ .addLogisticChain(createDirectChain(scenario, lspName, depotLinkId, vehicleTypesDirect))
+ .setInitialShipmentAssigner(MultipleChainsUtils.createRandomLogisticChainShipmentAssigner());
+
+ LSP lsp =
+ LSPUtils.LSPBuilder.getInstance(Id.create(lspName, LSP.class))
+ .setInitialPlan(lspPlan)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlans(List.of(lspPlan))))
+ .build();
+
+ log.info("assign the shipments to the LSP");
+ for (LSPShipment shipment : lspShipments) {
+ lsp.assignShipmentToLSP(shipment);
+ }
+
+ log.info("schedule the LSP with the shipments and according to the scheduler of the Resource");
+ lsp.scheduleLogisticChains();
+
+ return lsp;
+ }
+
+
+ private static LogisticChain createDirectChain(Scenario scenario, String lspName, Id depotLinkFromVehicles, CarrierVehicleTypes vehicleTypes) {
+ LogisticChain directChain;
+ Carrier directCarrier = CarriersUtils.createCarrier(Id.create(lspName +"_directCarrier", Carrier.class));
+ directCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(directCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("directCarrierTruck"),
+ depotLinkFromVehicles,
+ vehicleTypes.getVehicleTypes().get(Id.create("heavy40t", VehicleType.class))));
+ LSPResource singleCarrierResource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ directCarrier, scenario.getNetwork())
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler())
+ .build();
+
+ LogisticChainElement singleCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("directCarrierElement", LogisticChainElement.class))
+ .setResource(singleCarrierResource)
+ .build();
+
+ directChain =
+ LSPUtils.LogisticChainBuilder.newInstance(Id.create("directChain", LogisticChain.class))
+ .addLogisticChainElement(singleCarrierElement)
+ .build();
+ return directChain;
+ }
+
+ private static List createResourcesListFromLSPPlans(List lspPlans) {
+ log.info("Collecting all LSPResources from the LSPPlans");
+ List resourceList = new ArrayList<>();
+ for (LSPPlan lspPlan : lspPlans) {
+ for (LogisticChain logisticChain : lspPlan.getLogisticChains()) {
+ for (LogisticChainElement logisticChainElement : logisticChain.getLogisticChainElements()) {
+ resourceList.add(logisticChainElement.getResource());
+ }
+ }
+ }
+ return resourceList;
+ }
+}