diff --git a/contribs/freight/README.md b/contribs/freight/README.md
index afdcbcd4abc..746cdba1bc1 100644
--- a/contribs/freight/README.md
+++ b/contribs/freight/README.md
@@ -1,10 +1,25 @@
# Freight
-Package that plugs freight algorithms (programmed in external package jsprit) into matsim.
+This contrib contains the following packages:
+
+## Carriers
+(This is formally knows as 'freight contrib')
+
+Package that plugs vehicle routing problem algorithms (programmed in external package jsprit) into MATSim.
A good starting point for jsprit is [ https://github.com/graphhopper/jsprit](https://github.com/graphhopper/jsprit).
-For runnable code see, e.g., the packages org.matsim.contrib.freight.usecases.* above .
+For runnable code see, e.g., the packages org.matsim.contrib.freight.carriers.usecases.* above .
+
+## Logistics
+(This code comes from [https://github.com/matsim-vsp/logistics/](https://github.com/matsim-vsp/logistics/) )
+
+This code deals with creating logistics chains for freight transport.
+
+Here the decision agent is the logistics service provider (LSP) who decides on the logistics chain to be used for a given freight transport request.
+Therefore, it can use carriers (see above) and hubs.
+
+This package bases on work in the dfg-freight project.
\ No newline at end of file
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/ForwardLogisticChainSchedulerImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/ForwardLogisticChainSchedulerImpl.java
new file mode 100644
index 00000000000..71d76c307ce
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/ForwardLogisticChainSchedulerImpl.java
@@ -0,0 +1,189 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import org.matsim.api.core.v01.Id;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * .... Macht 3 Schritte: 1.) the LSPShipments are handed over to the first {@link
+ * LogisticChainElement} of their {@link LogisticChain} 2.) the neighbors, i.e. the predecessors and
+ * successors of all {@link LSPResource}s are determined 3.) the Resources are brought into the
+ * right sequence according to the algorithm.
+ *
+ *
When traversing this list of {@link LSPResource}s, the operations in each {@link LSPResource}
+ * are scheduled individually by calling their {@link LSPResourceScheduler}.
+ */
+/* package-private */ class ForwardLogisticChainSchedulerImpl implements LogisticChainScheduler {
+
+ /**
+ * The Resources are brought into the right sequence according to the algorithm. The result of
+ * this algorithm is a list of Resources that is later traversed from the front to the back, i.e.
+ * starting with the entry at index 0. In the algorithm, this list is called sortedResourceList.
+ */
+ private final ArrayList sortedResourceList;
+
+ /**
+ * The determination of the neighborhood structure among the Resources resulted in the
+ * neighborList.
+ */
+ private final ArrayList neighbourList;
+
+ private LSP lsp;
+ private int bufferTime;
+
+ ForwardLogisticChainSchedulerImpl() {
+ this.sortedResourceList = new ArrayList<>();
+ this.neighbourList = new ArrayList<>();
+ }
+
+ @Override
+ public void scheduleLogisticChain() {
+ insertShipmentsAtBeginning();
+ setResourceNeighbours();
+ sortResources();
+ for (LSPResource resource : sortedResourceList) {
+ resource.schedule(bufferTime, lsp.getSelectedPlan());
+ }
+ }
+
+ @Override
+ public void setEmbeddingContainer(LSP lsp) {
+ this.lsp = lsp;
+ }
+
+ private void setResourceNeighbours() {
+ // internal data structure, try to ignore when looking from outside. kai/kai, jan'22
+ neighbourList.clear();
+ for (LSPResource resource : lsp.getResources()) {
+ ResourceNeighbours neighbours = new ResourceNeighbours(resource);
+ for (LogisticChainElement element : resource.getClientElements()) {
+ LogisticChainElement predecessor = element.getPreviousElement();
+ LSPResource previousResource = predecessor.getResource();
+ neighbours.addPredecessor(previousResource);
+ LogisticChainElement successor = element.getNextElement();
+ LSPResource nextResource = successor.getResource();
+ neighbours.addSuccessor(nextResource);
+ }
+ neighbourList.add(neighbours);
+ }
+ }
+
+ private void sortResources() {
+ sortedResourceList.clear();
+ while (!neighbourList.isEmpty()) {
+ for (ResourceNeighbours neighbours : neighbourList) {
+ if (allPredecessorsAlreadyScheduled(neighbours)
+ && noSuccessorAlreadyScheduled(neighbours)) {
+ sortedResourceList.add(neighbours.resource);
+ neighbourList.remove(neighbours);
+ }
+ }
+ }
+ }
+
+ private boolean allPredecessorsAlreadyScheduled(ResourceNeighbours neighbours) {
+ if (neighbours.predecessors.isEmpty()) {
+ return true;
+ }
+
+ for (LSPResource predecessor : neighbours.predecessors) {
+ if (!sortedResourceList.contains(predecessor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean noSuccessorAlreadyScheduled(ResourceNeighbours neighbours) {
+ if (neighbours.successors.isEmpty()) {
+ return true;
+ }
+
+ for (LSPResource successor : neighbours.successors) {
+ if (!sortedResourceList.contains(successor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void insertShipmentsAtBeginning() {
+ for (LogisticChain solution : lsp.getSelectedPlan().getLogisticChains()) {
+ LogisticChainElement firstElement = getFirstElement(solution);
+ assert firstElement != null;
+ for (Id lspShipmentId : solution.getLspShipmentIds()) {
+ var shipment = LSPUtils.findLspShipment(lsp, lspShipmentId);
+ assert shipment != null;
+ firstElement
+ .getIncomingShipments()
+ .addShipment(shipment.getPickupTimeWindow().getStart(), shipment);
+ }
+ }
+ }
+
+ private LogisticChainElement getFirstElement(LogisticChain solution) {
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ if (element.getPreviousElement() == null) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setBufferTime(int bufferTime) {
+ this.bufferTime = bufferTime;
+ }
+
+ /**
+ * The relationship between different {@link LSPResource}s allows to handle various supply
+ * structures that the {@link LSP} might decide to maintain. Thus, a {@link LSPResource} can have
+ * several successors or predecessors or can be used by several different {@link LogisticChain}s.
+ * The neighborhood structure among the {@link LSPResource}s is stored in instances of the class
+ * {@link ResourceNeighbours} which contain references on the considered {@link LSPResource} and
+ * on the set of immediate successors respective predecessors. As the result of this step, a
+ * collection of {@link ResourceNeighbours} called neighborList is created that contains the
+ * neighbors of all {@link LSPResource}s in the plan of the considered {@link LSP}.
+ */
+ private static class ResourceNeighbours {
+ // internal data structure, try to ignore when looking from outside. kai/kai, jan'22
+
+ private final ArrayList predecessors;
+ private final ArrayList successors;
+ private final LSPResource resource;
+
+ private ResourceNeighbours(LSPResource resource) {
+ this.resource = resource;
+ this.predecessors = new ArrayList<>();
+ this.successors = new ArrayList<>();
+ }
+
+ private void addPredecessor(LSPResource resource) {
+ this.predecessors.add(resource);
+ }
+
+ private void addSuccessor(LSPResource resource) {
+ this.successors.add(resource);
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/FreightLogisticsConfigGroup.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/FreightLogisticsConfigGroup.java
new file mode 100644
index 00000000000..b346cd92a38
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/FreightLogisticsConfigGroup.java
@@ -0,0 +1,143 @@
+/*
+ *********************************************************************** *
+ * 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;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Map;
+import org.matsim.core.config.ConfigGroup;
+import org.matsim.core.config.ReflectiveConfigGroup;
+
+public class FreightLogisticsConfigGroup extends ReflectiveConfigGroup {
+
+ public static final String GROUPNAME="freightLogistics" ;
+
+ private String lspsFile;
+ static final String LSPS_FILE = "lspsFile";
+ private static final String LSPS_FILE_DESC = "Freight LogisticsServiceProviders (LSP)s File, according to MATSim logistics extension as part of MATSim's freight contrib.";
+
+ public FreightLogisticsConfigGroup() {
+ super(GROUPNAME);
+ }
+
+ //### CarriersFile ###
+ /**
+ * @return -- {@value #LSPS_FILE_DESC}
+ */
+ @StringGetter(LSPS_FILE)
+ public String getLspsFile() {
+ return lspsFile;
+ }
+
+ URL getLspsFileUrl(URL context) {
+ return ConfigGroup.getInputFileURL(context, this.lspsFile);
+ }
+
+ /**
+ * @param -- {@value #LSPS_FILE_DESC}
+ */
+ @StringSetter(LSPS_FILE)
+ public void setLspsFile(String lspsFile) {
+ this.lspsFile = lspsFile;
+ }
+
+
+
+ //---
+ // Commenting this out, because in a frist step I think it is better/ more streight forward to have the VRP logic in the carriers as an attribute.
+ // please see {@link CarrierSchedulerUtils#setVrpLogic(carrier, VRPLogic)} and {@link CarrierSchedulerUtils#getVrpLogic(carrier)}
+ //---
+
+// static final String VRP_LOGIC_OF_DISTRIBUTION_CARRIER = "vrpLogicOfDistributionCarrier";
+// private LSPUtils.LogicOfVrp vrpLogicOfDistributionCarrier = LSPUtils.LogicOfVrp.serviceBased;
+// private static final String VRP_LOGIC_OF_DISTRIBUTION_CARRIER_DESC = "Define, on which type of jobs the VRP of the **distribution** carrier will base on:" + Arrays.toString(LSPUtils.LogicOfVrp.values());
+//
+// static final String VRP_LOGIC_OF_MAINRUN_CARRIER = "vrpLogicOfMainRunCarrier";
+// private LSPUtils.LogicOfVrp vrpLogicOfMainRunCarrier = LSPUtils.LogicOfVrp.serviceBased;
+// private static final String VRP_LOGIC_OF_MAINRUN_CARRIER_DESC = "Define, on which type of jobs the VRP of the **MainRun** carrier will base on:" + Arrays.toString(LSPUtils.LogicOfVrp.values());
+//
+// static final String VRP_LOGIC_OF_COLLECTION_CARRIER = "vrpLogicOfCollectionCarrier";
+// private LSPUtils.LogicOfVrp vrpLogicOfCollectionCarrier = LSPUtils.LogicOfVrp.serviceBased;
+// private static final String VRP_LOGIC_OF_COLLECTION_CARRIER_DESC = "Define, on which type of jobs the VRP of the **Collection** carrier will base on:" + Arrays.toString(LSPUtils.LogicOfVrp.values());
+//
+// /**
+// *
+// * @return The internal type of jobs, on which the VRPs of the distribution carrier bases on.
+// */
+// @StringGetter(VRP_LOGIC_OF_DISTRIBUTION_CARRIER)
+// public LSPUtils.LogicOfVrp getVrpLogicOfDistributionCarrier() {
+// return vrpLogicOfDistributionCarrier;
+// }
+//
+// /**
+// * @param vrpLogicOfDistributionCarrier {@value #VRP_LOGIC_OF_DISTRIBUTION_CARRIER}
+// */
+// @StringSetter(VRP_LOGIC_OF_DISTRIBUTION_CARRIER)
+// public void setVrpLogicOfDistributionCarrier(LSPUtils.LogicOfVrp vrpLogicOfDistributionCarrier) {
+// this.vrpLogicOfDistributionCarrier = vrpLogicOfDistributionCarrier;
+// }
+//
+// /**
+// * @return The internal type of jobs, on which the VRPs of the main run carrier bases on.
+// */
+// @StringGetter(FreightLogisticsConfigGroup.VRP_LOGIC_OF_MAINRUN_CARRIER)
+// public LSPUtils.LogicOfVrp getVrpLogicOfMainRunCarrier() {
+// return vrpLogicOfMainRunCarrier;
+// }
+//
+// /**
+// * @param vrpLogicOfMainRunCarrier {@value #VRP_LOGIC_OF_MAINRUN_CARRIER}
+// */
+// @StringSetter(FreightLogisticsConfigGroup.VRP_LOGIC_OF_MAINRUN_CARRIER)
+// public void setVrpLogicOfMainRunCarrier(LSPUtils.LogicOfVrp vrpLogicOfMainRunCarrier) {
+// this.vrpLogicOfMainRunCarrier = vrpLogicOfMainRunCarrier;
+// }
+//
+// /**
+// * @return The internal type of jobs, on which the VRPs of the collection carrier bases on.
+// */
+// @StringGetter(FreightLogisticsConfigGroup.VRP_LOGIC_OF_COLLECTION_CARRIER)
+// public LSPUtils.LogicOfVrp getVrpLogicOfCollectionCarrier() {
+// return vrpLogicOfCollectionCarrier;
+// }
+//
+// /**
+// * @param vrpLogicOfCollectionCarrier {@value #VRP_LOGIC_OF_COLLECTION_CARRIER}
+// */
+// @StringSetter(FreightLogisticsConfigGroup.VRP_LOGIC_OF_COLLECTION_CARRIER)
+// public void setVrpLogicOfCollectionCarrier(LSPUtils.LogicOfVrp vrpLogicOfCollectionCarrier) {
+// this.vrpLogicOfCollectionCarrier = vrpLogicOfCollectionCarrier;
+// }
+
+ //---
+ //---
+ @Override
+ public Map getComments() {
+ Map map = super.getComments();
+ map.put(LSPS_FILE, LSPS_FILE_DESC);
+// map.put(VRP_LOGIC_OF_DISTRIBUTION_CARRIER, VRP_LOGIC_OF_DISTRIBUTION_CARRIER_DESC);
+// map.put(VRP_LOGIC_OF_MAINRUN_CARRIER, VRP_LOGIC_OF_MAINRUN_CARRIER_DESC);
+// map.put(VRP_LOGIC_OF_COLLECTION_CARRIER, VRP_LOGIC_OF_COLLECTION_CARRIER_DESC);
+ return map;
+ }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/HasBackpointer.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasBackpointer.java
new file mode 100644
index 00000000000..73f97386951
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasBackpointer.java
@@ -0,0 +1,12 @@
+package org.matsim.freight.logistics;
+
+@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
+public interface HasBackpointer {
+ // In general, we set backpointers when we add to the container.
+
+ // yy maybe also have interface HasSettableBackpointer?
+ void setEmbeddingContainer(T pointer);
+
+ // T getEmbeddingContainer();
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/HasLspShipmentId.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasLspShipmentId.java
new file mode 100644
index 00000000000..e3ddd1fc2f5
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasLspShipmentId.java
@@ -0,0 +1,32 @@
+/* *********************************************************************** *
+ * project: org.matsim.* *
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2008 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * *********************************************************************** */
+package org.matsim.freight.logistics;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * @author Kai Martins-Turner (kturner)
+ */
+public interface HasLspShipmentId {
+
+ String ATTRIBUTE_LSP_SHIPMENT_ID = "lspShipmentId";
+
+ Id getLspShipmentId();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/HasSimulationTrackers.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasSimulationTrackers.java
new file mode 100644
index 00000000000..33ba50074ed
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/HasSimulationTrackers.java
@@ -0,0 +1,14 @@
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+
+// One could say that the simulation trackers are the decorators that convert the data objects into
+// behavioral objects. In core matsim, we instead
+// create behavioral objects, which contain the data objects. E.g. MobsimAgent, DriverAgent,
+// CarrierAgent, etc. kai, may'22
+public interface HasSimulationTrackers {
+
+ void addSimulationTracker(LSPSimulationTracker tracker);
+
+ Collection> getSimulationTrackers();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/InitialShipmentAssigner.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/InitialShipmentAssigner.java
new file mode 100644
index 00000000000..eb6ff6287e7
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/InitialShipmentAssigner.java
@@ -0,0 +1,43 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * Takes an {@link LspShipment} and normally assigns it to something that belongs to an {@link LSP}.
+ *
+ * After changes in fall 2023 (see master thesis of nrichter), the assingment is
+ * there to be done one time initially.
+ *
+ * If there are several {@link LogisticChain}s in a {@link LSPPlan}, the {@link LSP} has to assign each {@link
+ * LspShipment} to the suitable {@link LogisticChain}. For this purpose, each {@link LSPPlan}
+ * (or only the LSP? - kmt'jan'24), contains a pluggable strategy
+ * that is contained in classes implementing the interface {@link InitialShipmentAssigner}.
+ *
+ * During iterations, it can happen that the {@link LspShipment} should be moved to another
+ * {@link LogisticChain} of the same {@link LSPPlan}. This is now (since fall 2023; see master
+ * thesis of nrichter) part of the (innovative) **Replanning** strategies.
+ */
+public interface InitialShipmentAssigner {
+
+ void assignToPlan(LSPPlan lspPlan, LspShipment lspShipment);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/KnowsLSP.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/KnowsLSP.java
new file mode 100644
index 00000000000..60a9c8ea190
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/KnowsLSP.java
@@ -0,0 +1,7 @@
+package org.matsim.freight.logistics;
+
+interface KnowsLSP {
+ LSP getLSP();
+
+ void setLSP(LSP lsp);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSP.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSP.java
new file mode 100644
index 00000000000..0486cd0f958
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSP.java
@@ -0,0 +1,53 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import org.matsim.api.core.v01.population.HasPlansAndId;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * In the class library, the interface LSP has the following tasks: 1. Maintain one or several
+ * transport chains through which {@link LspShipment}s are routed. 2. Assign {@link LspShipment}s to
+ * the suitable transport chain. --> {@link InitialShipmentAssigner}. 3. Interact with the agents that
+ * embody the demand side of the freight transport market, if they are specified in the setting. 4.
+ * Coordinate carriers that are in charge of the physical transport.
+ */
+public interface LSP extends HasPlansAndId, HasSimulationTrackers {
+
+ /** yyyy does this have to be exposed? */
+ Collection getLspShipments();
+
+ /** ok (behavioral method) */
+ void scheduleLogisticChains();
+
+ /** yyyy does this have to be exposed? */
+ Collection getResources();
+
+ /** ok (behavioral method) */
+ void scoreSelectedPlan();
+
+ /**
+ * @param lspShipment ok (LSP needs to be told that it is responsible for lspShipment)
+ */
+ void assignShipmentToLSP(LspShipment lspShipment);
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPCarrierResource.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPCarrierResource.java
new file mode 100644
index 00000000000..1a26be79a61
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPCarrierResource.java
@@ -0,0 +1,28 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.freight.carriers.Carrier;
+
+public interface LSPCarrierResource extends LSPResource {
+
+ Carrier getCarrier();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPConstants.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPConstants.java
new file mode 100644
index 00000000000..44df1603232
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPConstants.java
@@ -0,0 +1,29 @@
+package org.matsim.freight.logistics;
+
+import org.matsim.freight.carriers.CarrierConstants;
+
+public abstract class LSPConstants extends CarrierConstants {
+
+ public static final String CAPACITY_NEED_FIXED = "capacityNeedFixed";
+ public static final String CAPACITY_NEED_LINEAR = "capacityNeedLinear";
+ public static final String CHAIN_ID = "chainId";
+ public static final String ELEMENT = "element";
+ public static final String END_TIME = "endTime";
+ public static final String FIXED_COST = "fixedCost";
+ public static final String HUB = "hub";
+ public static final String LOCATION = "location";
+ public static final String LOGISTIC_CHAIN = "logisticChain";
+ public static final String LOGISTIC_CHAINS = "logisticChains";
+ public static final String LOGISTIC_CHAIN_ELEMENT = "logisticChainElement";
+ public static final String LSP = "lsp";
+ public static final String LSPS = "lsps";
+ public static final String LSP_PLAN = "LspPlan";
+ public static final String LSP_PLANS = "LspPlans";
+ public static final String RESOURCES = "resources";
+ public static final String RESOURCE_ID = "resourceId";
+ public static final String SCHEDULER = "scheduler";
+ public static final String SHIPMENT_PLAN = "shipmentPlan";
+ public static final String SHIPMENT_PLANS = "shipmentPlans";
+ public static final String START_TIME = "startTime";
+ public static final String TYPE = "type";
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPControlerListener.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPControlerListener.java
new file mode 100644
index 00000000000..e3c864be9bb
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPControlerListener.java
@@ -0,0 +1,254 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import jakarta.inject.Inject;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.core.api.experimental.events.EventsManager;
+import org.matsim.core.controler.MatsimServices;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.controler.events.*;
+import org.matsim.core.controler.listener.*;
+import org.matsim.core.events.handler.EventHandler;
+import org.matsim.core.gbl.Gbl;
+import org.matsim.freight.carriers.Carrier;
+import org.matsim.freight.carriers.CarrierPlanWriter;
+import org.matsim.freight.carriers.Carriers;
+import org.matsim.freight.carriers.CarriersUtils;
+import org.matsim.freight.carriers.controler.CarrierAgentTracker;
+import org.matsim.freight.logistics.io.LSPPlanXmlWriter;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+class LSPControlerListener
+ implements StartupListener,
+ BeforeMobsimListener,
+ AfterMobsimListener,
+ ScoringListener,
+ ReplanningListener,
+ IterationStartsListener,
+ IterationEndsListener,
+ ShutdownListener {
+ private static final Logger log = LogManager.getLogger(LSPControlerListener.class);
+ private final Scenario scenario;
+ private final List registeredHandlers = new ArrayList<>();
+
+ private static int addListenerCnt = 0;
+ private static final int maxAddListenerCnt = 1;
+
+ @Inject private EventsManager eventsManager;
+ @Inject private MatsimServices matsimServices;
+ @Inject private LSPScorerFactory lspScoringFunctionFactory;
+ @Inject @Nullable private LSPStrategyManager strategyManager;
+ @Inject private OutputDirectoryHierarchy controlerIO;
+ @Inject private CarrierAgentTracker carrierAgentTracker;
+
+
+ @Inject
+ LSPControlerListener(Scenario scenario) {
+ this.scenario = scenario;
+ }
+
+ @Override
+ public void notifyStartup(StartupEvent event) {
+ //Ensure that all ressource Ids are only there once.
+
+ checkForUniqueResourceIds();
+
+ }
+
+/**
+* For later steps, e.g. scoring the Ids of the {@link LSPResource} ids must be unique.
+ * Otherwise, there are scored several times.
+ *
+ * For the future we may reduce it to unique {@link LSPResource} ids PER {@link LSP}.
+ * This means, that the events (also from the carriers) need to have an information obout the LSP it belongs to and that
+ * in scoring and analysis this must be taken into account. What itself is another source for errors...
+ * KMT jul'24
+*/
+ private void checkForUniqueResourceIds() {
+ List duplicates = new ArrayList<>();
+ Set set = new HashSet<>();
+
+ LSPs lsps = LSPUtils.getLSPs(scenario);
+ for (LSP lsp : lsps.getLSPs().values()) {
+ for (LSPResource lspResource : lsp.getResources()) {
+ String idString = lspResource.getId().toString();
+ if (set.contains(idString)) {
+ duplicates.add(idString);
+ } else {
+ set.add(idString);
+ }
+ }
+ }
+
+ if (!duplicates.isEmpty()) {
+ log.error("There are non-unique ressource Ids. This must not be! The duplicate ids are: {}.", duplicates.toString());
+ log.error("You may also use output_lsp.xml to check were the duplicates are located");
+ log.error("Aborting now ...");
+ throw new RuntimeException();
+ }
+ }
+
+ @Override
+ public void notifyBeforeMobsim(BeforeMobsimEvent event) {
+ LSPs lsps = LSPUtils.getLSPs(scenario);
+
+ // TODO: Why do we add all simTrackers in every iteration beforeMobsim starts?
+ // Doing so results in a lot of "not adding eventsHandler since already added" warnings.
+ // @KN: Would it be possible to do it in (simulation) startup and therefor only oce?
+ for (LSP lsp : lsps.getLSPs().values()) {
+ ((LSPImpl) lsp).setScorer(lspScoringFunctionFactory.createScoringFunction());
+
+ // simulation trackers of lsp:
+ registerSimulationTrackers(lsp);
+
+ // simulation trackers of resources:
+ for (LSPResource resource : lsp.getResources()) {
+ registerSimulationTrackers(resource);
+ }
+
+ // simulation trackers of shipments:
+ for (LspShipment lspShipment : lsp.getLspShipments()) {
+ registerSimulationTrackers(lspShipment);
+ }
+
+ // simulation trackers of solutions:
+ for (LogisticChain solution : lsp.getSelectedPlan().getLogisticChains()) {
+ registerSimulationTrackers(solution);
+
+ // simulation trackers of solution elements:
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ registerSimulationTrackers(element);
+
+ // simulation trackers of resources:
+ registerSimulationTrackers(element.getResource());
+ }
+ }
+ }
+ }
+
+ private void registerSimulationTrackers(HasSimulationTrackers> hasSimulationTrackers) {
+ // get all simulation trackers ...
+ for (LSPSimulationTracker> simulationTracker :
+ hasSimulationTrackers.getSimulationTrackers()) {
+ // ... register them ...
+ if (!registeredHandlers.contains(simulationTracker)) {
+ log.info("adding eventsHandler: {}", simulationTracker);
+ eventsManager.addHandler(simulationTracker);
+ registeredHandlers.add(simulationTracker);
+ matsimServices.addControlerListener(simulationTracker);
+ simulationTracker.setEventsManager(eventsManager);
+ } else if ( addListenerCnt < maxAddListenerCnt ){
+ log.warn("not adding eventsHandler since already added: {}", simulationTracker);
+ addListenerCnt++;
+ if (addListenerCnt == maxAddListenerCnt) {
+ log.warn(Gbl.FUTURE_SUPPRESSED);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void notifyReplanning(ReplanningEvent event) {
+ if (strategyManager == null) {
+ throw new RuntimeException(
+ "You need to set LSPStrategyManager to something meaningful to run iterations.");
+ }
+
+ LSPs lsps = LSPUtils.getLSPs(scenario);
+ strategyManager.run(
+ lsps.getLSPs().values(), event.getIteration(), event.getReplanningContext());
+
+ for (LSP lsp : lsps.getLSPs().values()) {
+ lsp.getSelectedPlan()
+ .getShipmentPlans()
+ .clear(); // clear ShipmentPlans to start with clear(n) state. Otherwise, some of the times were
+ // accumulating over the time. :(
+ lsp.scheduleLogisticChains();
+ }
+
+ // Update carriers in scenario and CarrierAgentTracker
+ carrierAgentTracker.getCarriers().getCarriers().clear();
+ for (Carrier carrier : getCarriersFromLSP().getCarriers().values()) {
+ CarriersUtils.getCarriers(scenario).addCarrier(carrier);
+ carrierAgentTracker.getCarriers().addCarrier(carrier);
+ }
+ }
+
+ @Override
+ public void notifyScoring(ScoringEvent scoringEvent) {
+ for (LSP lsp : LSPUtils.getLSPs(scenario).getLSPs().values()) {
+ lsp.scoreSelectedPlan();
+ }
+ // yyyyyy might make more sense to register the lsps directly as scoring controler listener (??)
+ }
+
+ @Override
+ public void notifyAfterMobsim(AfterMobsimEvent event) {}
+
+ Carriers getCarriersFromLSP() {
+ LSPs lsps = LSPUtils.getLSPs(scenario);
+ assert !lsps.getLSPs().isEmpty();
+
+ Carriers carriers = new Carriers();
+ for (LSP lsp : lsps.getLSPs().values()) {
+ LSPPlan selectedPlan = lsp.getSelectedPlan();
+ for (LogisticChain solution : selectedPlan.getLogisticChains()) {
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ if (element.getResource() instanceof LSPCarrierResource carrierResource) {
+ Carrier carrier = carrierResource.getCarrier();
+ if (!carriers.getCarriers().containsKey(carrier.getId())) {
+ carriers.addCarrier(carrier);
+ }
+ }
+ }
+ }
+ }
+ return carriers;
+ }
+
+ @Override
+ public void notifyIterationStarts(IterationStartsEvent event) {}
+
+ @Override
+ public void notifyIterationEnds(IterationEndsEvent event) {
+ new LSPPlanXmlWriter(LSPUtils.getLSPs(scenario))
+ .write(controlerIO.getIterationFilename(event.getIteration(), "lsps.xml"));
+ }
+
+ @Override
+ public void notifyShutdown(ShutdownEvent event) {
+ new LSPPlanXmlWriter(LSPUtils.getLSPs(scenario))
+ .write(controlerIO.getOutputPath() + "/output_lsps.xml.gz");
+ new CarrierPlanWriter(CarriersUtils.getCarriers(scenario))
+ .write(controlerIO.getOutputPath() + "/output_carriers.xml.gz");
+ }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPDataObject.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPDataObject.java
new file mode 100644
index 00000000000..ff150a79d88
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPDataObject.java
@@ -0,0 +1,45 @@
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Identifiable;
+import org.matsim.utils.objectattributes.attributable.Attributable;
+import org.matsim.utils.objectattributes.attributable.Attributes;
+import org.matsim.utils.objectattributes.attributable.AttributesImpl;
+
+public class LSPDataObject implements HasSimulationTrackers, Attributable, Identifiable {
+
+ private final Collection> trackers = new LinkedList<>();
+ private final Attributes attributes = new AttributesImpl();
+ private final Id id;
+
+ public LSPDataObject(Id id) {
+ this.id = id;
+ }
+
+ @Override
+ public final void addSimulationTracker(LSPSimulationTracker tracker) {
+ this.trackers.add(tracker);
+ tracker.setEmbeddingContainer((T) this);
+ // It may not be possible to do this without this cast. Since "this" only knows that it is at
+ // least an LSPDataObject, and only we
+ // know that it is truly of type T. kai, jun'22
+ }
+
+ @Override
+ public final Collection> getSimulationTrackers() {
+ return Collections.unmodifiableCollection(this.trackers);
+ }
+
+ @Override
+ public final Attributes getAttributes() {
+ return attributes;
+ }
+
+ @Override
+ public final Id getId() {
+ return id;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPImpl.java
new file mode 100644
index 00000000000..54840359388
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPImpl.java
@@ -0,0 +1,163 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/* package-private */ class LSPImpl extends LSPDataObject implements LSP {
+ private static final Logger log = LogManager.getLogger(LSPImpl.class);
+
+ private final Collection lspShipments;
+ private final ArrayList lspPlans;
+ private final LogisticChainScheduler logisticChainScheduler;
+ private final Collection resources;
+ private LSPPlan selectedPlan;
+ private LSPScorer scorer;
+
+ // private LSPReplanner replanner;
+
+ LSPImpl(LSPUtils.LSPBuilder builder) {
+ super(builder.id);
+ this.lspShipments = new ArrayList<>();
+ this.lspPlans = new ArrayList<>();
+ this.logisticChainScheduler = builder.logisticChainScheduler;
+ this.logisticChainScheduler.setEmbeddingContainer(this);
+ this.selectedPlan = builder.initialPlan;
+ this.selectedPlan.setLSP(this);
+ this.lspPlans.add(builder.initialPlan);
+ this.resources = builder.resources;
+ }
+
+ public static LSPPlan copyPlan(LSPPlan plan2copy) {
+ List newPlanChains = new ArrayList<>();
+ for (LogisticChain initialPlanChain : plan2copy.getLogisticChains()) {
+ LogisticChain newPlanChain =
+ LSPUtils.LogisticChainBuilder.newInstance(initialPlanChain.getId()).build();
+ newPlanChain.getLogisticChainElements().addAll(initialPlanChain.getLogisticChainElements());
+ newPlanChain.getLspShipmentIds().addAll(initialPlanChain.getLspShipmentIds());
+ newPlanChains.add(newPlanChain);
+ }
+
+ LSPPlan copiedPlan = LSPUtils.createLSPPlan();
+ copiedPlan.setInitialShipmentAssigner(plan2copy.getInitialShipmentAssigner());
+ copiedPlan.setLSP(plan2copy.getLSP());
+ copiedPlan.setScore(plan2copy.getScore());
+ copiedPlan.setType(plan2copy.getType());
+ copiedPlan.getLogisticChains().addAll(newPlanChains);
+ return copiedPlan;
+ }
+
+ /**
+ * This is used from {@link LSPControlerListener} and not meant to be used from user code. Users
+ * should bind {@link LSPScorerFactory}.
+ */
+ /* package-private */ void setScorer(LSPScorer scorer) {
+ this.scorer = scorer;
+ scorer.setEmbeddingContainer(this);
+ this.addSimulationTracker(scorer);
+ }
+
+ @Override
+ public void scheduleLogisticChains() {
+ logisticChainScheduler.scheduleLogisticChain();
+ }
+
+ @Override
+ public boolean addPlan(LSPPlan plan) {
+ for (LogisticChain solution : plan.getLogisticChains()) {
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ if (!resources.contains(element.getResource())) {
+ resources.add(element.getResource());
+ }
+ }
+ }
+ plan.setLSP(this);
+ return lspPlans.add(plan);
+ }
+
+ @Override
+ public LSPPlan createCopyOfSelectedPlanAndMakeSelected() {
+ LSPPlan newPlan = LSPImpl.copyPlan(this.selectedPlan);
+ this.setSelectedPlan(newPlan);
+ return newPlan;
+ }
+
+ @Override
+ public ArrayList getPlans() {
+ return lspPlans;
+ }
+
+ @Override
+ public LSPPlan getSelectedPlan() {
+ return selectedPlan;
+ }
+
+ @Override
+ public void setSelectedPlan(LSPPlan selectedPlan) {
+ if (!lspPlans.contains(selectedPlan)) {
+ lspPlans.add(selectedPlan);
+ }
+ this.selectedPlan = selectedPlan;
+ }
+
+ @Override
+ public boolean removePlan(LSPPlan lspPlan) {
+ if (lspPlans.contains(lspPlan)) {
+ lspPlans.remove(lspPlan);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Collection getResources() {
+ return resources;
+ }
+
+ public void scoreSelectedPlan() {
+ if (this.scorer != null) {
+ this.selectedPlan.setScore(scorer.getScoreForCurrentPlan());
+ } else {
+ throw new RuntimeException("trying to score the current LSP plan, but scorer is not set.");
+ }
+ }
+
+ @Override
+ public void assignShipmentToLSP(LspShipment lspShipment) {
+ // shipment.setLspId(this.getId()); // und rückweg dann auch darüber und dann
+ // lsp.getselectedPlan.getShipment...
+ lspShipments.add(lspShipment);
+ for (LSPPlan lspPlan : lspPlans) {
+ lspPlan.getInitialShipmentAssigner().assignToPlan(lspPlan, lspShipment);
+ }
+ }
+
+ @Override
+ public Collection getLspShipments() {
+ return this.lspShipments;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPModule.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPModule.java
new file mode 100644
index 00000000000..2eb4085ec56
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPModule.java
@@ -0,0 +1,184 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import java.util.List;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.population.HasPlansAndId;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.controler.AbstractModule;
+import org.matsim.core.controler.events.BeforeMobsimEvent;
+import org.matsim.core.controler.listener.BeforeMobsimListener;
+import org.matsim.core.mobsim.qsim.AbstractQSimModule;
+import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigGroup;
+import org.matsim.core.replanning.GenericPlanStrategy;
+import org.matsim.core.replanning.ReplanningContext;
+import org.matsim.core.replanning.selectors.PlanSelector;
+import org.matsim.freight.carriers.FreightCarriersConfigGroup;
+import org.matsim.freight.carriers.controler.*;
+import org.matsim.freight.logistics.analysis.LspScoreStatsModule;
+
+public class LSPModule extends AbstractModule {
+ private static final Logger log = LogManager.getLogger(LSPModule.class);
+
+ @Override
+ public void install() {
+ FreightCarriersConfigGroup freightConfig =
+ ConfigUtils.addOrGetModule(getConfig(), FreightCarriersConfigGroup.class);
+
+ bind(LSPControlerListener.class).in(Singleton.class);
+ addControlerListenerBinding().to(LSPControlerListener.class);
+
+ install(new CarrierModule());
+ install(new LspScoreStatsModule());
+
+ // this switches on certain qsim components:
+ QSimComponentsConfigGroup qsimComponents =
+ ConfigUtils.addOrGetModule(getConfig(), QSimComponentsConfigGroup.class);
+ List abc = qsimComponents.getActiveComponents();
+ abc.add(FreightAgentSource.COMPONENT_NAME);
+ switch (freightConfig.getTimeWindowHandling()) {
+ case ignore:
+ break;
+ case enforceBeginnings:
+ //// abc.add( WithinDayActivityReScheduling.COMPONENT_NAME );
+ log.warn(
+ "LSP has never hedged against time window openings; this is probably wrong; but I don't know what to do ...");
+ // break;
+ default:
+ throw new IllegalStateException(
+ "Unexpected value: " + freightConfig.getTimeWindowHandling());
+ }
+ qsimComponents.setActiveComponents(abc);
+
+ // this installs qsim components, which are switched on (or not) via the above syntax:
+ this.installQSimModule(
+ new AbstractQSimModule() {
+ @Override
+ protected void configureQSim() {
+ this.bind(FreightAgentSource.class).in(Singleton.class);
+ this.addQSimComponentBinding(FreightAgentSource.COMPONENT_NAME)
+ .to(FreightAgentSource.class);
+ switch (freightConfig.getTimeWindowHandling()) {
+ case ignore:
+ break;
+ case enforceBeginnings:
+ ////
+ // this.addQSimComponentBinding(WithinDayActivityReScheduling.COMPONENT_NAME).to(
+ // WithinDayActivityReScheduling.class );
+ log.warn(
+ "LSP has never hedged against time window openings; this is probably wrong; but I don't know what to do ...");
+ // break;
+ default:
+ throw new IllegalStateException(
+ "Unexpected value: " + freightConfig.getTimeWindowHandling());
+ }
+ }
+ });
+
+ // the scorers are necessary to run a zeroth iteration to the end:
+ bind(LSPScorerFactory.class).to(LSPScoringFunctionFactoryDummyImpl.class);
+
+ // for iterations, one needs to replace the following with something meaningful. If nothing
+ // else, there are "empty implementations" that do nothing. kai, jul'22
+ bind(LSPStrategyManager.class).toProvider(() -> null);
+
+ this.addControlerListenerBinding().to(DumpLSPPlans.class);
+ }
+
+ private static class LSPScoringFunctionFactoryDummyImpl implements LSPScorerFactory {
+ @Override
+ public LSPScorer createScoringFunction() {
+ return new LSPScorer() {
+ @Override
+ public double getScoreForCurrentPlan() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ @Override
+ public void setEmbeddingContainer(LSP pointer) {}
+ };
+ }
+ }
+
+ public static final class LSPStrategyManagerEmptyImpl implements LSPStrategyManager {
+
+ @Override
+ public void addStrategy(
+ GenericPlanStrategy strategy, String subpopulation, double weight) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public void run(
+ Iterable extends HasPlansAndId> persons,
+ int iteration,
+ ReplanningContext replanningContext) {
+ log.warn("Running iterations without a strategy may lead to unclear results."); // "run" is
+ // possible, but will not do anything. kai, jul'22
+ }
+
+ @Override
+ public void setMaxPlansPerAgent(int maxPlansPerAgent) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public void addChangeRequest(int iteration, GenericPlanStrategy strategy, String subpopulation, double newWeight) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public void setPlanSelectorForRemoval(PlanSelector planSelector) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public List> getStrategies(String subpopulation) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public List getWeights(String subpopulation) {
+ throw new RuntimeException("not implemented");
+ }
+ }
+
+ public static final class DumpLSPPlans implements BeforeMobsimListener {
+ @Inject Scenario scenario;
+
+ @Override
+ public void notifyBeforeMobsim(BeforeMobsimEvent event) {
+ LSPs lsps = LSPUtils.getLSPs(scenario);
+ for (LSP lsp : lsps.getLSPs().values()) {
+ log.info("Dumping plan(s) of [LSP={}] ; [No of plans={}]", lsp.getId(), lsp.getPlans().size());
+ for (LSPPlan plan : lsp.getPlans()) {
+ log.info("[LSPPlan: {}]", plan.toString());
+ }
+ log.info("Plan(s) of [LSP={}] dumped.", lsp.getId());
+ }
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlan.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlan.java
new file mode 100644
index 00000000000..c6c3d48d4f2
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlan.java
@@ -0,0 +1,59 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import org.matsim.api.core.v01.population.BasicPlan;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlan;
+
+/**
+ * This interface has the following properties:
+ *
+ *
+ *
As a {@link BasicPlan} it has a score, so it can be used for evolutionary learning. kai,
+ * may'22
+ *
An {@link LspShipment} is added via lspPlan#getAssigner().assignToSolution(shipment). The
+ * {@link InitialShipmentAssigner} assigns it deterministically to a {@link LogisticChain}.
+ *
+ */
+public interface LSPPlan extends BasicPlan, KnowsLSP {
+
+ LSPPlan addLogisticChain(LogisticChain solution);
+
+ Collection getLogisticChains();
+
+ /**
+ * yy My intuition would be to replace lspPlan#getAssigner().assignToSolution( shipment ) by
+ * lspPlan.addShipment( shipment ). kai, may'22
+ */
+ InitialShipmentAssigner getInitialShipmentAssigner();
+
+ LSPPlan setInitialShipmentAssigner(InitialShipmentAssigner assigner);
+
+ Collection getShipmentPlans();
+
+ LSPPlan addShipmentPlan(LspShipmentPlan lspShipmentPlan);
+
+ String getType();
+
+ void setType(final String type);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlanImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlanImpl.java
new file mode 100644
index 00000000000..a1bfe4e25cc
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPPlanImpl.java
@@ -0,0 +1,127 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import org.matsim.freight.logistics.shipment.LspShipmentPlan;
+
+public class LSPPlanImpl implements LSPPlan {
+
+ private final Collection logisticChains;
+ private final Collection lspShipmentPlans;
+ private LSP lsp;
+ private Double score = null;
+ private InitialShipmentAssigner assigner;
+ private String type = null;
+
+ public LSPPlanImpl() {
+ this.logisticChains = new ArrayList<>();
+ this.lspShipmentPlans = new ArrayList<>();
+ }
+
+ @Override
+ public LSPPlan addLogisticChain(LogisticChain solution) {
+ this.logisticChains.add(solution);
+ solution.setLSP(this.lsp);
+ return this;
+ }
+
+ @Override
+ public Collection getLogisticChains() {
+ return logisticChains;
+ }
+
+ @Override
+ public InitialShipmentAssigner getInitialShipmentAssigner() {
+ return assigner;
+ }
+
+ @Override
+ public LSPPlan setInitialShipmentAssigner(InitialShipmentAssigner assigner) {
+ this.assigner = assigner;
+ return this;
+ }
+
+ @Override
+ public Collection getShipmentPlans() {
+ return this.lspShipmentPlans;
+ }
+
+ @Override
+ public LSPPlan addShipmentPlan(LspShipmentPlan lspShipmentPlan) {
+ this.lspShipmentPlans.add(lspShipmentPlan);
+ return null;
+ }
+
+ @Override
+ public Double getScore() {
+ return score;
+ }
+
+ @Override
+ public void setScore(Double score) {
+ this.score = score;
+ }
+
+ @Override
+ public String getType() {
+ return this.type;
+ }
+
+ @Override
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ @Override
+ public LSP getLSP() {
+ return lsp;
+ }
+
+ @Override
+ public void setLSP(LSP lsp) {
+ this.lsp = lsp;
+ for (LogisticChain solution : logisticChains) {
+ solution.setLSP(lsp);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder();
+ strb.append("[score=").append(this.score).append("]");
+ strb.append(", [type=").append(this.type).append("]");
+ for (LogisticChain logisticChain : this.logisticChains) {
+ strb.append(", [LogisticChainId=")
+ .append(logisticChain.getId())
+ .append("], [No of LogisticChainElements=")
+ .append(logisticChain.getLogisticChainElements().size())
+ .append("] \n");
+ if (!logisticChain.getLogisticChainElements().isEmpty()) {
+ for (LogisticChainElement solutionElement : logisticChain.getLogisticChainElements()) {
+ strb.append("\t \t").append(solutionElement.toString()).append("\n");
+ }
+ }
+ }
+ return strb.toString();
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResource.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResource.java
new file mode 100644
index 00000000000..4f1e595ebe0
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResource.java
@@ -0,0 +1,40 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Identifiable;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.utils.objectattributes.attributable.Attributable;
+
+/** */
+public interface LSPResource
+ extends Identifiable, HasSimulationTrackers, Attributable {
+
+ Id getStartLinkId();
+
+ Id getEndLinkId();
+
+ Collection getClientElements();
+
+ void schedule(int bufferTime, LSPPlan lspPlan);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResourceScheduler.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResourceScheduler.java
new file mode 100644
index 00000000000..c63d9258742
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPResourceScheduler.java
@@ -0,0 +1,106 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+
+/**
+ * Resources are scheduled separately by calling their individual scheduling algorithm.
+ *
+ *
Within this algorithm, some methods are abstract, whereas others have a default implementation
+ * for forward scheduling. The former ones are specified in a suitable way by the corresponding
+ * Resource whereas the latter are only specified in the abstract parent class in order to
+ * coordinate the way in which the LSPShipments are handed over between subsequent Resources. The
+ * abstract methods deal with aspects that are specific to the Resource which contains the
+ * implementation of the ResourceScheduler.
+ *
+ *
Forwarding of LSPShipments is done by the two methods presortIncomingShipments() and
+ * switchHandledShipments(int bufferTime).
+ */
+public abstract class LSPResourceScheduler {
+
+ protected LSPResource resource;
+ protected ArrayList lspShipmentsToSchedule;
+
+ protected LSPPlan lspPlan;
+
+ public final void scheduleShipments(LSPPlan lspPlan, LSPResource resource, int bufferTime) {
+ this.lspPlan = lspPlan;
+ this.resource = resource;
+ this.lspShipmentsToSchedule = new ArrayList<>();
+ initializeValues(resource);
+ presortIncomingShipments();
+ scheduleResource();
+ updateShipments();
+ switchHandledShipments(bufferTime);
+ lspShipmentsToSchedule.clear();
+ }
+
+ /**
+ * Is in charge of the initialization of the actual scheduling process for the concerned Resource.
+ * Depending on the concrete shape of this process, there are mainly values to be deleted that are
+ * still stored from the previous iteration or the infrastructure for the used algorithm has to be
+ * set up.
+ *
+ * @param resource The LSPRessource
+ */
+ protected abstract void initializeValues(LSPResource resource);
+
+ /** Controls the actual scheduling process that depends on the shape and task of the Resource. */
+ protected abstract void scheduleResource();
+
+ /**
+ * Endows the involved {@link LspShipment}s with information that resulted from the scheduling in
+ * a narrow sense in scheduleResource(). The information can be divided into two main components.
+ * 1.) the schedule of the {@link LspShipment}s is updated if necessary 2.) the information for a
+ * later logging of the is added.
+ */
+ protected abstract void updateShipments();
+
+ private void presortIncomingShipments() {
+ this.lspShipmentsToSchedule = new ArrayList<>();
+ for (LogisticChainElement element : resource.getClientElements()) {
+ lspShipmentsToSchedule.addAll(element.getIncomingShipments().getLspShipmentsWTime());
+ }
+ lspShipmentsToSchedule.sort(Comparator.comparingDouble(LspShipmentUtils::getTimeOfLspShipment));
+ }
+
+ private void switchHandledShipments(int bufferTime) {
+ for (LspShipment lspShipmentWithTime : lspShipmentsToSchedule) {
+ var shipmentPlan = LspShipmentUtils.getOrCreateShipmentPlan(lspPlan, lspShipmentWithTime.getId());
+ double endOfTransportTime = shipmentPlan.getMostRecentEntry().getEndTime() + bufferTime;
+ LspShipmentUtils.setTimeOfLspShipment(lspShipmentWithTime, endOfTransportTime);
+ for (LogisticChainElement element : resource.getClientElements()) {
+ if (element.getIncomingShipments().getLspShipmentsWTime().contains(lspShipmentWithTime)) {
+ element.getIncomingShipments().getLspShipmentsWTime().remove(lspShipmentWithTime);
+ element.getOutgoingShipments().getLspShipmentsWTime().add(lspShipmentWithTime);
+ if (element.getNextElement() != null) {
+ element.getNextElement().getIncomingShipments().getLspShipmentsWTime().add(lspShipmentWithTime);
+ element.getOutgoingShipments().getLspShipmentsWTime().remove(lspShipmentWithTime);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorer.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorer.java
new file mode 100644
index 00000000000..4f4916c0510
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorer.java
@@ -0,0 +1,43 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.api.core.v01.population.Activity;
+import org.matsim.core.controler.listener.ControlerListener;
+import org.matsim.core.events.handler.EventHandler;
+import org.matsim.core.scoring.ScoringFunction;
+import org.matsim.freight.carriers.Carrier;
+
+/**
+ * This is somewhat similar to the MATSim core {@link ScoringFunction}, which is also used for
+ * {@link Carrier}s. A difference, however, is that it does not implement the separate methods
+ * {@link ScoringFunction#handleActivity(Activity)} etc., but is just an {@link EventHandler} and a
+ * {@link ControlerListener}. (This is, in some sense, the old design for {@link ScoringFunction},
+ * and one, where I am still not sure if the new design is truly better.) In any case, here there is
+ * not a question: LSP scoring is not so much about activities and legs, since those are handled
+ * through the carrier scoring, and need to be pulled in by the lsp scoring if the company is
+ * vertically integrated (i.e. if the LSP owns its carriers).
+ *
+ *
also @see {@link LSPScorerFactory}
+ */
+public interface LSPScorer extends LSPSimulationTracker {
+ double getScoreForCurrentPlan();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorerFactory.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorerFactory.java
new file mode 100644
index 00000000000..e5a5d7ea78f
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPScorerFactory.java
@@ -0,0 +1,7 @@
+package org.matsim.freight.logistics;
+
+import org.matsim.core.api.internal.MatsimFactory;
+
+public interface LSPScorerFactory extends MatsimFactory {
+ LSPScorer createScoringFunction();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPSimulationTracker.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPSimulationTracker.java
new file mode 100644
index 00000000000..55d967c293b
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPSimulationTracker.java
@@ -0,0 +1,32 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.core.api.experimental.events.EventsManager;
+import org.matsim.core.controler.listener.ControlerListener;
+import org.matsim.core.events.handler.EventHandler;
+
+public interface LSPSimulationTracker
+ extends ControlerListener, EventHandler, HasBackpointer {
+ // In general, we set backpointers when we add to the container. So specifically, we set the
+ // backpointer to which the tracker points when the tracker is added.
+ default void setEventsManager(EventsManager eventsManager) {}
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManager.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManager.java
new file mode 100644
index 00000000000..79d7c5a64bc
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManager.java
@@ -0,0 +1,44 @@
+/*
+ * *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 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;
+
+import jakarta.inject.Provider;
+import org.matsim.core.replanning.GenericStrategyManager;
+
+/**
+ * The current (jul'22) logic of this is:
+ *
+ *
+ *
There is a null binding of this interface in {@link LSPModule}. If one wants to use
+ * strategies, this needs to be overwritten.
+ *
Normally, the strategy manager is fixed infrastructure, and should just be configured.
+ * However, since it is not yet there before injection, it also cannot be configured before
+ * injection. Core matsim solves that by writing the corresponding configuration into the
+ * config. We could, in principle, do the same here. Don't want to do this yet.
+ *
So way to configure this "in code" is to bind {@link LSPStrategyManager} to a {@link
+ * Provider } and then configure it in the provider.
+ *
+ */
+public interface LSPStrategyManager extends GenericStrategyManager {
+ // (this is mostly there so that it can be guice-bound. kai, jul'22)
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManagerImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManagerImpl.java
new file mode 100644
index 00000000000..f628d4f2ab4
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPStrategyManagerImpl.java
@@ -0,0 +1,71 @@
+package org.matsim.freight.logistics;
+
+import java.util.List;
+import org.matsim.api.core.v01.population.HasPlansAndId;
+import org.matsim.core.replanning.GenericPlanStrategy;
+import org.matsim.core.replanning.GenericStrategyManager;
+import org.matsim.core.replanning.GenericStrategyManagerImpl;
+import org.matsim.core.replanning.ReplanningContext;
+import org.matsim.core.replanning.selectors.PlanSelector;
+
+/**
+ * Normally, this would be infrastructure that is configurable via the config. Since we ain't there
+ * yet, the way to configure this is something like:
+ *
+ *
+ */
+public class LSPStrategyManagerImpl implements LSPStrategyManager {
+ final GenericStrategyManager delegate = new GenericStrategyManagerImpl<>();
+
+ @Override
+ public void addStrategy(
+ GenericPlanStrategy strategy, String subpopulation, double weight) {
+ delegate.addStrategy(strategy, subpopulation, weight);
+ }
+
+ @Override
+ public void run(
+ Iterable extends HasPlansAndId> persons,
+ int iteration,
+ ReplanningContext replanningContext) {
+ delegate.run(persons, iteration, replanningContext);
+ }
+
+ @Override
+ public void setMaxPlansPerAgent(int maxPlansPerAgent) {
+ delegate.setMaxPlansPerAgent(maxPlansPerAgent);
+ }
+
+ @Override
+ public void addChangeRequest(
+ int iteration,
+ GenericPlanStrategy strategy,
+ String subpopulation,
+ double newWeight) {
+ delegate.addChangeRequest(iteration, strategy, subpopulation, newWeight);
+ }
+
+ @Override
+ public void setPlanSelectorForRemoval(PlanSelector planSelector) {
+ delegate.setPlanSelectorForRemoval(planSelector);
+ }
+
+ @Override
+ public List> getStrategies(String subpopulation) {
+ return delegate.getStrategies(subpopulation);
+ }
+
+ @Override
+ public List getWeights(String subpopulation) {
+ return delegate.getWeights(subpopulation);
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPUtils.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPUtils.java
new file mode 100644
index 00000000000..e40c44e95b6
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPUtils.java
@@ -0,0 +1,271 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.freight.carriers.Carriers;
+import org.matsim.freight.carriers.CarriersUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlan;
+import org.matsim.utils.objectattributes.attributable.Attributable;
+
+public final class LSPUtils {
+ private static final String lspsString = "lsps";
+
+ private LSPUtils() {} // do not instantiate
+
+ public static LSPPlan createLSPPlan() {
+ return new LSPPlanImpl();
+ }
+
+
+ /**
+ * Checks, is the plan the selcted plan.
+ * (This is adapted copy from PersonUtils.isSelected(plan) )
+ * @param lspPlan the plan to check
+ * @return true if the plan is the selected plan. false, if not.
+ */
+ public static boolean isPlanTheSelectedPlan(LSPPlan lspPlan) {
+ return lspPlan.getLSP().getSelectedPlan() == lspPlan;
+ }
+
+ public static LogisticChainScheduler createForwardLogisiticChainScheduler() {
+ return new ForwardLogisticChainSchedulerImpl();
+ }
+
+ public static WaitingShipments createWaitingShipments() {
+ return new WaitingShipmentsImpl();
+ }
+
+ public static void addLSPs(Scenario scenario, LSPs lsps) {
+ Carriers carriers = CarriersUtils.addOrGetCarriers(scenario);
+ // Register carriers from all lsps
+ for (LSP lsp : lsps.getLSPs().values()) {
+ for (LSPResource lspResource : lsp.getResources()) {
+ if (lspResource instanceof LSPCarrierResource lspCarrierResource) {
+ carriers.addCarrier(lspCarrierResource.getCarrier());
+ }
+ }
+ }
+ scenario.addScenarioElement(lspsString, lsps);
+ }
+
+ public static LSPs getLSPs(Scenario scenario) {
+ Object result = scenario.getScenarioElement(lspsString);
+ if (result == null) {
+ throw new RuntimeException(
+ "there is no scenario element of type "
+ + lspsString
+ + ". You will need something like LSPUtils.addLSPs( scenario, lsps) somewhere.");
+ }
+ return (LSPs) result;
+ }
+
+ public static Double getVariableCost(Attributable attributable) {
+ return (Double) attributable.getAttributes().getAttribute("variableCost");
+ }
+
+ public static void setVariableCost(Attributable attributable, Double variableCost) {
+ attributable.getAttributes().putAttribute("variableCost", variableCost);
+ }
+
+ public static Double getFixedCost(Attributable attributable) {
+ return (Double) attributable.getAttributes().getAttribute("fixedCost");
+ }
+
+ // The following would be closer to how we have done it elsewhere (scenario containers are
+ // mutable). kai, may'22'
+ // public static LSPs createOrGetLPSs( Scenario scenario ){
+ // Object result = scenario.getScenarioElement( lspsString );
+ // LSPs lsps;
+ // if ( result != null ) {
+ // lsps = (LSPs) result;
+ // } else {
+ // lsps = new LSPs( );
+ // scenario.addScenarioElement( lspsString, lsps );
+ // }
+ // return lsps;
+ // }
+
+ public static void setFixedCost(Attributable attributable, Double fixedCost) {
+ attributable.getAttributes().putAttribute("fixedCost", fixedCost);
+ }
+
+ /**
+ * Gives back the {@link LspShipment} object of the {@link LSP}, which matches to the shipmentId
+ *
+ * @param lsp In this LSP this method tries to find the shipment.
+ * @param shipmentId Id of the shipment that should be found.
+ * @return the lspShipment object or null, if it is not found.
+ */
+ public static LspShipment findLspShipment(LSP lsp, Id shipmentId) {
+ for (LspShipment lspShipment : lsp.getLspShipments()) {
+ if (lspShipment.getId().equals(shipmentId)) {
+ return lspShipment;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link LspShipmentPlan} of an {@link LspShipment}.
+ *
+ * @param lspPlan the lspPlan: It contains the information of its shipmentPlans
+ * @param shipmentId Id of the shipment that should be found.
+ * @return the shipmentPlan object or null, if it is not found.
+ */
+ public static LspShipmentPlan findLspShipmentPlan(LSPPlan lspPlan, Id shipmentId) {
+ for (LspShipmentPlan lspShipmentPlan : lspPlan.getShipmentPlans()) {
+ if (lspShipmentPlan.getLspShipmentId().equals(shipmentId)) {
+ return lspShipmentPlan;
+ }
+ }
+ return null;
+ }
+
+ public enum LogicOfVrp {serviceBased, shipmentBased}
+
+ public static final class LSPBuilder {
+ final Collection resources;
+ final Id id;
+ LogisticChainScheduler logisticChainScheduler;
+ LSPPlan initialPlan;
+
+ private LSPBuilder(Id id) {
+ this.id = id; // this line was not there until today. kai, may'22
+ this.resources = new ArrayList<>();
+ }
+
+ public static LSPBuilder getInstance(Id id) {
+ return new LSPBuilder(id);
+ }
+
+ public LSPBuilder setLogisticChainScheduler(LogisticChainScheduler logisticChainScheduler) {
+ this.logisticChainScheduler = logisticChainScheduler;
+ return this;
+ }
+
+ // /**
+ // * @deprecated -- It feels attractive to attach this to the "agent". A big disadvantage
+ // with this approach, however, is that
+ // * we cannot use injection ... since we cannot inject as many scorers as we have agents.
+ // (At least this is what I think.) Which means
+ // * that the approach in matsim core and in carriers to have XxxScoringFunctionFactory is
+ // better for what we are doing here. yyyyyy So
+ // * this needs to be changed. kai, jul'22
+ // */
+ // public LSPBuilder setSolutionScorer(LSPScorer scorer) {
+ // this.scorer = scorer;
+ // return this;
+ // }
+
+ // /**
+ // * @deprecated -- It feels attractive to attach this to the "agent". A big disadvantage
+ // with this approach, however, is that
+ // * we cannot use injection ... since we cannot inject as many replanners as we have
+ // agents. (At least this is what I think.) yyyyyy So
+ // * this needs to be changed. kai, jul'22
+ // */
+ // public LSPBuilder setReplanner(LSPReplanner replanner) {
+ // this.replanner = replanner;
+ // return this;
+ // }
+ // never used. Thus disabling it. kai, jul'22
+
+ public LSPBuilder setInitialPlan(LSPPlan plan) {
+ this.initialPlan = plan;
+ for (LogisticChain solution : plan.getLogisticChains()) {
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ if (!resources.contains(element.getResource())) {
+ resources.add(element.getResource());
+ }
+ }
+ }
+ return this;
+ }
+
+ public LSP build() {
+ return new LSPImpl(this);
+ }
+ }
+
+ public static final class LogisticChainBuilder {
+ final Id id;
+ final Collection elements;
+ // final Collection eventHandlers;
+ final Collection> trackers;
+
+ private LogisticChainBuilder(Id id) {
+ this.elements = new ArrayList<>();
+ this.trackers = new ArrayList<>();
+ this.id = id;
+ }
+
+ public static LogisticChainBuilder newInstance(Id id) {
+ return new LogisticChainBuilder(id);
+ }
+
+ public LogisticChainBuilder addLogisticChainElement(LogisticChainElement element) {
+ elements.add(element);
+ return this;
+ }
+
+ public LogisticChainBuilder addTracker(LSPSimulationTracker tracker) {
+ trackers.add(tracker);
+ return this;
+ }
+
+ public LogisticChain build() {
+ //TODO: Prüfe of das alle Elemente Verbunden sind (in irgendeiner Art). Plus Hinweis auf die Änderung.
+
+ return new LogisticChainImpl(this);
+ }
+ }
+
+ public static final class LogisticChainElementBuilder {
+ final Id id;
+ final WaitingShipments incomingShipments;
+ final WaitingShipments outgoingShipments;
+ LSPResource resource;
+
+ private LogisticChainElementBuilder(Id id) {
+ this.id = id;
+ this.incomingShipments = createWaitingShipments();
+ this.outgoingShipments = createWaitingShipments();
+ }
+
+ public static LogisticChainElementBuilder newInstance(Id id) {
+ return new LogisticChainElementBuilder(id);
+ }
+
+ public LogisticChainElementBuilder setResource(LSPResource resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ public LogisticChainElement build() {
+ return new LogisticChainElementImpl(this);
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPs.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPs.java
new file mode 100644
index 00000000000..7adfc6d430e
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LSPs.java
@@ -0,0 +1,45 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.matsim.api.core.v01.Id;
+
+public class LSPs {
+
+ private final Map, LSP> lsps = new LinkedHashMap<>();
+
+ public LSPs(Collection lsps) {
+ makeMap(lsps);
+ }
+
+ private void makeMap(Collection lsps) {
+ for (LSP c : lsps) {
+ this.lsps.put(c.getId(), c);
+ }
+ }
+
+ public Map, LSP> getLSPs() {
+ return lsps;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChain.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChain.java
new file mode 100644
index 00000000000..f1b19be6c8f
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChain.java
@@ -0,0 +1,48 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Identifiable;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.utils.objectattributes.attributable.Attributable;
+
+/**
+ * A LogisticsSolution can be seen as a representative of a transport chain. It consists of several
+ * chain links that implement the interface {@link LogisticChainElement}. The latter is more a
+ * logical than a physical entity. Physical entities, in turn, are housed inside classes that
+ * implement the interface {@link LSPResource}. This introduction of an intermediate layer allows
+ * physical Resources to be used by several {@link LogisticChain}s and thus transport chains.
+ */
+@SuppressWarnings("GrazieInspection")
+public interface LogisticChain
+ extends Identifiable,
+ KnowsLSP,
+ HasSimulationTrackers,
+ Attributable {
+
+ Collection getLogisticChainElements();
+
+ Collection> getLspShipmentIds();
+
+ void addShipmentToChain(LspShipment lspShipment);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElement.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElement.java
new file mode 100644
index 00000000000..67a16b43462
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElement.java
@@ -0,0 +1,55 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.api.core.v01.Identifiable;
+import org.matsim.utils.objectattributes.attributable.Attributable;
+
+public interface LogisticChainElement
+ extends Identifiable,
+ HasBackpointer,
+ HasSimulationTrackers,
+ Attributable {
+
+ void connectWithNextElement(LogisticChainElement element);
+
+ /**
+ * The logistics solution element wraps around a resource. Don't know why we need this wrapping.
+ *
+ * @return the resource
+ */
+ LSPResource getResource();
+
+ LogisticChainElement getPreviousElement();
+
+ LogisticChainElement getNextElement();
+
+ /**
+ * This collection stores LSPShipments that are waiting for their treatment in this element or
+ * more precisely the Resource that is in charge of the actual physical handling.
+ *
+ * @return WaitingShipments
+ */
+ WaitingShipments getIncomingShipments();
+
+ /** Shipments that have already been treated. */
+ WaitingShipments getOutgoingShipments();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElementImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElementImpl.java
new file mode 100644
index 00000000000..757c7714e5b
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainElementImpl.java
@@ -0,0 +1,103 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+/* package-private */ class LogisticChainElementImpl extends LSPDataObject
+ implements LogisticChainElement {
+
+ private final LSPResource resource;
+ private final WaitingShipments incomingShipments;
+ private final WaitingShipments outgoingShipments;
+ // die beiden nicht im Builder. Die können erst in der Solution als ganzes gesetzt werden
+ private LogisticChainElement previousElement;
+ private LogisticChainElement nextElement;
+
+ LogisticChainElementImpl(LSPUtils.LogisticChainElementBuilder builder) {
+ super(builder.id);
+ this.resource = builder.resource;
+ this.incomingShipments = builder.incomingShipments;
+ this.outgoingShipments = builder.outgoingShipments;
+ resource.getClientElements().add(this);
+ }
+
+ @Override
+ public void connectWithNextElement(LogisticChainElement element) {
+ this.nextElement = element;
+ ((LogisticChainElementImpl) element).previousElement = this;
+ }
+
+ @Override
+ public LSPResource getResource() {
+ return resource;
+ }
+
+ @Override
+ public WaitingShipments getIncomingShipments() {
+ return incomingShipments;
+ }
+
+ @Override
+ public WaitingShipments getOutgoingShipments() {
+ return outgoingShipments;
+ }
+
+ @Override
+ public void setEmbeddingContainer(LogisticChain logisticChain) {
+ /* not */
+ }
+
+ @Override
+ public LogisticChainElement getPreviousElement() {
+ return previousElement;
+ }
+
+ @Override
+ public LogisticChainElement getNextElement() {
+ return nextElement;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder();
+ strb.append("LogisticsSolutionElementImpl{")
+ .append("resourceId=")
+ .append(resource.getId())
+ .append(", incomingShipments=")
+ .append(incomingShipments)
+ .append(", outgoingShipments=")
+ .append(outgoingShipments);
+
+ if (previousElement != null) {
+ strb.append(", previousElementId=").append(previousElement.getId());
+ } else {
+ strb.append(", previousElementId=").append("null");
+ }
+
+ if (nextElement != null) {
+ strb.append(", nextElementId=").append(nextElement.getId());
+ } else {
+ strb.append(", nextElementId=").append("null");
+ }
+
+ strb.append('}');
+ return strb.toString();
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainImpl.java
new file mode 100644
index 00000000000..a8d50917504
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainImpl.java
@@ -0,0 +1,97 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.api.core.v01.Id;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/* package-private */ class LogisticChainImpl extends LSPDataObject
+ implements LogisticChain {
+ private static final Logger log = LogManager.getLogger(LogisticChainImpl.class);
+
+ private final Collection logisticChainElements;
+ private final Collection> lspShipmentIds;
+ private LSP lsp;
+
+ LogisticChainImpl(LSPUtils.LogisticChainBuilder builder) {
+ super(builder.id);
+ this.logisticChainElements = builder.elements;
+ for (LogisticChainElement element : this.logisticChainElements) {
+ element.setEmbeddingContainer(this);
+ }
+ this.lspShipmentIds = new ArrayList<>();
+ }
+
+ @Override
+ public LSP getLSP() {
+ return lsp;
+ }
+
+ @Override
+ public void setLSP(LSP lsp) {
+ this.lsp = lsp;
+ }
+
+ @Override
+ public Collection getLogisticChainElements() {
+ return logisticChainElements;
+ }
+
+ @Override
+ public Collection> getLspShipmentIds() {
+ return lspShipmentIds;
+ }
+
+ @Override
+ public void addShipmentToChain(LspShipment lspShipment) {
+ lspShipmentIds.add(lspShipment.getId());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder();
+ strb.append("LogisticsSolutionImpl{")
+ .append("[No of SolutionsElements=")
+ .append(logisticChainElements.size())
+ .append("] \n");
+ if (!logisticChainElements.isEmpty()) {
+ strb.append("{SolutionElements=");
+ for (LogisticChainElement solutionElement : logisticChainElements) {
+ strb.append("\n [").append(solutionElement.toString()).append("]");
+ }
+ strb.append("}");
+ }
+ strb.append("[No of Shipments=").append(lspShipmentIds.size()).append("] \n");
+ if (!lspShipmentIds.isEmpty()) {
+ strb.append("{ShipmentIds=");
+ for (Id lspShipmentId : lspShipmentIds) {
+ strb.append("[").append(lspShipmentId.toString()).append("]");
+ }
+ strb.append("}");
+ }
+ strb.append('}');
+ return strb.toString();
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainScheduler.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainScheduler.java
new file mode 100644
index 00000000000..e950405e979
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/LogisticChainScheduler.java
@@ -0,0 +1,47 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * Serve the purpose of routing a set of {@link LspShipment}s
+ * through a set of {@link LogisticChain}s, which, in turn, consist of several {@link
+ * LogisticChainElement}s and the corresponding {@link LSPResource}s.
+ */
+public interface LogisticChainScheduler extends HasBackpointer {
+
+ void scheduleLogisticChain();
+
+ /**
+ * The buffer time is only taken into account in planning / scheduling. The idea is to
+ * ensure that the goods are available for the next ressource "in time", because the scheduling
+ * does not take into account any congestion during the simulation. E.g. if multiple vehicle are
+ * leaving the depot at the same time and thus influence each other.
+ * It is not intended to be available as buffer in the simulation itself -> It does not
+ * influence the events and shipmentLogs. As a consequence, the transportation (in simulation,
+ * events, ...) is in many cases earlier than scheduled. (Information from TM after asking; KMT
+ * 17.11.23)
+ *
+ * @param bufferTime for scheduling [in sec]
+ */
+ void setBufferTime(int bufferTime);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipments.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipments.java
new file mode 100644
index 00000000000..55e72fa3e5d
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipments.java
@@ -0,0 +1,51 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.Collection;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * Each LogisticsSolutionElement maintains two collections of WaitingShipments. Instances of the
+ * latter class contain tuples of LSPShipments and time stamps.
+ *
+ *
The first of these collections stores LSPShipments that are waiting for their treatment in
+ * this element or more precisely the Resource that is in charge of the actual physical handling.
+ *
+ *
The second one stores shipments that have already been treated.
+ *
+ *
At the beginning of the scheduling process, all LSPShipments are added to the collection of
+ * incoming shipments of the first LogisticsSolutionElement of the LogisticsSolution to which they
+ * were assigned before. The tuples in the collection of WaitingShipments thus consist of the
+ * shipments themselves and a time stamp that states when they arrived there (see 3.9). In the case
+ * of the first LogisticsSolutionElement, this time stamp corresponds to the start time window of
+ * the LSPShipment
+ */
+public interface WaitingShipments {
+
+ void addShipment(double time, LspShipment lspShipment);
+
+ Collection getSortedLspShipments();
+
+ Collection getLspShipmentsWTime();
+
+ void clear();
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipmentsImpl.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipmentsImpl.java
new file mode 100644
index 00000000000..6404d32a802
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/WaitingShipmentsImpl.java
@@ -0,0 +1,73 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2022 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+
+/* package-private */ class WaitingShipmentsImpl implements WaitingShipments {
+
+ private final List shipments;
+
+ WaitingShipmentsImpl() {
+ this.shipments = new ArrayList<>();
+ }
+
+ @Override
+ public void addShipment(double time, LspShipment lspShipment) {
+ LspShipmentUtils.setTimeOfLspShipment(lspShipment, time);
+ this.shipments.add(lspShipment);
+ shipments.sort(Comparator.comparingDouble(LspShipmentUtils::getTimeOfLspShipment));
+ }
+
+ @Override
+ public Collection getSortedLspShipments() {
+ shipments.sort(Comparator.comparingDouble(LspShipmentUtils::getTimeOfLspShipment));
+ return shipments;
+ }
+
+ public void clear() {
+ shipments.clear();
+ }
+
+ @Override
+ public Collection getLspShipmentsWTime() {
+ return shipments;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strb = new StringBuilder();
+ strb.append("WaitingShipmentsImpl{").append("No of Shipments= ").append(shipments.size());
+ if (!shipments.isEmpty()) {
+ strb.append("; ShipmentIds=");
+ for (LspShipment shipment : getSortedLspShipments()) {
+ strb.append("[").append(shipment.getId()).append("]");
+ }
+ }
+ strb.append('}');
+ return strb.toString();
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Driver2VehicleEventHandler.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Driver2VehicleEventHandler.java
new file mode 100644
index 00000000000..5cfde304930
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Driver2VehicleEventHandler.java
@@ -0,0 +1,72 @@
+/*
+ *********************************************************************** *
+ * 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.analysis;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent;
+import org.matsim.api.core.v01.events.VehicleLeavesTrafficEvent;
+import org.matsim.api.core.v01.events.handler.VehicleEntersTrafficEventHandler;
+import org.matsim.api.core.v01.events.handler.VehicleLeavesTrafficEventHandler;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.vehicles.Vehicle;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Basic event handler that collects the relation between vehicles and drivers.
+ * Necessary since link enter and leave events do not contain the driver anymore.
+ *
+ * This is the vice-versa implementation of {@link org.matsim.core.events.algorithms.Vehicle2DriverEventHandler}.
+ *
+ * In a first step only used internally. When needed more often, I have nothing against putting it more central. -> matsim-libs
+ *
+ * @author kturner
+ */
+public class Driver2VehicleEventHandler implements VehicleEntersTrafficEventHandler, VehicleLeavesTrafficEventHandler {
+
+ private final Map, Id> driversVehicles = new ConcurrentHashMap<>();
+
+ @Override
+ public void reset(int iteration) {
+ driversVehicles.clear();
+ }
+
+ @Override
+ public void handleEvent(VehicleEntersTrafficEvent event) {
+ driversVehicles.put(event.getPersonId(), event.getVehicleId());
+ }
+
+ @Override
+ public void handleEvent(VehicleLeavesTrafficEvent event) {
+ driversVehicles.remove(event.getPersonId());
+ }
+
+ /**
+ * @param personId the unique driver identifier.
+ * @return vehicle id of the driver's vehicle
+ */
+ public Id getVehicleOfDriver(Id personId) {
+ return driversVehicles.get(personId);
+ }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStats.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStats.java
new file mode 100644
index 00000000000..297e99fc5b7
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStats.java
@@ -0,0 +1,35 @@
+
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * ScoreStats.java
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2014 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.analysis;
+
+import java.util.Map;
+
+public interface LspScoreStats {
+
+ /**
+ * @return the history of scores in last iterations
+ */
+ Map> getScoreHistory();
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsControlerListener.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsControlerListener.java
new file mode 100644
index 00000000000..2a687b82f0f
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsControlerListener.java
@@ -0,0 +1,286 @@
+/* *********************************************************************** *
+ * project: org.matsim.*
+ * ScoreStats.java
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2007 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * *********************************************************************** */
+
+package org.matsim.freight.logistics.analysis;
+
+import jakarta.inject.Inject;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.*;
+import java.util.stream.Collectors;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.core.config.groups.ControllerConfigGroup;
+import org.matsim.core.config.groups.GlobalConfigGroup;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.controler.events.IterationEndsEvent;
+import org.matsim.core.controler.events.ShutdownEvent;
+import org.matsim.core.controler.events.StartupEvent;
+import org.matsim.core.controler.listener.IterationEndsListener;
+import org.matsim.core.controler.listener.ShutdownListener;
+import org.matsim.core.controler.listener.StartupListener;
+import org.matsim.core.population.PopulationUtils;
+import org.matsim.core.utils.charts.XYLineChart;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.freight.logistics.LSP;
+import org.matsim.freight.logistics.LSPPlan;
+import org.matsim.freight.logistics.LSPUtils;
+import org.matsim.freight.logistics.LSPs;
+
+/**
+ * Calculates at the end of each iteration the following statistics:
+ *
+ *
average score of the selected plan
+ *
average of the score of the worst plan of each agent
+ *
average of the score of the best plan of each agent
+ *
average of the average score of all plans of each agent
+ *
+ * Plans with undefined scores
+ * are not included in the statistics. The calculated values are written to a file, each iteration on
+ * a separate line.
+ *
+ * @author mrieser
+ */
+public class LspScoreStatsControlerListener implements StartupListener, IterationEndsListener, ShutdownListener, LspScoreStats {
+
+ private static final String LSP_SCORESTATS = "lsp_scorestats";
+
+ public enum ScoreItem { worst, best, average, executed }
+
+ private final LSPs lsps;
+ private final OutputDirectoryHierarchy controllerIO;
+ private final String delimiter;
+ private final BufferedWriter out;
+
+ private final ControllerConfigGroup controllerConfigGroup;
+
+ private final Map> scoreHistory = new HashMap<>();
+
+ private final Map perLsp = new HashMap<>();
+
+ private int minIteration = 0;
+
+ private final static Logger log = LogManager.getLogger(LspScoreStatsControlerListener.class);
+
+ @Inject
+ LspScoreStatsControlerListener(ControllerConfigGroup controllerConfigGroup, Scenario scenario, OutputDirectoryHierarchy controllerIO,
+ GlobalConfigGroup globalConfig ) {
+ this.controllerConfigGroup = controllerConfigGroup;
+ this.lsps = LSPUtils.getLSPs(scenario);
+ this.controllerIO = controllerIO;
+ this.delimiter = globalConfig.getDefaultDelimiter();
+ this.out = IOUtils.getBufferedWriter(controllerIO.getOutputFilename(LSP_SCORESTATS + ".csv"));
+
+ //TODO: Das hier dann mal ansehen, weil es ja nun nicht mehr via Subpobulations ist.. Vermutlich brauche ich nur die LSPIds...
+ Set lspIds = lsps.getLSPs().values().stream()
+ .map(PopulationUtils::getSubpopulation)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+
+ for (String lspId : lspIds) {
+ this.perLsp.put(lspId, new ScoreHist(new HashMap<>(), IOUtils.getBufferedWriter(controllerIO.getOutputFilename(LSP_SCORESTATS + "_" + lspId + ".csv"))));
+ }
+
+ try {
+ this.out.write("iteration" + this.delimiter + "avg_executed" + this.delimiter
+ + "avg_worst" + this.delimiter + "avg_average" + this.delimiter + "avg_best\n");
+ for (Map.Entry e : this.perLsp.entrySet()) {
+ e.getValue().out.write("iteration" + this.delimiter + "avg_executed" + this.delimiter
+ + "avg_worst" + this.delimiter + "avg_average" + this.delimiter + "avg_best\n");
+ }
+
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public void notifyStartup(final StartupEvent event) {
+ this.minIteration = this.controllerConfigGroup.getFirstIteration();
+ // int maxIter = controlerConfigGroup.getLastIteration();
+ // int iterations = maxIter - this.minIteration;
+ // if (iterations > 5000) iterations = 5000; // limit the history size
+ for ( ScoreItem item : ScoreItem.values() ) {
+ this.scoreHistory.put( item, new TreeMap<>() ) ;
+ this.perLsp.forEach((s, data) -> data.hist.put(item, new TreeMap<>()));
+ }
+ }
+
+ @Override
+ public void notifyIterationEnds(final IterationEndsEvent event) {
+ collectScoreInfo(event);
+ if (isWriteGraph(event)) {
+ writePng();
+ }
+ }
+
+ private boolean isWriteGraph(IterationEndsEvent event) {
+ // create chart when data of more than one iteration is available.
+ return this.controllerConfigGroup.getCreateGraphsInterval() > 0 &&
+ event.getIteration() % this.controllerConfigGroup.getCreateGraphsInterval() == 0 &&
+ event.getIteration() > this.minIteration;
+ }
+
+ private void collectScoreInfo(final IterationEndsEvent event) {
+ ScoreInfo info = new ScoreInfo();
+
+ Map perLsp = new HashMap<>();
+ this.perLsp.forEach((subpop, d) -> perLsp.put(subpop, new ScoreInfo()));
+
+ for (LSP lsp : this.lsps.getLSPs().values()) {
+ info.update(lsp);
+ String subpop = PopulationUtils.getSubpopulation(lsp);
+ if (subpop != null)
+ perLsp.get(subpop).update(lsp);
+ }
+
+
+ log.info("-- avg. score of the executed plan of each agent: " + (info.sumExecutedScores / info.nofExecutedScores));
+ log.info("-- avg. score of the worst plan of each agent: " + (info.sumScoreWorst / info.nofScoreWorst));
+ log.info("-- avg. of the avg. plan score per agent: " + (info.sumAvgScores / info.nofAvgScores));
+ log.info("-- avg. score of the best plan of each agent: " + (info.sumScoreBest / info.nofScoreBest));
+
+ try {
+ info.write(event.getIteration(), this.out, this.delimiter);
+ for (Map.Entry e : this.perLsp.entrySet()) {
+ perLsp.get(e.getKey()).write(event.getIteration(), e.getValue().out, this.delimiter);
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+// int index = event.getIteration() - this.minIteration;
+
+ this.scoreHistory.get( ScoreItem.worst ).put( event.getIteration(), info.sumScoreWorst / info.nofScoreWorst ) ;
+ this.scoreHistory.get( ScoreItem.best ).put( event.getIteration(), info.sumScoreBest / info.nofScoreBest ) ;
+ this.scoreHistory.get( ScoreItem.average ).put( event.getIteration(), info.sumAvgScores / info.nofAvgScores ) ;
+ this.scoreHistory.get( ScoreItem.executed ).put( event.getIteration(), info.sumExecutedScores / info.nofExecutedScores ) ;
+ }
+
+ private void writePng() {
+ XYLineChart chart = new XYLineChart("Score Statistics", "iteration", "score");
+ chart.addSeries("avg. worst score", this.scoreHistory.get( ScoreItem.worst ) ) ;
+ chart.addSeries("avg. best score", this.scoreHistory.get( ScoreItem.best) );
+ chart.addSeries("avg. of plans' average score", this.scoreHistory.get( ScoreItem.average) );
+ chart.addSeries("avg. executed score", this.scoreHistory.get( ScoreItem.executed ) );
+ chart.addMatsimLogo();
+ chart.saveAsPng(this.controllerIO.getOutputFilename(LSP_SCORESTATS + ".png"), 800, 600);
+ }
+
+ @Override
+ public void notifyShutdown(final ShutdownEvent controlerShudownEvent) {
+ try {
+ this.out.close();
+ for (ScoreHist data : this.perLsp.values()) {
+ data.out.close();
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Map> getScoreHistory() {
+ return Collections.unmodifiableMap( this.scoreHistory ) ;
+ }
+
+ private record ScoreHist(Map> hist, BufferedWriter out) {}
+
+ private static final class ScoreInfo {
+ double sumScoreWorst = 0.0;
+ double sumScoreBest = 0.0;
+ double sumAvgScores = 0.0;
+ double sumExecutedScores = 0.0;
+ int nofScoreWorst = 0;
+ int nofScoreBest = 0;
+ int nofAvgScores = 0;
+ int nofExecutedScores = 0;
+
+ private void update(LSP lsp) {
+ LSPPlan worstPlan = null;
+ LSPPlan bestPlan = null;
+ double worstScore = Double.POSITIVE_INFINITY;
+ double bestScore = Double.NEGATIVE_INFINITY;
+ double sumScores = 0.0;
+ double cntScores = 0;
+ for (LSPPlan plan : lsp.getPlans()) {
+
+ if (plan.getScore() == null) {
+ continue;
+ }
+ double score = plan.getScore();
+
+ // worst plan
+ if (worstPlan == null) {
+ worstPlan = plan;
+ worstScore = score;
+ } else if (score < worstScore) {
+ worstPlan = plan;
+ worstScore = score;
+ }
+
+ // best plan
+ if (bestPlan == null) {
+ bestPlan = plan;
+ bestScore = score;
+ } else if (score > bestScore) {
+ bestPlan = plan;
+ bestScore = score;
+ }
+
+ // avg. score
+ sumScores += score;
+ cntScores++;
+
+ // executed plan?
+ if (LSPUtils.isPlanTheSelectedPlan(plan)) {
+ this.sumExecutedScores += score;
+ this.nofExecutedScores++;
+ }
+ }
+
+ if (worstPlan != null) {
+ this.nofScoreWorst++;
+ this.sumScoreWorst += worstScore;
+ }
+ if (bestPlan != null) {
+ this.nofScoreBest++;
+ this.sumScoreBest += bestScore;
+ }
+ if (cntScores > 0) {
+ this.sumAvgScores += (sumScores / cntScores);
+ this.nofAvgScores++;
+ }
+ }
+
+ private void write(int iteration, BufferedWriter out, String delimiter) throws IOException {
+ out.write(iteration + delimiter
+ + (this.sumExecutedScores / this.nofExecutedScores) + delimiter
+ + (this.sumScoreWorst / this.nofScoreWorst) + delimiter
+ + (this.sumAvgScores / this.nofAvgScores) + delimiter
+ + (this.sumScoreBest / this.nofScoreBest) + "\n");
+ out.flush();
+ }
+
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsModule.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsModule.java
new file mode 100644
index 00000000000..948b206388d
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/LspScoreStatsModule.java
@@ -0,0 +1,35 @@
+/*
+ * *********************************************************************** *
+ * * project: org.matsim.*
+ * * ScoreStatsModule.java
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * copyright : (C) 2014 by the members listed in the COPYING, *
+ * * LICENSE and WARRANTY file. *
+ * * email : info at matsim dot org *
+ * * *
+ * * *********************************************************************** *
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * See also COPYING, LICENSE and WARRANTY file *
+ * * *
+ * * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.analysis;
+
+import com.google.inject.Singleton;
+import org.matsim.core.controler.AbstractModule;
+
+public class LspScoreStatsModule extends AbstractModule {
+ @Override
+ public void install() {
+ bind(LspScoreStatsControlerListener.class).in(Singleton.class);
+ addControlerListenerBinding().to(LspScoreStatsControlerListener.class);
+ bind(LspScoreStats.class).to(LspScoreStatsControlerListener.class);
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Vehicle2CarrierEventHandler.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Vehicle2CarrierEventHandler.java
new file mode 100644
index 00000000000..d7230ab3d41
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/analysis/Vehicle2CarrierEventHandler.java
@@ -0,0 +1,71 @@
+/*
+ *********************************************************************** *
+ * 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.analysis;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.matsim.api.core.v01.Id;
+import org.matsim.freight.carriers.Carrier;
+import org.matsim.freight.carriers.events.CarrierTourEndEvent;
+import org.matsim.freight.carriers.events.CarrierTourStartEvent;
+import org.matsim.freight.carriers.events.eventhandler.CarrierTourEndEventHandler;
+import org.matsim.freight.carriers.events.eventhandler.CarrierTourStartEventHandler;
+import org.matsim.vehicles.Vehicle;
+
+/**
+ * Basic event handler that collects the relation between vehicles and carrier.
+ * Necessary since there is no event having all this information together.
+ *
+ * This is a modified implementation of {@link org.matsim.core.events.algorithms.Vehicle2DriverEventHandler}.
+ *
+ * In a first step only used internally. When needed more often, I have nothing against putting it more central. -> matsim-libs
+ *
+ * @author kturner
+ */
+public class Vehicle2CarrierEventHandler implements CarrierTourStartEventHandler, CarrierTourEndEventHandler {
+
+ private final Map, Id> vehicle2carrier = new ConcurrentHashMap<>();
+
+ @Override
+ public void reset(int iteration) {
+ vehicle2carrier.clear();
+ }
+
+ @Override
+ public void handleEvent(CarrierTourStartEvent event) {
+ vehicle2carrier.put(event.getVehicleId(), event.getCarrierId());
+ }
+
+ @Override
+ public void handleEvent(CarrierTourEndEvent event) {
+ vehicle2carrier.remove(event.getVehicleId());
+ }
+
+ /**
+ * @param vehicleId the unique vehicle Id
+ * @return id of the vehicle's carrier
+ */
+ public Id getCarrierOfVehicle(Id vehicleId) {
+ return vehicle2carrier.get(vehicleId);
+ }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/AbstractLogisticEvent.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/AbstractLogisticEvent.java
new file mode 100644
index 00000000000..fd9791a6831
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/AbstractLogisticEvent.java
@@ -0,0 +1,55 @@
+package org.matsim.freight.logistics.events;
+
+import java.util.Map;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.events.Event;
+import org.matsim.api.core.v01.events.HasLinkId;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.freight.logistics.HasLspShipmentId;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * A general logistic event contains the information (= {@link Id}) of the - the location (= {@link
+ * Link}) - the lspShipment (= {@link LspShipment}) belonging to it.
+ *
+ *
Please note, that general _freight_ events can be found in the freight contrib.
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+public abstract class AbstractLogisticEvent extends Event implements HasLinkId, HasLspShipmentId {
+
+ private final Id linkId;
+ private final Id lspShipmentId;
+
+ public AbstractLogisticEvent(double time, Id linkId, Id lspShipmentId) {
+ super(time);
+ this.linkId = linkId;
+ this.lspShipmentId = lspShipmentId;
+ }
+
+ /**
+ * @return id of the {@link LspShipment}
+ */
+ @Override
+ public final Id getLspShipmentId() {
+ return lspShipmentId;
+ }
+
+ @Override
+ public final Id getLinkId() {
+ return linkId;
+ }
+
+ /**
+ * Adds the {@link Id< LspShipment >} to the list of attributes. {@link Id} is handled by
+ * superclass {@link Event}
+ *
+ * @return The map of attributes
+ */
+ @Override
+ public Map getAttributes() {
+ Map attr = super.getAttributes();
+ attr.put(ATTRIBUTE_LSP_SHIPMENT_ID, lspShipmentId.toString());
+ return attr;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartedEventHandler.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartedEventHandler.java
new file mode 100644
index 00000000000..b8b9c6e900e
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartedEventHandler.java
@@ -0,0 +1,11 @@
+package org.matsim.freight.logistics.events;
+
+import org.matsim.core.events.handler.EventHandler;
+
+/**
+ * @author Kai Martins-Turner (kturner)
+ */
+public interface HandlingInHubStartedEventHandler extends EventHandler {
+
+ void handleEvent(HandlingInHubStartsEvent event);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartsEvent.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartsEvent.java
new file mode 100644
index 00000000000..c7e9f4a754d
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/HandlingInHubStartsEvent.java
@@ -0,0 +1,88 @@
+/*
+ * *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 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.events;
+
+import static org.matsim.freight.logistics.events.LspEventAttributes.ATTRIBUTE_EXP_HANDLING_DURATION;
+import static org.matsim.freight.logistics.events.LspEventAttributes.ATTRIBUTE_HUB_ID;
+
+import java.util.Map;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.events.GenericEvent;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.freight.logistics.LSPResource;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * An event, that informs that the handling of a {@link LspShipment} in a hub has started.
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+public final class HandlingInHubStartsEvent extends AbstractLogisticEvent {
+
+ public static final String EVENT_TYPE = "Handling_started";
+ private final Id hubId;
+ private final double expHandlingDuration;
+
+ public HandlingInHubStartsEvent(
+ double time,
+ Id linkId,
+ Id lspShipmentId,
+ Id hubId,
+ double expHandlingDuration) {
+ super(time, linkId, lspShipmentId);
+ this.hubId = hubId;
+ this.expHandlingDuration = expHandlingDuration;
+ }
+
+ public static HandlingInHubStartsEvent convert(GenericEvent event) {
+ Map attributes = event.getAttributes();
+ double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME));
+ Id linkId = Id.createLinkId(attributes.get(ATTRIBUTE_LINK));
+ Id lspSipmentId =
+ Id.create(attributes.get(ATTRIBUTE_LSP_SHIPMENT_ID), LspShipment.class);
+ var hubId = Id.create(attributes.get(ATTRIBUTE_HUB_ID), LSPResource.class);
+ double expHandlingDuration =
+ Double.parseDouble(attributes.get(ATTRIBUTE_EXP_HANDLING_DURATION));
+ return new HandlingInHubStartsEvent(time, linkId, lspSipmentId, hubId, expHandlingDuration);
+ }
+
+ @Override
+ public String getEventType() {
+ return EVENT_TYPE;
+ }
+
+ public Id getHubId() {
+ return hubId;
+ }
+
+ public double getExpHandlingDuration() {
+ return expHandlingDuration;
+ }
+
+ @Override
+ public Map getAttributes() {
+ Map attr = super.getAttributes();
+ attr.put(ATTRIBUTE_HUB_ID, hubId.toString());
+ attr.put(ATTRIBUTE_EXP_HANDLING_DURATION, String.valueOf(expHandlingDuration));
+ return attr;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventAttributes.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventAttributes.java
new file mode 100644
index 00000000000..830862b6f5b
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventAttributes.java
@@ -0,0 +1,32 @@
+/*
+ * *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 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.events;
+
+/**
+ * Some constants, that are used for the Attributes of different logistic events.
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+public class LspEventAttributes {
+ public static final String ATTRIBUTE_HUB_ID = "hubId";
+ public static final String ATTRIBUTE_EXP_HANDLING_DURATION = "expHandlingDuration";
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventCreator.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventCreator.java
new file mode 100644
index 00000000000..b5a0933fa7c
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventCreator.java
@@ -0,0 +1,36 @@
+/*
+ * *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 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.events;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.events.Event;
+import org.matsim.api.core.v01.population.Activity;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+ * @author Kai Martins-Turner (kturner)
+ */
+public interface LspEventCreator {
+
+ // I am unsure, if I need the activity or not. kmt 'dec22
+ Event createEvent(Event event, Id lspShipmentId, Activity activity);
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventsReader.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventsReader.java
new file mode 100644
index 00000000000..1380da69044
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/events/LspEventsReader.java
@@ -0,0 +1,54 @@
+/*
+ * *********************************************************************** *
+ * project: org.matsim.*
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 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.events;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.matsim.freight.logistics.LSP;
+import org.matsim.core.api.experimental.events.EventsManager;
+import org.matsim.core.events.MatsimEventsReader;
+import org.matsim.freight.carriers.events.CarrierEventsReaders;
+
+/**
+ * Creates an {@link MatsimEventsReader} that also handles the {@link
+ * LSP} specific events.
+ *
+ * @author kturner (Kai Martins-Turner)
+ */
+public class LspEventsReader {
+
+ public static Map createCustomEventMappers() {
+ Map map =
+ new TreeMap<>(
+ CarrierEventsReaders
+ .createCustomEventMappers()); // also get all the Carrier-related EventMapper
+ map.put(HandlingInHubStartsEvent.EVENT_TYPE, HandlingInHubStartsEvent::convert);
+ return map;
+ }
+
+ public static MatsimEventsReader createEventsReader(EventsManager eventsManager) {
+ MatsimEventsReader reader = new MatsimEventsReader(eventsManager);
+ createCustomEventMappers().forEach(reader::addCustomEventMapper);
+ return reader;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/ExampleConstants.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/ExampleConstants.java
new file mode 100644
index 00000000000..02939778990
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/ExampleConstants.java
@@ -0,0 +1,83 @@
+/*
+ *********************************************************************** *
+ * 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 *
+ * *
+ * ***********************************************************************
+ */
+
+/*
+ *********************************************************************** *
+ * 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;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ExampleConstants {
+ //Both, the ingoing and outgoing links are tolled. -> Please be aware of it.
+ public static final List TOLLED_LINK_LIST_BERLIN_BOTH_DIRECTIONS =
+ Arrays.asList(
+ "70831", "14691", "49319", "70830", "17284", "65008", "65007", "62413", "17283", "144164",
+ "144165", "4606", "118311", "4607", "15423", "53820", "15422", "138286", "69167",
+ "138287", "17057", "74648", "74647", "113641", "10307", "10306", "51775", "155051",
+ "51776", "150042", "150043", "150164", "90583", "96329", "19320", "132511", "19321",
+ "64851", "144180", "34042", "124770", "34041", "74891", "144184", "124769", "35018",
+ "35017", "77379", "35256", "108717", "113640", "157261", "142799", "157262", "52995",
+ "934", "52996", "935", "95587", "95588", "17150", "147460", "147461", "54024", "54023",
+ "152801", "144506", "145715", "144505", "156464", "17125", "17126", "114545", "114546",
+ "140792", "17127", "17248", "17128", "17249", "156458", "35463", "159609", "35462",
+ "159608", "22046", "154715", "22047", "144373", "154716", "155927", "155926", "144372",
+ "96330", "61139", "98190", "144126", "144127", "61011", "61010", "156463", "63682",
+ "47555", "73006", "94867", "138930", "94866", "133488", "138931", "47554", "73005",
+ "58893", "116395", "116394", "144136", "1158", "1157", "58894", "61269", "79237",
+ "144137", "732", "149702", "733", "77854", "4785", "55946", "77855", "4786", "55945",
+ "90018", "61264", "61263", "86201", "77738", "120646", "77739", "26507", "108414",
+ "108415", "17115", "66841", "26506", "78255", "78254", "118561", "35447", "147535",
+ "17116", "118560", "61270", "102480", "51917", "62494", "72973", "51918", "72972",
+ "72050", "72051", "147027", "33258", "61169", "18419", "102479", "20863", "61170",
+ "43048", "43049", "69459", "73037", "18420", "69458", "3255", "3254", "73036", "27017",
+ "76094", "41429", "74241", "76095", "149583", "74240", "35426", "81688", "81689", "12686",
+ "25848", "25849", "64459", "115416", "149592", "74374", "115417", "81474", "81475",
+ "36983", "36984", "36985", "36986", "52917", "52918", "64460", "40311", "108695", "40310",
+ "79385", "119212", "155909", "119213", "119334", "119335", "112023", "48277", "48278",
+ "106946", "91853", "91854", "102288", "69129", "102287", "13607", "2985", "64482",
+ "156612", "8983", "156613", "67517", "28548", "28549", "83543", "145734", "83542",
+ "149536", "149537", "151175", "151174", "18159", "8994", "93250", "147370", "53001",
+ "5918", "24153", "79875", "147369", "36147", "53002", "138543", "138542", "104212",
+ "137699", "137698", "41960", "104211", "18160", "41723", "41724", "3505", "123744",
+ "81389", "104205", "104206", "112065", "49320", "84772", "37107", "142803");
+ public static final List TOLLED_LINK_LIST_GRID = Arrays.asList(
+ "i(3,4)", "i(3,6)", "i(7,5)R", "i(7,7)R", "j(4,8)R", "j(6,8)R", "j(3,4)", "j(5,4)");
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfInitialPlan.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfInitialPlan.java
new file mode 100644
index 00000000000..29afe8dbb14
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfInitialPlan.java
@@ -0,0 +1,211 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.initialPlans;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlanElement;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/*package-private*/ class ExampleSchedulingOfInitialPlan {
+
+ private static LSP createInitialLSP(Scenario scenario) {
+
+ // The Carrier for the resource of the sole LogisticsSolutionElement of the LSP is created
+ Id carrierId = Id.create("CollectionCarrier", Carrier.class);
+ Id vehicleTypeId = Id.create("CollectionCarrierVehicleType", VehicleType.class);
+ VehicleType collectionVehType = VehicleUtils.createVehicleType(vehicleTypeId, TransportMode.car);
+ collectionVehType.getCapacity().setOther(10);
+ collectionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ collectionVehType.getCostInformation().setCostsPerSecond(0.38);
+ collectionVehType.getCostInformation().setFixedCost(49.);
+ collectionVehType.setMaximumVelocity(50 / 3.6);
+
+ Id collectionLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id vollectionVehicleId = Id.createVehicleId("CollectionVehicle");
+ CarrierVehicle carrierVehicle =
+ CarrierVehicle.newInstance(vollectionVehicleId, collectionLinkId, collectionVehType);
+
+ CarrierCapabilities capabilities = CarrierCapabilities.Builder.newInstance()
+ .addVehicle(carrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+
+ Carrier carrier = CarriersUtils.createCarrier(carrierId);
+ carrier.setCarrierCapabilities(capabilities);
+
+ // The Resource i.e. the Resource is created
+ LSPResource collectionResource =
+ ResourceImplementationUtils.CollectionCarrierResourceBuilder.newInstance(
+ carrier)
+ .setCollectionScheduler(
+ ResourceImplementationUtils.createDefaultCollectionCarrierScheduler(scenario))
+ .setLocationLinkId(collectionLinkId)
+ .build();
+
+ // The adapter is now inserted into the only LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ LogisticChainElement collectionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("CollectionElement", LogisticChainElement.class))
+ .setResource(collectionResource)
+ .build();
+
+ // The LogisticsSolutionElement is now inserted into the only LogisticsSolution of the LSP
+ LogisticChain collectionSolution =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("CollectionSolution", LogisticChain.class))
+ .addLogisticChainElement(collectionElement)
+ .build();
+
+ // The initial plan of the lsp is generated and the assigner and the solution from above are
+ // added
+ LSPPlan collectionPlan = LSPUtils.createLSPPlan();
+ InitialShipmentAssigner assigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ collectionPlan.setInitialShipmentAssigner(assigner);
+ collectionPlan.addLogisticChain(collectionSolution);
+
+ // The exogenous list of Resources for the SolutionScheduler is compiled and the Scheduler is
+ // added to the LSPBuilder
+ ArrayList resourcesList = new ArrayList<>();
+ resourcesList.add(collectionResource);
+ LogisticChainScheduler simpleScheduler =
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(resourcesList);
+
+ return LSPUtils.LSPBuilder.getInstance(Id.create("CollectionLSP", LSP.class))
+ .setInitialPlan(collectionPlan)
+ .setLogisticChainScheduler(simpleScheduler)
+ .build();
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ ArrayList shipmentList = new ArrayList<>();
+ ArrayList linkList = new ArrayList<>(network.getLinks().values());
+
+ // Create five LSPShipments that are located in the left half of the network.
+ for (int i = 1; i < 6; i++) {
+ Id id = Id.create(i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+ Random random = new Random(1);
+ int capacityDemand = random.nextInt(4);
+ builder.setCapacityDemand(capacityDemand);
+
+ while (true) {
+ Collections.shuffle(linkList, random);
+ Link pendingFromLink = linkList.getFirst();
+ if (pendingFromLink.getFromNode().getCoord().getX() <= 4000
+ && pendingFromLink.getFromNode().getCoord().getY() <= 4000
+ && pendingFromLink.getToNode().getCoord().getX() <= 4000
+ && pendingFromLink.getToNode().getCoord().getY() <= 4000) {
+ builder.setFromLinkId(pendingFromLink.getId());
+ break;
+ }
+ }
+
+ builder.setToLinkId(Id.createLinkId("(4 2) (4 3)"));
+ TimeWindow endTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setEndTimeWindow(endTimeWindow);
+ TimeWindow startTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setStartTimeWindow(startTimeWindow);
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+
+ public static void main(String[] args) {
+
+ // Set up required MATSim classes
+ Config config = new Config();
+ config.addCoreModules();
+ Scenario scenario = ScenarioUtils.createScenario(config);
+ new MatsimNetworkReader(scenario.getNetwork())
+ .readFile(IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "logistics-2regions" ), "2regions-network.xml" ).getFile());
+ Network network = scenario.getNetwork();
+
+ // Create LSP and lspShipments
+ LSP lsp = createInitialLSP(scenario);
+ Collection lspShipments = createInitialLSPShipments(network);
+
+ // assign the lspShipments to the LSP
+ for (LspShipment lspShipment : lspShipments) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ // schedule the LSP with the lspShipments and according to the scheduler of the Resource
+ lsp.scheduleLogisticChains();
+
+ // print the schedules for the assigned LSPShipments
+ for (LspShipment lspShipment : lspShipments) {
+ System.out.println("Shipment: " + lspShipment.getId());
+ ArrayList scheduleElements =
+ new ArrayList<>(
+ LspShipmentUtils.getOrCreateShipmentPlan(lsp.getSelectedPlan(), lspShipment.getId())
+ .getPlanElements()
+ .values());
+ scheduleElements.sort(LspShipmentUtils.createShipmentPlanElementComparator());
+ ArrayList logElements =
+ new ArrayList<>(lspShipment.getShipmentLog().getPlanElements().values());
+ logElements.sort(LspShipmentUtils.createShipmentPlanElementComparator());
+
+ for (LspShipmentPlanElement element :
+ LspShipmentUtils.getOrCreateShipmentPlan(lsp.getSelectedPlan(), lspShipment.getId())
+ .getPlanElements()
+ .values()) {
+ System.out.println(
+ // "Solution Id: " + element.getSolutionElement().getEmbeddingContainer().getId() +
+ " SolutionElement Id: "
+ + element.getLogisticChainElement().getId()
+ + " Resource Id: "
+ + element.getResourceId()
+ + " Type: "
+ + element.getElementType()
+ + " Start Time: "
+ + element.getStartTime()
+ + " End Time: "
+ + element.getEndTime());
+ }
+ System.out.println();
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChain.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChain.java
new file mode 100644
index 00000000000..fc3ea48678f
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChain.java
@@ -0,0 +1,390 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.initialPlans;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils.DistributionCarrierResourceBuilder;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils.TransshipmentHubBuilder;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlanElement;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/*A transport chain with five elements (collection-> reloading -> main run -> reloading -> delivery) is created and scheduled
+ *
+ */
+
+/*package-private*/ class ExampleSchedulingOfTransportChain {
+
+ private static LSP createInitialLSP(Scenario scenario) {
+
+ Network network = scenario.getNetwork();
+
+ // The Carrier for collection is created
+ Id collectionCarrierId = Id.create("CollectionCarrier", Carrier.class);
+ Id vehicleTypeId = Id.create("CollectionCarrierVehicleType", VehicleType.class);
+ VehicleType collectionVehType = VehicleUtils.createVehicleType(vehicleTypeId, TransportMode.car);
+ collectionVehType.getCapacity().setOther(10);
+ collectionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ collectionVehType.getCostInformation().setCostsPerSecond(0.38);
+ collectionVehType.getCostInformation().setFixedCost(49.);
+ collectionVehType.setMaximumVelocity(50 / 3.6);
+
+ Id collectionLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id vollectionVehicleId = Id.createVehicleId("CollectionVehicle");
+ CarrierVehicle carrierVehicle =
+ CarrierVehicle.newInstance(vollectionVehicleId, collectionLinkId, collectionVehType);
+
+ CarrierCapabilities.Builder capabilitiesBuilder = CarrierCapabilities.Builder.newInstance();
+ capabilitiesBuilder.addVehicle(carrierVehicle);
+ capabilitiesBuilder.setFleetSize(FleetSize.INFINITE);
+ CarrierCapabilities capabilities = capabilitiesBuilder.build();
+
+ Carrier collectionCarrier = CarriersUtils.createCarrier(collectionCarrierId);
+ collectionCarrier.setCarrierCapabilities(capabilities);
+
+ // The collection adapter i.e. the Resource is created
+ LSPResource collectionResource =
+ ResourceImplementationUtils.CollectionCarrierResourceBuilder.newInstance(
+ collectionCarrier)
+ .setCollectionScheduler(
+ ResourceImplementationUtils.createDefaultCollectionCarrierScheduler(scenario))
+ .setLocationLinkId(collectionLinkId)
+ .build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ LogisticChainElement collectionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("CollectionElement", LogisticChainElement.class))
+ .setResource(collectionResource)
+ .build();
+
+ // The first reloading adapter i.e. the Resource is created
+ Id firstTransshipmentHubId = Id.create("TranshipmentHub1", LSPResource.class);
+ Id firstTransshipmentHub_LinkId = Id.createLinkId("(4 2) (4 3)");
+ TransshipmentHubBuilder firstTransshipmentHubBuilder =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ firstTransshipmentHubId, firstTransshipmentHub_LinkId, scenario);
+
+ // The scheduler for the first reloading point is created
+ final LSPResourceScheduler firstHubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10)
+ .setCapacityNeedLinear(1)
+ .build();
+
+ // The scheduler is added to the Resource and the Resource is created
+ firstTransshipmentHubBuilder.setTransshipmentHubScheduler(firstHubScheduler);
+ LSPResource firstTranshipmentHubResource = firstTransshipmentHubBuilder.build();
+
+ // The SolutionElement for the first reloading point is created
+ Id firstHubElementId =
+ Id.create("FirstHubElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder firstHubElementBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(firstHubElementId);
+ firstHubElementBuilder.setResource(firstTranshipmentHubResource);
+ LogisticChainElement firstHubElement = firstHubElementBuilder.build();
+
+ // The Carrier for the main run Resource is created
+ Id mainRunCarrierId = Id.create("MainRunCarrier", Carrier.class);
+ Id mainRunVehTypeId = Id.create("MainRunCarrierVehicleType", VehicleType.class);
+ final VehicleType mainRunVehType = VehicleUtils.createVehicleType(mainRunVehTypeId, TransportMode.car);
+ mainRunVehType.getCapacity().setOther(30);
+ mainRunVehType.getCostInformation().setCostsPerMeter(0.0002);
+ mainRunVehType.getCostInformation().setCostsPerSecond(0.38);
+ mainRunVehType.getCostInformation().setFixedCost(120.);
+ mainRunVehType.setMaximumVelocity(50 / 3.6);
+ mainRunVehType.setNetworkMode(TransportMode.car);
+
+
+ Id fromLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id mainRunVehicleId = Id.createVehicleId("MainRunVehicle");
+ CarrierVehicle mainRunCarrierVehicle =
+ CarrierVehicle.newInstance(mainRunVehicleId, fromLinkId, mainRunVehType);
+
+ CarrierCapabilities mainRunCapabilities =
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(mainRunCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+ Carrier mainRunCarrier = CarriersUtils.createCarrier(mainRunCarrierId);
+ mainRunCarrier.setCarrierCapabilities(mainRunCapabilities);
+
+ // The adapter i.e. the main run resource is created
+ LSPResource mainRunResource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(mainRunCarrier)
+ .setFromLinkId(Id.createLinkId("(4 2) (4 3)"))
+ .setToLinkId(Id.createLinkId("(14 2) (14 3)"))
+ .setMainRunCarrierScheduler(ResourceImplementationUtils.createDefaultMainRunCarrierScheduler(scenario))
+ .build();
+
+ // The LogisticsSolutionElement for the main run Resource is created
+ LogisticChainElement mainRunElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("MainRunElement", LogisticChainElement.class))
+ .setResource(mainRunResource)
+ .build();
+
+ // The second reloading adapter i.e. the Resource is created
+ Id secondTransshipmentHubId = Id.create("TranshipmentHub2", LSPResource.class);
+ Id secondTransshipmentHub_LinkId = Id.createLinkId("(14 2) (14 3)");
+
+ // The scheduler for the second reloading point is created
+ LSPResourceScheduler secondHubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10)
+ .setCapacityNeedLinear(1)
+ .build();
+
+ LSPResource secondTransshipmentHubResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ secondTransshipmentHubId, secondTransshipmentHub_LinkId, scenario)
+ .setTransshipmentHubScheduler(secondHubScheduler)
+ .build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ Id secondHubElementId =
+ Id.create("SecondHubElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder secondHubElementBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(secondHubElementId);
+ secondHubElementBuilder.setResource(secondTransshipmentHubResource);
+ LogisticChainElement secondHubElement = secondHubElementBuilder.build();
+
+ // The Carrier for distribution is created
+ Id distributionCarrierId = Id.create("DistributionCarrier", Carrier.class);
+ Id distributionVehTypeId = Id.create("DistributionCarrierVehicleType", VehicleType.class);
+ final VehicleType distributionVehType = VehicleUtils.createVehicleType(distributionVehTypeId, TransportMode.car);
+ distributionVehType.getCapacity().setOther(10);
+ distributionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ distributionVehType.getCostInformation().setCostsPerSecond(0.38);
+ distributionVehType.getCostInformation().setFixedCost(49.);
+ distributionVehType.setMaximumVelocity(50 / 3.6);
+ distributionVehType.setNetworkMode(TransportMode.car);
+
+
+ Id distributionLinkId = Id.createLinkId("(14 2) (14 3)");
+ Id distributionVehicleId = Id.createVehicleId("DistributionVehicle");
+ CarrierVehicle distributionCarrierVehicle =
+ CarrierVehicle.newInstance(distributionVehicleId, distributionLinkId, distributionVehType);
+
+ CarrierCapabilities.Builder distributionCapabilitiesBuilder =
+ CarrierCapabilities.Builder.newInstance();
+ distributionCapabilitiesBuilder.addVehicle(distributionCarrierVehicle);
+ distributionCapabilitiesBuilder.setFleetSize(FleetSize.INFINITE);
+ CarrierCapabilities distributionCapabilities = distributionCapabilitiesBuilder.build();
+ Carrier distributionCarrier = CarriersUtils.createCarrier(distributionCarrierId);
+ distributionCarrier.setCarrierCapabilities(distributionCapabilities);
+
+ // The distribution adapter i.e. the Resource is created
+ DistributionCarrierResourceBuilder distributionResourceBuilder =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier);
+ distributionResourceBuilder.setLocationLinkId(distributionLinkId);
+
+ // The scheduler for the Resource is created and added. This is where jsprit comes into play.
+ distributionResourceBuilder.setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario));
+ LSPResource distributionResource = distributionResourceBuilder.build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ Id distributionElementId =
+ Id.create("DistributionElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder distributionBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(distributionElementId);
+ distributionBuilder.setResource(distributionResource);
+ LogisticChainElement distributionElement = distributionBuilder.build();
+
+ // The Order of the logisticsSolutionElements is now specified
+ collectionElement.connectWithNextElement(firstHubElement);
+ firstHubElement.connectWithNextElement(mainRunElement);
+ mainRunElement.connectWithNextElement(secondHubElement);
+ secondHubElement.connectWithNextElement(distributionElement);
+
+ // The SolutionElements are now inserted into the only LogisticsSolution of the LSP
+ Id solutionId = Id.create("SolutionId", LogisticChain.class);
+ LSPUtils.LogisticChainBuilder completeSolutionBuilder =
+ LSPUtils.LogisticChainBuilder.newInstance(solutionId);
+ completeSolutionBuilder.addLogisticChainElement(collectionElement);
+ completeSolutionBuilder.addLogisticChainElement(firstHubElement);
+ completeSolutionBuilder.addLogisticChainElement(mainRunElement);
+ completeSolutionBuilder.addLogisticChainElement(secondHubElement);
+ completeSolutionBuilder.addLogisticChainElement(distributionElement);
+ LogisticChain completeSolution = completeSolutionBuilder.build();
+
+ // The initial plan of the lsp is generated and the assigner and the solution from above are
+ // added
+ LSPPlan completePlan = LSPUtils.createLSPPlan();
+ InitialShipmentAssigner assigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ completePlan.setInitialShipmentAssigner(assigner);
+ completePlan.addLogisticChain(completeSolution);
+
+ LSPUtils.LSPBuilder completeLSPBuilder =
+ LSPUtils.LSPBuilder.getInstance(Id.create("CollectionLSP", LSP.class));
+ completeLSPBuilder.setInitialPlan(completePlan);
+
+ // The exogenous list of Resources for the SolutionScheduler is compiled and the Scheduler is
+ // added to the LSPBuilder
+ ArrayList resourcesList = new ArrayList<>();
+ resourcesList.add(collectionResource);
+ resourcesList.add(firstTranshipmentHubResource);
+ resourcesList.add(mainRunResource);
+ resourcesList.add(secondTransshipmentHubResource);
+ resourcesList.add(distributionResource);
+
+ // SolutionScheduler forwardSolutionScheduler = LSPUtils.createForwardSolutionScheduler();
+ // //Ist der "nicht einfache" Scheduler. TODO braucht der keine RessourcenListe oder ähnliches?
+ // --> Offenbar ja, weil Null Pointer. argh!
+ // completeLSPBuilder.setSolutionScheduler(forwardSolutionScheduler);
+
+ LogisticChainScheduler simpleScheduler =
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(resourcesList);
+ completeLSPBuilder.setLogisticChainScheduler(simpleScheduler);
+
+ return completeLSPBuilder.build();
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ ArrayList shipmentList = new ArrayList<>();
+ ArrayList linkList = new ArrayList<>(network.getLinks().values());
+ Random rand = new Random(1);
+ for (int i = 1; i < 6; i++) {
+ Id id = Id.create(i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+ int capacityDemand = rand.nextInt(10);
+ builder.setCapacityDemand(capacityDemand);
+
+ while (true) {
+ Collections.shuffle(linkList, rand);
+ Link pendingToLink = linkList.getFirst();
+ if ((pendingToLink.getFromNode().getCoord().getX() <= 18000
+ && pendingToLink.getFromNode().getCoord().getY() <= 4000
+ && pendingToLink.getFromNode().getCoord().getX() >= 14000
+ && pendingToLink.getToNode().getCoord().getX() <= 18000
+ && pendingToLink.getToNode().getCoord().getY() <= 4000
+ && pendingToLink.getToNode().getCoord().getX() >= 14000)) {
+ builder.setToLinkId(pendingToLink.getId());
+ break;
+ }
+ }
+
+ while (true) {
+ Collections.shuffle(linkList, rand);
+ Link pendingFromLink = linkList.getFirst();
+ if (pendingFromLink.getFromNode().getCoord().getX() <= 4000
+ && pendingFromLink.getFromNode().getCoord().getY() <= 4000
+ && pendingFromLink.getToNode().getCoord().getX() <= 4000
+ && pendingFromLink.getToNode().getCoord().getY() <= 4000) {
+ builder.setFromLinkId(pendingFromLink.getId());
+ break;
+ }
+ }
+
+ TimeWindow endTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setEndTimeWindow(endTimeWindow);
+ TimeWindow startTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setStartTimeWindow(startTimeWindow);
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+
+ public static void main(String[] args) {
+
+ // Set up required MATSim classes
+ Config config = new Config();
+ config.addCoreModules();
+ Scenario scenario = ScenarioUtils.createScenario(config);
+ new MatsimNetworkReader(scenario.getNetwork())
+ .readFile(IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "logistics-2regions" ), "2regions-network.xml" ).getFile());
+
+ // Create LSP and shipments
+ LSP lsp = createInitialLSP(scenario);
+ Collection lspShipments = createInitialLSPShipments(scenario.getNetwork());
+
+ // assign the shipments to the LSP
+ for (LspShipment lspShipment : lspShipments) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ // schedule the LSP with the shipments and according to the scheduler of the Resource
+ lsp.scheduleLogisticChains();
+
+ // print the schedules for the assigned LSPShipments
+ for (LspShipment lspShipment : lsp.getLspShipments()) {
+ ArrayList elementList =
+ new ArrayList<>(
+ LspShipmentUtils.getOrCreateShipmentPlan(lsp.getSelectedPlan(), lspShipment.getId())
+ .getPlanElements()
+ .values());
+ elementList.sort(LspShipmentUtils.createShipmentPlanElementComparator());
+ System.out.println("Shipment: " + lspShipment.getId());
+ for (LspShipmentPlanElement element : elementList) {
+ System.out.println(
+ element.getLogisticChainElement().getId()
+ + "\t\t"
+ + element.getResourceId()
+ + "\t\t"
+ + element.getElementType()
+ + "\t\t"
+ + element.getStartTime()
+ + "\t\t"
+ + element.getEndTime());
+ }
+ System.out.println();
+ }
+
+ // for (LSPResource lspResource : lsp.getResources()) {
+ // if (lspResource instanceof Carrier ) {
+ // ((Carrier) lspResource).getShipments().toString();
+ // }
+ // }
+ // the above cast keeps complaining when I refactor; in consequence, I am becoming doubtful if
+ // this condition can ever be satisfied.
+ // also not sure what the code stub might be doing: It is converting to string, but not doing
+ // anything with it. kai, may'22
+
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChainHubsVsDirect.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChainHubsVsDirect.java
new file mode 100644
index 00000000000..d2062b18450
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleSchedulingOfTransportChainHubsVsDirect.java
@@ -0,0 +1,624 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.initialPlans;
+
+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.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+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.RandomPlanSelector;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.carriers.controler.CarrierControlerUtils;
+import org.matsim.freight.carriers.controler.CarrierStrategyManager;
+import org.matsim.freight.carriers.events.CarrierServiceEndEvent;
+import org.matsim.freight.carriers.events.CarrierTourEndEvent;
+import org.matsim.freight.carriers.events.eventhandler.CarrierServiceEndEventHandler;
+import org.matsim.freight.carriers.events.eventhandler.CarrierTourEndEventHandler;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.examples.lspReplanning.AssignmentStrategyFactory;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/**
+ * The LSP have to possibilities to send the goods from the first depot to the recipients: A) via
+ * another hub and then distributed or B) directly from the depot
+ *
+ *
This examples bases on the Example ExampleSchedulingOfTransportChain.class -- the collection
+ * Part is removed, Chain is now starting at the CollectionHub
+ *
+ *
Scheduler = Macht die Pläne für die Fahrzeuge für die nächste MATSim-Iteration. Er plant es
+ * für jede Ressource. --> jede Ressource hat einen eigenen Scheduler: 1.) Simple: Nimm die
+ * mitgegebene Reihenfolge. 2.)
+ */
+/*package-private*/ final class ExampleSchedulingOfTransportChainHubsVsDirect {
+
+ private static final Logger log =
+ LogManager.getLogger(ExampleSchedulingOfTransportChainHubsVsDirect.class);
+
+ private ExampleSchedulingOfTransportChainHubsVsDirect() {} // so it cannot be instantiated
+
+ public static void main(String[] args) {
+
+ final SolutionType solutionType;
+
+ for (String arg : args) {
+ log.warn(arg);
+ }
+
+ // Set up required MATSim classes
+
+ Config config = ConfigUtils.createConfig();
+ if (args.length != 0) {
+ for (String arg : args) {
+ log.warn(arg);
+ }
+ ConfigUtils.applyCommandline(config, args);
+
+ CommandLine cmd = ConfigUtils.getCommandLine(args);
+ solutionType = SolutionType.valueOf(cmd.getOption("solutionType").orElseThrow());
+
+ } else {
+ solutionType = SolutionType.onePlan_direct;
+ log.warn("SolutionType was set in code to: {}", solutionType);
+ config.controller().setOutputDirectory("output/ChainVsDirect/" + solutionType);
+ config.controller().setLastIteration(2);
+ }
+ config
+ .controller()
+ .setOverwriteFileSetting(
+ OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists);
+ // 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
+ config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn);
+
+ var freightConfig = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+
+ log.warn("solutionType= {}", solutionType);
+
+ config.network().setInputFile(IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "logistics-2regions" ), "2regions-network.xml" ).getFile());
+
+ log.info("Starting ...");
+ log.info("Set up required MATSim classes");
+
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ // ########
+
+ log.info("create LSP");
+ LSP lsp = createInitialLSP(scenario, solutionType);
+
+ log.info("create initial LSPShipments");
+ log.info("assign the shipments to the LSP");
+ for (LspShipment lspShipment : createInitialLSPShipments(scenario.getNetwork())) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ log.info("schedule the LSP with the shipments and according to the scheduler of the Resource");
+ lsp.scheduleLogisticChains();
+
+ log.info("Set up simulation controler and LSPModule");
+ LSPUtils.addLSPs(scenario, new LSPs(Collections.singletonList(lsp)));
+
+ // @KMT: LSPModule ist vom Design her nur im Zusammenhang mit dem Controler sinnvoll. Damit kann
+ // man dann auch vollständig auf
+ // Injection setzen.
+
+ Controler controler = new Controler(scenario);
+ controler.addOverridingModule(new LSPModule());
+ controler.addOverridingModule(
+ new AbstractModule() {
+ @Override
+ public void install() {
+ bind(LSPScorerFactory.class).toInstance(MyLSPScorer::new);
+
+ // bind( LSPStrategyManager.class ).toInstance( new
+ // LSPModule.LSPStrategyManagerEmptyImpl() );
+ // The above means there will be no replanning. The below needs at least one strategy
+ // to be happy. kai, jul'22
+ bind(LSPStrategyManager.class)
+ .toProvider(
+ () -> {
+ LSPStrategyManager strategyManager = new LSPStrategyManagerImpl();
+ strategyManager.addStrategy(
+ new AssignmentStrategyFactory().createStrategy(), null, 1);
+ return strategyManager;
+ });
+ bind(CarrierStrategyManager.class)
+ .toProvider(
+ () -> {
+ CarrierStrategyManager strategyManager =
+ CarrierControlerUtils.createDefaultCarrierStrategyManager();
+ strategyManager.addStrategy(
+ new GenericPlanStrategyImpl<>(new RandomPlanSelector<>()), null, 1);
+ return strategyManager;
+ });
+ }
+ });
+
+ log.info("Run MATSim");
+
+ controler.run();
+
+ // print the schedules for the assigned LSPShipments
+ log.info("print the schedules for the assigned LSPShipments");
+ ResourceImplementationUtils.printResults_shipmentPlan(
+ config.controller().getOutputDirectory(), lsp);
+
+ log.info("Done.");
+ }
+
+ private static LSP createInitialLSP(Scenario scenario, SolutionType solutionType) {
+
+ Network network = scenario.getNetwork();
+
+ LSPUtils.LSPBuilder lspBuilder =
+ switch (solutionType) {
+ case onePlan_withHub -> LSPUtils.LSPBuilder.getInstance(
+ Id.create("LSPwithReloading", LSP.class));
+ case onePlan_direct, twoPlans_directAndHub -> LSPUtils.LSPBuilder.getInstance(Id.create("LSPdirect", LSP.class));
+ };
+
+ // lspBuilder.setSolutionScorer(new MyLSPScorer());
+
+ final Id depotLinkId =
+ Id.createLinkId("(4 2) (4 3)"); // TODO: Hochziehen aber non-static.
+ final Id hubLinkId = Id.createLinkId("(14 2) (14 3)");
+
+ LogisticChainElement depotElement;
+ {
+ // The SolutionElement for the first reloading point is created
+
+ log.info("");
+ log.info("Create depot");
+
+ // The scheduler for the first reloading point is created --> this will be the depot in this
+ // use case
+ LSPResourceScheduler depotScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10) // Time needed, fixed (for Scheduler)
+ .setCapacityNeedLinear(1) // additional time needed per shipmentSize (for Scheduler)
+ .build();
+
+ // The scheduler is added to the Resource and the Resource is created
+ LSPResource depotResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ Id.create("Depot", LSPResource.class), depotLinkId, scenario)
+ .setTransshipmentHubScheduler(depotScheduler)
+ .build();
+
+ depotElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("DepotElement", LogisticChainElement.class))
+ .setResource(depotResource)
+ .build(); // Nicht unbedingt nötig, aber nehme den alten Hub nun als Depot. Waren
+ // werden dann dort "Zusammengestellt".
+ // Maybe TODO: Depot als LogisticSolutionElement raus nehmen.(?)
+ }
+
+ // The LogisticsSolutionElement for the main run Resource is created
+ LogisticChainElement mainRunElement;
+ {
+ log.info("");
+ log.info("The Carrier for the main run is created");
+ Carrier mainRunCarrier =
+ CarriersUtils.createCarrier(Id.create("MainRunCarrier", Carrier.class));
+
+ Id mainRunCarrierVehicleType = Id.create("MainRunCarrierVehicleType", VehicleType.class);
+ VehicleType mainRunVehType = VehicleUtils.createVehicleType(mainRunCarrierVehicleType, TransportMode.car);
+ mainRunVehType.getCapacity().setOther(30);
+ mainRunVehType.getCostInformation().setCostsPerMeter(0.0002);
+ mainRunVehType.getCostInformation().setCostsPerSecond(0.38);
+ mainRunVehType.getCostInformation().setFixedCost(120.);
+ mainRunVehType.setMaximumVelocity(50 / 3.6);
+ mainRunVehType.setNetworkMode(TransportMode.car);
+
+ CarrierVehicle mainRunCarrierVehicle =
+ CarrierVehicle.Builder.newInstance(
+ Id.createVehicleId("MainRunVehicle"), depotLinkId, mainRunVehType)
+ .build();
+
+ mainRunCarrier.setCarrierCapabilities(
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(mainRunCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build());
+
+ // The scheduler for the main run Resource is created and added to the Resource
+ LSPResource mainRunResource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(mainRunCarrier)
+ .setFromLinkId(depotLinkId)
+ .setToLinkId(hubLinkId)
+ .setMainRunCarrierScheduler(
+ ResourceImplementationUtils.createDefaultMainRunCarrierScheduler(scenario))
+ .build();
+
+ mainRunElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("MainRunElement", LogisticChainElement.class))
+ .setResource(mainRunResource)
+ .build();
+ }
+
+ LogisticChainElement hubElement;
+ {
+ log.info("");
+ log.info("The second reloading adapter (hub) i.e. the Resource is created");
+ // The scheduler for the second reloading point is created
+ LSPResourceScheduler hubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10)
+ .setCapacityNeedLinear(1)
+ .build();
+
+ // The scheduler is added to the Resource and the Resource is created
+ // The second reloading adapter i.e. the Resource is created
+ Id secondTransshipmentHubId = Id.create("TranshipmentHub2", LSPResource.class);
+ LSPResource hubResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ secondTransshipmentHubId, hubLinkId, scenario)
+ .setTransshipmentHubScheduler(hubScheduler)
+ .build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ hubElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("SecondHubElement", LogisticChainElement.class))
+ .setResource(hubResource)
+ .build();
+ }
+
+ LogisticChainElement distributionElement;
+ {
+ // The Carrier for distribution from reloading Point is created
+ VehicleType distributionVehType =
+ createCarrierVehicleType("DistributionCarrierVehicleType");
+
+ CarrierVehicle distributionCarrierVehicle =
+ CarrierVehicle.Builder.newInstance(
+ Id.createVehicleId("DistributionVehicle"), hubLinkId, distributionVehType)
+ .build();
+
+ Carrier distributionCarrier =
+ CarriersUtils.createCarrier(Id.create("DistributionCarrier", Carrier.class));
+ distributionCarrier.setCarrierCapabilities(
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(distributionCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build());
+
+ // The distribution adapter i.e. the Resource is created
+ LSPResource distributionResource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier)
+ .setLocationLinkId(hubLinkId)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+ // (The scheduler is where jsprit comes into play.)
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+
+ distributionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("DistributionElement", LogisticChainElement.class))
+ .setResource(distributionResource)
+ .build();
+ }
+
+ // ### New (KMT): Carrier for direct distribution from Depot (without 2nd reloading Point)
+ LogisticChainElement directDistributionElement;
+ {
+ // The Carrier for distribution from reloading Point is created
+ VehicleType directdistributionVehType =
+ createCarrierVehicleType("DirectDistributionCarrierVehicleType");
+
+ CarrierVehicle directDistributionCarrierVehicle =
+ CarrierVehicle.Builder.newInstance(
+ Id.createVehicleId("DirectDistributionVehicle"),
+ depotLinkId,
+ directdistributionVehType)
+ .build();
+
+ CarrierCapabilities directDistributionCarrierCapabilities =
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(directDistributionCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+ Carrier directDistributionCarrier =
+ CarriersUtils.createCarrier(Id.create("DirectDistributionCarrier", Carrier.class));
+ directDistributionCarrier.setCarrierCapabilities(directDistributionCarrierCapabilities);
+
+ // The distribution adapter i.e. the Resource is created
+ LSPResource directDistributionResource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ directDistributionCarrier)
+ .setLocationLinkId(depotLinkId)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+ // (The scheduler is where jsprit comes into play.)
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ directDistributionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("DirectDistributionElement", LogisticChainElement.class))
+ .setResource(directDistributionResource)
+ .build();
+ }
+ // ### end new
+
+ // TODO: Beide Lösungen anbieten und "bessere" oder zunächst "eine" auswählen"
+
+ // TODO: Für die Auswahl "CostInfo an die Solutions dran heften.
+
+ // The SolutionElements are now inserted into the only LogisticsSolution of the LSP.
+ // Die Reihenfolge des Hinzufügens ist egal, da weiter oben die jeweils direkten
+ // Vorgänger/Nachfolger bestimmt wurden.
+
+ switch (solutionType) {
+ case onePlan_withHub -> {
+ // ### This is the original solution with mainRun - TranshipmentHub - distributionRun
+ log.info("Creating LSP with one plan: reloading at hub");
+
+ LSPPlan lspPlan_Reloading =
+ createLSPPlan_reloading(depotElement, mainRunElement, hubElement, distributionElement);
+
+ return lspBuilder
+ .setInitialPlan(lspPlan_Reloading)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlan(lspPlan_Reloading)))
+ .build();
+ }
+ case onePlan_direct -> {
+ // ### This is the new solution with directDistribution from the Depot.
+ log.info("Creating LSP with one plan: direct distribution from the depot");
+
+ LSPPlan lspPlan_direct = createLSPPlan_direct(depotElement, directDistributionElement);
+
+ return lspBuilder
+ .setInitialPlan(lspPlan_direct)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlan(lspPlan_direct)))
+ .build();
+ }
+ case twoPlans_directAndHub -> {
+ log.info(
+ "Creating LSP with two plans: i) direct distribution from the depot ii) reloading at hub");
+
+ log.error(
+ "This is totally untested. I can neither say if it will work nor if it will do anything useful - kmt feb22");
+
+ // TODO: Habe das vorziehen vor das switch statement rückgängig gemacht, weil es sideeffekte
+ // hatte -> Die dürften hier auch sein!!!! (KMT may22)
+ // Die createLSPPlan_reloading(..) Methoden sind nicht unabhängig voneinander.
+ // Das liegt wohl am statischen und das dann dort wieder Verknüpfungen gesetzt werden -->
+ // Hier auch aufpassen
+ LSPPlan lspPlan_Reloading =
+ createLSPPlan_reloading(depotElement, mainRunElement, hubElement, distributionElement);
+ LSPPlan lspPlan_direct = createLSPPlan_direct(depotElement, directDistributionElement);
+
+ // TODO: Müsste nicht eigentlich der SolutionScheduler dann auf Ebene der einzelnen Pläne
+ // (mit ihren Solutions) sein?? kmt Feb22
+ // So muss ich erst die Ressourcen aus beiden hinzuzufügenden Plänen aufsammeln, damit die
+ // dann schon in den Builder können. Irgendwie unschön.
+ List resourcesList = createResourcesListFromLSPPlan(lspPlan_direct);
+ resourcesList.addAll(createResourcesListFromLSPPlan(lspPlan_Reloading));
+
+ final LSP lsp =
+ lspBuilder
+ .setInitialPlan(lspPlan_direct)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ resourcesList))
+ .build();
+
+ lsp.addPlan(lspPlan_Reloading); // adding the second plan
+
+ return lsp;
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + solutionType);
+ }
+ }
+
+ private static List createResourcesListFromLSPPlan(LSPPlan lspPlanWithReloading) {
+ log.info("Collecting all LSPResources from the LSPPlan");
+ List resourcesList =
+ new ArrayList<>(); // TODO: Mahe daraus ein Set, damit jede Resource nur einmal drin ist?
+ // kmt Feb22
+ for (LogisticChain solution : lspPlanWithReloading.getLogisticChains()) {
+ for (LogisticChainElement solutionElement : solution.getLogisticChainElements()) {
+ resourcesList.add(solutionElement.getResource());
+ }
+ }
+ return resourcesList;
+ }
+
+ private static LSPPlan createLSPPlan_direct(
+ LogisticChainElement depotElement, LogisticChainElement directDistributionElement) {
+ log.info("");
+ log.info("The order of the logisticsSolutionElements is now specified");
+ depotElement.connectWithNextElement(directDistributionElement);
+
+ log.info("");
+ log.info("set up logistic Solution - direct distribution from the depot is created");
+
+ LogisticChain completeSolutionDirect =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("SolutionDirectId", LogisticChain.class))
+ .addLogisticChainElement(depotElement)
+ .addLogisticChainElement(directDistributionElement)
+ .build();
+
+ log.info("");
+ log.info(
+ "The initial plan of the lsp is generated and the assigner and the solution from above are added");
+
+ return LSPUtils.createLSPPlan()
+ .setInitialShipmentAssigner(ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner())
+ .addLogisticChain(completeSolutionDirect);
+ }
+
+ private static LSPPlan createLSPPlan_reloading(
+ LogisticChainElement depotElement,
+ LogisticChainElement mainRunElement,
+ LogisticChainElement hubElement,
+ LogisticChainElement distributionElement) {
+ log.info("");
+ log.info("set up logistic Solution - original with hub usage solution is created");
+
+ // Das ist wichtig, damit er die Kette zur Verfügung hat.
+ depotElement.connectWithNextElement(mainRunElement);
+ mainRunElement.connectWithNextElement(hubElement);
+ hubElement.connectWithNextElement(distributionElement);
+
+ LogisticChain completeSolutionWithReloading =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("SolutionWithTransshipmentHubId", LogisticChain.class))
+ .addLogisticChainElement(depotElement)
+ .addLogisticChainElement(mainRunElement)
+ .addLogisticChainElement(hubElement)
+ .addLogisticChainElement(distributionElement)
+ .build();
+
+ log.info("");
+ log.info(
+ "The initial plan of the lsp is generated and the assigner and the solution from above are added");
+
+ return LSPUtils.createLSPPlan()
+ .setInitialShipmentAssigner(ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner())
+ .addLogisticChain(completeSolutionWithReloading);
+ }
+
+ private static VehicleType createCarrierVehicleType(String vehicleTypeId) {
+ VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create(vehicleTypeId, VehicleType.class), TransportMode.car);
+ vehicleType.getCapacity().setOther(10);
+ vehicleType.getCostInformation().setCostsPerMeter(0.0004);
+ vehicleType.getCostInformation().setCostsPerSecond(0.38);
+ vehicleType.getCostInformation().setFixedCost(49.);
+ vehicleType.setMaximumVelocity(50 / 3.6);
+ vehicleType.setNetworkMode(TransportMode.car);
+
+ return vehicleType;
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ ArrayList shipmentList = new ArrayList<>();
+ ArrayList linkList = new ArrayList<>(network.getLinks().values());
+ Random rand = new Random(1);
+ for (int i = 1; i < 6; i++) {
+ Id id = Id.create(i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+ int capacityDemand = rand.nextInt(10);
+ builder.setCapacityDemand(capacityDemand);
+
+ while (true) {
+ Collections.shuffle(linkList, rand);
+ Link pendingToLink = linkList.getFirst();
+ if ((pendingToLink.getFromNode().getCoord().getX() <= 18000
+ && pendingToLink.getFromNode().getCoord().getY() <= 4000
+ && pendingToLink.getFromNode().getCoord().getX() >= 14000
+ && pendingToLink.getToNode().getCoord().getX() <= 18000
+ && pendingToLink.getToNode().getCoord().getY() <= 4000
+ && pendingToLink.getToNode().getCoord().getX() >= 14000)) {
+ builder.setToLinkId(pendingToLink.getId());
+ break;
+ }
+ }
+
+ builder.setFromLinkId(
+ Id.createLinkId(
+ "(4 2) (4 3)")); // Here was the "first" reloading Point, now called depot.
+
+ TimeWindow endTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setEndTimeWindow(endTimeWindow);
+ TimeWindow startTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setStartTimeWindow(startTimeWindow);
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+ LspShipment lspShipment = builder.build();
+ shipmentList.add(lspShipment);
+ }
+ return shipmentList;
+ }
+
+ enum SolutionType {
+ onePlan_withHub,
+ onePlan_direct,
+ twoPlans_directAndHub
+ }
+
+ private static class MyLSPScorer
+ implements LSPScorer, CarrierTourEndEventHandler, CarrierServiceEndEventHandler {
+ private double score = 0.;
+
+ @Override
+ public double getScoreForCurrentPlan() {
+ return score;
+ }
+
+ @Override
+ public void setEmbeddingContainer(LSP pointer) {}
+
+ @Override
+ public void handleEvent(CarrierTourEndEvent event) {
+ score++;
+ // use event handlers to compute score. In this case, score is incremented by one every time
+ // a CarrierService and a tour ends.
+ }
+
+ @Override
+ public void reset(int iteration) {
+ score = 0.;
+ }
+
+ @Override
+ public void handleEvent(CarrierServiceEndEvent event) {
+ score++;
+ // use event handlers to compute score. In this case, score is incremented by one every time
+ // a CarrierService and a tour ends.
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTestOutput.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTestOutput.java
new file mode 100644
index 00000000000..8a0fbd8ce12
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTestOutput.java
@@ -0,0 +1,60 @@
+/*
+ *********************************************************************** *
+ * 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.initialPlans;
+
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.groups.VspExperimentalConfigGroup;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+
+class ExampleTestOutput {
+
+ public static void main(String[] args) {
+
+ Config config =
+ ConfigUtils.loadConfig(
+ IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("equil"), "config.xml"));
+
+ config
+ .controller()
+ .setOverwriteFileSetting(
+ OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists);
+ config.controller().setLastIteration(1);
+
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ Controler controler = new Controler(scenario);
+
+ // 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();
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid.java
new file mode 100644
index 00000000000..a776131e925
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid.java
@@ -0,0 +1,537 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.initialPlans;
+
+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.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.groups.VspExperimentalConfigGroup;
+import org.matsim.core.controler.AbstractModule;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.gbl.MatsimRandom;
+import org.matsim.core.replanning.GenericPlanStrategyImpl;
+import org.matsim.core.replanning.selectors.BestPlanSelector;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+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.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/**
+ * This is an academic example for the 2-echelon problem. It uses the 9x9-grid network from the
+ * matsim-examples.
+ *
+ *
The depot is located at the outer border of the network, while the jobs are located in the
+ * middle area. The {@link LSP} has two different {@link LSPPlan}s: 1) direct delivery from the
+ * depot 2) Using a TransshipmentHubResource: All goods were brought from the depot to the hub,
+ * reloaded and then brought from the hub to the customers
+ *
+ *
The decision which of these plans is chosen should be made via the Score of the plans. We will
+ * modify the costs of the vehicles and/or for using(having) the Transshipment hub. Depending on
+ * this setting, the plan selection should be done accordingly.
+ *
+ *
Please note: This example is in part on existing examples, but I start from the scratch for a)
+ * see, if this works and b) have a "clean" class :)
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+final class ExampleTwoEchelonGrid {
+
+ // Run Settings
+ static final double HUBCOSTS_FIX = 100;
+ private static final DemandSetting demandSetting = DemandSetting.oneCustomer;
+ private static final CarrierCostSetting costSetting = CarrierCostSetting.lowerCost4LastMile;
+ private static final double TOLL_VALUE = 1000;
+
+ private static final Logger log = LogManager.getLogger(ExampleTwoEchelonGrid.class);
+
+ private static final Id DEPOT_LINK_ID = Id.createLinkId("i(5,0)");
+ private static final Id HUB_LINK_ID = Id.createLinkId("j(5,3)");
+ private static final VehicleType VEH_TYPE_LARGE_50 = createVehTypeLarge50();
+ private static final VehicleType VEH_TYPE_SMALL_05 = createVehTypeSmall05();
+
+
+ private ExampleTwoEchelonGrid() {} // so it cannot be instantiated
+
+ public static void main(String[] args) {
+ log.info("Prepare Config");
+ Config config = prepareConfig(args);
+
+ log.info("Prepare scenario");
+ Scenario scenario = prepareScenario(config);
+
+ 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() {
+ // bind( CarrierScoringFunctionFactory.class ).toInstance( new MyCarrierScorer());
+ final MyEventBasedCarrierScorer carrierScorer = new MyEventBasedCarrierScorer();
+ carrierScorer.setToll(TOLL_VALUE);
+
+ bind(CarrierScoringFunctionFactory.class).toInstance(carrierScorer);
+ 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 BestPlanSelector<>()), null, 1);
+ return strategyManager;
+ });
+ bind(LSPScorerFactory.class).toInstance(MyLSPScorer::new);
+ }
+ });
+
+ log.info("Run MATSim");
+ log.warn("Runs settings were: Demand: {}\n CarrierCosts: {}\n HubCosts: " + HUBCOSTS_FIX + "\n tollValue: " + TOLL_VALUE, demandSetting, costSetting);
+
+ // 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("Some results ....");
+
+ for (LSP lsp : LSPUtils.getLSPs(controler.getScenario()).getLSPs().values()) {
+ ResourceImplementationUtils.printScores(controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printShipmentsOfLSP(
+ controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printResults_shipmentPlan(
+ controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printResults_shipmentLog(
+ controler.getControlerIO().getOutputPath(), lsp);
+ }
+ log.info("Done.");
+ }
+
+ 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);
+
+ CommandLine cmd = ConfigUtils.getCommandLine(args);
+ } else {
+ config
+ .controller()
+ .setOutputDirectory(
+ "output/2echelon_"
+ + demandSetting
+ + "_"
+ + costSetting
+ + "_"
+ + HUBCOSTS_FIX
+ + "_"
+ + TOLL_VALUE);
+ config.controller().setLastIteration(2);
+ }
+
+ config
+ .network()
+ .setInputFile(
+ String.valueOf(
+ IOUtils.extendUrl(
+ ExamplesUtils.getTestScenarioURL("freight-chessboard-9x9"), "grid9x9.xml")));
+ config
+ .controller()
+ .setOverwriteFileSetting(
+ OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists);
+ config.controller().setWriteEventsInterval(1);
+
+ FreightCarriersConfigGroup freightConfig =
+ ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+
+ return config;
+ }
+
+ private static Scenario prepareScenario(Config config) {
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ // Change speed on all links to 30 km/h (8.33333 m/s) for easier computation --> Freeflow TT per
+ // link is 2min
+ for (Link link : scenario.getNetwork().getLinks().values()) {
+ link.setFreespeed(30 / 3.6);
+ link.setCapacity(1000);
+ }
+
+ log.info("Add LSP to the scenario");
+ LSPUtils.addLSPs(scenario, new LSPs(Collections.singletonList(createLSP(scenario))));
+
+ return scenario;
+ }
+
+ private static LSP createLSP(Scenario scenario) {
+ log.info("create LSP");
+ Network network = scenario.getNetwork();
+
+ LSPPlan lspPlan_direct;
+ {
+ log.info("Create lspPlan for direct delivery");
+
+ Carrier directCarrier =
+ CarriersUtils.createCarrier(Id.create("directCarrier", Carrier.class));
+ directCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ directCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("directTruck"), DEPOT_LINK_ID, VEH_TYPE_LARGE_50));
+ LSPResource directCarrierRessource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ directCarrier)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+
+ LogisticChainElement directCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("directCarrierLSE", LogisticChainElement.class))
+ .setResource(directCarrierRessource)
+ .build();
+
+ LogisticChain solution_direct =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("directSolution", LogisticChain.class))
+ .addLogisticChainElement(directCarrierElement)
+ .build();
+
+ final InitialShipmentAssigner singleSolutionShipmentAssigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ lspPlan_direct =
+ LSPUtils.createLSPPlan()
+ .addLogisticChain(solution_direct)
+ .setInitialShipmentAssigner(singleSolutionShipmentAssigner);
+ }
+
+ LSPPlan lspPlan_withHub;
+ {
+ log.info("Create lspPlan with Hub");
+
+ Carrier mainCarrier = CarriersUtils.createCarrier(Id.create("mainCarrier", Carrier.class));
+ mainCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ mainCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("mainTruck"), DEPOT_LINK_ID, VEH_TYPE_LARGE_50));
+ LSPResource mainCarrierRessource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(mainCarrier)
+ .setFromLinkId(DEPOT_LINK_ID)
+ .setMainRunCarrierScheduler(
+ ResourceImplementationUtils.createDefaultMainRunCarrierScheduler(scenario))
+ .setToLinkId(HUB_LINK_ID)
+ .setVehicleReturn(ResourceImplementationUtils.VehicleReturn.returnToFromLink)
+ .build();
+
+ LogisticChainElement mainCarrierLSE =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("mainCarrierLSE", LogisticChainElement.class))
+ .setResource(mainCarrierRessource)
+ .build();
+
+ // The scheduler for the first reloading point is created --> this will be the depot in this
+ // use case
+ LSPResourceScheduler hubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10) // Time needed, fixed (for Scheduler)
+ .setCapacityNeedLinear(1) // additional time needed per shipmentSize (for Scheduler)
+ .build();
+
+ // The scheduler is added to the Resource and the Resource is created
+ LSPResource hubResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ Id.create("Hub", LSPResource.class), HUB_LINK_ID, scenario)
+ .setTransshipmentHubScheduler(hubScheduler)
+ .build();
+ LSPUtils.setFixedCost(
+ hubResource, HUBCOSTS_FIX); // Set fixed costs (per day) for the availability of the hub.
+
+ LogisticChainElement hubLSE =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("HubLSE", LogisticChainElement.class))
+ .setResource(hubResource)
+ .build(); // Nicht unbedingt nötig, aber nehme den alten Hub nun als Depot. Waren
+ // werden dann dort "Zusammengestellt".
+
+ Carrier distributionCarrier =
+ CarriersUtils.createCarrier(Id.create("distributionCarrier", Carrier.class));
+ distributionCarrier
+ .getCarrierCapabilities()
+ .setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ final VehicleType vehType;
+ switch (costSetting) {
+ case sameCost -> vehType = VEH_TYPE_LARGE_50;
+ case lowerCost4LastMile -> vehType = VEH_TYPE_SMALL_05;
+ default -> throw new IllegalStateException("Unexpected value: " + costSetting);
+ }
+ CarriersUtils.addCarrierVehicle(
+ distributionCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("distributionTruck"), HUB_LINK_ID, vehType));
+ LSPResource distributionCarrierRessource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+
+ LogisticChainElement distributionCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("distributionCarrierLSE", LogisticChainElement.class))
+ .setResource(distributionCarrierRessource)
+ .build();
+
+ // Kettenbildung per hand, damit dann klar ist, wie das Scheduling ablaufen soll. TODO:
+ // Vielleicht bekommt man das noch eleganter hin.
+ // z.B. in der Reihenfolge in der die solutionsElements der LogisticsSolution zugeordnet
+ // werden: ".addSolutionElement(..)"
+ mainCarrierLSE.connectWithNextElement(hubLSE);
+ hubLSE.connectWithNextElement(distributionCarrierElement);
+
+ LogisticChain solution_withHub =
+ LSPUtils.LogisticChainBuilder.newInstance(Id.create("hubSolution", LogisticChain.class))
+ .addLogisticChainElement(mainCarrierLSE)
+ .addLogisticChainElement(hubLSE)
+ .addLogisticChainElement(distributionCarrierElement)
+ .build();
+
+ lspPlan_withHub =
+ LSPUtils.createLSPPlan()
+ .addLogisticChain(solution_withHub)
+ .setInitialShipmentAssigner(ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner());
+ }
+
+ // Todo: Auch das ist wirr: Muss hier alle sammeln, damit man die dann im LSPBuilder dem
+ // SolutionScheduler mitgeben kann. Im Nachgang packt man dann aber erst den zweiten Plan dazu
+ // ... urgs KMT'Jul22
+ List lspPlans = new ArrayList<>();
+ lspPlans.add(lspPlan_withHub);
+ lspPlans.add(lspPlan_direct);
+
+ LSP lsp =
+ LSPUtils.LSPBuilder.getInstance(Id.create("myLSP", LSP.class))
+ .setInitialPlan(lspPlan_direct)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlans(lspPlans)))
+ .build();
+ lsp.addPlan(lspPlan_withHub); // add the second plan to the lsp
+
+ log.info("create initial LSPShipments");
+ log.info("assign the shipments to the LSP");
+ for (LspShipment lspShipment : createInitialLSPShipments(network)) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ log.info("schedule the LSP with the shipments and according to the scheduler of the Resource");
+ lsp.scheduleLogisticChains();
+
+ return lsp;
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ List shipmentList = new ArrayList<>();
+
+ switch (demandSetting) {
+ case oneCustomer -> {
+ Id id = Id.create("Shipment_" + 1, LspShipment.class);
+ int capacityDemand = 1;
+
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+
+ builder.setCapacityDemand(capacityDemand);
+ builder.setFromLinkId(DEPOT_LINK_ID);
+ builder.setToLinkId(Id.createLinkId("i(5,5)R"));
+ builder.setEndTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setStartTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+
+ shipmentList.add(builder.build());
+
+ return shipmentList;
+ }
+ case tenCustomers -> {
+ Random rand1 = MatsimRandom.getLocalInstance();
+ Random rand2 = MatsimRandom.getLocalInstance();
+
+ List zoneLinkList =
+ Arrays.asList(
+ "i(4,4)", "i(5,4)", "i(6,4)", "i(4,6)", "i(5,6)", "i(6,6)", "j(3,5)", "j(3,6)",
+ "j(3,7)", "j(5,5)", "j(5,6)", "j(5,7)", "i(4,5)R", "i(5,5)R", "i(6,5)R", "i(4,7)R",
+ "i(5,7)R", "i(6,7)R", "j(4,5)R", "j(4,6)R", "j(4,7)R", "j(6,5)R", "j(6,6)R",
+ "j(6,7)R");
+ for (String linkIdString : zoneLinkList) {
+ if (!network.getLinks().containsKey(Id.createLinkId(linkIdString))) {
+ throw new RuntimeException("Link is not in Network!");
+ }
+ }
+
+ for (int i = 1; i <= 10; i++) {
+ Id id = Id.create("Shipment_" + i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder =
+ LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+
+ int capacityDemand =
+ rand1.nextInt(5) + 1; // Random is drawn from 0 (incl) to bound (excl) -> adding 1.
+ builder.setCapacityDemand(capacityDemand);
+
+ builder.setFromLinkId(DEPOT_LINK_ID);
+ final Id toLinkId =
+ Id.createLinkId(zoneLinkList.get(rand2.nextInt(zoneLinkList.size() - 1)));
+ builder.setToLinkId(toLinkId);
+
+ builder.setEndTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setStartTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + demandSetting);
+ }
+ }
+
+ // TODO: This is maybe something that can go into a utils class ... KMT jul22
+ private static List createResourcesListFromLSPPlans(List lspPlans) {
+ log.info("Collecting all LSPResources from the LSPPlans");
+ List resourcesList =
+ new ArrayList<>(); // TODO: Mache daraus ein Set, damit jede Resource nur einmal drin ist?
+ // kmt Feb22
+ for (LSPPlan lspPlan : lspPlans) {
+ for (LogisticChain solution : lspPlan.getLogisticChains()) {
+ for (LogisticChainElement solutionElement : solution.getLogisticChainElements()) {
+ resourcesList.add(solutionElement.getResource());
+ }
+ }
+ }
+ return resourcesList;
+ }
+
+ enum DemandSetting {
+ oneCustomer,
+ tenCustomers
+ }
+
+ enum CarrierCostSetting {
+ sameCost,
+ lowerCost4LastMile
+ }
+
+
+ private static VehicleType createVehTypeLarge50() {
+ VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create("large50", VehicleType.class), TransportMode.car);
+ vehicleType.getCapacity().setOther(50);
+ vehicleType.getCostInformation().setCostsPerMeter(0.01);
+ vehicleType.getCostInformation().setCostsPerSecond(0.01);
+ vehicleType.getCostInformation().setFixedCost(150.);
+ vehicleType.setMaximumVelocity(10);
+ vehicleType.setNetworkMode(TransportMode.car);
+
+ return vehicleType;
+ }
+
+ private static VehicleType createVehTypeSmall05() {
+ VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create("small05", VehicleType.class), TransportMode.car);
+ vehicleType.getCapacity().setOther(5);
+ vehicleType.getCostInformation().setCostsPerMeter(0.001);
+ vehicleType.getCostInformation().setCostsPerSecond(0.005);
+ vehicleType.getCostInformation().setFixedCost(25.);
+ vehicleType.setMaximumVelocity(10);
+ vehicleType.setNetworkMode(TransportMode.car);
+
+ return vehicleType;
+ }
+
+ // @Override public ScoringFunction createScoringFunction(Carrier carrier ){
+ //
+ // return new ScoringFunction(){
+ //
+ // private double score;
+ //
+ // @Override public void handleActivity( Activity activity ){
+ // score--;
+ // }
+ // @Override public void handleLeg( Leg leg ){
+ // score = score - 10;
+ // }
+ // @Override public void agentStuck( double time ){
+ // }
+ // @Override public void addMoney( double amount ){
+ // }
+ // @Override public void addScore( double amount ){
+ // }
+ // @Override public void finish(){
+ // }
+ // @Override public double getScore(){
+ // return score;
+ // }
+ // @Override public void handleEvent( Event event ){
+ // score = score - 0.01;
+ // }
+ // };
+ // }
+ // }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid_NR.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid_NR.java
new file mode 100644
index 00000000000..18525885ce7
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/ExampleTwoEchelonGrid_NR.java
@@ -0,0 +1,546 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.initialPlans;
+
+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.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.groups.VspExperimentalConfigGroup;
+import org.matsim.core.controler.AbstractModule;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.gbl.MatsimRandom;
+import org.matsim.core.replanning.GenericPlanStrategyImpl;
+import org.matsim.core.replanning.selectors.BestPlanSelector;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+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.io.LSPPlanXmlReader;
+import org.matsim.freight.logistics.io.LSPPlanXmlWriter;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/**
+ * This is an academic example for the 2-echelon problem. It uses the 9x9-grid network from the
+ * matsim-examples.
+ *
+ *
The depot is located at the outer border of the network, while the jobs are located in the
+ * middle area. The {@link LSP} has two different {@link LSPPlan}s: 1) direct delivery from the
+ * depot 2) Using a TransshipmentHubResource: All goods were brought from the depot to the hub,
+ * reloaded and then brought from the hub to the customers
+ *
+ *
The decision which of these plans is chosen should be made via the Score of the plans. We will
+ * modify the costs of the vehicles and/or for using(having) the Transshipment hub. Depending on
+ * this setting, the plan selection should be done accordingly.
+ *
+ *
Please note: This example is in part on existing examples, but I start from the scratch for a)
+ * see, if this works and b) have a "clean" class :)
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+final class ExampleTwoEchelonGrid_NR {
+
+ // Run Settings
+ static final double HUBCOSTS_FIX = 100;
+ private static final DemandSetting demandSetting = DemandSetting.tenCustomers;
+ private static final CarrierCostSetting costSetting = CarrierCostSetting.lowerCost4LastMile;
+ private static final double TOLL_VALUE = 1000;
+
+ private static final Logger log = LogManager.getLogger(ExampleTwoEchelonGrid_NR.class);
+
+ private static final Id DEPOT_LINK_ID = Id.createLinkId("i(5,0)");
+ private static final Id HUB_LINK_ID = Id.createLinkId("j(5,3)");
+
+ private static final VehicleType VEH_TYPE_LARGE_50 = createVehTypeLarge50();
+ private static final VehicleType VEH_TYPE_SMALL_05 = createVehTypeSmall05();
+
+ private static VehicleType createVehTypeLarge50() {
+ VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create("large50", VehicleType.class), TransportMode.car);
+ vehicleType.getCapacity().setOther(50);
+ vehicleType.getCostInformation().setCostsPerMeter(0.01);
+ vehicleType.getCostInformation().setCostsPerSecond(0.01);
+ vehicleType.getCostInformation().setFixedCost(150.);
+ vehicleType.setMaximumVelocity(10);
+ vehicleType.setNetworkMode(TransportMode.car);
+
+ return vehicleType;
+ }
+
+ private static VehicleType createVehTypeSmall05() {
+ VehicleType vehicleType = VehicleUtils.createVehicleType(Id.create("small05", VehicleType.class), TransportMode.car);
+ vehicleType.getCapacity().setOther(5);
+ vehicleType.getCostInformation().setCostsPerMeter(0.001);
+ vehicleType.getCostInformation().setCostsPerSecond(0.005);
+ vehicleType.getCostInformation().setFixedCost(25.);
+ vehicleType.setMaximumVelocity(10);
+ vehicleType.setNetworkMode(TransportMode.car);
+
+ return vehicleType;
+ }
+
+ private ExampleTwoEchelonGrid_NR() {} // so it cannot be instantiated
+
+ public static void main(String[] args) {
+ log.info("Prepare Config");
+ Config config = prepareConfig(args);
+
+ log.info("Prepare scenario");
+ Scenario scenario = prepareScenario(config);
+
+ 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() {
+ // bind( CarrierScoringFunctionFactory.class ).toInstance( new MyCarrierScorer());
+ final MyEventBasedCarrierScorer carrierScorer = new MyEventBasedCarrierScorer();
+ carrierScorer.setToll(TOLL_VALUE);
+
+ bind(CarrierScoringFunctionFactory.class).toInstance(carrierScorer);
+ 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 BestPlanSelector<>()), null, 1);
+ return strategyManager;
+ });
+ bind(LSPScorerFactory.class).toInstance(MyLSPScorer::new);
+ }
+ });
+
+ log.info("Run MATSim");
+ log.warn("Runs settings were: Demand: {}\n CarrierCosts: {}\n HubCosts: " + HUBCOSTS_FIX + "\n tollValue: " + TOLL_VALUE, demandSetting, costSetting);
+ // 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();
+
+ // Ggf. muss der Ordner noch erstellt werden (?)
+ new LSPPlanXmlWriter(LSPUtils.getLSPs(controler.getScenario()))
+ .write(controler.getConfig().controller().getOutputDirectory() + "/lsps.xml");
+ new LSPPlanXmlReader(
+ LSPUtils.getLSPs(controler.getScenario()),
+ CarriersUtils.getCarriers(controler.getScenario()));
+ new CarrierPlanWriter(CarriersUtils.getCarriers(controler.getScenario()))
+ .write(controler.getConfig().controller().getOutputDirectory() + "/carriers.xml");
+
+ log.info("Some results ....");
+
+ for (LSP lsp : LSPUtils.getLSPs(controler.getScenario()).getLSPs().values()) {
+ ResourceImplementationUtils.printScores(controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printShipmentsOfLSP(
+ controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printResults_shipmentPlan(
+ controler.getControlerIO().getOutputPath(), lsp);
+ ResourceImplementationUtils.printResults_shipmentLog(
+ controler.getControlerIO().getOutputPath(), lsp);
+ }
+ log.info("Done.");
+ }
+
+ 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);
+
+ CommandLine cmd = ConfigUtils.getCommandLine(args);
+ } else {
+ config
+ .controller()
+ .setOutputDirectory(
+ "output/2echelon_"
+ + demandSetting
+ + "_"
+ + costSetting
+ + "_"
+ + HUBCOSTS_FIX
+ + "_"
+ + TOLL_VALUE);
+ config.controller().setLastIteration(2);
+ }
+
+ config
+ .network()
+ .setInputFile(
+ String.valueOf(
+ IOUtils.extendUrl(
+ ExamplesUtils.getTestScenarioURL("freight-chessboard-9x9"), "grid9x9.xml")));
+ config
+ .controller()
+ .setOverwriteFileSetting(
+ OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles);
+ config.controller().setWriteEventsInterval(1);
+
+ FreightCarriersConfigGroup freightConfig =
+ ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+
+ return config;
+ }
+
+ private static Scenario prepareScenario(Config config) {
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ // Change speed on all links to 30 km/h (8.33333 m/s) for easier computation --> Freeflow TT per
+ // link is 2min
+ for (Link link : scenario.getNetwork().getLinks().values()) {
+ link.setFreespeed(30 / 3.6);
+ link.setCapacity(1000);
+ }
+
+ log.info("Add LSP to the scenario");
+ LSPUtils.addLSPs(scenario, new LSPs(Collections.singletonList(createLSP(scenario))));
+
+ return scenario;
+ }
+
+ private static LSP createLSP(Scenario scenario) {
+ log.info("create LSP");
+ Network network = scenario.getNetwork();
+
+ LSPPlan lspPlan_direct;
+ {
+ log.info("Create lspPlan for direct delivery");
+
+ Carrier directCarrier =
+ CarriersUtils.createCarrier(Id.create("directCarrier", Carrier.class));
+ directCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ directCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("directTruck"), DEPOT_LINK_ID, VEH_TYPE_LARGE_50));
+ LSPResource directCarrierRessource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ directCarrier)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+
+ LogisticChainElement directCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("directCarrierLSE", LogisticChainElement.class))
+ .setResource(directCarrierRessource)
+ .build();
+
+ LogisticChain solution_direct =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("directSolution", LogisticChain.class))
+ .addLogisticChainElement(directCarrierElement)
+ .build();
+
+ final InitialShipmentAssigner singleSolutionShipmentAssigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ lspPlan_direct =
+ LSPUtils.createLSPPlan()
+ .addLogisticChain(solution_direct)
+ .setInitialShipmentAssigner(singleSolutionShipmentAssigner);
+ }
+
+ LSPPlan lspPlan_withHub;
+ {
+ log.info("Create lspPlan with Hub");
+
+ Carrier mainCarrier = CarriersUtils.createCarrier(Id.create("mainCarrier", Carrier.class));
+ mainCarrier.getCarrierCapabilities().setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ CarriersUtils.addCarrierVehicle(
+ mainCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("mainTruck"), DEPOT_LINK_ID, VEH_TYPE_LARGE_50));
+ LSPResource mainCarrierRessource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(mainCarrier)
+ .setFromLinkId(DEPOT_LINK_ID)
+ .setMainRunCarrierScheduler(
+ ResourceImplementationUtils.createDefaultMainRunCarrierScheduler(scenario))
+ .setToLinkId(HUB_LINK_ID)
+ .setVehicleReturn(ResourceImplementationUtils.VehicleReturn.returnToFromLink)
+ .build();
+
+ LogisticChainElement mainCarrierLSE =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("mainCarrierLSE", LogisticChainElement.class))
+ .setResource(mainCarrierRessource)
+ .build();
+
+ // The scheduler for the first reloading point is created --> this will be the depot in this
+ // use case
+ LSPResourceScheduler hubScheduler =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance()
+ .setCapacityNeedFixed(10) // Time needed, fixed (for Scheduler)
+ .setCapacityNeedLinear(1) // additional time needed per shipmentSize (for Scheduler)
+ .build();
+
+ // The scheduler is added to the Resource and the Resource is created
+ LSPResource hubResource =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ Id.create("Hub", LSPResource.class), HUB_LINK_ID, scenario)
+ .setTransshipmentHubScheduler(hubScheduler)
+ .build();
+ LSPUtils.setFixedCost(
+ hubResource, HUBCOSTS_FIX); // Set fixed costs (per day) for the availability of the hub.
+
+ LogisticChainElement hubLSE =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("HubLSE", LogisticChainElement.class))
+ .setResource(hubResource)
+ .build(); // Nicht unbedingt nötig, aber nehme den alten Hub nun als Depot. Waren
+ // werden dann dort "Zusammengestellt".
+
+ Carrier distributionCarrier =
+ CarriersUtils.createCarrier(Id.create("distributionCarrier", Carrier.class));
+ distributionCarrier
+ .getCarrierCapabilities()
+ .setFleetSize(CarrierCapabilities.FleetSize.INFINITE);
+
+ final VehicleType vehType;
+ switch (costSetting) {
+ case sameCost -> vehType = VEH_TYPE_LARGE_50;
+ case lowerCost4LastMile -> vehType = VEH_TYPE_SMALL_05;
+ default -> throw new IllegalStateException("Unexpected value: " + costSetting);
+ }
+ CarriersUtils.addCarrierVehicle(
+ distributionCarrier,
+ CarrierVehicle.newInstance(
+ Id.createVehicleId("distributionTruck"), HUB_LINK_ID, vehType));
+ LSPResource distributionCarrierRessource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier)
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+
+ LogisticChainElement distributionCarrierElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("distributionCarrierLSE", LogisticChainElement.class))
+ .setResource(distributionCarrierRessource)
+ .build();
+
+ // Kettenbildung per hand, damit dann klar ist, wie das Scheduling ablaufen soll. TODO:
+ // Vielleicht bekommt man das noch eleganter hin.
+ // z.B. in der Reihenfolge in der die solutionsElements der LogisticsSolution zugeordnet
+ // werden: ".addSolutionElement(..)"
+ mainCarrierLSE.connectWithNextElement(hubLSE);
+ hubLSE.connectWithNextElement(distributionCarrierElement);
+
+ LogisticChain solution_withHub =
+ LSPUtils.LogisticChainBuilder.newInstance(Id.create("hubSolution", LogisticChain.class))
+ .addLogisticChainElement(mainCarrierLSE)
+ .addLogisticChainElement(hubLSE)
+ .addLogisticChainElement(distributionCarrierElement)
+ .build();
+
+ lspPlan_withHub =
+ LSPUtils.createLSPPlan()
+ .addLogisticChain(solution_withHub)
+ .setInitialShipmentAssigner(ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner());
+ }
+
+ // Todo: Auch das ist wirr: Muss hier alle sammeln, damit man die dann im LSPBuilder dem
+ // SolutionScheduler mitgeben kann. Im Nachgang packt man dann aber erst den zweiten Plan dazu
+ // ... urgs KMT'Jul22
+ List lspPlans = new ArrayList<>();
+ lspPlans.add(lspPlan_withHub);
+ lspPlans.add(lspPlan_direct);
+
+ LSP lsp =
+ LSPUtils.LSPBuilder.getInstance(Id.create("myLSP", LSP.class))
+ .setInitialPlan(lspPlan_direct)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ createResourcesListFromLSPPlans(lspPlans)))
+ .build();
+ lsp.addPlan(lspPlan_withHub); // add the second plan to the lsp
+
+ log.info("create initial LSPShipments");
+ log.info("assign the shipments to the LSP");
+ for (LspShipment lspShipment : createInitialLSPShipments(network)) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ log.info("schedule the LSP with the shipments and according to the scheduler of the Resource");
+ lsp.scheduleLogisticChains();
+
+ return lsp;
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ List shipmentList = new ArrayList<>();
+
+ switch (demandSetting) {
+ case oneCustomer -> {
+ Id id = Id.create("Shipment_" + 1, LspShipment.class);
+ int capacityDemand = 1;
+
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+
+ builder.setCapacityDemand(capacityDemand);
+ builder.setFromLinkId(DEPOT_LINK_ID);
+ builder.setToLinkId(Id.createLinkId("i(5,5)R"));
+ builder.setEndTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setStartTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+
+ shipmentList.add(builder.build());
+
+ return shipmentList;
+ }
+ case tenCustomers -> {
+ Random rand1 = MatsimRandom.getLocalInstance();
+ Random rand2 = MatsimRandom.getLocalInstance();
+
+ List zoneLinkList =
+ Arrays.asList(
+ "i(4,4)", "i(5,4)", "i(6,4)", "i(4,6)", "i(5,6)", "i(6,6)", "j(3,5)", "j(3,6)",
+ "j(3,7)", "j(5,5)", "j(5,6)", "j(5,7)", "i(4,5)R", "i(5,5)R", "i(6,5)R", "i(4,7)R",
+ "i(5,7)R", "i(6,7)R", "j(4,5)R", "j(4,6)R", "j(4,7)R", "j(6,5)R", "j(6,6)R",
+ "j(6,7)R");
+ for (String linkIdString : zoneLinkList) {
+ if (!network.getLinks().containsKey(Id.createLinkId(linkIdString))) {
+ throw new RuntimeException("Link is not in Network!");
+ }
+ }
+
+ for (int i = 1; i <= 10; i++) {
+ Id id = Id.create("Shipment_" + i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder =
+ LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+
+ int capacityDemand =
+ rand1.nextInt(5) + 1; // Random is drawn from 0 (incl) to bound (excl) -> adding 1.
+ builder.setCapacityDemand(capacityDemand);
+
+ builder.setFromLinkId(DEPOT_LINK_ID);
+ final Id toLinkId =
+ Id.createLinkId(zoneLinkList.get(rand2.nextInt(zoneLinkList.size() - 1)));
+ builder.setToLinkId(toLinkId);
+
+ builder.setEndTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setStartTimeWindow(TimeWindow.newInstance(0, (24 * 3600)));
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + demandSetting);
+ }
+ }
+
+ // TODO: This is maybe something that can go into a utils class ... KMT jul22
+ private static List createResourcesListFromLSPPlans(List lspPlans) {
+ log.info("Collecting all LSPResources from the LSPPlans");
+ List resourcesList =
+ new ArrayList<>(); // TODO: Mache daraus ein Set, damit jede Resource nur einmal drin ist?
+ // kmt Feb22
+ for (LSPPlan lspPlan : lspPlans) {
+ for (LogisticChain solution : lspPlan.getLogisticChains()) {
+ for (LogisticChainElement solutionElement : solution.getLogisticChainElements()) {
+ resourcesList.add(solutionElement.getResource());
+ }
+ }
+ }
+ return resourcesList;
+ }
+
+ enum DemandSetting {
+ oneCustomer,
+ tenCustomers
+ }
+
+ enum CarrierCostSetting {
+ sameCost,
+ lowerCost4LastMile
+ }
+
+ // @Override public ScoringFunction createScoringFunction(Carrier carrier ){
+ //
+ // return new ScoringFunction(){
+ //
+ // private double score;
+ //
+ // @Override public void handleActivity( Activity activity ){
+ // score--;
+ // }
+ // @Override public void handleLeg( Leg leg ){
+ // score = score - 10;
+ // }
+ // @Override public void agentStuck( double time ){
+ // }
+ // @Override public void addMoney( double amount ){
+ // }
+ // @Override public void addScore( double amount ){
+ // }
+ // @Override public void finish(){
+ // }
+ // @Override public double getScore(){
+ // return score;
+ // }
+ // @Override public void handleEvent( Event event ){
+ // score = score - 0.01;
+ // }
+ // };
+ // }
+ // }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyCarrierScorer.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyCarrierScorer.java
new file mode 100644
index 00000000000..7ee4a9f4d74
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyCarrierScorer.java
@@ -0,0 +1,61 @@
+/*
+ *********************************************************************** *
+ * 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.initialPlans;
+
+import org.matsim.core.scoring.ScoringFunction;
+import org.matsim.core.scoring.SumScoringFunction;
+import org.matsim.freight.carriers.Carrier;
+import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory;
+
+/**
+ * @author Kai Martins-Turner (kturner)
+ */
+class MyCarrierScorer implements CarrierScoringFunctionFactory {
+
+ public ScoringFunction createScoringFunction(Carrier carrier) {
+ SumScoringFunction sf = new SumScoringFunction();
+ TakeJspritScore takeJspritScore = new TakeJspritScore(carrier);
+ sf.addScoringFunction(takeJspritScore);
+ return sf;
+ }
+
+ private class TakeJspritScore implements SumScoringFunction.BasicScoring {
+
+ private final Carrier carrier;
+
+ public TakeJspritScore(Carrier carrier) {
+ super();
+ this.carrier = carrier;
+ }
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public double getScore() {
+ if (carrier.getSelectedPlan().getScore() != null) {
+ return carrier.getSelectedPlan().getScore();
+ }
+ return Double.NEGATIVE_INFINITY;
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyEventBasedCarrierScorer.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyEventBasedCarrierScorer.java
new file mode 100644
index 00000000000..da264c45ca1
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyEventBasedCarrierScorer.java
@@ -0,0 +1,219 @@
+/*
+ *********************************************************************** *
+ * 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.initialPlans;
+
+import jakarta.inject.Inject;
+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.events.Event;
+import org.matsim.api.core.v01.events.LinkEnterEvent;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.scoring.ScoringFunction;
+import org.matsim.core.scoring.SumScoringFunction;
+import org.matsim.freight.carriers.Carrier;
+import org.matsim.freight.carriers.Tour;
+import org.matsim.freight.carriers.controler.CarrierScoringFunctionFactory;
+import org.matsim.freight.carriers.events.CarrierTourEndEvent;
+import org.matsim.freight.carriers.events.CarrierTourStartEvent;
+import org.matsim.freight.logistics.examples.ExampleConstants;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/**
+ * @author Kai Martins-Turner (kturner)
+ */
+class MyEventBasedCarrierScorer implements CarrierScoringFunctionFactory {
+
+ @Inject private Network network;
+
+ @Inject private Scenario scenario;
+
+ private double toll;
+
+ public ScoringFunction createScoringFunction(Carrier carrier) {
+ SumScoringFunction sf = new SumScoringFunction();
+ sf.addScoringFunction(new EventBasedScoring());
+ sf.addScoringFunction(new LinkBasedTollScoring(toll, List.of("large50")));
+ return sf;
+ }
+
+ void setToll(double toll) {
+ this.toll = toll;
+ }
+
+ /**
+ * Calculate the carrier's score based on Events. Currently, it includes: - fixed costs (using
+ * CarrierTourEndEvent) - time-dependent costs (using FreightTourStart- and -EndEvent) -
+ * distance-dependent costs (using LinkEnterEvent)
+ */
+ private class EventBasedScoring implements SumScoringFunction.ArbitraryEventScoring {
+
+ final Logger log = LogManager.getLogger(EventBasedScoring.class);
+ private final double MAX_SHIFT_DURATION = 8 * 3600;
+ private final Map vehicleType2TourDuration = new LinkedHashMap<>();
+ private final Map vehicleType2ScoredFixCosts = new LinkedHashMap<>();
+ private final Map, Double> tourStartTime = new LinkedHashMap<>();
+ private double score;
+
+ public EventBasedScoring() {
+ super();
+ }
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public double getScore() {
+ return score;
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ log.debug(event.toString());
+ switch (event) {
+ case CarrierTourStartEvent freightTourStartEvent -> handleEvent(freightTourStartEvent);
+ case CarrierTourEndEvent freightTourEndEvent -> handleEvent(freightTourEndEvent);
+ case LinkEnterEvent linkEnterEvent -> handleEvent(linkEnterEvent);
+ default -> {
+ }
+ }
+ }
+
+ private void handleEvent(CarrierTourStartEvent event) {
+ // Save time of freight tour start
+ tourStartTime.put(event.getTourId(), event.getTime());
+ }
+
+ // Fix costs for vehicle usage
+ private void handleEvent(CarrierTourEndEvent event) {
+ // Fix costs for vehicle usage
+ final VehicleType vehicleType =
+ (VehicleUtils.findVehicle(event.getVehicleId(), scenario)).getType();
+
+ double tourDuration = event.getTime() - tourStartTime.get(event.getTourId());
+ { // limit fixed costs of vehicles if vehicles could be reused during shift
+ if (tourDuration > MAX_SHIFT_DURATION) {
+ throw new RuntimeException(
+ "Duration of tour is longer than max shift defined in scoring fct, caused by event:"
+ + event
+ + " tourDuration: "
+ + tourDuration
+ + " max shift duration: "
+ + MAX_SHIFT_DURATION);
+ }
+
+ // sum up tour durations
+ if (vehicleType2TourDuration.containsKey(vehicleType)) {
+ vehicleType2TourDuration.put(
+ vehicleType, vehicleType2TourDuration.get(vehicleType) + tourDuration);
+ } else {
+ vehicleType2TourDuration.put(vehicleType, tourDuration);
+ }
+
+ // scoring needed?
+ final double currentNuOfVehiclesNeeded =
+ Math.ceil(vehicleType2TourDuration.get(vehicleType) / MAX_SHIFT_DURATION);
+ final Integer nuAlreadyScored = vehicleType2ScoredFixCosts.get(vehicleType);
+ if (nuAlreadyScored == null) {
+ log.info("Score fixed costs for vehicle type: {}", vehicleType.getId().toString());
+ score = score - vehicleType.getCostInformation().getFixedCosts();
+ vehicleType2ScoredFixCosts.put(vehicleType, 1);
+ } else if (currentNuOfVehiclesNeeded > nuAlreadyScored) {
+ log.info("Score fixed costs for vehicle type: {}", vehicleType.getId().toString());
+ score = score - vehicleType.getCostInformation().getFixedCosts();
+ vehicleType2ScoredFixCosts.put(
+ vehicleType, vehicleType2ScoredFixCosts.get(vehicleType) + 1);
+ }
+ }
+
+ // variable costs per time
+ score = score - (tourDuration * vehicleType.getCostInformation().getCostsPerSecond());
+ }
+
+ private void handleEvent(LinkEnterEvent event) {
+ final double distance = network.getLinks().get(event.getLinkId()).getLength();
+ final double costPerMeter =
+ (VehicleUtils.findVehicle(event.getVehicleId(), scenario))
+ .getType()
+ .getCostInformation()
+ .getCostsPerMeter();
+ // variable costs per distance
+ score = score - (distance * costPerMeter);
+ }
+ }
+
+ /**
+ * Calculate some toll for driving on a link This a lazy implementation of a cordon toll. A
+ * vehicle is only tolled once.
+ */
+ class LinkBasedTollScoring implements SumScoringFunction.ArbitraryEventScoring {
+
+ final Logger log = LogManager.getLogger(EventBasedScoring.class);
+
+ private final double toll;
+ private final List vehicleTypesToBeTolled;
+ private final List> tolledVehicles = new ArrayList<>();
+ private double score;
+
+ public LinkBasedTollScoring(double toll, List vehicleTypesToBeTolled) {
+ super();
+ this.toll = toll;
+ this.vehicleTypesToBeTolled = vehicleTypesToBeTolled;
+ }
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public double getScore() {
+ return score;
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ if (event instanceof LinkEnterEvent linkEnterEvent) {
+ handleEvent(linkEnterEvent);
+ }
+ }
+
+ private void handleEvent(LinkEnterEvent event) {
+ List tolledLinkList = ExampleConstants.TOLLED_LINK_LIST_GRID;
+
+ final Id vehicleTypeId =
+ (VehicleUtils.findVehicle(event.getVehicleId(), scenario)).getType().getId();
+
+ // toll a vehicle only once.
+ if (!tolledVehicles.contains(event.getVehicleId()))
+ if (vehicleTypesToBeTolled.contains(vehicleTypeId.toString())) {
+ if (tolledLinkList.contains(event.getLinkId().toString())) {
+ log.info("Tolling caused by event: {}", event);
+ tolledVehicles.add(event.getVehicleId());
+ score = score - toll;
+ }
+ }
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyLSPScorer.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyLSPScorer.java
new file mode 100644
index 00000000000..3a70e72d252
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/initialPlans/MyLSPScorer.java
@@ -0,0 +1,103 @@
+/*
+ *********************************************************************** *
+ * 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.initialPlans;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.TransshipmentHubResource;
+
+/**
+ * A scorer for the LSP. It uses the scores of the - carriers: Take the carrier's score and add it
+ * to the LSP's score - hubs: currently a very simple fixed costs scoring (see below {@link
+ * #scoreHub()})
+ *
+ * @author Kai Martins-Turner (kturner)
+ */
+/*package-private*/ class MyLSPScorer implements LSPScorer {
+ final Logger logger = LogManager.getLogger(MyLSPScorer.class);
+ private double score = 0;
+ private LSP lsp;
+
+ @Override
+ public void reset(int iteration) {
+ score = 0.;
+ }
+
+ @Override
+ public double getScoreForCurrentPlan() {
+ scoreLspCarriers();
+ scoreHub();
+ scoreMissingShipments();
+ return score;
+ }
+
+ private void scoreLspCarriers() {
+ var lspPlan = lsp.getSelectedPlan();
+ for (LogisticChain logisticChain : lspPlan.getLogisticChains()) {
+ for (LogisticChainElement logisticChainElement : logisticChain.getLogisticChainElements()) {
+ if (logisticChainElement.getResource() instanceof LSPCarrierResource carrierResource) {
+ var carriersScore = carrierResource.getCarrier().getSelectedPlan().getScore();
+ if (carriersScore != null) {
+ score = score + carriersScore;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * If a hub resource is in the selected plan of the LSP, it will get scored.
+ *
+ *
This is somehow a quickfix, because the hubs do **not** have any own events yet. This needs
+ * to be implemented later KMT oct'22
+ */
+ private void scoreHub() {
+ var lspPlan = lsp.getSelectedPlan();
+ for (LogisticChain logisticChain : lspPlan.getLogisticChains()) {
+ for (LogisticChainElement logisticChainElement : logisticChain.getLogisticChainElements()) {
+ if (logisticChainElement.getResource() instanceof TransshipmentHubResource hub) {
+ score = score - LSPUtils.getFixedCost(hub);
+ }
+ }
+ }
+ }
+
+ private void scoreMissingShipments() {
+ LSPPlan lspPlan = lsp.getSelectedPlan();
+ int lspPlanShipmentCount =
+ lspPlan.getLogisticChains().stream()
+ .mapToInt(logisticChain -> logisticChain.getLspShipmentIds().size())
+ .sum();
+ if (lspPlanShipmentCount != lsp.getLspShipments().size()) {
+ logger.error(
+ "LspPlan doesn't contain the same number of shipments as LSP, "
+ + "shipments probably lost during replanning.");
+ score -= 10000;
+ }
+ }
+
+ @Override
+ public void setEmbeddingContainer(LSP pointer) {
+ this.lsp = pointer;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/AssignmentStrategyFactory.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/AssignmentStrategyFactory.java
new file mode 100644
index 00000000000..cf2056d9bbc
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/AssignmentStrategyFactory.java
@@ -0,0 +1,88 @@
+/*
+ *********************************************************************** *
+ * 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.lspReplanning;
+
+import org.matsim.core.replanning.GenericPlanStrategy;
+import org.matsim.core.replanning.GenericPlanStrategyImpl;
+import org.matsim.core.replanning.ReplanningContext;
+import org.matsim.core.replanning.modules.GenericPlanStrategyModule;
+import org.matsim.core.replanning.selectors.RandomPlanSelector;
+import org.matsim.freight.logistics.LSP;
+import org.matsim.freight.logistics.LSPPlan;
+import org.matsim.freight.logistics.LogisticChain;
+import org.matsim.freight.logistics.LogisticChainElement;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+
+/**
+ * @deprecated This class is a work-around. Please do not use this method for any new runs!
+ *
The plan-handling below was previously done in the LSPControlerListener during replanning.
+ * Since we (nrichter, kturner) are now running with one replanning strategies, which will allow
+ * to move shipments within the same (LSP) plan to a different logisticChain. As a consequence,
+ * the code below is not needed for us (starting from now). More than that, it is in part
+ * working against the new workflow, because the (re) assignment should **not** be done during
+ * replanning anymore.
+ *
The code here is used so that the old stuff from (tm). It keeps this old behaviour (and
+ * tests passing) with its own strategy, instead running it for everyone.
+ *
nrichter, kturner Jul'23
+ */
+@Deprecated
+public class AssignmentStrategyFactory {
+
+ public AssignmentStrategyFactory() {}
+
+ public GenericPlanStrategy createStrategy() {
+
+ GenericPlanStrategyImpl strategy =
+ new GenericPlanStrategyImpl<>(new RandomPlanSelector<>());
+ GenericPlanStrategyModule assignmentModule =
+ new GenericPlanStrategyModule<>() {
+
+ @Override
+ public void prepareReplanning(ReplanningContext replanningContext) {}
+
+ @Override
+ public void handlePlan(LSPPlan lspPlan) {
+
+ for (LogisticChain solution : lspPlan.getLogisticChains()) {
+ solution.getLspShipmentIds().clear();
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ element.getIncomingShipments().clear();
+ element.getOutgoingShipments().clear();
+ }
+ }
+
+ for (LspShipment lspShipment : lspPlan.getLSP().getLspShipments()) {
+ LspShipmentUtils.getOrCreateShipmentPlan(lspPlan, lspShipment.getId()).clear();
+ lspShipment.getShipmentLog().clear();
+ lspPlan.getInitialShipmentAssigner().assignToPlan(lspPlan, lspShipment);
+ }
+ }
+
+ @Override
+ public void finishReplanning() {}
+ };
+
+ strategy.addStrategyModule(assignmentModule);
+ return strategy;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/ExampleLSPReplanning.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/ExampleLSPReplanning.java
new file mode 100644
index 00000000000..20bde8fb6a5
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/ExampleLSPReplanning.java
@@ -0,0 +1,31 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.lspReplanning;
+
+/**
+* Please refer to the
+ * - org.matsim.freight.logistics.example.lsp.multipleChains.ExampleMultipleOneEchelonChainsReplanning for an example with Replanning
+ *
+ * - CollectionLSPReplanningTest for an idea, how replanning works (old way -- will be removed in the future).
+*/
+@Deprecated
+/*package-private*/ class ExampleLSPReplanning {}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/MaybeTodayAssigner.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/MaybeTodayAssigner.java
new file mode 100644
index 00000000000..15850ba29de
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/MaybeTodayAssigner.java
@@ -0,0 +1,56 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.lspReplanning;
+
+import java.util.Random;
+import org.matsim.core.gbl.Gbl;
+import org.matsim.freight.logistics.InitialShipmentAssigner;
+import org.matsim.freight.logistics.LSPPlan;
+import org.matsim.freight.logistics.shipment.LspShipment;
+
+/**
+* This class is deprecated and will be removed in the future.
+ * It follows the old and no longer wanted approach.
+ * Now, an Assigner is used to assign all LSPShipments to one LogisticChain of the LSPPlan.
+ *
+ * This class here is in contrast used as a Replanning strategy. This behavior is not wanted anymore.
+ * KMT, Jul'24
+*/
+@Deprecated
+/*package-private*/ class MaybeTodayAssigner implements InitialShipmentAssigner {
+
+ private final Random random;
+
+ public MaybeTodayAssigner() {
+ this.random = new Random(1);
+ }
+
+ @Override
+ public void assignToPlan(LSPPlan lspPlan, LspShipment lspShipment) {
+ boolean assignToday = random.nextBoolean();
+ if (assignToday) {
+ Gbl.assertIf(lspPlan.getLogisticChains().size() == 1);
+ lspPlan.getLogisticChains().iterator().next().addShipmentToChain(lspShipment);
+ }
+ }
+
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/TomorrowShipmentAssignerStrategyFactory.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/TomorrowShipmentAssignerStrategyFactory.java
new file mode 100644
index 00000000000..2c96a7910b9
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/TomorrowShipmentAssignerStrategyFactory.java
@@ -0,0 +1,92 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.lspReplanning;
+
+import java.util.Collection;
+import org.matsim.core.replanning.GenericPlanStrategy;
+import org.matsim.core.replanning.GenericPlanStrategyImpl;
+import org.matsim.core.replanning.ReplanningContext;
+import org.matsim.core.replanning.modules.GenericPlanStrategyModule;
+import org.matsim.core.replanning.selectors.BestPlanSelector;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+
+@Deprecated
+/*package-private*/ class TomorrowShipmentAssignerStrategyFactory {
+
+ private final InitialShipmentAssigner assigner;
+
+ /*package-private*/ TomorrowShipmentAssignerStrategyFactory(InitialShipmentAssigner assigner) {
+ this.assigner = assigner;
+ }
+
+ /*package-private*/ GenericPlanStrategy createStrategy() {
+ GenericPlanStrategyImpl strategy =
+ new GenericPlanStrategyImpl<>(new BestPlanSelector<>());
+
+ GenericPlanStrategyModule tomorrowModule =
+ new GenericPlanStrategyModule<>() {
+
+ @Override
+ public void prepareReplanning(ReplanningContext replanningContext) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void handlePlan(LSPPlan plan) {
+ plan.getLogisticChains().iterator().next().getLspShipmentIds().clear();
+ plan.setInitialShipmentAssigner(assigner);
+ // LSP lsp = assigner.getLSP();
+ LSP lsp = plan.getLSP();
+ Collection lspShipments = lsp.getLspShipments();
+ for (LspShipment lspShipment : lspShipments) {
+ assigner.assignToPlan(plan, lspShipment);
+ }
+
+ for (LogisticChain solution : plan.getLogisticChains()) {
+ solution.getLspShipmentIds().clear();
+ for (LogisticChainElement element : solution.getLogisticChainElements()) {
+ element.getIncomingShipments().clear();
+ element.getOutgoingShipments().clear();
+ }
+ }
+
+ for (LspShipment lspShipment : plan.getLSP().getLspShipments()) {
+ LspShipmentUtils.getOrCreateShipmentPlan(plan, lspShipment.getId()).clear();
+ lspShipment.getShipmentLog().clear();
+ plan.getInitialShipmentAssigner().assignToPlan(plan, lspShipment);
+ }
+ }
+
+ @Override
+ public void finishReplanning() {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
+ strategy.addStrategyModule(tomorrowModule);
+ return strategy;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/package-info.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/package-info.java
new file mode 100644
index 00000000000..3a42f344ada
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspReplanning/package-info.java
@@ -0,0 +1,36 @@
+/*
+ *********************************************************************** *
+ * 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 *
+ * *
+ * ***********************************************************************
+ */
+
+/**
+ * @deprecated This package is deprecated and will be removed in the future.
+ * It follows the old and no longer wanted approach.
+ * Now, an Assigner is used to assign all LSPShipments to one LogisticChain of the LSPPlan.
+ *
+ * This class here is in contrast used as a Replanning strategy. This behavior is not wanted anymore.
+ *
+ * Please use the new approach as shown in
+ * {@link org.matsim.freight.logistics.example.lsp.multipleChains} ExampleMultipleOneEchelonChainsReplanning instead.
+ * ((The old approach is still available in CollectionLSPReplanningTest but will get removed earlier or later)).
+ *
+ * KMT, Jul'24
+ * */
+@Deprecated
+package org.matsim.freight.logistics.examples.lspReplanning;
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspScoring/ExampleLSPScoring.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspScoring/ExampleLSPScoring.java
new file mode 100644
index 00000000000..917c60a2567
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/lspScoring/ExampleLSPScoring.java
@@ -0,0 +1,266 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.lspScoring;
+
+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.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+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.OverwriteFileSetting;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.carriers.events.CarrierServiceEndEvent;
+import org.matsim.freight.carriers.events.eventhandler.CarrierServiceEndEventHandler;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/* Example for customized scoring. Each customer that is visited will give a random tip between zero and five
+ *
+ *
+ */
+
+/*package-private*/ final class ExampleLSPScoring {
+
+ private ExampleLSPScoring() {}
+
+ private static LSP createLSPWithScorer(Scenario scenario) {
+
+ // The Carrier for the resource of the sole LogisticsSolutionElement of the LSP is created
+ final VehicleType carrierVehType = VehicleUtils.createVehicleType( Id.create("CollectionCarrierVehicleType", VehicleType.class), TransportMode.car);
+ carrierVehType.getCapacity().setOther(10);
+ carrierVehType.getCostInformation().setCostsPerMeter(0.0004);
+ carrierVehType.getCostInformation().setCostsPerSecond(0.38);
+ carrierVehType.getCostInformation().setFixedCost(49.);
+ carrierVehType.setMaximumVelocity(50 / 3.6);
+
+ Id collectionLinkId = Id.createLinkId("(4 2) (4 3)");
+
+ CarrierCapabilities capabilities =
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(CarrierVehicle.newInstance(Id.createVehicleId("CollectionVehicle"), collectionLinkId, carrierVehType))
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+
+ Carrier carrier = CarriersUtils.createCarrier(Id.create("CollectionCarrier", Carrier.class));
+ carrier.setCarrierCapabilities(capabilities);
+
+ // The Resource i.e. the Resource is created
+ // The scheduler for the Resource is created and added. This is where jsprit comes into play.
+ LSPResource lspResource =
+ ResourceImplementationUtils.CollectionCarrierResourceBuilder.newInstance(
+ carrier)
+ .setCollectionScheduler(
+ ResourceImplementationUtils.createDefaultCollectionCarrierScheduler(scenario))
+ .setLocationLinkId(collectionLinkId)
+ .build();
+
+ // The adapter is now inserted into the only LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ LogisticChainElement logisticChainElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("CollectionElement", LogisticChainElement.class))
+ .setResource(lspResource)
+ .build();
+
+ // The LogisticsSolutionElement is now inserted into the only LogisticsSolution of the LSP
+ LogisticChain logisticChain =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("CollectionSolution", LogisticChain.class))
+ .addLogisticChainElement(logisticChainElement)
+ .build();
+
+ // The initial plan of the lsp is generated and the assigner and the solution from above are
+ // added
+ LSPPlan lspPlan =
+ LSPUtils.createLSPPlan()
+ .setInitialShipmentAssigner(ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner())
+ .addLogisticChain(logisticChain);
+
+ // The exogenous list of Resources for the SolutionScheduler is compiled and the Scheduler is
+ // added to the LSPBuilder
+
+ return LSPUtils.LSPBuilder.getInstance(Id.create("CollectionLSP", LSP.class))
+ .setInitialPlan(lspPlan)
+ .setLogisticChainScheduler(
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(
+ Collections.singletonList(lspResource)))
+ // .setSolutionScorer(new TipScorer())
+ .build();
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ List shipmentList = new ArrayList<>();
+ ArrayList linkList = new ArrayList<>(network.getLinks().values());
+
+ // Create five LSPShipments that are located in the left half of the network.
+ for (int i = 1; i < 6; i++) {
+ Id id = Id.create(i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+ Random random = new Random(1);
+ int capacityDemand = random.nextInt(4);
+ builder.setCapacityDemand(capacityDemand);
+
+ while (true) {
+ Collections.shuffle(linkList, random);
+ Link pendingFromLink = linkList.getFirst();
+ if (pendingFromLink.getFromNode().getCoord().getX() <= 4000
+ && pendingFromLink.getFromNode().getCoord().getY() <= 4000
+ && pendingFromLink.getToNode().getCoord().getX() <= 4000
+ && pendingFromLink.getToNode().getCoord().getY() <= 4000) {
+ builder.setFromLinkId(pendingFromLink.getId());
+ break;
+ }
+ }
+
+ builder.setToLinkId(Id.createLinkId("(4 2) (4 3)"));
+ TimeWindow endTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setEndTimeWindow(endTimeWindow);
+ TimeWindow startTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setStartTimeWindow(startTimeWindow);
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+
+ public static void main(String[] args) {
+
+ Config config = prepareConfig();
+
+ Scenario scenario = prepareScenario(config);
+
+ Controler controler = prepareControler(scenario);
+
+ // 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();
+
+ for (LSP lsp2 : LSPUtils.getLSPs(scenario).getLSPs().values()) {
+ System.out.println("The tip of all customers was: " + lsp2.getSelectedPlan().getScore());
+ }
+ }
+
+ static Controler prepareControler(Scenario scenario) {
+ // Start the Mobsim one iteration is sufficient for scoring
+ Controler controler = new Controler(scenario);
+ controler.addOverridingModule(new LSPModule());
+ controler.addOverridingModule(
+ new AbstractModule() {
+ @Override
+ public void install() {
+ bind(LSPScorerFactory.class).toInstance(TipScorer::new);
+ }
+ });
+ return controler;
+ }
+
+ static Scenario prepareScenario(Config config) {
+ Scenario scenario = ScenarioUtils.loadScenario(config);
+
+ // Create LSP and lspShipments
+ LSP lsp = createLSPWithScorer(scenario);
+ Collection lspShipments = createInitialLSPShipments(scenario.getNetwork());
+
+ // assign the lspShipments to the LSP
+ for (LspShipment lspShipment : lspShipments) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ // schedule the LSP with the lspShipments and according to the scheduler of the Resource
+ lsp.scheduleLogisticChains();
+
+ // Prepare LSPModule and add the LSP
+ LSPs lsps = new LSPs(Collections.singletonList(lsp));
+ LSPUtils.addLSPs(scenario, lsps);
+ return scenario;
+ }
+
+ static Config prepareConfig() {
+ // Set up required MATSim classes
+ Config config = ConfigUtils.createConfig();
+
+ config.network().setInputFile(IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "logistics-2regions" ), "2regions-network.xml" ).getFile());
+
+ config.controller().setLastIteration(0);
+ config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists);
+
+ var freightConfig = ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+ return config;
+ }
+
+ /*package-private*/ static class TipScorer
+ implements LSPScorer, LSPSimulationTracker, CarrierServiceEndEventHandler {
+
+ private static final Logger log = LogManager.getLogger(TipScorer.class);
+
+ private final Random tipRandom;
+ private double tipSum;
+
+ /*package-private*/ TipScorer() {
+ tipRandom = new Random(1);
+ tipSum = 0;
+ }
+
+ @Override
+ public double getScoreForCurrentPlan() {
+ return tipSum;
+ }
+
+ @Override
+ public void setEmbeddingContainer(LSP pointer) {
+ // backpointer not needed here, therefor not memorizing it. kai, jun'22
+ }
+
+ @Override
+ public void handleEvent(CarrierServiceEndEvent event) {
+ double tip = tipRandom.nextDouble() * 5;
+ log.warn("tipSum={}; tip={}", tipSum, tip);
+ tipSum += tip;
+ }
+
+ @Override
+ public void reset(int iteration) {
+ tipSum = 0.;
+ }
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfSimpleLSP.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfSimpleLSP.java
new file mode 100644
index 00000000000..dbcf83690e8
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfSimpleLSP.java
@@ -0,0 +1,265 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.mobsimExamples;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+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.OverwriteFileSetting;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlanElement;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/*package-private*/ class ExampleMobsimOfSimpleLSP {
+
+ public static void main(String[] args) {
+ // Set up required MATSim classes
+ Config config = new Config();
+ config.addCoreModules();
+
+ if (args.length != 0) {
+ ConfigUtils.applyCommandline(config, args);
+ }
+
+ FreightCarriersConfigGroup freightConfig =
+ ConfigUtils.addOrGetModule(config, FreightCarriersConfigGroup.class);
+ freightConfig.setTimeWindowHandling(FreightCarriersConfigGroup.TimeWindowHandling.ignore);
+
+ Scenario scenario = ScenarioUtils.createScenario(config);
+
+ new MatsimNetworkReader(scenario.getNetwork())
+ .readFile(IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "logistics-2regions" ), "2regions-network.xml" ).getFile());
+
+ // Create LSP and lspShipments
+ LSP lsp = createInitialLSP(scenario);
+ Collection lspShipments = createInitialLSPShipments(scenario.getNetwork());
+
+ // assign the lspShipments to the LSP
+ for (LspShipment lspShipment : lspShipments) {
+ lsp.assignShipmentToLSP(lspShipment);
+ }
+
+ // schedule the LSP with the lspShipments and according to the scheduler of the Resource
+ lsp.scheduleLogisticChains();
+
+ // set up simulation controler and LSPModule
+ ArrayList lspList = new ArrayList<>();
+ lspList.add(lsp);
+ LSPs lsps = new LSPs(lspList);
+ LSPUtils.addLSPs(scenario, lsps);
+
+ config.controller().setFirstIteration(0);
+ config.controller().setLastIteration(0);
+ config.controller().setOverwriteFileSetting(OverwriteFileSetting.overwriteExistingFiles);
+
+ Controler controler = new Controler(scenario);
+ controler.addOverridingModule(
+ new AbstractModule() {
+ @Override
+ public void install() {
+ install(new LSPModule());
+ }
+ });
+ // 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();
+
+ for (LspShipment lspShipment : lsp.getLspShipments()) {
+ System.out.println("LspShipment: " + lspShipment.getId());
+ ArrayList scheduleElements =
+ new ArrayList<>(
+ LspShipmentUtils.getOrCreateShipmentPlan(lsp.getSelectedPlan(), lspShipment.getId())
+ .getPlanElements()
+ .values());
+ scheduleElements.sort(LspShipmentUtils.createShipmentPlanElementComparator());
+ ArrayList logElements =
+ new ArrayList<>(lspShipment.getShipmentLog().getPlanElements().values());
+ logElements.sort(LspShipmentUtils.createShipmentPlanElementComparator());
+
+ for (int i = 0;
+ i
+ < LspShipmentUtils.getOrCreateShipmentPlan(lsp.getSelectedPlan(), lspShipment.getId())
+ .getPlanElements()
+ .size();
+ i++) {
+ System.out.println(
+ "Scheduled: "
+ + scheduleElements.get(i).getLogisticChainElement().getId()
+ + " "
+ + scheduleElements.get(i).getResourceId()
+ + " "
+ + scheduleElements.get(i).getElementType()
+ + " Start: "
+ + scheduleElements.get(i).getStartTime()
+ + " End: "
+ + scheduleElements.get(i).getEndTime());
+ }
+ System.out.println();
+ for (int i = 0; i < lspShipment.getShipmentLog().getPlanElements().size(); i++) {
+ System.out.println(
+ "Logged: "
+ + logElements.get(i).getLogisticChainElement().getId()
+ + " "
+ + logElements.get(i).getResourceId()
+ + " "
+ + logElements.get(i).getElementType()
+ + " Start: "
+ + logElements.get(i).getStartTime()
+ + " End: "
+ + logElements.get(i).getEndTime());
+ }
+ System.out.println();
+ }
+ }
+
+ private static LSP createInitialLSP(Scenario scenario) {
+
+ // The Carrier for the resource of the sole LogisticsSolutionElement of the LSP is created
+ Id carrierId = Id.create("CollectionCarrier", Carrier.class);
+ Id vehicleTypeId = Id.create("CollectionCarrierVehicleType", VehicleType.class);
+ VehicleType collectionVehType = VehicleUtils.createVehicleType(vehicleTypeId, TransportMode.car);
+ collectionVehType.getCapacity().setOther(10);
+ collectionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ collectionVehType.getCostInformation().setCostsPerSecond(0.38);
+ collectionVehType.getCostInformation().setFixedCost(49.);
+ collectionVehType.setMaximumVelocity(50 / 3.6);
+
+ Id collectionLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id vollectionVehicleId = Id.createVehicleId("CollectionVehicle");
+ CarrierVehicle carrierVehicle =
+ CarrierVehicle.newInstance(vollectionVehicleId, collectionLinkId, collectionVehType);
+
+ CarrierCapabilities capabilities = CarrierCapabilities.Builder.newInstance()
+ .addVehicle(carrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+
+ Carrier carrier = CarriersUtils.createCarrier(carrierId);
+ carrier.setCarrierCapabilities(capabilities);
+
+ // The Resource i.e. the Resource is created
+ LSPResource collectionResource =
+ ResourceImplementationUtils.CollectionCarrierResourceBuilder.newInstance(carrier)
+ .setCollectionScheduler(
+ ResourceImplementationUtils.createDefaultCollectionCarrierScheduler(scenario))
+ .setLocationLinkId(collectionLinkId)
+ .build();
+
+ // The adapter is now inserted into the only LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ LogisticChainElement collectionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("CollectionElement", LogisticChainElement.class))
+ .setResource(collectionResource)
+ .build();
+
+ // The LogisticsSolutionElement is now inserted into the only LogisticsSolution of the LSP
+ LogisticChain collectionSolution =
+ LSPUtils.LogisticChainBuilder.newInstance(
+ Id.create("CollectionSolution", LogisticChain.class))
+ .addLogisticChainElement(collectionElement)
+ .build();
+
+ // The initial plan of the lsp is generated and the assigner and the solution from above are
+ // added
+ LSPPlan collectionPlan = LSPUtils.createLSPPlan();
+ InitialShipmentAssigner assigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ collectionPlan.setInitialShipmentAssigner(assigner);
+ collectionPlan.addLogisticChain(collectionSolution);
+
+ LSPUtils.LSPBuilder collectionLSPBuilder =
+ LSPUtils.LSPBuilder.getInstance(Id.create("CollectionLSP", LSP.class));
+ collectionLSPBuilder.setInitialPlan(collectionPlan);
+
+ // The exogenous list of Resources for the SolutionScheduler is compiled and the Scheduler is
+ // added to the LSPBuilder
+ ArrayList resourcesList = new ArrayList<>();
+ resourcesList.add(collectionResource);
+ LogisticChainScheduler simpleScheduler =
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(resourcesList);
+ collectionLSPBuilder.setLogisticChainScheduler(simpleScheduler);
+
+ return collectionLSPBuilder.build();
+ }
+
+ private static Collection createInitialLSPShipments(Network network) {
+ ArrayList shipmentList = new ArrayList<>();
+ ArrayList linkList = new ArrayList<>(network.getLinks().values());
+
+ // Create five LSPShipments that are located in the left half of the network.
+ for (int i = 1; i < 6; i++) {
+ Id id = Id.create(i, LspShipment.class);
+ LspShipmentUtils.LspShipmentBuilder builder = LspShipmentUtils.LspShipmentBuilder.newInstance(id);
+ Random random = new Random(1);
+ int capacityDemand = random.nextInt(4);
+ builder.setCapacityDemand(capacityDemand);
+
+ while (true) {
+ Collections.shuffle(linkList, random);
+ Link pendingFromLink = linkList.getFirst();
+ if (pendingFromLink.getFromNode().getCoord().getX() <= 4000
+ && pendingFromLink.getFromNode().getCoord().getY() <= 4000
+ && pendingFromLink.getToNode().getCoord().getX() <= 4000
+ && pendingFromLink.getToNode().getCoord().getY() <= 4000) {
+ builder.setFromLinkId(pendingFromLink.getId());
+ break;
+ }
+ }
+
+ builder.setToLinkId(Id.createLinkId("(4 2) (4 3)"));
+ TimeWindow endTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setEndTimeWindow(endTimeWindow);
+ TimeWindow startTimeWindow = TimeWindow.newInstance(0, (24 * 3600));
+ builder.setStartTimeWindow(startTimeWindow);
+ builder.setDeliveryServiceTime(capacityDemand * 60);
+ shipmentList.add(builder.build());
+ }
+ return shipmentList;
+ }
+}
diff --git a/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfTransportChain.java b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfTransportChain.java
new file mode 100644
index 00000000000..b65592ed3bc
--- /dev/null
+++ b/contribs/freight/src/main/java/org/matsim/freight/logistics/examples/mobsimExamples/ExampleMobsimOfTransportChain.java
@@ -0,0 +1,421 @@
+/*
+ *********************************************************************** *
+ * project: org.matsim.*
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2022 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * ***********************************************************************
+ */
+
+package org.matsim.freight.logistics.examples.mobsimExamples;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.TransportMode;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.Config;
+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.OverwriteFileSetting;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.freight.carriers.*;
+import org.matsim.freight.carriers.CarrierCapabilities.FleetSize;
+import org.matsim.freight.logistics.*;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils.TranshipmentHubSchedulerBuilder;
+import org.matsim.freight.logistics.resourceImplementations.ResourceImplementationUtils.TransshipmentHubBuilder;
+import org.matsim.freight.logistics.shipment.LspShipment;
+import org.matsim.freight.logistics.shipment.LspShipmentPlanElement;
+import org.matsim.freight.logistics.shipment.LspShipmentUtils;
+import org.matsim.vehicles.Vehicle;
+import org.matsim.vehicles.VehicleType;
+import org.matsim.vehicles.VehicleUtils;
+
+/*package-private*/ class ExampleMobsimOfTransportChain {
+
+ private static LSP createInitialLSP(Scenario scenario) {
+
+ Network network = scenario.getNetwork();
+
+ // The Carrier for collection is created
+ Id collectionCarrierId = Id.create("CollectionCarrier", Carrier.class);
+ Id vehicleTypeId = Id.create("CollectionCarrierVehicleType", VehicleType.class);
+ VehicleType collectionVehType = VehicleUtils.createVehicleType(vehicleTypeId, TransportMode.car);
+ collectionVehType.getCapacity().setOther(10);
+ collectionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ collectionVehType.getCostInformation().setCostsPerSecond(0.38);
+ collectionVehType.getCostInformation().setFixedCost(49.);
+ collectionVehType.setMaximumVelocity(50 / 3.6);
+
+ Id collectionLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id vollectionVehicleId = Id.createVehicleId("CollectionVehicle");
+ CarrierVehicle carrierVehicle =
+ CarrierVehicle.newInstance(vollectionVehicleId, collectionLinkId, collectionVehType);
+
+ CarrierCapabilities capabilities = CarrierCapabilities.Builder.newInstance()
+ .addVehicle(carrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+
+ Carrier collectionCarrier = CarriersUtils.createCarrier(collectionCarrierId);
+ collectionCarrier.setCarrierCapabilities(capabilities);
+
+ // The collection adapter i.e. the Resource is created
+ LSPResource collectionResource =
+ ResourceImplementationUtils.CollectionCarrierResourceBuilder.newInstance(
+ collectionCarrier)
+ .setCollectionScheduler(
+ ResourceImplementationUtils.createDefaultCollectionCarrierScheduler(scenario))
+ .setLocationLinkId(collectionLinkId)
+ .build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ LogisticChainElement collectionElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(
+ Id.create("CollectionElement", LogisticChainElement.class))
+ .setResource(collectionResource)
+ .build();
+
+ // The first reloading adapter i.e. the Resource is created
+ Id firstTransshipmentHubId = Id.create("TranshipmentHub1", LSPResource.class);
+ Id firstTransshipmentHub_LinkId = Id.createLinkId("(4 2) (4 3)");
+ TransshipmentHubBuilder firstTransshipmentHubBuilder =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ firstTransshipmentHubId, firstTransshipmentHub_LinkId, scenario);
+
+ // The scheduler for the first reloading point is created
+ TranshipmentHubSchedulerBuilder firstReloadingSchedulerBuilder =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance();
+ firstReloadingSchedulerBuilder.setCapacityNeedFixed(10);
+ firstReloadingSchedulerBuilder.setCapacityNeedLinear(1);
+
+ // The scheduler is added to the Resource and the Resource is created
+ firstTransshipmentHubBuilder.setTransshipmentHubScheduler(
+ firstReloadingSchedulerBuilder.build());
+ LSPResource firstTranshipmentHubResource = firstTransshipmentHubBuilder.build();
+
+ // The SolutionElement for the first reloading point is created
+ Id firstHubElementId =
+ Id.create("FirstHubElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder firstHubElementBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(firstHubElementId);
+ firstHubElementBuilder.setResource(firstTranshipmentHubResource);
+ LogisticChainElement firstHubElement = firstHubElementBuilder.build();
+
+ // The Carrier for the main run Resource is created
+ final Id mainRunCarrierId = Id.create("MainRunCarrier", Carrier.class);
+ final Id mainRunVehTypeId = Id.create("MainRunCarrierVehicleType", VehicleType.class);
+ final VehicleType mainRunVehType = VehicleUtils.createVehicleType(mainRunVehTypeId, TransportMode.car);
+ mainRunVehType.getCapacity().setOther(30);
+ mainRunVehType.getCostInformation().setCostsPerMeter(0.0002);
+ mainRunVehType.getCostInformation().setCostsPerSecond(0.38);
+ mainRunVehType.getCostInformation().setFixedCost(120.);
+ mainRunVehType.setMaximumVelocity(50 / 3.6);
+ mainRunVehType.setNetworkMode(TransportMode.car);
+
+ Id fromLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id mainRunVehicleId = Id.createVehicleId("MainRunVehicle");
+ CarrierVehicle mainRunCarrierVehicle = CarrierVehicle.newInstance(mainRunVehicleId, fromLinkId, mainRunVehType);
+
+ CarrierCapabilities mainRunCapabilities =
+ CarrierCapabilities.Builder.newInstance()
+ .addVehicle(mainRunCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+ Carrier mainRunCarrier = CarriersUtils.createCarrier(mainRunCarrierId);
+ mainRunCarrier.setCarrierCapabilities(mainRunCapabilities);
+
+ // The adapter i.e. the main run resource is created
+ LSPResource mainRunResource =
+ ResourceImplementationUtils.MainRunCarrierResourceBuilder.newInstance(mainRunCarrier)
+ .setFromLinkId(Id.createLinkId("(4 2) (4 3)"))
+ .setToLinkId(Id.createLinkId("(14 2) (14 3)"))
+ .setMainRunCarrierScheduler(ResourceImplementationUtils.createDefaultMainRunCarrierScheduler(scenario))
+ .build();
+
+ // The LogisticsSolutionElement for the main run Resource is created
+ Id mainRunElementId =
+ Id.create("MainRunElement", LogisticChainElement.class);
+ LogisticChainElement mainRunElement =
+ LSPUtils.LogisticChainElementBuilder.newInstance(mainRunElementId)
+ .setResource(mainRunResource)
+ .build();
+
+ // The second reloading adapter i.e. the Resource is created
+ Id secondTransshipmentHubId = Id.create("TranshipmentHub2", LSPResource.class);
+ Id secondTransshipmentHub_LinkId = Id.createLinkId("(14 2) (14 3)");
+ TransshipmentHubBuilder secondTransshipmentHubBuilder =
+ ResourceImplementationUtils.TransshipmentHubBuilder.newInstance(
+ secondTransshipmentHubId, secondTransshipmentHub_LinkId, scenario);
+
+ // The scheduler for the second reloading point is created
+ TranshipmentHubSchedulerBuilder secondSchedulerBuilder =
+ ResourceImplementationUtils.TranshipmentHubSchedulerBuilder.newInstance();
+ secondSchedulerBuilder.setCapacityNeedFixed(10);
+ secondSchedulerBuilder.setCapacityNeedLinear(1);
+
+ // The scheduler is added to the Resource and the Resource is created
+ secondTransshipmentHubBuilder.setTransshipmentHubScheduler(secondSchedulerBuilder.build());
+ LSPResource secondTranshipmentHubResource = secondTransshipmentHubBuilder.build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ Id secondHubElementId =
+ Id.create("SecondHubElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder secondHubElementBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(secondHubElementId);
+ secondHubElementBuilder.setResource(secondTranshipmentHubResource);
+ LogisticChainElement secondHubElement = secondHubElementBuilder.build();
+
+ // The Carrier for distribution is created
+ Id distributionCarrierId = Id.create("DistributionCarrier", Carrier.class);
+ Id distributionVehTypeId =
+ Id.create("DistributionCarrierVehicleType", VehicleType.class);
+ VehicleType distributionVehType = VehicleUtils.createVehicleType(vehicleTypeId, TransportMode.car);
+ distributionVehType.getCapacity().setOther(10);
+ distributionVehType.getCostInformation().setCostsPerMeter(0.0004);
+ distributionVehType.getCostInformation().setCostsPerSecond(0.38);
+ distributionVehType.getCostInformation().setFixedCost(49.);
+ distributionVehType.setMaximumVelocity(50 / 3.6);
+
+ Id distributionLinkId = Id.createLinkId("(4 2) (4 3)");
+ Id distributionVehicleId = Id.createVehicleId("DistributionVehicle");
+ CarrierVehicle distributionCarrierVehicle =
+ CarrierVehicle.newInstance(distributionVehicleId, distributionLinkId, distributionVehType);
+
+ CarrierCapabilities distributionCapabilities = CarrierCapabilities.Builder.newInstance()
+ .addVehicle(distributionCarrierVehicle)
+ .setFleetSize(FleetSize.INFINITE)
+ .build();
+
+ Carrier distributionCarrier = CarriersUtils.createCarrier(distributionCarrierId);
+ distributionCarrier.setCarrierCapabilities(distributionCapabilities);
+
+ // The distribution adapter i.e. the Resource is created
+ LSPResource distributionResource =
+ ResourceImplementationUtils.DistributionCarrierResourceBuilder.newInstance(
+ distributionCarrier)
+ .setLocationLinkId(distributionLinkId)
+ // The scheduler for the Resource is created and added. This is where jsprit comes into
+ // play.
+ .setDistributionScheduler(
+ ResourceImplementationUtils.createDefaultDistributionCarrierScheduler(scenario))
+ .build();
+
+ // The adapter is now inserted into the corresponding LogisticsSolutionElement of the only
+ // LogisticsSolution of the LSP
+ Id distributionElementId =
+ Id.create("DistributionElement", LogisticChainElement.class);
+ LSPUtils.LogisticChainElementBuilder distributionBuilder =
+ LSPUtils.LogisticChainElementBuilder.newInstance(distributionElementId);
+ distributionBuilder.setResource(distributionResource);
+ LogisticChainElement distributionElement = distributionBuilder.build();
+
+ // The Order of the logisticsSolutionElements is now specified
+ collectionElement.connectWithNextElement(firstHubElement);
+ firstHubElement.connectWithNextElement(mainRunElement);
+ mainRunElement.connectWithNextElement(secondHubElement);
+ secondHubElement.connectWithNextElement(distributionElement);
+
+ // The SolutionElements are now inserted into the only LogisticsSolution of the LSP
+ Id solutionId = Id.create("SolutionId", LogisticChain.class);
+ LSPUtils.LogisticChainBuilder completeSolutionBuilder =
+ LSPUtils.LogisticChainBuilder.newInstance(solutionId);
+ completeSolutionBuilder.addLogisticChainElement(collectionElement);
+ completeSolutionBuilder.addLogisticChainElement(firstHubElement);
+ completeSolutionBuilder.addLogisticChainElement(mainRunElement);
+ completeSolutionBuilder.addLogisticChainElement(secondHubElement);
+ completeSolutionBuilder.addLogisticChainElement(distributionElement);
+ LogisticChain completeSolution = completeSolutionBuilder.build();
+
+ // The initial plan of the lsp is generated and the assigner and the solution from above are
+ // added
+ LSPPlan completePlan = LSPUtils.createLSPPlan();
+ InitialShipmentAssigner assigner =
+ ResourceImplementationUtils.createSingleLogisticChainShipmentAssigner();
+ completePlan.setInitialShipmentAssigner(assigner);
+ completePlan.addLogisticChain(completeSolution);
+
+ LSPUtils.LSPBuilder completeLSPBuilder =
+ LSPUtils.LSPBuilder.getInstance(Id.create("CollectionLSP", LSP.class));
+ completeLSPBuilder.setInitialPlan(completePlan);
+
+ // The exogenous list of Resources for the SolutionScheduler is compiled and the Scheduler is
+ // added to the LSPBuilder
+ ArrayList resourcesList = new ArrayList<>();
+ resourcesList.add(collectionResource);
+ resourcesList.add(firstTranshipmentHubResource);
+ resourcesList.add(mainRunResource);
+ resourcesList.add(secondTranshipmentHubResource);
+ resourcesList.add(distributionResource);
+ LogisticChainScheduler simpleScheduler =
+ ResourceImplementationUtils.createDefaultSimpleForwardLogisticChainScheduler(resourcesList);
+ completeLSPBuilder.setLogisticChainScheduler(simpleScheduler);
+
+ return completeLSPBuilder.build();
+ }
+
+ private static Collection