From 9a3872d58e83cea697720c3d560311f5b31fb7f3 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Thu, 21 Nov 2024 14:02:27 +0100 Subject: [PATCH 1/8] update log massage --- .../freightDemandGeneration/FreightDemandGenerationUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java index c60a8a39390..ede28ace76d 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGenerationUtils.java @@ -152,7 +152,7 @@ static void createDemandLocationsFile(Controler controler) { writer.flush(); } catch (IOException e) { - e.printStackTrace(); + log.error("Could not write job locations file under " + "/outputLocationFile.xml.gz"); } log.info("Wrote job locations file under " + "/outputLocationFile.xml.gz"); } From ecb29e8e767b221aab8cb7428d65415b80f1c484 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 09:53:28 +0100 Subject: [PATCH 2/8] rename method --- .../matsim/freightDemandGeneration/DemandReaderFromCSV.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index e39eedd9ad4..d79a6e77a1c 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -1003,7 +1003,7 @@ else if (numberOfPickupLocations != null) { } } if (combineSimilarJobs) - reduceNumberOfJobsIfSameCharacteristics(scenario, newDemandInformationElement); + combineSimilarJobs(scenario, newDemandInformationElement); } /** Creates a single shipment. @@ -1163,8 +1163,8 @@ private static int calculateDemandBasedOnLinkLength(int countOfLinks, int distri * @param scenario Scenario * @param newDemandInformationElement single DemandInformationElement */ - private static void reduceNumberOfJobsIfSameCharacteristics(Scenario scenario, - DemandInformationElement newDemandInformationElement) { + private static void combineSimilarJobs(Scenario scenario, + DemandInformationElement newDemandInformationElement) { log.warn( "The number of Jobs will be reduced if jobs have the same characteristics (e.g. time, location, carrier)"); From eec0eaa3b7f6a0e4d0c5f93e716d0fa7d2ea6442 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 09:54:16 +0100 Subject: [PATCH 3/8] rename method --- .../org/matsim/freightDemandGeneration/DemandReaderFromCSV.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index d79a6e77a1c..25041119435 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -746,7 +746,7 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { } } if (combineSimilarJobs) - reduceNumberOfJobsIfSameCharacteristics(scenario, newDemandInformationElement); + combineSimilarJobs(scenario, newDemandInformationElement); } /** From 42e2633ce0bb69e933855fb2682e4e1647652e75 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 11:16:52 +0100 Subject: [PATCH 4/8] add configurable implementation of job duration calculation --- .../DefaultJobDurationCalculator.java | 42 ++++++++++ .../DemandReaderFromCSV.java | 84 +++++++++++-------- .../FreightDemandGeneration.java | 18 +++- .../JobDurationCalculator.java | 15 ++++ 4 files changed, 120 insertions(+), 39 deletions(-) create mode 100644 contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java create mode 100644 contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java new file mode 100644 index 00000000000..d64b7aad509 --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java @@ -0,0 +1,42 @@ +package org.matsim.freightDemandGeneration; + +import org.matsim.freight.carriers.Carrier; + +public class DefaultJobDurationCalculator implements JobDurationCalculator { + @Override + public double calculateServiceDuration(Integer serviceTimePerUnit, int demandForThisService) { + return getDefaultCalculation(serviceTimePerUnit, demandForThisService); + } + + @Override + public double calculatePickupDuration(Integer pickupDurationPerUnit, int demandForThisShipment) { + return getDefaultCalculation(pickupDurationPerUnit, demandForThisShipment); + } + + @Override + public double calculateDeliveryDuration(Integer deliveryDurationPerUnit, int demandForThisShipment) { + return getDefaultCalculation(deliveryDurationPerUnit, demandForThisShipment); + } + + @Override + public void recalculateServiceDurations(Carrier thisCarrier) { + // Do nothing + } + + @Override + public void recalculateShipmentDurations(Carrier thisCarrier) { + // Do nothing + } + + /** + * @param serviceTimePerUnit service time per unit + * @param demandForThisService demand for this service + * @return default calculation + */ + private int getDefaultCalculation(int serviceTimePerUnit, int demandForThisService) { + if (demandForThisService == 0) + return serviceTimePerUnit; + else + return serviceTimePerUnit * demandForThisService; + } +} diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index 25041119435..7407561a78c 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -324,16 +324,18 @@ public String getTypeOfDemand() { * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file * @param population Population * @param shapeCategory Column name in the shape file for the data connection in the csv files - * @throws IOException if the csv file cannot be read + * @param jobDurationCalculator Calculator for the job duration + * @throws IOException if the csv file cannot be read */ static void readAndCreateDemand(Scenario scenario, Path csvLocationDemand, ShpOptions.Index indexShape, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape, Population population, String shapeCategory) throws IOException { + CoordinateTransformation crsTransformationNetworkAndShape, Population population, String shapeCategory, + JobDurationCalculator jobDurationCalculator) throws IOException { Set demandInformation = readDemandInformation(csvLocationDemand); checkNewDemand(scenario, demandInformation, indexShape, shapeCategory); createDemandForCarriers(scenario, indexShape, demandInformation, population, combineSimilarJobs, - crsTransformationNetworkAndShape); + crsTransformationNetworkAndShape, jobDurationCalculator); } /** @@ -538,25 +540,26 @@ static void checkNewDemand(Scenario scenario, Set dema /** * Creates for every demand information the services/shipments for the carriers * - * @param scenario Scenario - * @param indexShape ShpOptions.Index for the shape file - * @param demandInformation Set with the demand information - * @param population Population - * @param combineSimilarJobs boolean if the jobs of the same carrier with same location and time will be combined - * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param scenario Scenario + * @param indexShape ShpOptions.Index for the shape file + * @param demandInformation Set with the demand information + * @param population Population + * @param combineSimilarJobs boolean if the jobs of the same carrier with same location and time will be combined + * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param jobDurationCalculator Calculator for the job duration */ static void createDemandForCarriers(Scenario scenario, ShpOptions.Index indexShape, - Set demandInformation, Population population, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape) { + Set demandInformation, Population population, boolean combineSimilarJobs, + CoordinateTransformation crsTransformationNetworkAndShape, JobDurationCalculator jobDurationCalculator) { for (DemandInformationElement newDemandInformationElement : demandInformation) { log.info("Create demand for carrier {}", newDemandInformationElement.getCarrierName()); if (newDemandInformationElement.getTypeOfDemand().equals("service")) - createServices(scenario, newDemandInformationElement, indexShape, population, combineSimilarJobs, - crsTransformationNetworkAndShape); + createServices(scenario, newDemandInformationElement, indexShape, population, + crsTransformationNetworkAndShape, jobDurationCalculator); else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) - createShipments(scenario, newDemandInformationElement, indexShape, population, combineSimilarJobs, - crsTransformationNetworkAndShape); + createShipments(scenario, newDemandInformationElement, indexShape, population, + crsTransformationNetworkAndShape, jobDurationCalculator); } } @@ -564,16 +567,16 @@ else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) /** * Creates the services. * - * @param scenario Scenario - * @param newDemandInformationElement single DemandInformationElement - * @param indexShape ShpOptions.Index - * @param population Population - * @param combineSimilarJobs boolean if the jobs of the same carrier with same location and time will be combined - * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param scenario Scenario + * @param newDemandInformationElement single DemandInformationElement + * @param indexShape ShpOptions.Index + * @param population Population + * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param jobDurationCalculator Calculator for the job duration */ private static void createServices(Scenario scenario, DemandInformationElement newDemandInformationElement, - ShpOptions.Index indexShape, Population population, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape) { + ShpOptions.Index indexShape, Population population, + CoordinateTransformation crsTransformationNetworkAndShape, JobDurationCalculator jobDurationCalculator) { int countOfLinks = 1; int distributedDemand = 0; @@ -643,8 +646,8 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { Link link = findNextUsedLink(scenario, indexShape, possibleLinksForService, numberOfJobs, areasForServiceLocations, locationsOfServices, usedServiceLocations, possiblePersonsForService, nearestLinkPerPerson, crsTransformationNetworkAndShape, i); - double serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit(); int demandForThisLink = 1; + double serviceTime = jobDurationCalculator.calculateServiceDuration(newDemandInformationElement.getFirstJobElementTimePerUnit(), demandForThisLink); usedServiceLocations.add(link.getId().toString()); Id idNewService = Id.create( createJobId(scenario, newDemandInformationElement, link.getId(), null), @@ -680,6 +683,9 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { singleDemandForThisLink = demandForThisLink - (numberOfJobsForDemand - 1) * singleDemandForThisLink; double serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit() * singleDemandForThisLink; + double serviceTime = jobDurationCalculator.calculateServiceDuration( + newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); + Id idNewService = Id.create( createJobId(scenario, newDemandInformationElement, link.getId(), null), CarrierService.class); @@ -728,6 +734,8 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit(); else serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit() * demandForThisLink; + double serviceTime = jobDurationCalculator.calculateServiceDuration( + newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); usedServiceLocations.add(link.getId().toString()); Id idNewService = Id.create( @@ -752,16 +760,16 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { /** * Creates the shipments of a carrier. * - * @param scenario Scenario - * @param newDemandInformationElement single DemandInformationElement - * @param indexShape ShpOptions.Index for the shape file - * @param population Population - * @param combineSimilarJobs boolean if the jobs of the same carrier with same location and time will be combined - * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param scenario Scenario + * @param newDemandInformationElement single DemandInformationElement + * @param indexShape ShpOptions.Index for the shape file + * @param population Population + * @param crsTransformationNetworkAndShape CoordinateTransformation for the network and shape file + * @param jobDurationCalculator Calculator for the job duration */ private static void createShipments(Scenario scenario, DemandInformationElement newDemandInformationElement, - ShpOptions.Index indexShape, Population population, boolean combineSimilarJobs, - CoordinateTransformation crsTransformationNetworkAndShape) { + ShpOptions.Index indexShape, Population population, + CoordinateTransformation crsTransformationNetworkAndShape, JobDurationCalculator jobDurationCalculator) { int countOfLinks = 1; int distributedDemand = 0; @@ -900,7 +908,7 @@ else if (population == null) if (!usedDeliveryLocations.contains(linkDelivery.getId().toString())) usedDeliveryLocations.add(linkDelivery.getId().toString()); - createSingleShipment(scenario, newDemandInformationElement, linkPickup, linkDelivery, demandForThisLink); + createSingleShipment(scenario, newDemandInformationElement, linkPickup, linkDelivery, demandForThisLink, jobDurationCalculator); } } else // creates a demand on each link, demand depends on the length of the link @@ -968,7 +976,7 @@ else if (numberOfPickupLocations != null) { if (demandForThisLink > 0) { createSingleShipment(scenario, newDemandInformationElement, linkPickup, linkDelivery, - demandForThisLink); + demandForThisLink, jobDurationCalculator); } distributedDemand = distributedDemand + demandForThisLink; } @@ -998,7 +1006,7 @@ else if (numberOfPickupLocations != null) { usedDeliveryLocations.add(linkDelivery.getId().toString()); createSingleShipment(scenario, newDemandInformationElement, linkPickup, linkDelivery, - demandForThisLink); + demandForThisLink, jobDurationCalculator); distributedDemand = distributedDemand + demandForThisLink; } } @@ -1012,9 +1020,10 @@ else if (numberOfPickupLocations != null) { * @param linkPickup Link for the pickup * @param linkDelivery Link for the delivery * @param demandForThisLink Demand for this link + * @param jobDurationCalculator Calculator for the job duration */ private static void createSingleShipment(Scenario scenario, DemandInformationElement newDemandInformationElement, - Link linkPickup, Link linkDelivery, int demandForThisLink) { + Link linkPickup, Link linkDelivery, int demandForThisLink, JobDurationCalculator jobDurationCalculator) { Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); @@ -1038,6 +1047,9 @@ private static void createSingleShipment(Scenario scenario, DemandInformationEle serviceTimePickup = newDemandInformationElement.getFirstJobElementTimePerUnit() * singleDemandForThisLink; serviceTimeDelivery = newDemandInformationElement.getSecondJobElementTimePerUnit() * singleDemandForThisLink; } + double serviceTimePickup = jobDurationCalculator.calculatePickupDuration(newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); + double serviceTimeDelivery = jobDurationCalculator.calculateDeliveryDuration(newDemandInformationElement.getSecondJobElementTimePerUnit(), singleDemandForThisLink); + CarrierShipment thisShipment = CarrierShipment.Builder .newInstance(idNewShipment, linkPickup.getId(), linkDelivery.getId(), singleDemandForThisLink) .setPickupServiceTime(serviceTimePickup).setPickupTimeWindow(timeWindowPickup) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java index 368dddaad69..ac3c44cb5a6 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/FreightDemandGeneration.java @@ -62,6 +62,8 @@ + " * these files are given in the example project. See: TODO", showDefaultValues = true) public class FreightDemandGeneration implements MATSimAppCommand { + private final JobDurationCalculator jobDurationCalculator; + private enum CarrierInputOptions { readCarrierFile, createCarriersFromCSV, addCSVDataToExistingCarrierFileData } @@ -154,6 +156,16 @@ private enum OptionsOfVRPSolutions { @CommandLine.Option(names = "--defaultJspritIterations", description = "Set the default number of jsprit iterations.") private int defaultJspritIterations; + public FreightDemandGeneration() { + this.jobDurationCalculator = new DefaultJobDurationCalculator(); + log.info("Using default {} for job duration calculation", jobDurationCalculator.getClass().getSimpleName()); + } + + public FreightDemandGeneration(JobDurationCalculator jobDurationCalculator) { + this.jobDurationCalculator = jobDurationCalculator; + log.info("Using {} for job duration calculation", jobDurationCalculator.getClass().getSimpleName()); + } + public static void main(String[] args) { System.exit(new CommandLine(new FreightDemandGeneration()).execute(args)); } @@ -353,7 +365,7 @@ private void createDemand(DemandGenerationOptions selectedDemandGenerationOption case createDemandFromCSV -> // creates the demand by using the information given in the read csv file DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, combineSimilarJobs, - crsTransformationNetworkAndShape, null, shapeCategory); + crsTransformationNetworkAndShape, null, shapeCategory, jobDurationCalculator); case createDemandFromCSVAndUsePopulation -> { /* * Option creates the demand by using the information given in the read csv file @@ -399,14 +411,14 @@ private void createDemand(DemandGenerationOptions selectedDemandGenerationOption case useHolePopulation: // uses the hole population as possible demand locations DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, - combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory); + combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory, jobDurationCalculator); break; case usePopulationInShape: // uses only the population with home location in the given shape file FreightDemandGenerationUtils.reducePopulationToShapeArea(population, shp.createIndex(populationCRS, "_")); DemandReaderFromCSV.readAndCreateDemand(scenario, csvLocationDemand, indexShape, - combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory); + combineSimilarJobs, crsTransformationNetworkAndShape, population, shapeCategory, jobDurationCalculator); break; default: throw new RuntimeException("No valid population option selected!"); diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java new file mode 100644 index 00000000000..3c38935138d --- /dev/null +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java @@ -0,0 +1,15 @@ +package org.matsim.freightDemandGeneration; + +import org.matsim.freight.carriers.Carrier; + +public interface JobDurationCalculator { + double calculateServiceDuration(Integer serviceDurationPerUnit, int demandForThisService); + + double calculatePickupDuration(Integer pickupDurationPerUnit, int demandForThisShipment); + + double calculateDeliveryDuration(Integer deliveryDurationPerUnit, int demandForThisShipment); + + void recalculateServiceDurations(Carrier thisCarrier); + + void recalculateShipmentDurations(Carrier thisCarrier); +} From ed9cd02dd5d9ecc6a07ff4bcaa56f1c36b2d0102 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 11:18:41 +0100 Subject: [PATCH 5/8] update method of cutting jobs in possible parts --- .../DemandReaderFromCSV.java | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index 7407561a78c..44e4b158650 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -676,13 +676,16 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { countOfLinks++; Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); - int numberOfJobsForDemand = calculateNumberOfJobsForDemand(thisCarrier, demandForThisLink); - for (int i = 0; i < numberOfJobsForDemand; i++) { - int singleDemandForThisLink = demandForThisLink / numberOfJobsForDemand; - if (i == numberOfJobsForDemand - 1) - singleDemandForThisLink = demandForThisLink - (numberOfJobsForDemand - 1) * singleDemandForThisLink; - double serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit() - * singleDemandForThisLink; + int handledDemand = 0; + //the number of jobs on this link is calculated based on the available vehicles + double largestPossibleDemandSize = getLargestVehicleCapacity(thisCarrier); + while (handledDemand < demandForThisLink) { + int singleDemandForThisLink; + if (demandForThisLink - handledDemand < largestPossibleDemandSize) + singleDemandForThisLink = demandForThisLink - handledDemand; + else + singleDemandForThisLink = (int)largestPossibleDemandSize; + handledDemand = handledDemand + singleDemandForThisLink; double serviceTime = jobDurationCalculator.calculateServiceDuration( newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); @@ -724,16 +727,16 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { int demandForThisLink = calculateDemandForThisLink(demandToDistribute, numberOfJobs, distributedDemand, i); Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); - int numberOfJobsForDemand = calculateNumberOfJobsForDemand(thisCarrier, demandForThisLink); - for (int j = 0; j < numberOfJobsForDemand; j++) { - int singleDemandForThisLink = demandForThisLink / numberOfJobsForDemand; - if (j == numberOfJobsForDemand - 1) - singleDemandForThisLink = demandForThisLink - (numberOfJobsForDemand - 1) * singleDemandForThisLink; - double serviceTime; - if (singleDemandForThisLink == 0) - serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit(); + int handledDemand = 0; + //the number of jobs on this link is calculated based on the available vehicles + double largestPossibleDemandSize = getLargestVehicleCapacity(thisCarrier); + while (handledDemand < demandForThisLink || demandToDistribute == 0) { + int singleDemandForThisLink; + if (demandForThisLink - handledDemand < largestPossibleDemandSize) + singleDemandForThisLink = demandForThisLink - handledDemand; else - serviceTime = newDemandInformationElement.getFirstJobElementTimePerUnit() * demandForThisLink; + singleDemandForThisLink = (int)largestPossibleDemandSize; + handledDemand = handledDemand + singleDemandForThisLink; double serviceTime = jobDurationCalculator.calculateServiceDuration( newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); usedServiceLocations.add(link.getId().toString()); @@ -748,6 +751,8 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)).getServices() .put(thisService.getId(), thisService); + if (demandToDistribute == 0) + break; } } distributedDemand = distributedDemand + demandForThisLink; @@ -1014,7 +1019,9 @@ else if (numberOfPickupLocations != null) { combineSimilarJobs(scenario, newDemandInformationElement); } - /** Creates a single shipment. + /** + * Creates a single shipment. + * * @param scenario Scenario * @param newDemandInformationElement single DemandInformationElement * @param linkPickup Link for the pickup @@ -1027,26 +1034,20 @@ private static void createSingleShipment(Scenario scenario, DemandInformationEle Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); - int numberOfJobsForDemand = calculateNumberOfJobsForDemand(thisCarrier, demandForThisLink); - + double largestPossibleDemandSize = getLargestVehicleCapacity(thisCarrier); + int handledDemand = 0; TimeWindow timeWindowPickup = newDemandInformationElement.getFirstJobElementTimeWindow(); TimeWindow timeWindowDelivery = newDemandInformationElement.getSecondJobElementTimeWindow(); - for (int i = 0; i < numberOfJobsForDemand; i++) { + while (handledDemand < demandForThisLink || demandForThisLink == 0) { Id idNewShipment = Id.create(createJobId(scenario, newDemandInformationElement, linkPickup.getId(), linkDelivery.getId()), CarrierShipment.class); - double serviceTimePickup; - double serviceTimeDelivery; - int singleDemandForThisLink = Math.round ((float) demandForThisLink / numberOfJobsForDemand); - if (i == numberOfJobsForDemand - 1) - singleDemandForThisLink = demandForThisLink - (numberOfJobsForDemand - 1) * singleDemandForThisLink; - if (singleDemandForThisLink == 0) { - serviceTimePickup = newDemandInformationElement.getFirstJobElementTimePerUnit(); - serviceTimeDelivery = newDemandInformationElement.getSecondJobElementTimePerUnit(); - } else { - serviceTimePickup = newDemandInformationElement.getFirstJobElementTimePerUnit() * singleDemandForThisLink; - serviceTimeDelivery = newDemandInformationElement.getSecondJobElementTimePerUnit() * singleDemandForThisLink; - } + int singleDemandForThisLink; + if (demandForThisLink - handledDemand < largestPossibleDemandSize) + singleDemandForThisLink = demandForThisLink - handledDemand; + else + singleDemandForThisLink = (int)largestPossibleDemandSize; + handledDemand = handledDemand + singleDemandForThisLink; double serviceTimePickup = jobDurationCalculator.calculatePickupDuration(newDemandInformationElement.getFirstJobElementTimePerUnit(), singleDemandForThisLink); double serviceTimeDelivery = jobDurationCalculator.calculateDeliveryDuration(newDemandInformationElement.getSecondJobElementTimePerUnit(), singleDemandForThisLink); @@ -1056,6 +1057,8 @@ private static void createSingleShipment(Scenario scenario, DemandInformationEle .setDeliveryServiceTime(serviceTimeDelivery).setDeliveryTimeWindow(timeWindowDelivery) .build(); thisCarrier.getShipments().put(thisShipment.getId(), thisShipment); + if (demandForThisLink == 0) + break; } } @@ -1063,21 +1066,16 @@ private static void createSingleShipment(Scenario scenario, DemandInformationEle * Method calculates the number of jobs for a demand on one link based on the largest vehicle capacity of the carrier. * * @param thisCarrier the carrier of a job - * @param demandForThisLink Demand for this link * @return Number of jobs for this demand */ - private static int calculateNumberOfJobsForDemand(Carrier thisCarrier, int demandForThisLink) { + private static double getLargestVehicleCapacity(Carrier thisCarrier) { double largestVehicleCapacity = 0; for (CarrierVehicle vehicle : thisCarrier.getCarrierCapabilities().getCarrierVehicles().values()) { if (vehicle.getType().getCapacity().getOther() > largestVehicleCapacity) { largestVehicleCapacity = vehicle.getType().getCapacity().getOther(); } } - if (demandForThisLink > largestVehicleCapacity) { - log.info("Demand {} is larger than the largest vehicle capacity ({}). Splitting demand into multiple jobs.", demandForThisLink, largestVehicleCapacity); - return (int) Math.ceil((double) demandForThisLink / largestVehicleCapacity); - } - return 1; + return largestVehicleCapacity; } /** From 0a1da4dd34d2e0b79a3159bfbc1b45fe0c025cb9 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 11:21:05 +0100 Subject: [PATCH 6/8] update combineJobs method and ad related test --- .../DemandReaderFromCSV.java | 158 ++++++++-------- .../DemandReaderFromCSVTest.java | 172 +++++++++++++++++- 2 files changed, 239 insertions(+), 91 deletions(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index 44e4b158650..3935e982e8f 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -561,7 +561,8 @@ else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) createShipments(scenario, newDemandInformationElement, indexShape, population, crsTransformationNetworkAndShape, jobDurationCalculator); } - + if (combineSimilarJobs) + combineSimilarJobs(scenario, jobDurationCalculator); } /** @@ -758,8 +759,6 @@ else if (samplingOption.equals("changeNumberOfLocationsWithDemand")) { distributedDemand = distributedDemand + demandForThisLink; } } - if (combineSimilarJobs) - combineSimilarJobs(scenario, newDemandInformationElement); } /** @@ -1015,8 +1014,6 @@ else if (numberOfPickupLocations != null) { distributedDemand = distributedDemand + demandForThisLink; } } - if (combineSimilarJobs) - combineSimilarJobs(scenario, newDemandInformationElement); } /** @@ -1170,109 +1167,104 @@ private static int calculateDemandBasedOnLinkLength(int countOfLinks, int distri * If jobs of a carrier have the same characteristics (time window, location), * they will be combined to one job. * - * @param scenario Scenario - * @param newDemandInformationElement single DemandInformationElement + * @param scenario Scenario + * @param jobDurationCalculator Calculator for the job duration */ - private static void combineSimilarJobs(Scenario scenario, - DemandInformationElement newDemandInformationElement) { + private static void combineSimilarJobs(Scenario scenario, JobDurationCalculator jobDurationCalculator) { log.warn( "The number of Jobs will be reduced if jobs have the same characteristics (e.g. time, location, carrier)"); - int connectedJobs = 0; - if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) { - HashMap, CarrierShipment> shipmentsToRemove = new HashMap<>(); - ArrayList shipmentsToAdd = new ArrayList<>(); - Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() - .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); - for (Id baseShipmentId : thisCarrier.getShipments().keySet()) { - if (!shipmentsToRemove.containsKey(baseShipmentId)) { - CarrierShipment baseShipment = thisCarrier.getShipments().get(baseShipmentId); - HashMap, CarrierShipment> shipmentsToConnect = new HashMap<>(); - shipmentsToConnect.put(baseShipmentId, baseShipment); - for (Id thisShipmentId : thisCarrier.getShipments().keySet()) { - if (!shipmentsToRemove.containsKey(thisShipmentId)) { - CarrierShipment thisShipment = thisCarrier.getShipments().get(thisShipmentId); - if (baseShipment.getId() != thisShipment.getId() + for (Carrier thisCarrier : CarriersUtils.getCarriers(scenario).getCarriers().values()) { + if (!thisCarrier.getShipments().isEmpty()) { + int shipmentsBeforeConnection = thisCarrier.getShipments().size(); + HashMap, CarrierShipment> shipmentsToRemove = new HashMap<>(); + ArrayList shipmentsToAdd = new ArrayList<>(); + for (Id baseShipmentId : thisCarrier.getShipments().keySet()) { + if (!shipmentsToRemove.containsKey(baseShipmentId)) { + CarrierShipment baseShipment = thisCarrier.getShipments().get(baseShipmentId); + HashMap, CarrierShipment> shipmentsToConnect = new HashMap<>(); + shipmentsToConnect.put(baseShipmentId, baseShipment); + for (Id thisShipmentId : thisCarrier.getShipments().keySet()) { + if (!shipmentsToRemove.containsKey(thisShipmentId)) { + CarrierShipment thisShipment = thisCarrier.getShipments().get(thisShipmentId); + if (baseShipment.getId() != thisShipment.getId() && baseShipment.getFrom() == thisShipment.getFrom() && baseShipment.getTo() == thisShipment.getTo() && baseShipment.getPickupTimeWindow() == thisShipment.getPickupTimeWindow() && baseShipment.getDeliveryTimeWindow() == thisShipment.getDeliveryTimeWindow()) - shipmentsToConnect.put(thisShipmentId, thisShipment); + shipmentsToConnect.put(thisShipmentId, thisShipment); + } } - } - Id idNewShipment = baseShipment.getId(); - int demandForThisLink = 0; - double serviceTimePickup = 0; - double serviceTimeDelivery = 0; - for (CarrierShipment carrierShipment : shipmentsToConnect.values()) { - demandForThisLink = demandForThisLink + carrierShipment.getSize(); - serviceTimePickup = serviceTimePickup + carrierShipment.getPickupServiceTime(); - serviceTimeDelivery = serviceTimeDelivery + carrierShipment.getDeliveryServiceTime(); - shipmentsToRemove.put(carrierShipment.getId(), carrierShipment); - connectedJobs++; - } - CarrierShipment newShipment = CarrierShipment.Builder + Id idNewShipment = baseShipment.getId(); + int demandForThisLink = 0; + double serviceTimePickup = 0; + double serviceTimeDelivery = 0; + for (CarrierShipment carrierShipment : shipmentsToConnect.values()) { + demandForThisLink = demandForThisLink + carrierShipment.getSize(); + serviceTimePickup = serviceTimePickup + carrierShipment.getPickupServiceTime(); + serviceTimeDelivery = serviceTimeDelivery + carrierShipment.getDeliveryServiceTime(); + shipmentsToRemove.put(carrierShipment.getId(), carrierShipment); + } + CarrierShipment newShipment = CarrierShipment.Builder .newInstance(idNewShipment, baseShipment.getFrom(), baseShipment.getTo(), demandForThisLink) .setPickupServiceTime(serviceTimePickup) .setPickupTimeWindow(baseShipment.getPickupTimeWindow()) .setDeliveryServiceTime(serviceTimeDelivery) .setDeliveryTimeWindow(baseShipment.getDeliveryTimeWindow()).build(); - - shipmentsToAdd.add(newShipment); - connectedJobs++; + shipmentsToAdd.add(newShipment); + } } - } - for (CarrierShipment id : shipmentsToRemove.values()) - thisCarrier.getShipments().remove(id.getId(), id); + for (CarrierShipment id : shipmentsToRemove.values()) + thisCarrier.getShipments().remove(id.getId(), id); - for (CarrierShipment carrierShipment : shipmentsToAdd) { - thisCarrier.getShipments().put(carrierShipment.getId(), carrierShipment); + for (CarrierShipment carrierShipment : shipmentsToAdd) { + thisCarrier.getShipments().put(carrierShipment.getId(), carrierShipment); + } + jobDurationCalculator.recalculateShipmentDurations(thisCarrier); + log.warn("Number of reduced shipments for carrier {}: {}", thisCarrier.getId().toString(), shipmentsBeforeConnection - thisCarrier.getShipments().size()); } - log.warn("Number of reduced shipments: {}", connectedJobs); - } - if (newDemandInformationElement.getTypeOfDemand().equals("service")) { - HashMap, CarrierService> servicesToRemove = new HashMap<>(); - ArrayList servicesToAdd = new ArrayList<>(); - Carrier thisCarrier = CarriersUtils.getCarriers(scenario).getCarriers() - .get(Id.create(newDemandInformationElement.getCarrierName(), Carrier.class)); - for (Id baseServiceId : thisCarrier.getServices().keySet()) { - if (!servicesToRemove.containsKey(baseServiceId)) { - CarrierService baseService = thisCarrier.getServices().get(baseServiceId); - HashMap, CarrierService> servicesToConnect = new HashMap<>(); - servicesToConnect.put(baseServiceId, baseService); - for (Id thisServiceId : thisCarrier.getServices().keySet()) { - if (!servicesToRemove.containsKey(thisServiceId)) { - CarrierService thisService = thisCarrier.getServices().get(thisServiceId); - if (baseService.getId() != thisService.getId() + if (!thisCarrier.getServices().isEmpty()) { + int servicesBeforeConnection = thisCarrier.getServices().size(); + HashMap, CarrierService> servicesToRemove = new HashMap<>(); + ArrayList servicesToAdd = new ArrayList<>(); + for (Id baseServiceId : thisCarrier.getServices().keySet()) { + if (!servicesToRemove.containsKey(baseServiceId)) { + CarrierService baseService = thisCarrier.getServices().get(baseServiceId); + HashMap, CarrierService> servicesToConnect = new HashMap<>(); + servicesToConnect.put(baseServiceId, baseService); + for (Id thisServiceId : thisCarrier.getServices().keySet()) { + if (!servicesToRemove.containsKey(thisServiceId)) { + CarrierService thisService = thisCarrier.getServices().get(thisServiceId); + if (baseService.getId() != thisService.getId() && baseService.getLocationLinkId() == thisService.getLocationLinkId() && baseService - .getServiceStartTimeWindow() == thisService.getServiceStartTimeWindow()) - servicesToConnect.put(thisServiceId, thisService); + .getServiceStartTimeWindow() == thisService.getServiceStartTimeWindow()) + servicesToConnect.put(thisServiceId, thisService); + } } - } - Id idNewService = baseService.getId(); - int demandForThisLink = 0; - double serviceTimeService = 0; - for (CarrierService carrierService : servicesToConnect.values()) { - demandForThisLink = demandForThisLink + carrierService.getCapacityDemand(); - serviceTimeService = serviceTimeService + carrierService.getServiceDuration(); - servicesToRemove.put(carrierService.getId(), carrierService); - connectedJobs++; - } - CarrierService newService = CarrierService.Builder + Id idNewService = baseService.getId(); + int demandForThisLink = 0; + double serviceTimeService = 0; + for (CarrierService carrierService : servicesToConnect.values()) { + demandForThisLink = demandForThisLink + carrierService.getCapacityDemand(); + serviceTimeService = serviceTimeService + carrierService.getServiceDuration(); + servicesToRemove.put(carrierService.getId(), carrierService); + } + CarrierService newService = CarrierService.Builder .newInstance(idNewService, baseService.getLocationLinkId()) .setServiceDuration(serviceTimeService) .setServiceStartTimeWindow(baseService.getServiceStartTimeWindow()) .setCapacityDemand(demandForThisLink).build(); - servicesToAdd.add(newService); - connectedJobs++; + servicesToAdd.add(newService); + } } + for (CarrierService id : servicesToRemove.values()) + thisCarrier.getServices().remove(id.getId(), id); + for (CarrierService carrierService : servicesToAdd) { + thisCarrier.getServices().put(carrierService.getId(), carrierService); + } + jobDurationCalculator.recalculateServiceDurations(thisCarrier); + log.warn("Number of reduced services for carrier {}: {}", thisCarrier.getId().toString(), servicesBeforeConnection - thisCarrier.getServices().size()); } - for (CarrierService id : servicesToRemove.values()) - thisCarrier.getServices().remove(id.getId(), id); - for (CarrierService carrierService : servicesToAdd) { - thisCarrier.getServices().put(carrierService.getId(), carrierService); - } - log.warn("Number of reduced shipments: {}", connectedJobs); } } diff --git a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java index c27b251afc4..9cd21c16d9d 100644 --- a/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java +++ b/contribs/application/src/test/java/org/matsim/freightDemandGeneration/DemandReaderFromCSVTest.java @@ -79,10 +79,10 @@ void demandCreationWithSampleWithChangeNumberOfLocations() throws IOException { String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); FreightDemandGenerationUtils.preparePopulation(population, 0.5, 1.0, "changeNumberOfLocationsWithDemand"); - + Boolean combineSimilarJobs = false; // run methods createDemandAndCheckCarrier(carrierCSVLocation, scenario, freightCarriersConfigGroup, indexShape, demandCSVLocation, shapeCategory, - population); + population, combineSimilarJobs); Network network = scenario.getNetwork(); @@ -147,10 +147,11 @@ void demandCreationWithSampleWithDemandOnLocation() throws IOException { String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); FreightDemandGenerationUtils.preparePopulation(population, 0.5, 1.0, "changeDemandOnLocation"); + Boolean combineSimilarJobs = false; - createDemandAndCheckCarrier(carrierCSVLocation, scenario, freightCarriersConfigGroup, indexShape, demandCSVLocation, shapeCategory, population); + createDemandAndCheckCarrier(carrierCSVLocation, scenario, freightCarriersConfigGroup, indexShape, demandCSVLocation, shapeCategory, + population, combineSimilarJobs); - // check carrier 1 Network network = scenario.getNetwork(); checkCarrier1and2(scenario, network, indexShape); @@ -194,6 +195,74 @@ void demandCreationWithSampleWithDemandOnLocation() throws IOException { } } + @Test + void demandCreationWithSampleWithDemandOnLocationWithCombiningJobs() throws IOException { + // read inputs + Config config = ConfigUtils.createConfig(); + config.network().setInputFile( + "https://raw.githubusercontent.com/matsim-org/matsim-libs/master/examples/scenarios/freight-chessboard-9x9/grid9x9.xml"); + Scenario scenario = ScenarioUtils.loadScenario(config); + FreightCarriersConfigGroup freightCarriersConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), + FreightCarriersConfigGroup.class); + freightCarriersConfigGroup.setCarriersVehicleTypesFile(utils.getPackageInputDirectory() + "testVehicleTypes.xml"); + Path carrierCSVLocation = Path.of(utils.getPackageInputDirectory() + "testCarrierCSV.csv"); + Path demandCSVLocation = Path.of(utils.getPackageInputDirectory() + "testDemandCSV.csv"); + Path shapeFilePath = Path.of(utils.getPackageInputDirectory() + "testShape/testShape.shp"); + ShpOptions shp = new ShpOptions(shapeFilePath, "WGS84", null); + String shapeCategory = "Ortsteil"; + ShpOptions.Index indexShape = shp.createIndex("Ortsteil"); + String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; + Population population = PopulationUtils.readPopulation(populationLocation); + FreightDemandGenerationUtils.preparePopulation(population, 0.5, 1.0, "changeDemandOnLocation"); + Boolean combineSimilarJobs = true; + + createDemandAndCheckCarrier(carrierCSVLocation, scenario, freightCarriersConfigGroup, indexShape, demandCSVLocation, shapeCategory, + population, combineSimilarJobs); + + Network network = scenario.getNetwork(); + + checkCarrier1and2WithCombiningJobs(scenario, network, indexShape); + int countDemand; + Object2IntMap countShipmentsWithCertainDemand; + Map> locationsPerShipmentElement; + + // check carrier 3 + Carrier testCarrier3 = CarriersUtils.getCarriers(scenario).getCarriers() + .get(Id.create("testCarrier3", Carrier.class)); + Assertions.assertEquals(0, testCarrier3.getServices().size()); + Assertions.assertEquals(2, testCarrier3.getShipments().size()); + countShipmentsWithCertainDemand = new Object2IntOpenHashMap<>(); + locationsPerShipmentElement = new HashMap<>(); + countDemand = 0; + for (CarrierShipment shipment : testCarrier3.getShipments().values()) { + countShipmentsWithCertainDemand.merge((Integer) shipment.getSize(), 1, Integer::sum); + countDemand = countDemand + shipment.getSize(); + Assertions.assertEquals(10, shipment.getSize()); + Assertions.assertEquals(4000, shipment.getPickupServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(2500, shipment.getDeliveryServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(TimeWindow.newInstance(8000, 50000), shipment.getPickupTimeWindow()); + Assertions.assertEquals(TimeWindow.newInstance(10000, 60000), shipment.getDeliveryTimeWindow()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement1_pickup", (k) -> new HashSet<>()) + .add(shipment.getFrom().toString()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement1_delivery", (k) -> new HashSet<>()) + .add(shipment.getTo().toString()); + } + Assertions.assertEquals(20, countDemand); + Assertions.assertEquals(2, countShipmentsWithCertainDemand.getInt(10)); + Assertions.assertEquals(1, locationsPerShipmentElement.get("ShipmentElement1_pickup").size()); + Assertions.assertEquals(2, locationsPerShipmentElement.get("ShipmentElement1_delivery").size()); + for (String locationsOfShipmentElement : locationsPerShipmentElement.get("ShipmentElement1_delivery")) { + Link link = network.getLinks().get(Id.createLinkId(locationsOfShipmentElement)); + Assertions.assertTrue( + FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, + new String[] { "area1" }, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, + new String[] { "area2" }, null)); + } + } + + @Test void demandCreationNoSampling() throws IOException { // read inputs @@ -213,10 +282,11 @@ void demandCreationNoSampling() throws IOException { String populationLocation = utils.getPackageInputDirectory() + "testPopulation.xml"; Population population = PopulationUtils.readPopulation(populationLocation); FreightDemandGenerationUtils.preparePopulation(population, 0.5, 0.5, "changeDemandOnLocation"); + Boolean combineSimilarJobs = false; // run methods createDemandAndCheckCarrier(carrierCSVLocation, scenario, freightCarriersConfigGroup, indexShape, demandCSVLocation, shapeCategory, - population); + population, combineSimilarJobs); // check carrier 1 Network network = scenario.getNetwork(); @@ -367,7 +437,8 @@ void csvDemandReader() throws IOException { private static void createDemandAndCheckCarrier(Path carrierCSVLocation, Scenario scenario, FreightCarriersConfigGroup freightCarriersConfigGroup, ShpOptions.Index indexShape, Path demandCSVLocation, String shapeCategory, - Population population) throws IOException { + Population population, Boolean combineSimilarJobs) throws IOException { + JobDurationCalculator jobDurationCalculator = new DefaultJobDurationCalculator(); // run methods Set allNewCarrierInformation = CarrierReaderFromCSV .readCarrierInformation(carrierCSVLocation); @@ -375,8 +446,8 @@ private static void createDemandAndCheckCarrier(Path carrierCSVLocation, Scenari indexShape, 1, null); Set demandInformation = DemandReaderFromCSV.readDemandInformation(demandCSVLocation); DemandReaderFromCSV.checkNewDemand(scenario, demandInformation, indexShape, shapeCategory); - DemandReaderFromCSV.createDemandForCarriers(scenario, indexShape, demandInformation, population, false, - null); + DemandReaderFromCSV.createDemandForCarriers(scenario, indexShape, demandInformation, population, combineSimilarJobs, + null, jobDurationCalculator); Assertions.assertEquals(3, CarriersUtils.getCarriers(scenario).getCarriers().size()); Assertions.assertTrue( CarriersUtils.getCarriers(scenario).getCarriers().containsKey(Id.create("testCarrier1", Carrier.class))); @@ -491,4 +562,89 @@ private static void checkCarrier1and2(Scenario scenario, Network network, ShpOpt Assertions.assertEquals(1, locationsPerShipmentElement.get("ShipmentElement2_pickup").size()); Assertions.assertEquals(2, locationsPerShipmentElement.get("ShipmentElement2_delivery").size()); } + + /** + * Results after combing jobs. + * + * @param scenario the scenario + * @param network the network + * @param indexShape the index of the shape + */ + private static void checkCarrier1and2WithCombiningJobs(Scenario scenario, Network network, ShpOptions.Index indexShape) { + Carrier testCarrier1 = CarriersUtils.getCarriers(scenario).getCarriers() + .get(Id.create("testCarrier1", Carrier.class)); + Assertions.assertEquals(8, testCarrier1.getServices().size()); + Assertions.assertEquals(0, testCarrier1.getShipments().size()); + Object2IntMap countServicesWithCertainDemand = new Object2IntOpenHashMap<>(); + Map> locationsPerServiceElement = new HashMap<>(); + int countDemand = 0; + for (CarrierService service : testCarrier1.getServices().values()) { + countServicesWithCertainDemand.merge((Integer) service.getCapacityDemand(), 1, Integer::sum); + countDemand = countDemand + service.getCapacityDemand(); + if (service.getCapacityDemand() == 0) { + Assertions.assertEquals(180, service.getServiceDuration(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(TimeWindow.newInstance(3000, 13000), service.getServiceStartTimeWindow()); + locationsPerServiceElement.computeIfAbsent("serviceElement1", (k) -> new HashSet<>()) + .add(service.getLocationLinkId().toString()); + } else { + Assertions.assertEquals(service.getCapacityDemand() * 100, service.getServiceDuration(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(TimeWindow.newInstance(5000, 20000), service.getServiceStartTimeWindow()); + locationsPerServiceElement.computeIfAbsent("serviceElement2", (k) -> new HashSet<>()) + .add(service.getLocationLinkId().toString()); + } + } + Assertions.assertEquals(12, countDemand); + Assertions.assertEquals(4, countServicesWithCertainDemand.getInt(0)); + Assertions.assertEquals(4, locationsPerServiceElement.get("serviceElement1").size()); + for (String locationsOfServiceElement : locationsPerServiceElement.get("serviceElement1")) { + Link link = network.getLinks().get(Id.createLinkId(locationsOfServiceElement)); + Assertions.assertTrue( + FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, null, null)); + Assertions.assertFalse(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, + new String[] { "area1" }, null)); + Assertions.assertTrue(FreightDemandGenerationUtils.checkPositionInShape(link, null, indexShape, + new String[] { "area2" }, null)); + } + Assertions.assertEquals(4, locationsPerServiceElement.get("serviceElement2").size()); + Assertions.assertTrue(locationsPerServiceElement.get("serviceElement2").contains("i(2,0)")); + + // check carrier 2 + Carrier testCarrier2 = CarriersUtils.getCarriers(scenario).getCarriers() + .get(Id.create("testCarrier2", Carrier.class)); + Assertions.assertEquals(0, testCarrier2.getServices().size()); + Assertions.assertEquals(6, testCarrier2.getShipments().size()); + Object2IntMap countShipmentsWithCertainDemand = new Object2IntOpenHashMap<>(); + Map> locationsPerShipmentElement = new HashMap<>(); + countDemand = 0; + for (CarrierShipment shipment : testCarrier2.getShipments().values()) { + countShipmentsWithCertainDemand.merge((Integer) shipment.getSize(), 1, Integer::sum); + countDemand = countDemand + shipment.getSize(); + if (shipment.getSize() == 0) { + Assertions.assertEquals(300, shipment.getPickupServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(350, shipment.getDeliveryServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(TimeWindow.newInstance(10000, 45000), shipment.getPickupTimeWindow()); + Assertions.assertEquals(TimeWindow.newInstance(11000, 44000), shipment.getDeliveryTimeWindow()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement1_pickup", (k) -> new HashSet<>()) + .add(shipment.getFrom().toString()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement1_delivery", (k) -> new HashSet<>()) + .add(shipment.getTo().toString()); + } else { + Assertions.assertEquals(shipment.getSize() * 200, shipment.getPickupServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(shipment.getSize() * 200, shipment.getDeliveryServiceTime(), MatsimTestUtils.EPSILON); + Assertions.assertEquals(TimeWindow.newInstance(11000, 44000), shipment.getPickupTimeWindow()); + Assertions.assertEquals(TimeWindow.newInstance(20000, 40000), shipment.getDeliveryTimeWindow()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement2_pickup", (k) -> new HashSet<>()) + .add(shipment.getFrom().toString()); + locationsPerShipmentElement.computeIfAbsent("ShipmentElement2_delivery", (k) -> new HashSet<>()) + .add(shipment.getTo().toString()); + } + } + Assertions.assertEquals(15, countDemand); + Assertions.assertEquals(4, countShipmentsWithCertainDemand.getInt(0)); + Assertions.assertEquals(4, locationsPerShipmentElement.get("ShipmentElement1_pickup").size()); + Assertions.assertEquals(1, locationsPerShipmentElement.get("ShipmentElement1_delivery").size()); + Assertions.assertTrue(locationsPerShipmentElement.get("ShipmentElement1_delivery").contains("i(2,0)")); + Assertions.assertEquals(1, locationsPerShipmentElement.get("ShipmentElement2_pickup").size()); + Assertions.assertEquals(2, locationsPerShipmentElement.get("ShipmentElement2_delivery").size()); + } } From 2cce61477c003e836362ae21cd70c73fb4062297 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 21:21:59 +0100 Subject: [PATCH 7/8] relocate method --- .../freightDemandGeneration/DemandReaderFromCSV.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java index 3935e982e8f..63179128cc4 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DemandReaderFromCSV.java @@ -562,7 +562,8 @@ else if (newDemandInformationElement.getTypeOfDemand().equals("shipment")) crsTransformationNetworkAndShape, jobDurationCalculator); } if (combineSimilarJobs) - combineSimilarJobs(scenario, jobDurationCalculator); + combineSimilarJobs(scenario); + jobDurationCalculator.recalculateJobDurations(scenario); } /** @@ -1168,9 +1169,8 @@ private static int calculateDemandBasedOnLinkLength(int countOfLinks, int distri * they will be combined to one job. * * @param scenario Scenario - * @param jobDurationCalculator Calculator for the job duration */ - private static void combineSimilarJobs(Scenario scenario, JobDurationCalculator jobDurationCalculator) { + private static void combineSimilarJobs(Scenario scenario) { log.warn( "The number of Jobs will be reduced if jobs have the same characteristics (e.g. time, location, carrier)"); @@ -1220,7 +1220,7 @@ private static void combineSimilarJobs(Scenario scenario, JobDurationCalculator for (CarrierShipment carrierShipment : shipmentsToAdd) { thisCarrier.getShipments().put(carrierShipment.getId(), carrierShipment); } - jobDurationCalculator.recalculateShipmentDurations(thisCarrier); + log.warn("Number of reduced shipments for carrier {}: {}", thisCarrier.getId().toString(), shipmentsBeforeConnection - thisCarrier.getShipments().size()); } if (!thisCarrier.getServices().isEmpty()) { @@ -1262,7 +1262,6 @@ private static void combineSimilarJobs(Scenario scenario, JobDurationCalculator for (CarrierService carrierService : servicesToAdd) { thisCarrier.getServices().put(carrierService.getId(), carrierService); } - jobDurationCalculator.recalculateServiceDurations(thisCarrier); log.warn("Number of reduced services for carrier {}: {}", thisCarrier.getId().toString(), servicesBeforeConnection - thisCarrier.getServices().size()); } } From a79f6552d8aa23aa3c7a5a0f31f2923cc3cf3635 Mon Sep 17 00:00:00 2001 From: Ricardo Ewert Date: Fri, 22 Nov 2024 21:26:44 +0100 Subject: [PATCH 8/8] update interface and implementation --- .../DefaultJobDurationCalculator.java | 19 ++++------- .../JobDurationCalculator.java | 32 ++++++++++++++++--- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java index d64b7aad509..42ddbc4068a 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/DefaultJobDurationCalculator.java @@ -1,6 +1,6 @@ package org.matsim.freightDemandGeneration; -import org.matsim.freight.carriers.Carrier; +import org.matsim.api.core.v01.Scenario; public class DefaultJobDurationCalculator implements JobDurationCalculator { @Override @@ -19,24 +19,19 @@ public double calculateDeliveryDuration(Integer deliveryDurationPerUnit, int dem } @Override - public void recalculateServiceDurations(Carrier thisCarrier) { - // Do nothing - } - - @Override - public void recalculateShipmentDurations(Carrier thisCarrier) { - // Do nothing + public void recalculateJobDurations(Scenario scenario) { + // do nothing } /** - * @param serviceTimePerUnit service time per unit + * @param timePerUnit time per unit * @param demandForThisService demand for this service * @return default calculation */ - private int getDefaultCalculation(int serviceTimePerUnit, int demandForThisService) { + private int getDefaultCalculation(int timePerUnit, int demandForThisService) { if (demandForThisService == 0) - return serviceTimePerUnit; + return timePerUnit; else - return serviceTimePerUnit * demandForThisService; + return timePerUnit * demandForThisService; } } diff --git a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java index 3c38935138d..85d30908fa0 100644 --- a/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java +++ b/contribs/application/src/main/java/org/matsim/freightDemandGeneration/JobDurationCalculator.java @@ -1,15 +1,39 @@ package org.matsim.freightDemandGeneration; -import org.matsim.freight.carriers.Carrier; +import org.matsim.api.core.v01.Scenario; public interface JobDurationCalculator { + /** + * Calculates the duration of a service in seconds. + * + * @param serviceDurationPerUnit in seconds + * @param demandForThisService amount of demand for this service + * @return duration in seconds + */ double calculateServiceDuration(Integer serviceDurationPerUnit, int demandForThisService); + /** + * Calculates the duration of a pickup in seconds. + * + * @param pickupDurationPerUnit in seconds + * @param demandForThisShipment amount of demand for this shipment + * @return duration in seconds + */ double calculatePickupDuration(Integer pickupDurationPerUnit, int demandForThisShipment); + /** + * Calculates the duration of a delivery in seconds. + * + * @param deliveryDurationPerUnit in seconds + * @param demandForThisShipment amount of demand for this shipment + * @return duration in seconds + */ double calculateDeliveryDuration(Integer deliveryDurationPerUnit, int demandForThisShipment); - void recalculateServiceDurations(Carrier thisCarrier); - - void recalculateShipmentDurations(Carrier thisCarrier); + /** + * Recalculates the job durations for all jobs in the scenario. The devault implementation does nothing. + * + * @param scenario scenario + */ + void recalculateJobDurations(Scenario scenario); }