From 0fab39a5d13523d88c1245a8259cbeb9053e7730 Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Thu, 23 Nov 2023 14:10:02 +0100 Subject: [PATCH 01/15] refactored PHbyModeCalculator --- .../matsim/analysis/PHbyModeCalculator.java | 205 +++++++++--------- 1 file changed, 100 insertions(+), 105 deletions(-) diff --git a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java index b4663b50388..41cf719a128 100644 --- a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java +++ b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java @@ -34,10 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.jfree.chart.axis.CategoryLabelPositions; import org.matsim.api.core.v01.IdMap; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.*; import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -72,118 +69,116 @@ void addIteration(int iteration, IdMap map) { Map phtbyMode = map.values() .parallelStream() .flatMap(plan -> plan.getPlanElements().stream()) - .map(pe->{ - if (pe instanceof Leg) { - Leg leg = (Leg) pe; - double travelTime = 0.0; - double waitTime = 0.0; - if (leg.getRoute()!=null) { - travelTime = leg.getRoute().getTravelTime().seconds(); - double enterVehicleTime = Double.NaN; - Object attr = leg.getAttributes().getAttribute(EventsToLegs.ENTER_VEHICLE_TIME_ATTRIBUTE_NAME); - if (attr != null) { - enterVehicleTime = (Double) attr; - } - waitTime = enterVehicleTime - leg.getDepartureTime().seconds(); - if (!Double.isFinite(waitTime)) {waitTime = 0.0;} - if (waitTime >= 0.0) { - travelTime -= waitTime; - } else { - throw new RuntimeException("negative wait time" + enterVehicleTime + " " + leg.getDepartureTime() - .seconds()); - } - } - - if (Double.isNaN(travelTime)) {travelTime = 0.0; } - - return new AbstractMap.SimpleEntry<>(leg.getMode(),new TravelTimeAndWaitTime(travelTime, waitTime)); - - } else if (pe instanceof Activity) { - Activity act = (Activity) pe; - if (StageActivityTypeIdentifier.isStageActivity(act.getType())) { - double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0); - return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY,new TravelTimeAndWaitTime(0.0, duration)); - } - } - return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY,new TravelTimeAndWaitTime(0.0, 0.0)); - }) - .collect(Collectors.toMap(e->e.getKey(),e->e.getValue(),(a,b)->TravelTimeAndWaitTime.sum(a, b))); + .map(PHbyModeCalculator::mapPlanElementToEntry) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, TravelTimeAndWaitTime::sum)); phtPerIteration.put(iteration,phtbyMode); } + private static AbstractMap.SimpleEntry mapPlanElementToEntry(PlanElement pe) { + if (pe instanceof Leg leg) { + double travelTime = 0.0; + double waitTime = 0.0; + if (leg.getRoute()!=null) { + travelTime = leg.getRoute().getTravelTime().seconds(); + double enterVehicleTime = Double.NaN; + Object attr = leg.getAttributes().getAttribute(EventsToLegs.ENTER_VEHICLE_TIME_ATTRIBUTE_NAME); + if (attr != null) { + enterVehicleTime = (Double) attr; + } + waitTime = enterVehicleTime - leg.getDepartureTime().seconds(); + if (!Double.isFinite(waitTime)) {waitTime = 0.0;} + if (waitTime >= 0.0) { + travelTime -= waitTime; + } else { + throw new RuntimeException("negative wait time" + enterVehicleTime + " " + leg.getDepartureTime() + .seconds()); + } + } + if (Double.isNaN(travelTime)) {travelTime = 0.0; } + return new AbstractMap.SimpleEntry<>(leg.getMode(), new TravelTimeAndWaitTime(travelTime, waitTime)); + } else if (pe instanceof Activity act) { + if (StageActivityTypeIdentifier.isStageActivity(act.getType())) { + double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0); + return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, duration)); + } + } + return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, 0.0)); + } void writeOutput() { - writePHTText(); - + writeCsv(); + if(writePng){ + writePng(); + } } - private void writePHTText() { - TreeSet allModes = new TreeSet<>(); - allModes.addAll(this.phtPerIteration.values() - .stream() - .flatMap(i->i.keySet().stream()) - .collect(Collectors.toSet())); - - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0)))) { - csvPrinter.print("Iteration"); - for (String mode: allModes) { - csvPrinter.print(mode + TRAVEL_TIME_SUFFIX); - csvPrinter.print(mode + WAIT_TIME_SUFFIX); - } - csvPrinter.println(); - - for (Map.Entry> e : phtPerIteration.entrySet()){ - csvPrinter.print(e.getKey()); - for (String mode : allModes){ - TravelTimeAndWaitTime travelTimeAndWaitTime = e.getValue().getOrDefault(mode, new TravelTimeAndWaitTime(0.0, 0.0)); - csvPrinter.print((int) Math.round(travelTimeAndWaitTime.travelTime / 3600.0)); - csvPrinter.print((int) Math.round(travelTimeAndWaitTime.waitTime / 3600.0)); - } - csvPrinter.println(); - } - - - } catch (IOException e) { + private void writeCsv() { + TreeSet allModes = getAllModes(); + try { + CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0))); + writeHeader(csvPrinter, allModes); + writeValues(csvPrinter, allModes); + } catch (IOException e) { LogManager.getLogger(getClass()).error("Could not write PH Modestats."); } - if (writePng){ - String[] categories = new String[phtPerIteration.size()]; - int i = 0; - for (Integer it : phtPerIteration.keySet()){ - categories[i++] = it.toString(); - } - - StackedBarChart chart = new StackedBarChart("Passenger hours traveled per Mode","Iteration","person hours",categories); - //rotate x-axis by 90degrees - chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90); - - for (String mode : allModes){ - double[] valueTravelTime = phtPerIteration.values().stream() - .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).travelTime/3600.0) - .toArray(); - chart.addSeries(mode + TRAVEL_TIME_SUFFIX, valueTravelTime); - double[] valueWaitTime = phtPerIteration.values().stream() - .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).waitTime/3600.0) - .toArray(); - chart.addSeries(mode + WAIT_TIME_SUFFIX, valueWaitTime); - } - chart.addMatsimLogo(); - chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768); - - } - - } - - private static class TravelTimeAndWaitTime { - private double travelTime; - private double waitTime; + } + + private void writeValues(CSVPrinter csvPrinter, TreeSet allModes) throws IOException { + for (Map.Entry> e : phtPerIteration.entrySet()){ + csvPrinter.print(e.getKey()); + for (String mode : allModes){ + TravelTimeAndWaitTime travelTimeAndWaitTime = e.getValue().getOrDefault(mode, new TravelTimeAndWaitTime(0.0, 0.0)); + csvPrinter.print((int) Math.round(travelTimeAndWaitTime.travelTime / 3600.0)); + csvPrinter.print((int) Math.round(travelTimeAndWaitTime.waitTime / 3600.0)); + } + csvPrinter.println(); + } + } - private TravelTimeAndWaitTime(double travelTime, double waitTime) { - this.travelTime = travelTime; - this.waitTime = waitTime; - } + private static void writeHeader(CSVPrinter csvPrinter, TreeSet allModes) throws IOException { + csvPrinter.print("Iteration"); + for (String mode: allModes) { + csvPrinter.print(mode + TRAVEL_TIME_SUFFIX); + csvPrinter.print(mode + WAIT_TIME_SUFFIX); + } + csvPrinter.println(); + } + + private void writePng(){ + TreeSet allModes = getAllModes(); + String[] categories = new String[phtPerIteration.size()]; + int i = 0; + for (Integer it : phtPerIteration.keySet()){ + categories[i++] = it.toString(); + } - private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) { + StackedBarChart chart = new StackedBarChart("Passenger hours traveled per Mode","Iteration","person hours",categories); + //rotate x-axis by 90degrees + chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90); + + for (String mode : allModes){ + double[] valueTravelTime = phtPerIteration.values().stream() + .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).travelTime/3600.0) + .toArray(); + chart.addSeries(mode + TRAVEL_TIME_SUFFIX, valueTravelTime); + double[] valueWaitTime = phtPerIteration.values().stream() + .mapToDouble(k->k.getOrDefault(mode,new TravelTimeAndWaitTime(0.0, 0.0)).waitTime/3600.0) + .toArray(); + chart.addSeries(mode + WAIT_TIME_SUFFIX, valueWaitTime); + } + chart.addMatsimLogo(); + chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768); + } + + private TreeSet getAllModes() { + return this.phtPerIteration.values() + .stream() + .flatMap(i -> i.keySet().stream()) + .collect(Collectors.toCollection(TreeSet::new)); + } + + private record TravelTimeAndWaitTime(double travelTime, double waitTime){ + private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) { return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.waitTime); } } From c2ebc79acb80d3e4327c80ca6c40f91f72468a8f Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 24 Nov 2023 11:24:25 +0100 Subject: [PATCH 02/15] fix test --- ...IterationTravelStatsControlerListener.java | 1 + .../matsim/analysis/PHbyModeCalculator.java | 63 ++++++++++--------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java index 17ea8796459..92ff3b139d4 100644 --- a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java +++ b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java @@ -56,6 +56,7 @@ class IterationTravelStatsControlerListener implements IterationEndsListener, Sh @Inject Scenario scenario; + @Inject private ExperiencedPlansService experiencedPlansService; diff --git a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java index 41cf719a128..7ea20608222 100644 --- a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java +++ b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java @@ -20,6 +20,7 @@ package org.matsim.analysis; +import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -48,31 +49,31 @@ */ public class PHbyModeCalculator { - private final Map> phtPerIteration = new TreeMap<>(); - private final boolean writePng; - private final OutputDirectoryHierarchy controllerIO; + private final Map> phtPerIteration = new TreeMap<>(); + private final boolean writePng; + private final OutputDirectoryHierarchy controllerIO; private final String delimiter; - private final static String FILENAME = "ph_modestats"; - - private static final String TRAVEL_TIME_SUFFIX = "_travel"; - private static final String WAIT_TIME_SUFFIX = "_wait"; - private static final String STAGE_ACTIVITY = "stageActivity"; - - @Inject - PHbyModeCalculator(ControllerConfigGroup controllerConfigGroup, OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { - this.writePng = controllerConfigGroup.isCreateGraphs(); - this.controllerIO = controllerIO; - this.delimiter = globalConfig.getDefaultDelimiter(); - } - - void addIteration(int iteration, IdMap map) { - Map phtbyMode = map.values() - .parallelStream() - .flatMap(plan -> plan.getPlanElements().stream()) - .map(PHbyModeCalculator::mapPlanElementToEntry) - .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, TravelTimeAndWaitTime::sum)); - phtPerIteration.put(iteration,phtbyMode); - } + private final static String FILENAME = "ph_modestats"; + + private static final String TRAVEL_TIME_SUFFIX = "_travel"; + private static final String WAIT_TIME_SUFFIX = "_wait"; + private static final String STAGE_ACTIVITY = "stageActivity"; + + @Inject + PHbyModeCalculator(ControllerConfigGroup controllerConfigGroup, OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { + this.writePng = controllerConfigGroup.isCreateGraphs(); + this.controllerIO = controllerIO; + this.delimiter = globalConfig.getDefaultDelimiter(); + } + + void addIteration(int iteration, IdMap map) { + Map phtbyMode = map.values() + .parallelStream() + .flatMap(plan -> plan.getPlanElements().stream()) + .map(PHbyModeCalculator::mapPlanElementToEntry) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, TravelTimeAndWaitTime::sum)); + phtPerIteration.put(iteration,phtbyMode); + } private static AbstractMap.SimpleEntry mapPlanElementToEntry(PlanElement pe) { if (pe instanceof Leg leg) { @@ -105,19 +106,21 @@ private static AbstractMap.SimpleEntry mapPlanEle return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, 0.0)); } - void writeOutput() { + void writeOutput() { writeCsv(); if(writePng){ writePng(); } - } + } private void writeCsv() { TreeSet allModes = getAllModes(); try { - CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0))); + BufferedWriter writer = Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename(FILENAME + ".csv"))); + CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.Builder.create().setDelimiter((this.delimiter.charAt(0))).build()); writeHeader(csvPrinter, allModes); writeValues(csvPrinter, allModes); + csvPrinter.close(); } catch (IOException e) { LogManager.getLogger(getClass()).error("Could not write PH Modestats."); } @@ -179,8 +182,8 @@ private TreeSet getAllModes() { private record TravelTimeAndWaitTime(double travelTime, double waitTime){ private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) { - return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.waitTime); - } - } + return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.waitTime); + } + } } From ff5746fa8f2294890ccbfed1eb85b818bfd6fc32 Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 1 Dec 2023 14:39:44 +0100 Subject: [PATCH 03/15] refactored analysis of ph, pkm and trips/legs --- .../org/matsim/analysis/ActivityWriter.java | 120 ++++++++++++ ...IterationTravelStatsControlerListener.java | 183 ++++-------------- .../analysis/IterationTravelStatsModule.java | 10 +- .../matsim/analysis/PHbyModeCalculator.java | 15 +- .../matsim/analysis/PKMbyModeCalculator.java | 111 ++++++----- .../org/matsim/analysis/PersonWriter.java | 105 ++++++++++ ...CSVWriter.java => TripsAndLegsWriter.java} | 157 ++++++++------- .../config/groups/ControllerConfigGroup.java | 12 ++ .../analysis/PHbyModeCalculatorTest.java | 10 +- .../analysis/PKMbyModeCalculatorTest.java | 8 +- ...rTest.java => TripsAndLegsWriterTest.java} | 24 +-- 11 files changed, 456 insertions(+), 299 deletions(-) create mode 100644 matsim/src/main/java/org/matsim/analysis/ActivityWriter.java create mode 100644 matsim/src/main/java/org/matsim/analysis/PersonWriter.java rename matsim/src/main/java/org/matsim/analysis/{TripsAndLegsCSVWriter.java => TripsAndLegsWriter.java} (78%) rename matsim/src/test/java/org/matsim/analysis/{TripsAndLegsCSVWriterTest.java => TripsAndLegsWriterTest.java} (96%) diff --git a/matsim/src/main/java/org/matsim/analysis/ActivityWriter.java b/matsim/src/main/java/org/matsim/analysis/ActivityWriter.java new file mode 100644 index 00000000000..9305dd02a51 --- /dev/null +++ b/matsim/src/main/java/org/matsim/analysis/ActivityWriter.java @@ -0,0 +1,120 @@ +package org.matsim.analysis; + +import jakarta.inject.Inject; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +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.population.Activity; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.Config; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.scoring.ExperiencedPlansService; +import org.matsim.core.utils.io.IOUtils; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ActivityWriter { + + @Inject + private Config config; + + @Inject + private ExperiencedPlansService experiencedPlansService; + + @Inject + private OutputDirectoryHierarchy outputDirectoryHierarchy; + + private static final Logger log = LogManager.getLogger(ActivityWriter.class); + + void writeCsv(int iteration) { + log.info("Writing all Activities to " + Controler.DefaultFiles.activitiescsv); + + List attributes = prepareAttributes(); + String[] header = prepareHeader(attributes); + + try{ + BufferedWriter bufferedWriter = IOUtils.getBufferedWriter(outputDirectoryHierarchy.getIterationFilename(iteration, Controler.DefaultFiles.activitiescsv)); + CSVPrinter csvPrinter = new CSVPrinter(bufferedWriter, CSVFormat.Builder.create() + .setDelimiter(config.global().getDefaultDelimiter().charAt(0)) + .setHeader(header).build()); + + for (Map.Entry, Plan> e : experiencedPlansService.getExperiencedPlans().entrySet()) { + writeActivitiesPerPerson(e.getKey(), e.getValue(), attributes, csvPrinter); + } + + csvPrinter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + log.info("...done"); + } + + private static void writeActivitiesPerPerson(Id personId, Plan plan, List attributes, CSVPrinter csvPrinter) throws IOException { + int i = 0; + for (Activity act : TripStructureUtils.getActivities(plan, TripStructureUtils.StageActivityHandling.ExcludeStageActivities)) { + + List line = new ArrayList<>(); + int id = ++i; + line.add(personId); + line.add(id); + line.add(personId.toString() + "_" + id); + line.add(act.getType()); + + line.add(act.getStartTime().isDefined() ? act.getStartTime().seconds() : ""); + line.add(act.getEndTime().isDefined() ? act.getEndTime().seconds() : ""); + line.add(act.getMaximumDuration().isDefined() ? act.getMaximumDuration().seconds() : ""); + line.add(act.getLinkId() != null ? act.getLinkId() : ""); + line.add(act.getFacilityId() != null ? act.getFacilityId(): ""); + + if (act.getCoord() != null) { + line.add(act.getCoord().getX()); + line.add(act.getCoord().getY()); + } else { + line.add(""); + line.add(""); + } + + for (String attribute : attributes) { + Object value = plan.getAttributes().getAttribute(attribute); + String result = value != null ? String.valueOf(value) : ""; + line.add(result); + } + + csvPrinter.printRecord(line); + } + } + + private List prepareAttributes() { + return experiencedPlansService.getExperiencedPlans().values().stream() + .flatMap(p -> TripStructureUtils.getActivities(p, TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream()) + .flatMap(act -> act.getAttributes().getAsMap().keySet().stream()) + .sorted().distinct().toList(); + } + + private static String[] prepareHeader(List attributes) { + List header = new ArrayList<>(); + header.add("person"); + header.add("activity_number"); + header.add("activity_id"); + header.add("activity_type"); + header.add("start_time"); + header.add("end_time"); + header.add("maximum_duration"); + header.add("link_id"); + header.add("facility_id"); + header.add("coord_x"); + header.add("coord_y"); + header.addAll(attributes); + return header.toArray(String[]::new); + } +} diff --git a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java index 92ff3b139d4..4f1e3aeda1a 100644 --- a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java +++ b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java @@ -21,15 +21,7 @@ package org.matsim.analysis; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVPrinter; -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.population.Activity; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; import org.matsim.core.config.Config; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -37,26 +29,15 @@ import org.matsim.core.controler.events.ShutdownEvent; import org.matsim.core.controler.listener.IterationEndsListener; import org.matsim.core.controler.listener.ShutdownListener; -import org.matsim.core.router.AnalysisMainModeIdentifier; -import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scoring.ExperiencedPlansService; -import org.matsim.core.utils.io.IOUtils; import jakarta.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; class IterationTravelStatsControlerListener implements IterationEndsListener, ShutdownListener { @Inject Config config; - @Inject - Scenario scenario; - @Inject private ExperiencedPlansService experiencedPlansService; @@ -68,163 +49,65 @@ class IterationTravelStatsControlerListener implements IterationEndsListener, Sh @Inject private PKMbyModeCalculator pkMbyModeCalculator; - @Inject - OutputDirectoryHierarchy outputDirectoryHierarchy; @Inject - TripsAndLegsCSVWriter.CustomTripsWriterExtension customTripsWriterExtension; - @Inject - TripsAndLegsCSVWriter.CustomLegsWriterExtension customLegsWriterExtension; - @Inject - TripsAndLegsCSVWriter.CustomTimeWriter customTimeWriter; + OutputDirectoryHierarchy outputDirectoryHierarchy; + @Inject + PersonWriter personWriter; - @Inject - AnalysisMainModeIdentifier mainModeIdentifier; + @Inject + ActivityWriter activityWriter; - Logger log = LogManager.getLogger(IterationTravelStatsControlerListener.class); + @Inject + TripsAndLegsWriter tripsAndLegsWriter; @Override public void notifyIterationEnds(IterationEndsEvent event) { + + if (config.controller().getCreateGraphsInterval()>0 && event.getIteration() % config.controller().getCreateGraphsInterval() == 0) { + //do something + } + travelDistanceStats.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); pHbyModeCalculator.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); pkMbyModeCalculator.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); - pHbyModeCalculator.writeOutput(); - pkMbyModeCalculator.writeOutput(); - final boolean writingTripsAtAll = config.controller().getWriteTripsInterval() > 0; - final boolean regularWriteEvents = writingTripsAtAll && ((event.getIteration() > 0 && event.getIteration() % config.controller().getWriteTripsInterval() == 0) || event.isLastIteration()); - if (regularWriteEvents || (writingTripsAtAll && event.getIteration() == 0)) { - new TripsAndLegsCSVWriter(scenario, customTripsWriterExtension, customLegsWriterExtension, mainModeIdentifier, customTimeWriter).write(experiencedPlansService.getExperiencedPlans() + + boolean writeGraph = isWriteGraph(event); + pHbyModeCalculator.writeOutput(writeGraph); + pkMbyModeCalculator.writeOutput(writeGraph); + + if (isWriteTripsAndLegs(event)) { + tripsAndLegsWriter.write(experiencedPlansService.getExperiencedPlans() , outputDirectoryHierarchy.getIterationFilename(event.getIteration(), Controler.DefaultFiles.tripscsv) , outputDirectoryHierarchy.getIterationFilename(event.getIteration(), Controler.DefaultFiles.legscsv)); - writeActivityCSV(event.getIteration()); + activityWriter.writeCsv(event.getIteration()); } } - @Override public void notifyShutdown(ShutdownEvent event) { travelDistanceStats.close(); if (config.controller().getWriteTripsInterval() > 0) { - writePersonsCSV(); + personWriter.writeCsv(); } } - private void writePersonsCSV() { - LogManager.getLogger(getClass()).info("Writing all Person and Attributes to " + Controler.DefaultFiles.personscsv); - List attributes = new ArrayList<>(scenario.getPopulation().getPersons().values().parallelStream().flatMap(p -> p.getAttributes().getAsMap().keySet().stream()).collect(Collectors.toSet())); - attributes.remove("vehicles"); - List header = new ArrayList<>(); - header.add("person"); - header.add("executed_score"); - header.add("first_act_x"); - header.add("first_act_y"); - header.add("first_act_type"); - header.addAll(attributes); - try (CSVPrinter csvPrinter = new CSVPrinter(IOUtils.getBufferedWriter(outputDirectoryHierarchy.getOutputFilename(Controler.DefaultFiles.personscsv)), - CSVFormat.DEFAULT.withDelimiter(config.global().getDefaultDelimiter().charAt(0)).withHeader(header.stream().toArray(String[]::new)))) { - for (Person p : scenario.getPopulation().getPersons().values()) { - if (p.getSelectedPlan() == null) { - log.error("Found person without a selected plan: " + p.getId().toString() + " will not be added to output_persons.csv"); - continue; - } - List line = new ArrayList<>(); - line.add(p.getId().toString()); - line.add(p.getSelectedPlan().getScore() == null ? "null" : p.getSelectedPlan().getScore().toString()); - String x = ""; - String y = ""; - String actType = ""; - if (p.getSelectedPlan().getPlanElements().size() > 0) { - Activity firstAct = (Activity) p.getSelectedPlan().getPlanElements().get(0); - if (firstAct.getCoord() != null) { - x = Double.toString(firstAct.getCoord().getX()); - y = Double.toString(firstAct.getCoord().getY()); - } - actType = firstAct.getType(); - } - line.add(x); - line.add(y); - line.add(actType); - for (String attribute : attributes) { - Object value = p.getAttributes().getAttribute(attribute); - String result = value != null ? String.valueOf(value) : ""; - line.add(result); - } - csvPrinter.printRecord(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - LogManager.getLogger(getClass()).info("...done"); - } + private boolean isWriteGraph(IterationEndsEvent event){ + return config.controller().getCreateGraphsInterval() > 0 && event.getIteration() % config.controller().getCreateGraphsInterval() == 0; + } - private void writeActivityCSV(int iteration) { - - List attributes = experiencedPlansService.getExperiencedPlans().values().stream() - .flatMap(p -> TripStructureUtils.getActivities(p, TripStructureUtils.StageActivityHandling.ExcludeStageActivities).stream()) - .flatMap(act -> act.getAttributes().getAsMap().keySet().stream()) - .sorted().distinct().toList(); - - List header = new ArrayList<>(); - header.add("person"); - header.add("activity_number"); - header.add("activity_id"); - header.add("activity_type"); - header.add("start_time"); - header.add("end_time"); - header.add("maximum_duration"); - header.add("link_id"); - header.add("facility_id"); - - header.add("coord_x"); - header.add("coord_y"); - - header.addAll(attributes); - - try (CSVPrinter csvPrinter = new CSVPrinter(IOUtils.getBufferedWriter(outputDirectoryHierarchy.getIterationFilename(iteration, Controler.DefaultFiles.activitiescsv)), - CSVFormat.DEFAULT.withDelimiter(config.global().getDefaultDelimiter().charAt(0)).withHeader(header.stream().toArray(String[]::new)))) { - - for (Map.Entry, Plan> e : experiencedPlansService.getExperiencedPlans().entrySet()) { - - int i = 0; - for (Activity act : TripStructureUtils.getActivities(e.getValue(), TripStructureUtils.StageActivityHandling.ExcludeStageActivities)) { - - List line = new ArrayList<>(); - - int id = ++i; - line.add(e.getKey()); - line.add(id); - line.add(e.getKey().toString() + "_" + id); - line.add(act.getType()); - - line.add(act.getStartTime().isDefined() ? act.getStartTime().seconds() : ""); - line.add(act.getEndTime().isDefined() ? act.getEndTime().seconds() : ""); - line.add(act.getMaximumDuration().isDefined() ? act.getMaximumDuration().seconds() : ""); - line.add(act.getLinkId() != null ? act.getLinkId() : ""); - line.add(act.getFacilityId() != null ? act.getFacilityId(): ""); - - if (act.getCoord() != null) { - line.add(act.getCoord().getX()); - line.add(act.getCoord().getY()); - } else { - line.add(""); - line.add(""); - } - - for (String attribute : attributes) { - Object value = e.getValue().getAttributes().getAttribute(attribute); - String result = value != null ? String.valueOf(value) : ""; - line.add(result); - } - - csvPrinter.printRecord(line); - } - } - } catch (IOException e) { - e.printStackTrace(); + private boolean isWriteTripsAndLegs(IterationEndsEvent event){ + if (config.controller().getWriteTripsInterval() <= 0) { + return false; } - } + if (event.getIteration() == 0 || event.isLastIteration()){ + return true; + } + + return event.getIteration() % config.controller().getWriteTripsInterval() == 0; + } } diff --git a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsModule.java b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsModule.java index 4b431499d01..32f54c9ca8c 100644 --- a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsModule.java +++ b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsModule.java @@ -31,10 +31,12 @@ public void install() { bind(TravelDistanceStats.class).asEagerSingleton(); bind(PKMbyModeCalculator.class).asEagerSingleton(); bind(PHbyModeCalculator.class).asEagerSingleton(); - bind(TripsAndLegsCSVWriter.CustomTripsWriterExtension.class).to(TripsAndLegsCSVWriter.NoTripWriterExtension.class).asEagerSingleton(); - bind(TripsAndLegsCSVWriter.CustomLegsWriterExtension.class).to(TripsAndLegsCSVWriter.NoLegsWriterExtension.class).asEagerSingleton(); - bind(TripsAndLegsCSVWriter.CustomTimeWriter.class).to(TripsAndLegsCSVWriter.DefaultTimeWriter.class).asEagerSingleton(); + bind(TripsAndLegsWriter.CustomTripsWriterExtension.class).to(TripsAndLegsWriter.NoTripWriterExtension.class).asEagerSingleton(); + bind(TripsAndLegsWriter.CustomLegsWriterExtension.class).to(TripsAndLegsWriter.NoLegsWriterExtension.class).asEagerSingleton(); + bind(TripsAndLegsWriter.CustomTimeWriter.class).to(TripsAndLegsWriter.DefaultTimeWriter.class).asEagerSingleton(); + bind(ActivityWriter.class); + bind(PersonWriter.class); + bind(TripsAndLegsWriter.class); addControlerListenerBinding().to(IterationTravelStatsControlerListener.class); } - } diff --git a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java index 7ea20608222..e572fbcc8a8 100644 --- a/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java +++ b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java @@ -50,9 +50,8 @@ public class PHbyModeCalculator { private final Map> phtPerIteration = new TreeMap<>(); - private final boolean writePng; private final OutputDirectoryHierarchy controllerIO; - private final String delimiter; + private final String delimiter; private final static String FILENAME = "ph_modestats"; private static final String TRAVEL_TIME_SUFFIX = "_travel"; @@ -60,8 +59,7 @@ public class PHbyModeCalculator { private static final String STAGE_ACTIVITY = "stageActivity"; @Inject - PHbyModeCalculator(ControllerConfigGroup controllerConfigGroup, OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { - this.writePng = controllerConfigGroup.isCreateGraphs(); + PHbyModeCalculator(OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { this.controllerIO = controllerIO; this.delimiter = globalConfig.getDefaultDelimiter(); } @@ -97,19 +95,22 @@ private static AbstractMap.SimpleEntry mapPlanEle } if (Double.isNaN(travelTime)) {travelTime = 0.0; } return new AbstractMap.SimpleEntry<>(leg.getMode(), new TravelTimeAndWaitTime(travelTime, waitTime)); - } else if (pe instanceof Activity act) { + } + + if (pe instanceof Activity act) { if (StageActivityTypeIdentifier.isStageActivity(act.getType())) { double duration = act.getEndTime().orElse(0) - act.getStartTime().orElse(0); return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, duration)); } } + return new AbstractMap.SimpleEntry<>(STAGE_ACTIVITY, new TravelTimeAndWaitTime(0.0, 0.0)); } - void writeOutput() { + void writeOutput(boolean writePng) { writeCsv(); if(writePng){ - writePng(); + new Thread(this::writePng).start(); } } diff --git a/matsim/src/main/java/org/matsim/analysis/PKMbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PKMbyModeCalculator.java index 1b05fccdb2f..c9979b9ae4f 100644 --- a/matsim/src/main/java/org/matsim/analysis/PKMbyModeCalculator.java +++ b/matsim/src/main/java/org/matsim/analysis/PKMbyModeCalculator.java @@ -20,6 +20,7 @@ package org.matsim.analysis; +import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -49,14 +50,12 @@ public class PKMbyModeCalculator { private final Map> pmtPerIteration = new TreeMap<>(); - private final boolean writePng; private final OutputDirectoryHierarchy controllerIO; private final String delimiter; private final static String FILENAME = "pkm_modestats"; @Inject - PKMbyModeCalculator(ControllerConfigGroup controllerConfigGroup, OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { - writePng = controllerConfigGroup.isCreateGraphs(); + PKMbyModeCalculator(OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { this.controllerIO = controllerIO; this.delimiter = globalConfig.getDefaultDelimiter(); } @@ -72,61 +71,75 @@ void addIteration(int iteration, IdMap map) { if (Double.isNaN(dist)) {dist = 0.0; } return new AbstractMap.SimpleEntry<>(leg.getMode(),dist); }) - .collect(Collectors.toMap(e->e.getKey(),e->e.getValue(),(a,b)->a+b)); + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue, Double::sum)); pmtPerIteration.put(iteration,pmtbyMode); } - - void writeOutput() { - writeVKTText(); - + void writeOutput(boolean writePng) { + writeCsv(); + if (writePng){ + new Thread(this::writePng).start(); + } } - private void writeVKTText() { - TreeSet allModes = new TreeSet<>(); - allModes.addAll(this.pmtPerIteration.values() - .stream() - .flatMap(i->i.keySet().stream()) - .collect(Collectors.toSet())); - - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename( FILENAME+ ".csv"))), CSVFormat.DEFAULT.withDelimiter(this.delimiter.charAt(0)))) { - csvPrinter.print("Iteration"); - csvPrinter.printRecord(allModes); - - for (Map.Entry> e : pmtPerIteration.entrySet()){ - csvPrinter.print(e.getKey()); - for (String mode : allModes){ - csvPrinter.print((int) Math.round(e.getValue().getOrDefault(mode, 0.0) / 1000.0)); - } - csvPrinter.println(); - } - + private void writeCsv() { + TreeSet allModes = getAllModes(); + try { + BufferedWriter writer = Files.newBufferedWriter(Paths.get(controllerIO.getOutputFilename(FILENAME + ".csv"))); + CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.Builder.create().setDelimiter((this.delimiter.charAt(0))).build()); + writeHeader(csvPrinter, allModes); + writeValues(csvPrinter, allModes); + csvPrinter.close(); } catch (IOException e) { LogManager.getLogger(getClass()).error("Could not write PKM Modestats."); } - if (writePng){ - String[] categories = new String[pmtPerIteration.size()]; - int i = 0; - for (Integer it : pmtPerIteration.keySet()){ - categories[i++] = it.toString(); - } - - StackedBarChart chart = new StackedBarChart("Passenger kilometers traveled per Mode","Iteration","pkm",categories); - //rotate x-axis by 90degrees - chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90); - - for (String mode : allModes){ - double[] value = pmtPerIteration.values().stream() - .mapToDouble(k->k.getOrDefault(mode,0.0)/1000.0) - .toArray(); - chart.addSeries(mode, value); - } - chart.addMatsimLogo(); - chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768); - - } - } + + private void writeHeader(CSVPrinter csvPrinter, TreeSet allModes) throws IOException { + csvPrinter.print("Iteration"); + csvPrinter.printRecord(allModes); + } + + private void writeValues(CSVPrinter csvPrinter, TreeSet allModes) throws IOException { + for (Map.Entry> e : pmtPerIteration.entrySet()){ + csvPrinter.print(e.getKey()); + for (String mode : allModes){ + csvPrinter.print((int) Math.round(e.getValue().getOrDefault(mode, 0.0) / 1000.0)); + } + csvPrinter.println(); + } + } + + private void writePng() { + String[] categories = new String[pmtPerIteration.size()]; + int i = 0; + for (Integer it : pmtPerIteration.keySet()){ + categories[i++] = it.toString(); + } + + StackedBarChart chart = new StackedBarChart("Passenger kilometers traveled per Mode","Iteration","pkm",categories); + //rotate x-axis by 90degrees + chart.getChart().getCategoryPlot().getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_90); + + for (String mode : getAllModes()){ + double[] value = pmtPerIteration.values().stream() + .mapToDouble(k->k.getOrDefault(mode,0.0)/1000.0) + .toArray(); + chart.addSeries(mode, value); + } + chart.addMatsimLogo(); + + synchronized (controllerIO) { + chart.saveAsPng(controllerIO.getOutputFilename(FILENAME+ ".png"), 1024, 768); + } + } + + private TreeSet getAllModes() { + return this.pmtPerIteration.values() + .stream() + .flatMap(i -> i.keySet().stream()) + .collect(Collectors.toCollection(TreeSet::new)); + } } diff --git a/matsim/src/main/java/org/matsim/analysis/PersonWriter.java b/matsim/src/main/java/org/matsim/analysis/PersonWriter.java new file mode 100644 index 00000000000..5c3257a2aa5 --- /dev/null +++ b/matsim/src/main/java/org/matsim/analysis/PersonWriter.java @@ -0,0 +1,105 @@ +package org.matsim.analysis; + +import jakarta.inject.Inject; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +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.Activity; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.config.Config; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.utils.io.IOUtils; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class PersonWriter { + + @Inject + Config config; + + @Inject + Scenario scenario; + + @Inject + OutputDirectoryHierarchy outputDirectoryHierarchy; + + private static final Logger log = LogManager.getLogger(PersonWriter.class); + + void writeCsv() { + log.info("Writing all Persons to " + Controler.DefaultFiles.personscsv); + + List attributes = prepareAttributes(); + String[] header = prepareHeader(attributes); + + try { + BufferedWriter bufferedWriter = IOUtils.getBufferedWriter(outputDirectoryHierarchy.getOutputFilename(Controler.DefaultFiles.personscsv)); + CSVPrinter csvPrinter = new CSVPrinter(bufferedWriter, CSVFormat.Builder.create() + .setDelimiter(config.global().getDefaultDelimiter().charAt(0)) + .setHeader(header).build()); + for (Person p : scenario.getPopulation().getPersons().values()) { + writePerson(p, attributes, csvPrinter); + } + + csvPrinter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + log.info("...done"); + } + + private void writePerson(Person p, List attributes, CSVPrinter csvPrinter) throws IOException { + if (p.getSelectedPlan() == null) { + log.error("Found person without a selected plan: " + p.getId().toString() + " will not be added to output_persons.csv"); + return; + } + List line = new ArrayList<>(); + line.add(p.getId().toString()); + line.add(p.getSelectedPlan().getScore() == null ? "null" : p.getSelectedPlan().getScore().toString()); + String x = ""; + String y = ""; + String actType = ""; + if (!p.getSelectedPlan().getPlanElements().isEmpty()) { + Activity firstAct = (Activity) p.getSelectedPlan().getPlanElements().get(0); + if (firstAct.getCoord() != null) { + x = Double.toString(firstAct.getCoord().getX()); + y = Double.toString(firstAct.getCoord().getY()); + } + actType = firstAct.getType(); + } + line.add(x); + line.add(y); + line.add(actType); + for (String attribute : attributes) { + Object value = p.getAttributes().getAttribute(attribute); + String result = value != null ? String.valueOf(value) : ""; + line.add(result); + } + csvPrinter.printRecord(line); + } + + private String[] prepareHeader(List attributes) { + List header = new ArrayList<>(); + header.add("person"); + header.add("executed_score"); + header.add("first_act_x"); + header.add("first_act_y"); + header.add("first_act_type"); + header.addAll(attributes); + return header.toArray(String[]::new); + } + + private List prepareAttributes() { + List attributes = scenario.getPopulation().getPersons().values().parallelStream() + .flatMap(p -> p.getAttributes().getAsMap().keySet().stream()).distinct() + .collect(Collectors.toList()); + attributes.remove("vehicles"); + return attributes; + } +} diff --git a/matsim/src/main/java/org/matsim/analysis/TripsAndLegsCSVWriter.java b/matsim/src/main/java/org/matsim/analysis/TripsAndLegsWriter.java similarity index 78% rename from matsim/src/main/java/org/matsim/analysis/TripsAndLegsCSVWriter.java rename to matsim/src/main/java/org/matsim/analysis/TripsAndLegsWriter.java index daf244648d1..ea222d892e4 100644 --- a/matsim/src/main/java/org/matsim/analysis/TripsAndLegsCSVWriter.java +++ b/matsim/src/main/java/org/matsim/analysis/TripsAndLegsWriter.java @@ -21,6 +21,7 @@ package org.matsim.analysis; +import jakarta.inject.Inject; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.lang3.ArrayUtils; @@ -34,7 +35,6 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.*; import org.matsim.core.router.AnalysisMainModeIdentifier; -import org.matsim.core.router.RoutingModeMainModeIdentifier; import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scoring.EventsToLegs; import org.matsim.core.utils.collections.Tuple; @@ -56,7 +56,7 @@ /** * @author jbischoff / SBB */ -public class TripsAndLegsCSVWriter { +public class TripsAndLegsWriter { public static final String[] TRIPSHEADER_BASE = {"person", "trip_number", "trip_id", "dep_time", "trav_time", "wait_time", "traveled_distance", "euclidean_distance", "main_mode", "longest_distance_mode", "modes", "start_activity_type", @@ -70,43 +70,43 @@ public class TripsAndLegsCSVWriter { private final String[] TRIPSHEADER; private final String[] LEGSHEADER; - private final String separator; private final CustomTripsWriterExtension tripsWriterExtension; private final Scenario scenario; private final CustomLegsWriterExtension legsWriterExtension; private final AnalysisMainModeIdentifier mainModeIdentifier; private final CustomTimeWriter customTimeWriter; - private static final Logger log = LogManager.getLogger(TripsAndLegsCSVWriter.class); - - public TripsAndLegsCSVWriter(Scenario scenario, CustomTripsWriterExtension tripsWriterExtension, - CustomLegsWriterExtension legWriterExtension, - AnalysisMainModeIdentifier mainModeIdentifier, CustomTimeWriter customTimeWriter) { - this.scenario = scenario; - this.separator = scenario.getConfig().global().getDefaultDelimiter(); - TRIPSHEADER = ArrayUtils.addAll(TRIPSHEADER_BASE, tripsWriterExtension.getAdditionalTripHeader()); - LEGSHEADER = ArrayUtils.addAll(LEGSHEADER_BASE, legWriterExtension.getAdditionalLegHeader()); - this.tripsWriterExtension = tripsWriterExtension; - this.legsWriterExtension = legWriterExtension; - this.mainModeIdentifier = mainModeIdentifier; - this.customTimeWriter = customTimeWriter; - } + private static final Logger log = LogManager.getLogger(TripsAndLegsWriter.class); + + @Inject + TripsAndLegsWriter(Scenario scenario, CustomTripsWriterExtension tripsWriterExtension, CustomLegsWriterExtension legWriterExtension, + AnalysisMainModeIdentifier mainModeIdentifier, CustomTimeWriter customTimeWriter) { + this.scenario = scenario; + this.tripsWriterExtension = tripsWriterExtension; + this.legsWriterExtension = legWriterExtension; + this.mainModeIdentifier = mainModeIdentifier; + this.customTimeWriter = customTimeWriter; + + TRIPSHEADER = ArrayUtils.addAll(TRIPSHEADER_BASE, tripsWriterExtension.getAdditionalTripHeader()); + LEGSHEADER = ArrayUtils.addAll(LEGSHEADER_BASE, legWriterExtension.getAdditionalLegHeader()); + } - public void write(IdMap experiencedPlans, String tripsFilename, String legsFilename) { + private char getDefaultDelimiter(){ + return scenario.getConfig().global().getDefaultDelimiter().charAt(0); + } + + public void write(IdMap experiencedPlans, String tripsFilename, String legsFilename) { try (CSVPrinter tripsCSVprinter = new CSVPrinter(IOUtils.getBufferedWriter(tripsFilename), - CSVFormat.DEFAULT.withDelimiter(separator.charAt(0)).withHeader(TRIPSHEADER)); + CSVFormat.Builder.create().setDelimiter(getDefaultDelimiter()).setHeader(TRIPSHEADER).build()); CSVPrinter legsCSVprinter = new CSVPrinter(IOUtils.getBufferedWriter(legsFilename), - CSVFormat.DEFAULT.withDelimiter(separator.charAt(0)).withHeader(LEGSHEADER)) - + CSVFormat.Builder.create().setDelimiter(getDefaultDelimiter()).setHeader(LEGSHEADER).build()) ) { - for (Map.Entry, Plan> entry : experiencedPlans.entrySet()) { Tuple, Iterable> tripsAndLegRecords = getPlanCSVRecords(entry.getValue(), entry.getKey()); tripsCSVprinter.printRecords(tripsAndLegRecords.getFirst()); legsCSVprinter.printRecords(tripsAndLegRecords.getSecond()); } } catch (IOException e) { - e.printStackTrace(); } } @@ -202,29 +202,14 @@ private Tuple, Iterable> getPlanCSVRecords(Plan experiencedPlan, tripRecord.add(firstPtBoardingStop != null ? firstPtBoardingStop : ""); tripRecord.add(lastPtEgressStop != null ? lastPtEgressStop : ""); tripRecord.addAll(tripsWriterExtension.getAdditionalTripColumns(personId, trip)); + if (TRIPSHEADER.length != tripRecord.size()) { // put the whole error message also into the RuntimeException, so maven shows it on the command line output (log messages are shown incompletely) - StringBuilder str = new StringBuilder(); - str.append("Custom CSV Trip Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is " + TRIPSHEADER.length + ", and number of values is " + tripRecord.size() + ".\n"); - str.append("TripsWriterExtension class was: " + tripsWriterExtension.getClass() + ". Column name to value pairs supplied were:\n"); - for(int j = 0; j < Math.max(TRIPSHEADER.length, tripRecord.size()); j++) { - String columnNameJ; - try { - columnNameJ = TRIPSHEADER[j]; - } catch (ArrayIndexOutOfBoundsException e) { - columnNameJ = "!COLUMN MISSING!"; - } - String tripRecordJ; - try { - tripRecordJ = tripRecord.get(j); - } catch (IndexOutOfBoundsException e) { - tripRecordJ = "!VALUE MISSING!"; - } - str.append(j + ": " + columnNameJ + ": " + tripRecordJ + "\n"); - } - log.error(str.toString()); - throw new RuntimeException(str.toString()); + String errorMessage = getTripErrorMessage(tripRecord).toString(); + log.error(errorMessage); + throw new RuntimeException(errorMessage); } + Activity prevAct = null; Leg prevLeg = null; List allElements = new ArrayList<>(); @@ -232,9 +217,8 @@ private Tuple, Iterable> getPlanCSVRecords(Plan experiencedPlan, allElements.addAll(trip.getTripElements()); allElements.add(trip.getDestinationActivity()); for (PlanElement pe : allElements) { - if (pe instanceof Activity) { - Activity currentAct = (Activity) pe; - if (prevLeg != null) { + if (pe instanceof Activity currentAct) { + if (prevLeg != null) { List legRecord = getLegRecord(prevLeg, personId.toString(), tripId, prevAct, currentAct, trip); legRecords.add(legRecord); } @@ -249,7 +233,32 @@ private Tuple, Iterable> getPlanCSVRecords(Plan experiencedPlan, return record; } - private List getLegRecord(Leg leg, String personId, String tripId, Activity previousAct, Activity nextAct, TripStructureUtils.Trip trip) { + private StringBuilder getTripErrorMessage(List tripRecord) { + StringBuilder str = new StringBuilder(); + str.append("Custom CSV Trip Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is ") + .append(TRIPSHEADER.length).append(", and number of values is ").append(tripRecord.size()) + .append(".\n") + .append("TripsWriterExtension class was: ").append(tripsWriterExtension.getClass()) + .append(". Column name to value pairs supplied were:\n"); + for(int j = 0; j < Math.max(TRIPSHEADER.length, tripRecord.size()); j++) { + String columnNameJ; + try { + columnNameJ = TRIPSHEADER[j]; + } catch (ArrayIndexOutOfBoundsException e) { + columnNameJ = "!COLUMN MISSING!"; + } + String tripRecordJ; + try { + tripRecordJ = tripRecord.get(j); + } catch (IndexOutOfBoundsException e) { + tripRecordJ = "!VALUE MISSING!"; + } + str.append(j + ": " + columnNameJ + ": " + tripRecordJ + "\n"); + } + return str; + } + + private List getLegRecord(Leg leg, String personId, String tripId, Activity previousAct, Activity nextAct, TripStructureUtils.Trip trip) { List record = new ArrayList<>(); record.add(personId); record.add(tripId); @@ -290,34 +299,46 @@ private List getLegRecord(Leg leg, String personId, String tripId, Activ record.add(vehicleId != null ? vehicleId.toString() : ""); record.addAll(legsWriterExtension.getAdditionalLegColumns(trip, leg)); + if (LEGSHEADER.length != record.size()) { // put the whole error message also into the RuntimeException, so maven shows it on the command line output (log messages are shown incompletely) - StringBuilder str = new StringBuilder(); - str.append("Custom CSV Leg Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is " + LEGSHEADER.length + ", and number of values is " + record.size() + ".\n"); - str.append("LegsWriterExtension class was: " + legsWriterExtension.getClass() + ". Column name to value pairs supplied were:\n"); - for(int j = 0; j < Math.max(LEGSHEADER.length, record.size()); j++) { - String columnNameJ; - try { - columnNameJ = LEGSHEADER[j]; - } catch (ArrayIndexOutOfBoundsException e) { - columnNameJ = "!COLUMN MISSING!"; - } - String recordJ; - try { - recordJ = record.get(j); - } catch (IndexOutOfBoundsException e) { - recordJ = "!VALUE MISSING!"; - } - str.append(j + ": " + columnNameJ + ": " + recordJ + "\n"); - } - log.error(str.toString()); - throw new RuntimeException(str.toString()); + String errorMessage = getLegErrorMessage(record); + log.error(errorMessage); + throw new RuntimeException(errorMessage); } return record; } - private Coord getCoordFromActivity(Activity activity) { + private String getLegErrorMessage(List record) { + StringBuilder str = new StringBuilder(); + str.append("Custom CSV Leg Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is ") + .append(LEGSHEADER.length) + .append(", and number of values is ") + .append(record.size()) + .append(".\n") + .append("LegsWriterExtension class was: ") + .append(legsWriterExtension.getClass()) + .append(". Column name to value pairs supplied were:\n"); + for(int j = 0; j < Math.max(LEGSHEADER.length, record.size()); j++) { + String columnNameJ; + try { + columnNameJ = LEGSHEADER[j]; + } catch (ArrayIndexOutOfBoundsException e) { + columnNameJ = "!COLUMN MISSING!"; + } + String recordJ; + try { + recordJ = record.get(j); + } catch (IndexOutOfBoundsException e) { + recordJ = "!VALUE MISSING!"; + } + str.append(j).append(": ").append(columnNameJ).append(": ").append(recordJ).append("\n"); + } + return str.toString(); + } + + private Coord getCoordFromActivity(Activity activity) { if (activity.getCoord() != null) { return activity.getCoord(); } else if (activity.getFacilityId() != null && scenario.getActivityFacilities().getFacilities().containsKey(activity.getFacilityId())) { diff --git a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java index f1d8bae8b6a..d68aa5bf6e6 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java @@ -73,6 +73,7 @@ public enum CleanIterations { private static final String WRITE_TRIPS_INTERVAL = "writeTripsInterval"; private static final String OVERWRITE_FILE = "overwriteFiles"; private static final String CREATE_GRAPHS = "createGraphs"; + private static final String CREATE_GRAPHS_INTERVAL = "createGraphsInterval"; private static final String DUMP_DATA_AT_END = "dumpDataAtEnd"; private static final String CLEAN_ITERS_AT_END = "cleanItersAtEnd"; private static final String COMPRESSION_TYPE = "compressionType"; @@ -102,6 +103,7 @@ public enum MobsimType {qsim, JDEQSim, hermes} private String mobsim = MobsimType.qsim.toString(); private int writeSnapshotsInterval = 1; private boolean createGraphs = true; + private int createGraphsInterval = 1; private boolean dumpDataAtEnd = true; private CompressionType compressionType = CompressionType.gzip; @@ -373,6 +375,16 @@ public void setCreateGraphs(boolean createGraphs) { this.createGraphs = createGraphs; } + @StringGetter( CREATE_GRAPHS_INTERVAL ) + public int getCreateGraphsInterval() { + return createGraphsInterval; + } + + @StringSetter( CREATE_GRAPHS_INTERVAL ) + public void setCreateGraphsInterval(int createGraphsInterval) { + this.createGraphsInterval = createGraphsInterval; + } + @StringGetter( OVERWRITE_FILE ) public OverwriteFileSetting getOverwriteFileSetting() { return overwriteFileSetting; diff --git a/matsim/src/test/java/org/matsim/analysis/PHbyModeCalculatorTest.java b/matsim/src/test/java/org/matsim/analysis/PHbyModeCalculatorTest.java index e123bfceea9..ae647a6fd59 100644 --- a/matsim/src/test/java/org/matsim/analysis/PHbyModeCalculatorTest.java +++ b/matsim/src/test/java/org/matsim/analysis/PHbyModeCalculatorTest.java @@ -93,28 +93,28 @@ private void performTest(IdMap map, String outputDirectory) { controllerConfigGroup.setCreateGraphs(true); controllerConfigGroup.setFirstIteration(0); controllerConfigGroup.setLastIteration(10); - PHbyModeCalculator phbyModeCalculator = new PHbyModeCalculator(controllerConfigGroup, controlerIO, new GlobalConfigGroup()); + PHbyModeCalculator phbyModeCalculator = new PHbyModeCalculator(controlerIO, new GlobalConfigGroup()); phbyModeCalculator.addIteration(1, map); - phbyModeCalculator.writeOutput(); + phbyModeCalculator.writeOutput(false); readAndValidateValues(1, map); // removing person 2 map.remove(person2); phbyModeCalculator.addIteration(2, map); - phbyModeCalculator.writeOutput(); + phbyModeCalculator.writeOutput(false); readAndValidateValues(2, map); // removing person 3 map.remove(person3); phbyModeCalculator.addIteration(3, map); - phbyModeCalculator.writeOutput(); + phbyModeCalculator.writeOutput(false); readAndValidateValues(3, map); // removing person 4 map.remove(person4); phbyModeCalculator.addIteration(4, map); - phbyModeCalculator.writeOutput(); + phbyModeCalculator.writeOutput(false); readAndValidateValues(4, map); } diff --git a/matsim/src/test/java/org/matsim/analysis/PKMbyModeCalculatorTest.java b/matsim/src/test/java/org/matsim/analysis/PKMbyModeCalculatorTest.java index ba62f04f994..810b9954e1d 100644 --- a/matsim/src/test/java/org/matsim/analysis/PKMbyModeCalculatorTest.java +++ b/matsim/src/test/java/org/matsim/analysis/PKMbyModeCalculatorTest.java @@ -160,10 +160,10 @@ private void performTest(IdMap map, HashMap modeCa controllerConfigGroup.setCreateGraphs(true); controllerConfigGroup.setFirstIteration(0); controllerConfigGroup.setLastIteration(10); - PKMbyModeCalculator pkmbyModeCalculator = new PKMbyModeCalculator(controllerConfigGroup, controlerIO, new GlobalConfigGroup()); + PKMbyModeCalculator pkmbyModeCalculator = new PKMbyModeCalculator(controlerIO, new GlobalConfigGroup()); // iteration 0 pkmbyModeCalculator.addIteration(0, map); - pkmbyModeCalculator.writeOutput(); + pkmbyModeCalculator.writeOutput(false); Double totalCarDist = modeCalcDist.get("person1CarDist") + modeCalcDist.get("person2CarDist") + modeCalcDist.get("person3CarDist"); Double totalPtDist = modeCalcDist.get("person1PtDist") + modeCalcDist.get("person4PtDist"); @@ -176,7 +176,7 @@ private void performTest(IdMap map, HashMap modeCa map.remove(person2.getId()); // iteration 1 pkmbyModeCalculator.addIteration(1, map); - pkmbyModeCalculator.writeOutput(); + pkmbyModeCalculator.writeOutput(false); totalCarDist = modeCalcDist.get("person1CarDist") + modeCalcDist.get("person3CarDist"); totalPtDist = modeCalcDist.get("person1PtDist") + modeCalcDist.get("person4PtDist"); totalWalkDist = modeCalcDist.get("person1WalkDist") + modeCalcDist.get("person3WalkDist") + modeCalcDist.get("person4WalkDist"); @@ -187,7 +187,7 @@ private void performTest(IdMap map, HashMap modeCa map.remove(person3.getId()); // iteration 2 pkmbyModeCalculator.addIteration(2, map); - pkmbyModeCalculator.writeOutput(); + pkmbyModeCalculator.writeOutput(false); totalCarDist = modeCalcDist.get("person1CarDist"); totalPtDist = modeCalcDist.get("person1PtDist") + modeCalcDist.get("person4PtDist"); totalWalkDist = modeCalcDist.get("person1WalkDist") + modeCalcDist.get("person4WalkDist"); diff --git a/matsim/src/test/java/org/matsim/analysis/TripsAndLegsCSVWriterTest.java b/matsim/src/test/java/org/matsim/analysis/TripsAndLegsWriterTest.java similarity index 96% rename from matsim/src/test/java/org/matsim/analysis/TripsAndLegsCSVWriterTest.java rename to matsim/src/test/java/org/matsim/analysis/TripsAndLegsWriterTest.java index 788452ea7bb..a2517b9f76d 100644 --- a/matsim/src/test/java/org/matsim/analysis/TripsAndLegsCSVWriterTest.java +++ b/matsim/src/test/java/org/matsim/analysis/TripsAndLegsWriterTest.java @@ -23,8 +23,8 @@ import org.junit.Assert; import org.junit.Rule; import org.junit.Test; -import org.matsim.analysis.TripsAndLegsCSVWriter.NoLegsWriterExtension; -import org.matsim.analysis.TripsAndLegsCSVWriter.NoTripWriterExtension; +import org.matsim.analysis.TripsAndLegsWriter.NoLegsWriterExtension; +import org.matsim.analysis.TripsAndLegsWriter.NoTripWriterExtension; import org.matsim.api.core.v01.*; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -60,7 +60,7 @@ * @author Aravind * */ -public class TripsAndLegsCSVWriterTest { +public class TripsAndLegsWriterTest { Config config = ConfigUtils.createConfig(); Scenario scenario = ScenarioUtils.createScenario(config); @@ -164,19 +164,19 @@ public void testTripsAndLegsCSVWriter() { } private void performTest(String tripsFilename, String legsFilename, IdMap map, AnalysisMainModeIdentifier mainModeIdentifier) { - TripsAndLegsCSVWriter.NoTripWriterExtension tripsWriterExtension = new NoTripWriterExtension(); - TripsAndLegsCSVWriter.NoLegsWriterExtension legWriterExtension = new NoLegsWriterExtension(); - TripsAndLegsCSVWriter.CustomTimeWriter timeWriter = new TripsAndLegsCSVWriter.DefaultTimeWriter(); - TripsAndLegsCSVWriter.CustomTripsWriterExtension customTripsWriterExtension = new CustomTripsWriterExtesion(); - TripsAndLegsCSVWriter.CustomLegsWriterExtension customLegsWriterExtension = new CustomLegsWriterExtesion(); - TripsAndLegsCSVWriter tripsAndLegsWriter = new TripsAndLegsCSVWriter(scenario, tripsWriterExtension, + TripsAndLegsWriter.NoTripWriterExtension tripsWriterExtension = new NoTripWriterExtension(); + TripsAndLegsWriter.NoLegsWriterExtension legWriterExtension = new NoLegsWriterExtension(); + TripsAndLegsWriter.CustomTimeWriter timeWriter = new TripsAndLegsWriter.DefaultTimeWriter(); + TripsAndLegsWriter.CustomTripsWriterExtension customTripsWriterExtension = new CustomTripsWriterExtesion(); + TripsAndLegsWriter.CustomLegsWriterExtension customLegsWriterExtension = new CustomLegsWriterExtesion(); + TripsAndLegsWriter tripsAndLegsWriter = new TripsAndLegsWriter(scenario, tripsWriterExtension, legWriterExtension, mainModeIdentifier, timeWriter); tripsAndLegsWriter.write(map, tripsFilename, legsFilename); readTripsFromPlansFile(map, mainModeIdentifier); readAndValidateTrips(persontrips, tripsFilename); readLegsFromPlansFile(map); readAndValidateLegs(legsfromplan, legsFilename); - TripsAndLegsCSVWriter tripsAndLegsWriterTest = new TripsAndLegsCSVWriter(scenario, customTripsWriterExtension, + TripsAndLegsWriter tripsAndLegsWriterTest = new TripsAndLegsWriter(scenario, customTripsWriterExtension, customLegsWriterExtension, mainModeIdentifier, timeWriter); tripsAndLegsWriterTest.write(map, tripsFilename, legsFilename); } @@ -671,7 +671,7 @@ private void createNetwork() { NetworkUtils.writeNetwork(network, utils.getOutputDirectory() + "/network.xml"); new MatsimNetworkReader(scenario.getNetwork()).readFile(utils.getOutputDirectory() + "/network.xml"); } - private static class CustomTripsWriterExtesion implements TripsAndLegsCSVWriter.CustomTripsWriterExtension{ + private static class CustomTripsWriterExtesion implements TripsAndLegsWriter.CustomTripsWriterExtension{ @Override public String[] getAdditionalTripHeader() { @@ -701,7 +701,7 @@ public List getAdditionalTripColumns(Trip trip) { } - static class CustomLegsWriterExtesion implements TripsAndLegsCSVWriter.CustomLegsWriterExtension { + static class CustomLegsWriterExtesion implements TripsAndLegsWriter.CustomLegsWriterExtension { @Override public String[] getAdditionalLegHeader() { String[] legHeader = new String[]{"isIntermodalWalkPt"}; From 025af2ab83be0e2144509843b16ba70976aaf877 Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 1 Dec 2023 15:55:24 +0100 Subject: [PATCH 04/15] refactored TravelDistanceStats --- ...IterationTravelStatsControlerListener.java | 6 +- .../matsim/analysis/TravelDistanceStats.java | 243 ++++++++++-------- .../analysis/TravelDistanceStatsTest.java | 25 +- 3 files changed, 159 insertions(+), 115 deletions(-) diff --git a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java index 4f1e3aeda1a..8055a2c04db 100644 --- a/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java +++ b/matsim/src/main/java/org/matsim/analysis/IterationTravelStatsControlerListener.java @@ -64,11 +64,6 @@ class IterationTravelStatsControlerListener implements IterationEndsListener, Sh @Override public void notifyIterationEnds(IterationEndsEvent event) { - - if (config.controller().getCreateGraphsInterval()>0 && event.getIteration() % config.controller().getCreateGraphsInterval() == 0) { - //do something - } - travelDistanceStats.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); pHbyModeCalculator.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); pkMbyModeCalculator.addIteration(event.getIteration(), experiencedPlansService.getExperiencedPlans()); @@ -76,6 +71,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { boolean writeGraph = isWriteGraph(event); pHbyModeCalculator.writeOutput(writeGraph); pkMbyModeCalculator.writeOutput(writeGraph); + travelDistanceStats.writeOutput(event.getIteration(), writeGraph); if (isWriteTripsAndLegs(event)) { tripsAndLegsWriter.write(experiencedPlansService.getExperiencedPlans() diff --git a/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java b/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java index cc46e9434fd..9d0176c8adf 100644 --- a/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java +++ b/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java @@ -26,7 +26,6 @@ import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; -import org.matsim.core.config.Config; import org.matsim.core.config.groups.ControllerConfigGroup; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -39,8 +38,7 @@ import java.io.BufferedWriter; import java.io.IOException; import java.io.UncheckedIOException; -import java.util.DoubleSummaryStatistics; -import java.util.Locale; +import java.util.*; import java.util.stream.Collectors; /** @@ -59,14 +57,16 @@ public class TravelDistanceStats { + private static final int HISTORY_SIZE = 5000; + private final ControllerConfigGroup controllerConfigGroup; - private final BufferedWriter out; + private BufferedWriter out; private final String legStatsPngName; private final String tripStatsPngName; private final String delimiter; - private double[] legStatsHistory = null; - private double[] tripStatsHistory = null; + private DoubleSummaryStatistics[] legStats; + private DoubleSummaryStatistics[] tripStats; private final static Logger log = LogManager.getLogger(TravelDistanceStats.class); @@ -75,136 +75,175 @@ public class TravelDistanceStats { this(controllerConfigGroup, controlerIO.getOutputFilename("traveldistancestats"), controlerIO.getOutputFilename("traveldistancestats") + "legs", controlerIO.getOutputFilename("traveldistancestats") + "trips", - controllerConfigGroup.isCreateGraphs(), globalConfig.getDefaultDelimiter()); } - /** - * @param filename including the path, excluding the file type extension - * @param createPNG true if in every iteration, the distance statistics should be visualized in a graph and written to disk. - * @throws UncheckedIOException - */ - public TravelDistanceStats(final Config config, final String filename, final boolean createPNG, final String delimiter) throws UncheckedIOException { - this(config.controller(), filename, filename + "legs", filename + "trips", createPNG, delimiter); - } - - private TravelDistanceStats(ControllerConfigGroup controllerConfigGroup, String travelDistanceStatsFileName, - String legStatsPngName, String tripStatsPngName, boolean createPNG, String delimiter) { + private TravelDistanceStats(ControllerConfigGroup controllerConfigGroup, String travelDistanceStatsFileName, String legStatsPngName, + String tripStatsPngName, String delimiter) { this.controllerConfigGroup = controllerConfigGroup; this.legStatsPngName = legStatsPngName; this.tripStatsPngName = tripStatsPngName; this.delimiter = delimiter; - if (createPNG) { - int iterations = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration(); - if (iterations > 5000) { - iterations = 5000; // limit the history size - } - this.legStatsHistory = new double[iterations+1]; - this.tripStatsHistory = new double[iterations+1]; - } + initStats(controllerConfigGroup); + initWriter(travelDistanceStatsFileName); + } + + private void initWriter(String travelDistanceStatsFileName) { if (travelDistanceStatsFileName.toLowerCase(Locale.ROOT).endsWith(".csv")) { this.out = IOUtils.getBufferedWriter(travelDistanceStatsFileName); } else { this.out = IOUtils.getBufferedWriter(travelDistanceStatsFileName + ".csv"); } + try { - this.out.write("ITERATION" + this.delimiter + "avg. Average Leg distance" + this.delimiter + "avg. Average Trip distance\n"); + writeCsvHeader(); } catch (IOException e) { throw new UncheckedIOException(e); } } + private void initStats(ControllerConfigGroup controllerConfigGroup) { + int iterations = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration(); + if (iterations > HISTORY_SIZE) { + iterations = HISTORY_SIZE; // limit the history size + } + this.legStats = new DoubleSummaryStatistics[iterations+1]; + this.tripStats = new DoubleSummaryStatistics[iterations+1]; + } + public void addIteration(int iteration, IdMap map) { - DoubleSummaryStatistics legStats = map.values() - //TODO: This probably doesn't control how many threads parallelStream is using despite the number of threads setting in config - .parallelStream() - .flatMap(plan -> plan.getPlanElements().stream()) - .filter(Leg.class::isInstance) - .mapToDouble(l -> { - Leg leg = (Leg) l; - return leg.getRoute() != null ? leg.getRoute().getDistance() : Double.NaN; - }) - // the following means legs with infinite distance are ignored - .filter(Double::isFinite) - .summaryStatistics(); - - DoubleSummaryStatistics tripStats = map.values() - .parallelStream() - .flatMap(plan -> TripStructureUtils.getTrips(plan).stream()) - .mapToDouble(t -> { - Trip trip = (Trip) t; - return trip.getTripElements() - .stream() - .filter(Leg.class::isInstance) - .collect(Collectors.summingDouble(l -> { - Leg leg = (Leg) l; - // TODO NaN handling of Collectors.summingDouble will lead to many NaNs... rethink - return leg.getRoute() != null ? leg.getRoute().getDistance() : Double.NaN; - })); - }) - // the following means trips with infinite distance are silently ignored. - .filter(Double::isFinite) - .summaryStatistics(); - - log.info("-- average leg distance per plan (executed plans only): " + legStats.getAverage() + " meters"); + DoubleSummaryStatistics legStats = getLegStats(map); + DoubleSummaryStatistics tripStats = getTripStats(map); + + log.info("-- average leg distance per plan (executed plans only): " + legStats.getAverage() + " meters"); log.info("average leg distance per Person (executed plans only): " + legStats.getSum() / map.size() + " meters (statistic on all " + legStats.getCount() + " legs which have a finite distance)"); log.info("-- average trip distance per plan (executed plans only): " + tripStats.getAverage() + " meters"); log.info("average trip distance per Person (executed plans only): " + tripStats.getSum() / map.size() + " meters (statistic on all " + tripStats.getCount() + " trips which have a finite distance)"); log.info("(TravelDistanceStats takes an average over all legs where the simulation reports travelled (network) distances"); log.info("(and teleported legs whose route contains a distance.)");// TODO: still valid? + int index = getIndex(iteration); + if (index >= this.legStats.length){ + return; + } + this.legStats[index] = legStats; + this.tripStats[index] = tripStats; + } + + void writeOutput(int iteration, boolean writePngs){ + writeCsvEntry(iteration); + + if (iteration >= HISTORY_SIZE){ + return; + } + + if(writePngs){ + writePngs(iteration); + } + } + + private void writeCsvHeader() throws IOException { + this.out.write("ITERATION" + this.delimiter + "avg. Average Leg distance" + this.delimiter + "avg. Average Trip distance\n"); + this.out.flush(); + } + + private void writeCsvEntry(int iteration) { + DoubleSummaryStatistics legStats = this.legStats[getIndex(iteration)]; + DoubleSummaryStatistics tripStats = this.tripStats[getIndex(iteration)]; + try { this.out.write(iteration + this.delimiter + legStats.getAverage() + this.delimiter + tripStats.getAverage() + "\n"); this.out.flush(); } catch (IOException e) { e.printStackTrace(); } + } + + void writePngs(int iteration){ + writeLegStatsPng(iteration); + writeTripStatsPng(iteration); + } - if (this.legStatsHistory != null) { - int index = iteration - controllerConfigGroup.getFirstIteration(); - this.legStatsHistory[index] = legStats.getAverage(); - - if (iteration != controllerConfigGroup.getFirstIteration()) { - // create chart when data of more than one iteration is available. - XYLineChart chart = new XYLineChart("Leg Travel Distance Statistics", "iteration", "average of the average leg distance per plan "); - double[] iterations = new double[index + 1]; - for (int i = 0; i <= index; i++) { - iterations[i] = i + controllerConfigGroup.getFirstIteration(); - } - double[] values = new double[index + 1]; - System.arraycopy(this.legStatsHistory, 0, values, 0, index + 1); - chart.addSeries("executed plan", iterations, values); - chart.addMatsimLogo(); - chart.saveAsPng(this.legStatsPngName + ".png", 800, 600); - } - if (index == (this.legStatsHistory.length - 1)) { - // we cannot store more information, so disable the graph feature. - this.legStatsHistory = null; - } + private void writeTripStatsPng(int iteration) { + if (iteration == controllerConfigGroup.getFirstIteration()){ + return; } - if (this.tripStatsHistory != null) { - int index = iteration - controllerConfigGroup.getFirstIteration(); - this.tripStatsHistory[index] = tripStats.getAverage(); - - if (iteration != controllerConfigGroup.getFirstIteration()) { - // create chart when data of more than one iteration is available. - XYLineChart chart = new XYLineChart("Trip Travel Distance Statistics", "iteration", "average of the average trip distance per plan "); - double[] iterations = new double[index + 1]; - for (int i = 0; i <= index; i++) { - iterations[i] = i + controllerConfigGroup.getFirstIteration(); - } - double[] values = new double[index + 1]; - System.arraycopy(this.tripStatsHistory, 0, values, 0, index + 1); - chart.addSeries("executed plan", iterations, values); - chart.addMatsimLogo(); - chart.saveAsPng(this.tripStatsPngName + ".png", 800, 600); - } - if (index == (this.tripStatsHistory.length - 1)) { - // we cannot store more information, so disable the graph feature. - this.tripStatsHistory = null; - } + int index = getIndex(iteration); + // create chart when data of more than one iteration is available. + XYLineChart chart = new XYLineChart("Trip Travel Distance Statistics", "iteration", "average of the average trip distance per plan "); + double[] iterations = new double[index + 1]; + double[] values = new double[index + 1]; + + for (int i = 0; i <= index; i++) { + iterations[i] = i + controllerConfigGroup.getFirstIteration(); + values[i] = Optional.ofNullable(this.tripStats[i]).map(DoubleSummaryStatistics::getAverage).orElse(0.); } + + chart.addSeries("executed plan", iterations, values); + chart.addMatsimLogo(); + chart.saveAsPng(this.tripStatsPngName + ".png", 800, 600); + } + + private void writeLegStatsPng(int iteration) { + if (iteration == controllerConfigGroup.getFirstIteration()){ + return; + } + + int index = getIndex(iteration); + // create chart when data of more than one iteration is available. + XYLineChart chart = new XYLineChart("Leg Travel Distance Statistics", "iteration", "average of the average leg distance per plan "); + double[] iterations = new double[index + 1]; + double[] values = new double[index + 1]; + + for (int i = 0; i <= index; i++) { + iterations[i] = i + controllerConfigGroup.getFirstIteration(); + values[i] = Optional.ofNullable(this.legStats[i]).map(DoubleSummaryStatistics::getAverage).orElse(0.); + + } + + chart.addSeries("executed plan", iterations, values); + chart.addMatsimLogo(); + chart.saveAsPng(this.legStatsPngName + ".png", 800, 600); + } + + private static DoubleSummaryStatistics getTripStats(IdMap map) { + return map.values() + .parallelStream() + .flatMap(plan -> TripStructureUtils.getTrips(plan).stream()) + .mapToDouble(t -> { + Trip trip = (Trip) t; + return trip.getTripElements() + .stream() + .filter(Leg.class::isInstance) + .collect(Collectors.summingDouble(l -> { + Leg leg = (Leg) l; + // TODO NaN handling of Collectors.summingDouble will lead to many NaNs... rethink + return leg.getRoute() != null ? leg.getRoute().getDistance() : Double.NaN; + })); + }) + // the following means trips with infinite distance are silently ignored. + .filter(Double::isFinite) + .summaryStatistics(); + } + + private static DoubleSummaryStatistics getLegStats(IdMap map) { + return map.values() + //TODO: This probably doesn't control how many threads parallelStream is using despite the number of threads setting in config + .parallelStream() + .flatMap(plan -> plan.getPlanElements().stream()) + .filter(Leg.class::isInstance) + .mapToDouble(l -> { + Leg leg = (Leg) l; + return leg.getRoute() != null ? leg.getRoute().getDistance() : Double.NaN; + }) + // the following means legs with infinite distance are ignored + .filter(Double::isFinite) + .summaryStatistics(); + } + + private int getIndex(int iteration) { + return iteration - controllerConfigGroup.getFirstIteration(); } public void close() { @@ -215,6 +254,4 @@ public void close() { } } - - } diff --git a/matsim/src/test/java/org/matsim/analysis/TravelDistanceStatsTest.java b/matsim/src/test/java/org/matsim/analysis/TravelDistanceStatsTest.java index e1e0a8fb0d2..4d471767085 100644 --- a/matsim/src/test/java/org/matsim/analysis/TravelDistanceStatsTest.java +++ b/matsim/src/test/java/org/matsim/analysis/TravelDistanceStatsTest.java @@ -337,25 +337,35 @@ public void testTravelDistanceStats() { private void performTest(IdMap map, String outputDirectory) { - ControllerConfigGroup controllerConfigGroup = new ControllerConfigGroup(); - OutputDirectoryHierarchy controlerIO = new OutputDirectoryHierarchy(outputDirectory, - OverwriteFileSetting.overwriteExistingFiles, CompressionType.gzip); - controllerConfigGroup.setCreateGraphs(true); - controllerConfigGroup.setFirstIteration(0); - controllerConfigGroup.setLastIteration(10); - TravelDistanceStats travelDistanceStats = new TravelDistanceStats(controllerConfigGroup, controlerIO, new GlobalConfigGroup()); + TravelDistanceStats travelDistanceStats = getTravelDistanceStats(outputDirectory); + travelDistanceStats.addIteration(0, map); + travelDistanceStats.writeOutput(0, false); readAndValidateValues(0, person1legsum + person2legsum + person3legsum, 12, person1TotalNumberOfLegs + person2TotalNumberOfLegs + person3TotalNumberOfLegs); + map.remove(person2.getId()); travelDistanceStats.addIteration(1, map); + travelDistanceStats.writeOutput(1, false); readAndValidateValues(1, person1legsum + person3legsum, 8, person1TotalNumberOfLegs + person3TotalNumberOfLegs); + map.remove(person3.getId()); travelDistanceStats.addIteration(2, map); + travelDistanceStats.writeOutput(2, false); readAndValidateValues(2, person1legsum, 6, person1TotalNumberOfLegs); travelDistanceStats.close(); } + private static TravelDistanceStats getTravelDistanceStats(String outputDirectory) { + ControllerConfigGroup controllerConfigGroup = new ControllerConfigGroup(); + OutputDirectoryHierarchy controlerIO = new OutputDirectoryHierarchy(outputDirectory, + OverwriteFileSetting.overwriteExistingFiles, CompressionType.gzip); + controllerConfigGroup.setCreateGraphs(true); + controllerConfigGroup.setFirstIteration(0); + controllerConfigGroup.setLastIteration(10); + return new TravelDistanceStats(controllerConfigGroup, controlerIO, new GlobalConfigGroup()); + } + private void readAndValidateValues(int itr, Double legSum, int totalTrip, long totalLeg) { String file = utils.getOutputDirectory() + "/TravelDistanceStat" + "/traveldistancestats.csv"; @@ -385,6 +395,7 @@ private void readAndValidateValues(int itr, Double legSum, int totalTrip, long t } iteration++; } + Assert.assertEquals("There are too less entries.", itr, iteration); } catch (IOException e) { e.printStackTrace(); } From 6fe7a3cf81023adf4b7ee4ba6a2a53a6ca80b781 Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 1 Dec 2023 17:34:10 +0100 Subject: [PATCH 05/15] removed every core dependency of isCreateGraph --- .../common/timeprofile/ProfileWriter.java | 5 +- .../matsim/analysis/LegHistogramListener.java | 3 +- .../analysis/ModeStatsControlerListener.java | 111 ++++++++---------- .../analysis/ScoreStatsControlerListener.java | 36 +++--- .../core/controler/AbstractController.java | 2 +- .../DependencyGraphControlerListener.java | 52 ++++---- .../ModeStatsControlerListenerTest.java | 1 + .../ScoreStatsControlerListenerTest.java | 3 + 8 files changed, 104 insertions(+), 109 deletions(-) diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java index 768ec3ea404..e98f9cbb2b4 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/ProfileWriter.java @@ -87,7 +87,8 @@ public void notifyIterationEnds(IterationEndsEvent event) { } } - if (this.matsimServices.getConfig().controller().isCreateGraphs()) { + int createGraphsInterval = this.matsimServices.getConfig().controller().getCreateGraphsInterval(); + if (createGraphsInterval > 0 && event.getIteration() % createGraphsInterval == 0) { DefaultTableXYDataset xyDataset = createXYDataset(times, profiles); generateImage(xyDataset, TimeProfileCharts.ChartType.Line); generateImage(xyDataset, TimeProfileCharts.ChartType.StackedArea); @@ -103,7 +104,7 @@ private DefaultTableXYDataset createXYDataset(double[] times, Map { XYSeries series = new XYSeries(name, true, false); for (int i = 0; i < times.length; i++) { - series.add((double)times[i] / 3600, profile[i]); + series.add((double) times[i] / 3600, profile[i]); } seriesList.add(series); }); diff --git a/matsim/src/main/java/org/matsim/analysis/LegHistogramListener.java b/matsim/src/main/java/org/matsim/analysis/LegHistogramListener.java index e5c1a9b8e2d..9393b17c2e7 100644 --- a/matsim/src/main/java/org/matsim/analysis/LegHistogramListener.java +++ b/matsim/src/main/java/org/matsim/analysis/LegHistogramListener.java @@ -55,7 +55,8 @@ public void notifyIterationStarts(final IterationStartsEvent event) { public void notifyIterationEnds(final IterationEndsEvent event) { this.histogram.write(controlerIO.getIterationFilename(event.getIteration(), "legHistogram.txt")); this.printStats(); - if (controllerConfigGroup.isCreateGraphs()) { + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + if (createGraphsInterval > 0 && event.getIteration() % createGraphsInterval == 0) { LegHistogramChart.writeGraphic(this.histogram, controlerIO.getIterationFilename(event.getIteration(), "legHistogram_all.png")); for (String legMode : this.histogram.getLegModes()) { LegHistogramChart.writeGraphic(this.histogram, controlerIO.getIterationFilename(event.getIteration(), "legHistogram_" + legMode + ".png"), legMode); diff --git a/matsim/src/main/java/org/matsim/analysis/ModeStatsControlerListener.java b/matsim/src/main/java/org/matsim/analysis/ModeStatsControlerListener.java index c9f4ff89ce0..dc351742a11 100644 --- a/matsim/src/main/java/org/matsim/analysis/ModeStatsControlerListener.java +++ b/matsim/src/main/java/org/matsim/analysis/ModeStatsControlerListener.java @@ -73,7 +73,6 @@ public final class ModeStatsControlerListener implements StartupListener, Iterat private final String modeFileName; private final String delimiter; - private final boolean createPNG; private final ControllerConfigGroup controllerConfigGroup; Map> modeHistories = new HashMap<>(); @@ -94,7 +93,6 @@ public final class ModeStatsControlerListener implements StartupListener, Iterat this.population = population1; this.modeFileName = controlerIO.getOutputFilename(FILENAME_MODESTATS); this.delimiter = globalConfigGroup.getDefaultDelimiter(); - this.createPNG = controllerConfigGroup.isCreateGraphs(); this.mainModeIdentifier = mainModeIdentifier; } @@ -105,7 +103,8 @@ public void notifyStartup(final StartupEvent event) { @Override public void notifyIterationEnds(final IterationEndsEvent event) { - collectModeShareInfo(event) ; + collectModeShareInfo(event); + writeOutput(event); } private void collectModeShareInfo(final IterationEndsEvent event) { @@ -156,7 +155,53 @@ private void collectModeShareInfo(final IterationEndsEvent event) { } modeHistory.put( event.getIteration(), share ) ; } + modeCnt.clear(); + } + + void writeOutput(IterationEndsEvent event) { + writeCsv(event); + + if (isWriteGraphs(event)) { + writePngs(); + } + } + + private boolean isWriteGraphs(IterationEndsEvent event) { + return this.controllerConfigGroup.getCreateGraphsInterval() > 0 && + event.getIteration() % this.controllerConfigGroup.getCreateGraphsInterval() == 0 && + event.getIteration() > this.minIteration; + } + private void writePngs() { + // create chart when data of more than one iteration is available. + XYLineChart chart = new XYLineChart("Mode Statistics", "iteration", "mode"); + for ( Entry> entry : this.modeHistories.entrySet() ) { + String mode = entry.getKey() ; + Map history = entry.getValue() ; + chart.addSeries(mode, history ) ; + } + chart.addMatsimLogo(); + chart.saveAsPng(this.modeFileName + ".png", 800, 600); + + /////// EDIT: STACKED_BAR /////////////////////////////////////////////////////// + // create chart when data of more than one iteration is available. + StackedBarChart chart2 = new StackedBarChart("Mode Statistics", "iteration", "share"); + for (Entry> entry : this.modeHistories.entrySet()) { + String mode = entry.getKey(); + Map history = entry.getValue(); + double[] historyArray = new double[history.size()]; + int i = 0; + for ( Entry entryHistory : history.entrySet() ) { + historyArray[i] = entryHistory.getValue(); + i++; + } + chart2.addSeries(mode, historyArray); + } + chart2.addMatsimLogo(); + chart2.saveAsPng(this.modeFileName + "_stackedbar.png", 800, 600); + } + + private void writeCsv(IterationEndsEvent event) { try (BufferedWriter modeOut = IOUtils.getBufferedWriter(this.modeFileName + ".csv")) { modeOut.write("iteration"); for ( String mode : modes ) { @@ -176,65 +221,5 @@ private void collectModeShareInfo(final IterationEndsEvent event) { e.printStackTrace(); throw new UncheckedIOException(e); } - - - // yyyy the following does not work!! - // Why? The charts seem to be useful (JB, April 2017) - if (this.createPNG && event.getIteration() > this.minIteration) { - // create chart when data of more than one iteration is available. - XYLineChart chart = new XYLineChart("Mode Statistics", "iteration", "mode"); - for ( Entry> entry : this.modeHistories.entrySet() ) { - String mode = entry.getKey() ; - Map history = entry.getValue() ; -// log.warn( "about to add the following series:" ) ; -// for ( Entry item : history.entrySet() ) { -// log.warn( item.getKey() + " -- " + item.getValue() ); -// } - chart.addSeries(mode, history ) ; - } - chart.addMatsimLogo(); - chart.saveAsPng(this.modeFileName + ".png", 800, 600); - - /////// EDIT: STACKED_BAR /////////////////////////////////////////////////////// - if (event.getIteration() > this.minIteration) { - // create chart when data of more than one iteration is available. - StackedBarChart chart2 = new StackedBarChart("Mode Statistics", "iteration", "share"); - for (Entry> entry : this.modeHistories.entrySet()) { - String mode = entry.getKey(); - Map history = entry.getValue(); - double[] historyArray = new double[history.size()]; - int i = 0; - for ( Entry entryHistory : history.entrySet() ) { - historyArray[i] = entryHistory.getValue(); - i++; - } - chart2.addSeries(mode, historyArray); - } - chart2.addMatsimLogo(); - chart2.saveAsPng(this.modeFileName + "_stackedbar.png", 800, 600); - } - } - modeCnt.clear(); - } - - public final Map> getModeHistories() { - return Collections.unmodifiableMap( this.modeHistories ) ; - } - - ////////////copied methods - to not depend on dvrp /////////////////////////////////////////////////////// - private void makeStayTaskSeriesGrey(XYPlot plot) { - XYDataset dataset = plot.getDataset(0); - for (int i = 0; i < dataset.getSeriesCount(); i++) { - plot.getRenderer().setSeriesPaint(i, Color.LIGHT_GRAY); - return; - } - } - private static void saveAsPNG(JFreeChart chart, String filename, int width, int height) { - try { - ChartUtils.writeChartAsPNG(new FileOutputStream(filename + ".png"), chart, width, height); - } catch (IOException e) { - throw new RuntimeException(e); - } } - ////////////////////////////////////////////////////////////////////////////////////////////////////////// } diff --git a/matsim/src/main/java/org/matsim/analysis/ScoreStatsControlerListener.java b/matsim/src/main/java/org/matsim/analysis/ScoreStatsControlerListener.java index 1593cec5243..8e82756527c 100644 --- a/matsim/src/main/java/org/matsim/analysis/ScoreStatsControlerListener.java +++ b/matsim/src/main/java/org/matsim/analysis/ScoreStatsControlerListener.java @@ -69,7 +69,6 @@ public enum ScoreItem { worst, best, average, executed }; private final String delimiter; private final BufferedWriter out; - private final boolean createPNG; private final ControllerConfigGroup controllerConfigGroup; private final Map> scoreHistory = new HashMap<>(); @@ -87,7 +86,6 @@ public enum ScoreItem { worst, best, average, executed }; this.population = population; this.controllerIO = controllerIO; this.delimiter = globalConfig.getDefaultDelimiter(); - this.createPNG = controllerConfigGroup.isCreateGraphs(); this.out = IOUtils.getBufferedWriter(controllerIO.getOutputFilename("scorestats.csv")); Set subpopulations = population.getPersons().values().stream() @@ -127,10 +125,19 @@ public void notifyStartup(final StartupEvent event) { @Override public void notifyIterationEnds(final IterationEndsEvent event) { collectScoreInfo(event); + if (isWriteGraph(event)) { + writePng(); + } } - private void collectScoreInfo(final IterationEndsEvent event) { + 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 perSubpop = new HashMap<>(); @@ -165,21 +172,16 @@ private void collectScoreInfo(final IterationEndsEvent event) { 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 ) ; + } - if (this.createPNG && event.getIteration() > this.minIteration) { - // create chart when data of more than one iteration is available. - XYLineChart chart = new XYLineChart("Score Statistics", "iteration", "score"); -// double[] iterations = new double[index + 1]; -// for (int i = 0; i <= index; i++) { -// iterations[i] = i + this.minIteration; -// } - 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("scorestats.png"), 800, 600); - } + 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("scorestats.png"), 800, 600); } @Override diff --git a/matsim/src/main/java/org/matsim/core/controler/AbstractController.java b/matsim/src/main/java/org/matsim/core/controler/AbstractController.java index 5edf2d01b20..93c1680a231 100644 --- a/matsim/src/main/java/org/matsim/core/controler/AbstractController.java +++ b/matsim/src/main/java/org/matsim/core/controler/AbstractController.java @@ -178,7 +178,7 @@ public void run() { } catch (UncheckedIOException e) { log.error("Could not write stopwatch file.", e); } - if (config.controller().isCreateGraphs()) { + if (config.controller().getCreateGraphsInterval() > 0 && iteration % config.controller().getCreateGraphsInterval() == 0) { this.getStopwatch().writeGraphFile(this.getControlerIO().getOutputFilename("stopwatch.png", ControllerConfigGroup.CompressionType.none)); } log.info(MARKER + "ITERATION " + iteration + " ENDS"); diff --git a/matsim/src/main/java/org/matsim/guice/DependencyGraphControlerListener.java b/matsim/src/main/java/org/matsim/guice/DependencyGraphControlerListener.java index 37492d3f785..9c108996bca 100644 --- a/matsim/src/main/java/org/matsim/guice/DependencyGraphControlerListener.java +++ b/matsim/src/main/java/org/matsim/guice/DependencyGraphControlerListener.java @@ -68,33 +68,35 @@ class DependencyGraphControlerListener implements StartupListener { } public void notifyStartup(StartupEvent event) { - if (event.getServices().getConfig().controller().isCreateGraphs()) { - try (PrintWriter out = new PrintWriter(new File(controlerIO.getOutputFilename("modules.dot")))) { - MatsimGrapher grapher = new MatsimGrapher(new AbstractInjectorGrapher.GrapherParameters() - .setAliasCreator(bindings -> { - List allAliases = Lists.newArrayList(); - for (Binding binding : bindings) { - if (binding instanceof ProviderBinding) { - allAliases.add(new Alias(NodeId.newTypeId(binding.getKey()), - NodeId.newTypeId(((ProviderBinding) binding).getProvidedKey()))); - } + if (event.getServices().getConfig().controller().getCreateGraphsInterval() <= 0) { + return; + } + + try (PrintWriter out = new PrintWriter(new File(controlerIO.getOutputFilename("modules.dot")))) { + MatsimGrapher grapher = new MatsimGrapher(new AbstractInjectorGrapher.GrapherParameters() + .setAliasCreator(bindings -> { + List allAliases = Lists.newArrayList(); + for (Binding binding : bindings) { + if (binding instanceof ProviderBinding) { + allAliases.add(new Alias(NodeId.newTypeId(binding.getKey()), + NodeId.newTypeId(((ProviderBinding) binding).getProvidedKey()))); } - allAliases.addAll(getMapBinderAliases(String.class, TravelTime.class, bindings)); - allAliases.addAll(getMapBinderAliases(String.class, TravelDisutilityFactory.class, bindings)); - allAliases.addAll(getMapBinderAliases(String.class, RoutingModule.class, bindings)); - allAliases.addAll(getMapBinderAliases(ReplanningConfigGroup.StrategySettings.class, PlanStrategy.class, bindings)); - allAliases.addAll(getMultibinderAliases(ControlerListener.class, bindings)); - allAliases.addAll(getMultibinderAliases(SnapshotWriter.class, bindings)); - allAliases.addAll(getMultibinderAliases(MobsimListener.class, bindings)); - allAliases.addAll(getMultibinderAliases(EventHandler.class, bindings)); - allAliases.addAll(getMultibinderAliases(AbstractQSimModule.class, bindings)); - return allAliases; } - ), out); - grapher.graph(injector); - } catch (IOException e) { - throw new RuntimeException(e); - } + allAliases.addAll(getMapBinderAliases(String.class, TravelTime.class, bindings)); + allAliases.addAll(getMapBinderAliases(String.class, TravelDisutilityFactory.class, bindings)); + allAliases.addAll(getMapBinderAliases(String.class, RoutingModule.class, bindings)); + allAliases.addAll(getMapBinderAliases(ReplanningConfigGroup.StrategySettings.class, PlanStrategy.class, bindings)); + allAliases.addAll(getMultibinderAliases(ControlerListener.class, bindings)); + allAliases.addAll(getMultibinderAliases(SnapshotWriter.class, bindings)); + allAliases.addAll(getMultibinderAliases(MobsimListener.class, bindings)); + allAliases.addAll(getMultibinderAliases(EventHandler.class, bindings)); + allAliases.addAll(getMultibinderAliases(AbstractQSimModule.class, bindings)); + return allAliases; + } + ), out); + grapher.graph(injector); + } catch (IOException e) { + throw new RuntimeException(e); } } diff --git a/matsim/src/test/java/org/matsim/analysis/ModeStatsControlerListenerTest.java b/matsim/src/test/java/org/matsim/analysis/ModeStatsControlerListenerTest.java index daf5a8cd90d..6ccaba6938c 100644 --- a/matsim/src/test/java/org/matsim/analysis/ModeStatsControlerListenerTest.java +++ b/matsim/src/test/java/org/matsim/analysis/ModeStatsControlerListenerTest.java @@ -460,6 +460,7 @@ private void readAndcompareValues(HashMap modes, int itr) { } iteration++; } + Assert.assertEquals(itr, iteration); } catch (IOException e) { e.printStackTrace(); } diff --git a/matsim/src/test/java/org/matsim/analysis/ScoreStatsControlerListenerTest.java b/matsim/src/test/java/org/matsim/analysis/ScoreStatsControlerListenerTest.java index 33345fdfb2b..dc94a183ea7 100644 --- a/matsim/src/test/java/org/matsim/analysis/ScoreStatsControlerListenerTest.java +++ b/matsim/src/test/java/org/matsim/analysis/ScoreStatsControlerListenerTest.java @@ -551,10 +551,13 @@ private void readAndValidateValues(String outDir, int itr, Population populatio 0); Assert.assertEquals("avg average score does not match", (getScore(population, "avgaverage")/getNoOfPlans(population)), avgAverage, 0); + break; } iteration++; } + Assert.assertEquals(itr, iteration); + Assertions.assertThat(new File(outDir, "scorestats_group1.csv")) .isFile(); From 57cc8ece08d6d8f6d159a3b83d7bf1b3dee6cdcd Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 1 Dec 2023 17:47:18 +0100 Subject: [PATCH 06/15] removed all dependencies of isCreateGraph --- .../contrib/common/timeprofile/TimeProfileCollector.java | 9 +++++++-- .../shifts/analysis/ShiftAnalysisControlerListener.java | 5 +++-- .../shifts/analysis/ShiftHistogramListener.java | 4 +++- .../ShiftEfficiencyAnalysisControlerListener.java | 5 +++-- .../drt/analysis/DrtAnalysisControlerListener.java | 3 ++- .../ChargerOccupancyTimeProfileCollectorProvider.java | 2 +- .../usage/analysis/CourtesyHistogramListener.java | 9 +++++---- .../matsim/core/config/groups/ControllerConfigGroup.java | 5 ----- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeProfileCollector.java b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeProfileCollector.java index e44bebb84d7..ce926be9116 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeProfileCollector.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/timeprofile/TimeProfileCollector.java @@ -99,8 +99,13 @@ public void notifyMobsimBeforeCleanup(@SuppressWarnings("rawtypes") MobsimBefore } } - for (ChartType t : chartTypes) { - generateImage(header, t); + int createGraphsInterval = matsimServices.getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && matsimServices.getIterationNumber() % createGraphsInterval == 0; + + if(createGraphs){ + for (ChartType t : chartTypes) { + generateImage(header, t); + } } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java index de7026de9e2..60c9c9e1cb0 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftAnalysisControlerListener.java @@ -52,9 +52,10 @@ public ShiftAnalysisControlerListener(Config config, DrtConfigGroup drtConfigGro @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; - writeAndPlotShiftDurationComparison(shiftDurationXY.getShift2plannedVsActualDuration(), + writeAndPlotShiftDurationComparison(shiftDurationXY.getShift2plannedVsActualDuration(), filename(event, "shiftDurationComparison", ".png"), filename(event, "shiftDurationComparison", ".csv"), createGraphs); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java index 15aef81a055..aceaa023f7d 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/ShiftHistogramListener.java @@ -35,7 +35,9 @@ public void notifyIterationStarts(final IterationStartsEvent event) { public void notifyIterationEnds(final IterationEndsEvent event) { this.shiftHistogram.write(matsimServices.getControlerIO().getIterationFilename(event.getIteration(), drtConfigGroup.getMode() + "_" + "shiftHistogram.txt")); this.printStats(); - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; + if (createGraphs) { ShiftHistogramChart.writeGraphic(this.shiftHistogram, matsimServices.getControlerIO().getIterationFilename(event.getIteration(),drtConfigGroup.getMode() + "_" + "shiftHistogram.png")); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java index f75637f76d7..57e451f31b0 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java @@ -56,9 +56,10 @@ public ShiftEfficiencyAnalysisControlerListener(DrtConfigGroup drtConfigGroup, @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; - writeAndPlotShiftEfficiency( + writeAndPlotShiftEfficiency( shiftEfficiencyTracker.getCurrentRecord().getRevenueByShift(), shiftEfficiencyTracker.getCurrentRecord().getRequestsByShift(), shiftEfficiencyTracker.getCurrentRecord().getFinishedShifts(), diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java index 1f33b8cf008..399804f9aee 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java @@ -160,7 +160,8 @@ private static List newDrtLegs(EventSequence sequence, Function @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; writeAndPlotWaitTimeEstimateComparison(drtEventSequenceCollector.getPerformedRequestSequences().values(), filename(event, "waitTimeComparison", ".png"), filename(event, "waitTimeComparison", ".csv"), createGraphs); diff --git a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerOccupancyTimeProfileCollectorProvider.java b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerOccupancyTimeProfileCollectorProvider.java index a11883f50f2..4827a4c351d 100644 --- a/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerOccupancyTimeProfileCollectorProvider.java +++ b/contribs/ev/src/main/java/org/matsim/contrib/ev/stats/ChargerOccupancyTimeProfileCollectorProvider.java @@ -67,7 +67,7 @@ public MobsimListener get() { }; var collector = new TimeProfileCollector(header, calc, 300, "charger_occupancy_time_profiles", matsimServices); - if (matsimServices.getConfig().controller().isCreateGraphs()) { + if (matsimServices.getConfig().controller().getCreateGraphsInterval()>0) { collector.setChartTypes(ChartType.Line, ChartType.StackedArea); } else { collector.setChartTypes(); diff --git a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/usage/analysis/CourtesyHistogramListener.java b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/usage/analysis/CourtesyHistogramListener.java index c659828ea59..4edd091b5d5 100644 --- a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/usage/analysis/CourtesyHistogramListener.java +++ b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/usage/analysis/CourtesyHistogramListener.java @@ -50,16 +50,14 @@ public class CourtesyHistogramListener implements IterationEndsListener, IterationStartsListener { private final CourtesyHistogram histogram; - private final boolean outputGraph; private static final Logger log = LogManager.getLogger(CourtesyHistogramListener.class); private final OutputDirectoryHierarchy controlerIO; @Inject - CourtesyHistogramListener(Config config, CourtesyHistogram histogram, OutputDirectoryHierarchy controlerIO) { + CourtesyHistogramListener(CourtesyHistogram histogram, OutputDirectoryHierarchy controlerIO) { this.controlerIO = controlerIO; this.histogram = histogram; - this.outputGraph = config.controller().isCreateGraphs(); } @Override @@ -70,7 +68,10 @@ public void notifyIterationStarts(final IterationStartsEvent event) { @Override public void notifyIterationEnds(final IterationEndsEvent event) { this.histogram.write(controlerIO.getIterationFilename(event.getIteration(), "courtesyHistogram.txt")); - if (this.outputGraph) { + + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; + if (createGraphs) { for ( String type : histogram.getDataFrames().keySet() ) { writeGraphic( this.histogram, diff --git a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java index d68aa5bf6e6..aff78717f7f 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java @@ -355,11 +355,6 @@ public void setWriteSnapshotsInterval(int writeSnapshotsInterval) { this.writeSnapshotsInterval = writeSnapshotsInterval; } - @StringGetter( CREATE_GRAPHS ) - public boolean isCreateGraphs() { - return createGraphs; - } - /** * Sets whether graphs showing some analyses should automatically be * generated during the simulation. The generation of graphs usually takes a From 196f70e88743bed593d01d5837b92a5a8c439a88 Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Fri, 1 Dec 2023 18:30:54 +0100 Subject: [PATCH 07/15] adapted controller config group --- .../config/groups/ControllerConfigGroup.java | 21 +++++++++++++++++-- .../groups/ControllerConfigGroupTest.java | 18 ++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java index aff78717f7f..8736a467ab6 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java @@ -102,7 +102,6 @@ public enum MobsimType {qsim, JDEQSim, hermes} private int writeTripsInterval = 50; private String mobsim = MobsimType.qsim.toString(); private int writeSnapshotsInterval = 1; - private boolean createGraphs = true; private int createGraphsInterval = 1; private boolean dumpDataAtEnd = true; @@ -136,6 +135,11 @@ public final Map getComments() { map.put(CREATE_GRAPHS, "Sets whether graphs showing some analyses should automatically be generated during the simulation." + " The generation of graphs usually takes a small amount of time that does not have any weight in big simulations," + " but add a significant overhead in smaller runs or in test cases where the graphical output is not even requested." ); + + map.put(CREATE_GRAPHS_INTERVAL, "Sets the interval in which graphs are generated. Default is 1. If set to 0, no graphs are generated." + + " The generation of graphs usually takes a small amount of time that does not have any weight in big simulations," + + " but add a significant overhead in smaller runs or in test cases where the graphical output is not even requested." ); + map.put(COMPRESSION_TYPE, "Compression algorithm to use when writing out data to files. Possible values: " + Arrays.toString(CompressionType.values())); map.put(EVENT_TYPE_TO_CREATE_SCORING_FUNCTIONS, "Defines when the scoring functions for the population are created. Default=IterationStarts. Possible values: " + Arrays.toString(EventTypeToCreateScoringFunctions.values())); @@ -355,6 +359,13 @@ public void setWriteSnapshotsInterval(int writeSnapshotsInterval) { this.writeSnapshotsInterval = writeSnapshotsInterval; } + @StringGetter( CREATE_GRAPHS ) + @Deprecated + public boolean getCreateGraphs() { + log.warn("Configuration 'createGraphs' is deprecated. Using 'createGraphsInterval' instead."); + return this.createGraphsInterval > 0; + } + /** * Sets whether graphs showing some analyses should automatically be * generated during the simulation. The generation of graphs usually takes a @@ -366,8 +377,14 @@ public void setWriteSnapshotsInterval(int writeSnapshotsInterval) { * true if graphs showing analyses' output should be generated. */ @StringSetter( CREATE_GRAPHS ) + @Deprecated public void setCreateGraphs(boolean createGraphs) { - this.createGraphs = createGraphs; + log.warn("Configuration 'createGraphs' is deprecated. Using 'createGraphsInterval' instead."); + if (createGraphs) { + this.setCreateGraphsInterval(1); + } else { + this.setCreateGraphsInterval(0); + } } @StringGetter( CREATE_GRAPHS_INTERVAL ) diff --git a/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java index a1c50b1d46f..545d28d9415 100644 --- a/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java @@ -161,5 +161,23 @@ public void testWriteSnapshotInterval(){ Assert.assertEquals(42, cg.getWriteSnapshotsInterval()); } + @Test + public void testCreateGraphsInterval() { + ControllerConfigGroup cg = new ControllerConfigGroup(); + //initial value + Assert.assertEquals(1, cg.getCreateGraphsInterval()); + //modify by string + cg.addParam("createGraphsInterval", "10"); + Assert.assertEquals(10, cg.getCreateGraphsInterval()); + //modify by setter + cg.setCreateGraphsInterval(42); + Assert.assertEquals("42", cg.getValue("createGraphsInterval")); + Assert.assertEquals(42, cg.getCreateGraphsInterval()); + //modify by deprecated setter + cg.setCreateGraphs(true); + Assert.assertEquals(1, cg.getCreateGraphsInterval()); + Assert.assertEquals(true, cg.getCreateGraphs()); + } + } From 40b89479469876f3a0379de1f13481f308014e8b Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Tue, 5 Dec 2023 14:33:24 +0100 Subject: [PATCH 08/15] allow orphan setters on config groups in order to provide backwards compatibility. --- .../core/config/ReflectiveConfigGroup.java | 4 +- .../config/groups/ControllerConfigGroup.java | 49 ++++++++----------- .../config/ReflectiveConfigGroupTest.java | 9 ++-- .../groups/ControllerConfigGroupTest.java | 1 - 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java index 1301c6daeb1..0cf673c78e4 100644 --- a/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/ReflectiveConfigGroup.java @@ -143,7 +143,9 @@ public ReflectiveConfigGroup(final String name, final boolean storeUnknownParame paramFields = getParamFields(); registeredParams = Sets.union(stringGetters.keySet(), paramFields.keySet()); - checkModuleConsistency(setters.keySet().equals(stringGetters.keySet()), "setters and getters inconsistent"); + // Each parameter which has a setter must have a getter. But there can be parameters which have a getter but no setter in order + // to provide backwards compatibility. + checkModuleConsistency(setters.keySet().containsAll(stringGetters.keySet()), "setters and getters inconsistent"); checkModuleConsistency(paramFields.keySet().stream().noneMatch(setters::containsKey), "Use either StringGetter/Setter or Parameter annotations"); } diff --git a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java index 8736a467ab6..9cbde96f1e3 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/ControllerConfigGroup.java @@ -84,7 +84,6 @@ public enum MobsimType {qsim, JDEQSim, hermes} private static final String WRITE_SNAPSHOTS_INTERVAL = "writeSnapshotsInterval"; - private String outputDirectory = "./output"; private int firstIteration = 0; private int lastIteration = 1000; @@ -359,44 +358,36 @@ public void setWriteSnapshotsInterval(int writeSnapshotsInterval) { this.writeSnapshotsInterval = writeSnapshotsInterval; } - @StringGetter( CREATE_GRAPHS ) - @Deprecated - public boolean getCreateGraphs() { - log.warn("Configuration 'createGraphs' is deprecated. Using 'createGraphsInterval' instead."); - return this.createGraphsInterval > 0; - } - - /** - * Sets whether graphs showing some analyses should automatically be - * generated during the simulation. The generation of graphs usually takes a - * small amount of time that does not have any weight in big simulations, - * but add a significant overhead in smaller runs or in test cases where the - * graphical output is not even requested. - * - * @param createGraphs - * true if graphs showing analyses' output should be generated. - */ - @StringSetter( CREATE_GRAPHS ) - @Deprecated - public void setCreateGraphs(boolean createGraphs) { - log.warn("Configuration 'createGraphs' is deprecated. Using 'createGraphsInterval' instead."); - if (createGraphs) { - this.setCreateGraphsInterval(1); - } else { - this.setCreateGraphsInterval(0); - } - } - @StringGetter( CREATE_GRAPHS_INTERVAL ) public int getCreateGraphsInterval() { return createGraphsInterval; } + /** + * Sets whether graphs showing some analyses should automatically be + * generated during the simulation. The generation of graphs usually takes a + * small amount of time that does not have any weight in big simulations, + * but add a significant overhead in smaller runs or in test cases where the + * graphical output is not even requested. + * + * @param createGraphsInterval iteration interval in which graphs are generated + */ @StringSetter( CREATE_GRAPHS_INTERVAL ) public void setCreateGraphsInterval(int createGraphsInterval) { this.createGraphsInterval = createGraphsInterval; } + @StringSetter( CREATE_GRAPHS ) + @Deprecated + public void setCreateGraphs(boolean createGraphs) { + log.warn("Parameter 'createGraphs' is deprecated. Using 'createGraphsInterval' instead. The output_config.xml will contain the new parameter."); + if (createGraphs) { + this.setCreateGraphsInterval(1); + } else { + this.setCreateGraphsInterval(0); + } + } + @StringGetter( OVERWRITE_FILE ) public OverwriteFileSetting getOverwriteFileSetting() { return overwriteFileSetting; diff --git a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java index a662f53d5ad..884777ff20b 100644 --- a/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/ReflectiveConfigGroupTest.java @@ -172,18 +172,19 @@ public void testComments() { } @Test - public void testFailOnConstructingOrphanSetter() { - assertThatThrownBy(() -> new ReflectiveConfigGroup("name") { + public void testAllowOnConstructingOrphanSetter() { + // no exception + new ReflectiveConfigGroup("name") { @StringSetter("setterWithoutGetter") public void setStuff(String s) { } - }).isInstanceOf(InconsistentModuleException.class); + }; } @Test public void testFailOnConstructingOrphanGetter() { assertThatThrownBy(() -> new ReflectiveConfigGroup("name") { - @StringGetter("setterWithoutGetter") + @StringGetter("getterWithoutSetter") public Coord getStuff() { return null; } diff --git a/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java b/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java index 545d28d9415..e6f47897853 100644 --- a/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java +++ b/matsim/src/test/java/org/matsim/core/config/groups/ControllerConfigGroupTest.java @@ -176,7 +176,6 @@ public void testCreateGraphsInterval() { //modify by deprecated setter cg.setCreateGraphs(true); Assert.assertEquals(1, cg.getCreateGraphsInterval()); - Assert.assertEquals(true, cg.getCreateGraphs()); } From 6cfdc995be333686317689b5f4152fbdcf3690bf Mon Sep 17 00:00:00 2001 From: Paul Heinrich Date: Tue, 5 Dec 2023 15:39:50 +0100 Subject: [PATCH 09/15] corrected TravelDistanceStats --- .../matsim/analysis/TravelDistanceStats.java | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java b/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java index 9d0176c8adf..9099b8f67f6 100644 --- a/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java +++ b/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java @@ -57,7 +57,7 @@ public class TravelDistanceStats { - private static final int HISTORY_SIZE = 5000; + private static final int MAX_ITERATIONS_IN_GRAPH = 5000; private final ControllerConfigGroup controllerConfigGroup; private BufferedWriter out; @@ -65,8 +65,8 @@ public class TravelDistanceStats { private final String tripStatsPngName; private final String delimiter; - private DoubleSummaryStatistics[] legStats; - private DoubleSummaryStatistics[] tripStats; + private Map legStats; + private Map tripStats; private final static Logger log = LogManager.getLogger(TravelDistanceStats.class); @@ -103,12 +103,9 @@ private void initWriter(String travelDistanceStatsFileName) { } private void initStats(ControllerConfigGroup controllerConfigGroup) { - int iterations = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration(); - if (iterations > HISTORY_SIZE) { - iterations = HISTORY_SIZE; // limit the history size - } - this.legStats = new DoubleSummaryStatistics[iterations+1]; - this.tripStats = new DoubleSummaryStatistics[iterations+1]; + int expectedIterations = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration(); + this.legStats = new HashMap<>(expectedIterations+1); + this.tripStats = new HashMap<>(expectedIterations+1); } public void addIteration(int iteration, IdMap map) { @@ -122,22 +119,16 @@ public void addIteration(int iteration, IdMap map) { log.info("(TravelDistanceStats takes an average over all legs where the simulation reports travelled (network) distances"); log.info("(and teleported legs whose route contains a distance.)");// TODO: still valid? - int index = getIndex(iteration); - if (index >= this.legStats.length){ - return; - } - this.legStats[index] = legStats; - this.tripStats[index] = tripStats; + this.legStats.put(iteration, legStats); + this.tripStats.put(iteration, tripStats); + + assert this.legStats.size() == this.tripStats.size(); } void writeOutput(int iteration, boolean writePngs){ writeCsvEntry(iteration); - if (iteration >= HISTORY_SIZE){ - return; - } - - if(writePngs){ + if(writePngs && this.legStats.size() < MAX_ITERATIONS_IN_GRAPH){ writePngs(iteration); } } @@ -148,8 +139,8 @@ private void writeCsvHeader() throws IOException { } private void writeCsvEntry(int iteration) { - DoubleSummaryStatistics legStats = this.legStats[getIndex(iteration)]; - DoubleSummaryStatistics tripStats = this.tripStats[getIndex(iteration)]; + DoubleSummaryStatistics legStats = this.legStats.get(iteration); + DoubleSummaryStatistics tripStats = this.tripStats.get(iteration); try { this.out.write(iteration + this.delimiter + legStats.getAverage() + this.delimiter + tripStats.getAverage() + "\n"); @@ -169,15 +160,14 @@ private void writeTripStatsPng(int iteration) { return; } - int index = getIndex(iteration); // create chart when data of more than one iteration is available. XYLineChart chart = new XYLineChart("Trip Travel Distance Statistics", "iteration", "average of the average trip distance per plan "); - double[] iterations = new double[index + 1]; - double[] values = new double[index + 1]; + double[] iterations = new double[iteration + 1]; + double[] values = new double[iteration + 1]; - for (int i = 0; i <= index; i++) { + for (int i = 0; i <= iteration; i++) { iterations[i] = i + controllerConfigGroup.getFirstIteration(); - values[i] = Optional.ofNullable(this.tripStats[i]).map(DoubleSummaryStatistics::getAverage).orElse(0.); + values[i] = Optional.ofNullable(this.tripStats.get(i)).map(DoubleSummaryStatistics::getAverage).orElse(0.); } chart.addSeries("executed plan", iterations, values); @@ -190,15 +180,14 @@ private void writeLegStatsPng(int iteration) { return; } - int index = getIndex(iteration); // create chart when data of more than one iteration is available. XYLineChart chart = new XYLineChart("Leg Travel Distance Statistics", "iteration", "average of the average leg distance per plan "); - double[] iterations = new double[index + 1]; - double[] values = new double[index + 1]; + double[] iterations = new double[iteration + 1]; + double[] values = new double[iteration + 1]; - for (int i = 0; i <= index; i++) { + for (int i = 0; i <= iteration; i++) { iterations[i] = i + controllerConfigGroup.getFirstIteration(); - values[i] = Optional.ofNullable(this.legStats[i]).map(DoubleSummaryStatistics::getAverage).orElse(0.); + values[i] = Optional.ofNullable(this.legStats.get(i)).map(DoubleSummaryStatistics::getAverage).orElse(0.); } @@ -242,10 +231,6 @@ private static DoubleSummaryStatistics getLegStats(IdMap map) { .summaryStatistics(); } - private int getIndex(int iteration) { - return iteration - controllerConfigGroup.getFirstIteration(); - } - public void close() { try { this.out.close(); From c092055fe2a8e760163a2822ff72494ad0ae742d Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Wed, 6 Dec 2023 10:43:15 +0100 Subject: [PATCH 10/15] sharing factor and pooling rate for DRT --- .../drt/analysis/DrtModeAnalysisModule.java | 3 + .../SharingFactorControlerListener.java | 144 ++++++++++++ .../sharingfactor/SharingFactorModule.java | 38 ++++ .../sharingfactor/SharingFactorTracker.java | 112 ++++++++++ .../drt/sharingfactor/SharingFactorTest.java | 205 ++++++++++++++++++ 5 files changed, 502 insertions(+) create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java create mode 100644 contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java index e362e4d4935..e5cd54688c2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java @@ -34,6 +34,7 @@ import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; +import org.matsim.contrib.drt.sharingfactor.SharingFactorModule; import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; @@ -136,5 +137,7 @@ public void install() { getter.getModal(DrtVehicleDistanceStats.class), getter.get(MatsimServices.class), getter.get(Network.class), getter.getModal(DrtEventSequenceCollector.class), getter.getModal(VehicleOccupancyProfileCalculator.class)))) .asEagerSingleton(); + + install(new SharingFactorModule(drtCfg)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java new file mode 100644 index 00000000000..2173b1c6840 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java @@ -0,0 +1,144 @@ +package org.matsim.contrib.drt.sharingfactor; + +import com.google.inject.Inject; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartUtils; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer; +import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset; +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.core.config.Config; +import org.matsim.core.controler.MatsimServices; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.utils.io.IOUtils; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorControlerListener implements IterationEndsListener { + private final MatsimServices matsimServices; + + private final DrtConfigGroup drtConfigGroup; + private final SharingFactorTracker sharingFactorTracker; + + private boolean headerWritten = false; + + private final String runId; + + private final String delimiter; + + private static final String notAvailableString = "NA"; + + + @Inject + public SharingFactorControlerListener(Config config, + DrtConfigGroup drtConfigGroup, + SharingFactorTracker sharingFactorTracker, + MatsimServices matsimServices) { + this.drtConfigGroup = drtConfigGroup; + this.sharingFactorTracker = sharingFactorTracker; + this.matsimServices = matsimServices; + runId = Optional.ofNullable(config.controller().getRunId()).orElse(notAvailableString); + this.delimiter = config.global().getDefaultDelimiter(); + + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + + Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); + Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); + + writeAndPlotShiftEfficiency( + sharingFactors, + poolingRates, + filename(event, "sharingFactors", ".png"), + filename(event, "poolingRates", ".png"), + filename(event, "poolingMetrics", ".csv"), + createGraphs); + + double nPooled = poolingRates.values().stream().filter(b -> b).count(); + double nTotal = poolingRates.values().size(); + double meanPoolingRate = nPooled / nTotal; + double meanSharingFactor = sharingFactors.values().stream().mapToDouble(d -> d).average().orElse(Double.NaN); + + writeIterationPoolingStats(meanPoolingRate + delimiter + meanSharingFactor + delimiter + nPooled +delimiter + nTotal, event.getIteration()); + } + + private void writeAndPlotShiftEfficiency(Map, Double> sharingFactorByRequest, + Map, Boolean> rates, + String sharingFactors, + String poolingRates, + String csvFile, + boolean createGraphs) { + try (var bw = IOUtils.getBufferedWriter(csvFile)) { + bw.append(line("Request", "SharingFactor", "Pooled")); + + for (Map.Entry, Double> sharingFactorEntry : sharingFactorByRequest.entrySet()) { + bw.append(line(sharingFactorEntry.getKey(), sharingFactorEntry.getValue(),rates.get(sharingFactorEntry.getKey()))); + } + bw.flush(); + + if (createGraphs) { + final DefaultBoxAndWhiskerCategoryDataset sharingFactorDataset + = new DefaultBoxAndWhiskerCategoryDataset(); + + final DefaultBoxAndWhiskerCategoryDataset poolingRateDataset + = new DefaultBoxAndWhiskerCategoryDataset(); + + sharingFactorDataset.add(sharingFactorByRequest.values().stream().toList(), "", ""); + poolingRateDataset.add(rates.values().stream().toList(), "", ""); + + JFreeChart chartRides = ChartFactory.createBoxAndWhiskerChart("Sharing factor", "", "Factor", sharingFactorDataset, false); + JFreeChart chartPooling = ChartFactory.createBoxAndWhiskerChart("Pooling rate", "", "Rate", poolingRateDataset, false); + + ((BoxAndWhiskerRenderer) chartRides.getCategoryPlot().getRenderer()).setMeanVisible(true); + ((BoxAndWhiskerRenderer) chartPooling.getCategoryPlot().getRenderer()).setMeanVisible(true); + ChartUtils.writeChartAsPNG(new FileOutputStream(sharingFactors), chartRides, 1500, 1500); + ChartUtils.writeChartAsPNG(new FileOutputStream(poolingRates), chartPooling, 1500, 1500); + } + } catch ( + IOException e) { + throw new RuntimeException(e); + } + } + + private String filename(IterationEndsEvent event, String prefix, String extension) { + return matsimServices.getControlerIO() + .getIterationFilename(event.getIteration(), prefix + "_" + drtConfigGroup.getMode() + extension); + } + + private String line(Object... cells) { + return Arrays.stream(cells).map(Object::toString).collect(Collectors.joining(delimiter, "", "\n")); + } + + + private void writeIterationPoolingStats(String summarizePooling, int it) { + try (var bw = getAppendingBufferedWriter("drt_pooling_stats", ".csv")) { + if (!headerWritten) { + headerWritten = true; + bw.write(line("runId", "iteration", "poolingRate", "sharingFactor", "nPooled", "nTotal")); + } + bw.write(runId + delimiter + it + delimiter + summarizePooling); + bw.newLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private BufferedWriter getAppendingBufferedWriter(String prefix, String extension) { + return IOUtils.getAppendingBufferedWriter(matsimServices.getControlerIO().getOutputFilename(prefix + "_" + drtConfigGroup.getMode() + extension)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java new file mode 100644 index 00000000000..3ce63eddc2e --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java @@ -0,0 +1,38 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.controler.MatsimServices; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup drtConfigGroup; + + public SharingFactorModule(DrtConfigGroup drtConfigGroup) { + super(drtConfigGroup.getMode()); + this.drtConfigGroup = drtConfigGroup; + } + + @Override + public void install() { + bindModal(SharingFactorTracker.class).toProvider(modalProvider(getter -> + new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + @Override + public boolean isGroupRepresentative(Id personId) { + return SharingFactorTracker.GroupPredicate.super.isGroupRepresentative(personId); + } + }))).asEagerSingleton(); + addEventHandlerBinding().to(modalKey(SharingFactorTracker.class)); + bindModal(SharingFactorControlerListener.class).toProvider(modalProvider(getter -> + new SharingFactorControlerListener(getConfig(), drtConfigGroup, + getter.getModal(SharingFactorTracker.class), + getter.get(MatsimServices.class)) + )); + addControlerListenerBinding().to(modalKey(SharingFactorControlerListener.class)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java new file mode 100644 index 00000000000..cc28f6131a9 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java @@ -0,0 +1,112 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEventHandler; +import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener; + +import java.util.*; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { + + private final GroupPredicate groupPredicate; + + record Segment(double start, int occupancy) { + } + + private final Map, List>> map = new HashMap<>(); + + private final Map, List> segments = new HashMap<>(); + + private final Map, Double> sharingFactors = new HashMap<>(); + private final Map, Boolean> poolingRate = new HashMap<>(); + + public interface GroupPredicate { + default boolean isGroupRepresentative(Id personId) { + return true; + } + } + + public SharingFactorTracker(GroupPredicate groupPredicate) { + this.groupPredicate = groupPredicate; + } + + + @Override + public void handleEvent(PassengerDroppedOffEvent event) { + if (groupPredicate.isGroupRepresentative(event.getPersonId())) { + + List> occupancy = map.get(event.getVehicleId()); + occupancy.remove(event.getRequestId()); + occupancy.forEach(p -> segments.get(p).add(new Segment(event.getTime(), occupancy.size()))); + + + List finishedSegments = segments.remove(event.getRequestId()); + + double total = 0; + double portion = 0; + + boolean pooled = false; + + Segment last = finishedSegments.get(0); + if (last.occupancy > 1) { + pooled = true; + } + for (int i = 1; i < finishedSegments.size(); i++) { + Segment next = finishedSegments.get(i); + double duration = next.start - last.start; + total += duration; + portion += duration / last.occupancy; + last = next; + if (last.occupancy > 1) { + pooled = true; + } + } + + double duration = event.getTime() - last.start; + total += duration; + portion += duration / last.occupancy; + + double sharingFactor = total / portion; + sharingFactors.put(event.getRequestId(), sharingFactor); + poolingRate.put(event.getRequestId(), pooled); + } + } + + @Override + public void handleEvent(PassengerPickedUpEvent event) { + if (groupPredicate.isGroupRepresentative(event.getPersonId())) { + map.computeIfAbsent(event.getVehicleId(), vehicleId -> new ArrayList<>()); + List> occupancy = map.get(event.getVehicleId()); + occupancy.add(event.getRequestId()); + occupancy.forEach( + p -> segments.computeIfAbsent(p, requestId -> new ArrayList<>()).add(new Segment(event.getTime(), occupancy.size())) + ); + } + } + + @Override + public void notifyMobsimBeforeCleanup(MobsimBeforeCleanupEvent e) { + map.clear(); + segments.clear(); + poolingRate.clear(); + sharingFactors.clear(); + } + + public Map, Double> getSharingFactors() { + return Collections.unmodifiableMap(sharingFactors); + } + + public Map, Boolean> getPoolingRates() { + return Collections.unmodifiableMap(poolingRate); + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java new file mode 100644 index 00000000000..fc2c8b96add --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java @@ -0,0 +1,205 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.junit.Assert; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; +import org.matsim.core.events.ParallelEventsManager; +import org.matsim.testcases.MatsimTestUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorTest { + + /** + * Test method for {@link SharingFactorTracker}. + */ + @Test + public void testDrtSharingFactorHandler() { + String mode = "mode_0"; + + var vehicleId = Id.create("v1", DvrpVehicle.class); + var personId1 = Id.createPersonId("p1"); + var personId2 = Id.createPersonId("p2"); + + + Set> groupRepresentatives = new HashSet<>(); + + // two separate bookings + groupRepresentatives.add(personId1); + groupRepresentatives.add(personId2); + + + ParallelEventsManager events = new ParallelEventsManager(false); + SharingFactorTracker sharingFactorTracker = new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + @Override + public boolean isGroupRepresentative(Id personId) { + return groupRepresentatives.contains(personId); + } + }); + events.addHandler(sharingFactorTracker); + + events.initProcessing(); + + + { + //single trip, no pooling + var requestId = Id.create(0, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId1, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + //two trips exactly after each other, no pooling + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(300.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(500.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + //two trips overlap half of the time + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(400.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals((100. + 100.) / (100 + 50), sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals((100. + 100.) / (50 + 100), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // second trip (sharing factor = 2) happens completely within first trip (sharing factor = 1.2) + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(400.0, mode, requestId1, personId1, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals((100. + 100. + 100.) / (100 + 50 + 100), sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals((100. ) / (50.), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // two persons share exact same trip but not part of a group + + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(2., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals(2., sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // two persons part of a group, only person 1 is representative -> not pooled + groupRepresentatives.remove(personId2); + + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + } + } +} From 4390990d69b4f505a49aeb7b124f979b3f396223 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:14:44 +0000 Subject: [PATCH 11/15] build(deps): bump io.grpc:grpc-all from 1.59.1 to 1.60.0 Bumps [io.grpc:grpc-all](https://github.com/grpc/grpc-java) from 1.59.1 to 1.60.0. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.59.1...v1.60.0) --- updated-dependencies: - dependency-name: io.grpc:grpc-all dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- contribs/hybridsim/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/hybridsim/pom.xml b/contribs/hybridsim/pom.xml index cc8bcea53c5..8954573f022 100644 --- a/contribs/hybridsim/pom.xml +++ b/contribs/hybridsim/pom.xml @@ -11,7 +11,7 @@ 3.25.1 - 1.59.1 + 1.60.0 From 0109267af2521fb36b2df53b70fa6cdcb4e80cce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:25:57 +0000 Subject: [PATCH 12/15] build(deps): bump org.apache.httpcomponents.client5:httpclient5 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.2.3 to 5.3. - [Changelog](https://github.com/apache/httpcomponents-client/blob/master/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.2.3...rel/v5.3) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- contribs/analysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contribs/analysis/pom.xml b/contribs/analysis/pom.xml index 83989a7fcbf..38facf17423 100644 --- a/contribs/analysis/pom.xml +++ b/contribs/analysis/pom.xml @@ -32,7 +32,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.2.3 + 5.3 From 4f97de8ae148b172f9a441d866a4dd50b6dbf49b Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:24:43 +0100 Subject: [PATCH 13/15] update sharing metrics code --- .../drt/analysis/DrtModeAnalysisModule.java | 4 +- .../sharingfactor/SharingFactorModule.java | 38 ------------------- .../SharingMetricsControlerListener.java} | 18 ++++----- .../sharingmetrics/SharingMetricsModule.java | 33 ++++++++++++++++ .../SharingMetricsTracker.java} | 10 ++--- .../SharingFactorTest.java | 6 +-- 6 files changed, 51 insertions(+), 58 deletions(-) delete mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java rename contribs/drt/src/main/java/org/matsim/contrib/drt/{sharingfactor/SharingFactorControlerListener.java => sharingmetrics/SharingMetricsControlerListener.java} (90%) create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java rename contribs/drt/src/main/java/org/matsim/contrib/drt/{sharingfactor/SharingFactorTracker.java => sharingmetrics/SharingMetricsTracker.java} (90%) rename contribs/drt/src/test/java/org/matsim/contrib/drt/{sharingfactor => sharingmetrics}/SharingFactorTest.java (97%) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java index e5cd54688c2..67d38dad708 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java @@ -34,7 +34,7 @@ import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; -import org.matsim.contrib.drt.sharingfactor.SharingFactorModule; +import org.matsim.contrib.drt.sharingmetrics.SharingMetricsModule; import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; @@ -138,6 +138,6 @@ public void install() { getter.getModal(DrtEventSequenceCollector.class), getter.getModal(VehicleOccupancyProfileCalculator.class)))) .asEagerSingleton(); - install(new SharingFactorModule(drtCfg)); + install(new SharingMetricsModule(drtCfg)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java deleted file mode 100644 index 3ce63eddc2e..00000000000 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.matsim.contrib.drt.sharingfactor; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; -import org.matsim.core.controler.MatsimServices; - -/** - * @author nkuehnel / MOIA - */ -public class SharingFactorModule extends AbstractDvrpModeModule { - - private final DrtConfigGroup drtConfigGroup; - - public SharingFactorModule(DrtConfigGroup drtConfigGroup) { - super(drtConfigGroup.getMode()); - this.drtConfigGroup = drtConfigGroup; - } - - @Override - public void install() { - bindModal(SharingFactorTracker.class).toProvider(modalProvider(getter -> - new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { - @Override - public boolean isGroupRepresentative(Id personId) { - return SharingFactorTracker.GroupPredicate.super.isGroupRepresentative(personId); - } - }))).asEagerSingleton(); - addEventHandlerBinding().to(modalKey(SharingFactorTracker.class)); - bindModal(SharingFactorControlerListener.class).toProvider(modalProvider(getter -> - new SharingFactorControlerListener(getConfig(), drtConfigGroup, - getter.getModal(SharingFactorTracker.class), - getter.get(MatsimServices.class)) - )); - addControlerListenerBinding().to(modalKey(SharingFactorControlerListener.class)); - } -} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java similarity index 90% rename from contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 2173b1c6840..521be363f49 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import com.google.inject.Inject; import org.jfree.chart.ChartFactory; @@ -26,11 +26,11 @@ /** * @author nkuehnel / MOIA */ -public class SharingFactorControlerListener implements IterationEndsListener { +public class SharingMetricsControlerListener implements IterationEndsListener { private final MatsimServices matsimServices; private final DrtConfigGroup drtConfigGroup; - private final SharingFactorTracker sharingFactorTracker; + private final SharingMetricsTracker sharingFactorTracker; private boolean headerWritten = false; @@ -42,10 +42,10 @@ public class SharingFactorControlerListener implements IterationEndsListener { @Inject - public SharingFactorControlerListener(Config config, - DrtConfigGroup drtConfigGroup, - SharingFactorTracker sharingFactorTracker, - MatsimServices matsimServices) { + public SharingMetricsControlerListener(Config config, + DrtConfigGroup drtConfigGroup, + SharingMetricsTracker sharingFactorTracker, + MatsimServices matsimServices) { this.drtConfigGroup = drtConfigGroup; this.sharingFactorTracker = sharingFactorTracker; this.matsimServices = matsimServices; @@ -61,7 +61,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); - writeAndPlotShiftEfficiency( + writeAndPlotSharingMetrics( sharingFactors, poolingRates, filename(event, "sharingFactors", ".png"), @@ -77,7 +77,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { writeIterationPoolingStats(meanPoolingRate + delimiter + meanSharingFactor + delimiter + nPooled +delimiter + nTotal, event.getIteration()); } - private void writeAndPlotShiftEfficiency(Map, Double> sharingFactorByRequest, + private void writeAndPlotSharingMetrics(Map, Double> sharingFactorByRequest, Map, Boolean> rates, String sharingFactors, String poolingRates, diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java new file mode 100644 index 00000000000..2ebe3777ac1 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java @@ -0,0 +1,33 @@ +package org.matsim.contrib.drt.sharingmetrics; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.controler.MatsimServices; + +/** + * @author nkuehnel / MOIA + */ +public class SharingMetricsModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup drtConfigGroup; + + public SharingMetricsModule(DrtConfigGroup drtConfigGroup) { + super(drtConfigGroup.getMode()); + this.drtConfigGroup = drtConfigGroup; + } + + @Override + public void install() { + bindModal(SharingMetricsTracker.class).toProvider(modalProvider(getter -> + new SharingMetricsTracker(personId -> true))).asEagerSingleton(); + addEventHandlerBinding().to(modalKey(SharingMetricsTracker.class)); + bindModal(SharingMetricsControlerListener.class).toProvider(modalProvider(getter -> + new SharingMetricsControlerListener(getConfig(), drtConfigGroup, + getter.getModal(SharingMetricsTracker.class), + getter.get(MatsimServices.class)) + )); + addControlerListenerBinding().to(modalKey(SharingMetricsControlerListener.class)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java similarity index 90% rename from contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java index cc28f6131a9..f5393b29144 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; @@ -16,7 +16,7 @@ /** * @author nkuehnel / MOIA */ -public class SharingFactorTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { +public class SharingMetricsTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { private final GroupPredicate groupPredicate; @@ -31,12 +31,10 @@ record Segment(double start, int occupancy) { private final Map, Boolean> poolingRate = new HashMap<>(); public interface GroupPredicate { - default boolean isGroupRepresentative(Id personId) { - return true; - } + boolean isGroupRepresentative(Id personId); } - public SharingFactorTracker(GroupPredicate groupPredicate) { + public SharingMetricsTracker(GroupPredicate groupPredicate) { this.groupPredicate = groupPredicate; } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java similarity index 97% rename from contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java rename to contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java index fc2c8b96add..e98a066d0ce 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import org.junit.Assert; import org.junit.Test; @@ -20,7 +20,7 @@ public class SharingFactorTest { /** - * Test method for {@link SharingFactorTracker}. + * Test method for {@link SharingMetricsTracker}. */ @Test public void testDrtSharingFactorHandler() { @@ -39,7 +39,7 @@ public void testDrtSharingFactorHandler() { ParallelEventsManager events = new ParallelEventsManager(false); - SharingFactorTracker sharingFactorTracker = new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + SharingMetricsTracker sharingFactorTracker = new SharingMetricsTracker(new SharingMetricsTracker.GroupPredicate() { @Override public boolean isGroupRepresentative(Id personId) { return groupRepresentatives.contains(personId); From 3f463412766be1ceef9034fda3f045e483a11c95 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:33:54 +0100 Subject: [PATCH 14/15] update to #2955 --- .../drt/sharingmetrics/SharingMetricsControlerListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 521be363f49..9257af67aee 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -56,7 +56,8 @@ public SharingMetricsControlerListener(Config config, @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); From 787e0bb82687893c1537ea393b27760c3ed998ca Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:50:00 +0100 Subject: [PATCH 15/15] change file names from pooling stats to sharing metrics --- .../drt/sharingmetrics/SharingMetricsControlerListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 9257af67aee..9884fce28cc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -67,7 +67,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { poolingRates, filename(event, "sharingFactors", ".png"), filename(event, "poolingRates", ".png"), - filename(event, "poolingMetrics", ".csv"), + filename(event, "sharingMetrics", ".csv"), createGraphs); double nPooled = poolingRates.values().stream().filter(b -> b).count(); @@ -127,7 +127,7 @@ private String line(Object... cells) { private void writeIterationPoolingStats(String summarizePooling, int it) { - try (var bw = getAppendingBufferedWriter("drt_pooling_stats", ".csv")) { + try (var bw = getAppendingBufferedWriter("drt_sharing_metrics", ".csv")) { if (!headerWritten) { headerWritten = true; bw.write(line("runId", "iteration", "poolingRate", "sharingFactor", "nPooled", "nTotal"));