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/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/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 17ea8796459..8055a2c04db 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,25 +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; @@ -67,163 +49,61 @@ 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) { 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); + travelDistanceStats.writeOutput(event.getIteration(), 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/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/PHbyModeCalculator.java b/matsim/src/main/java/org/matsim/analysis/PHbyModeCalculator.java index b4663b50388..e572fbcc8a8 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; @@ -34,10 +35,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; @@ -51,141 +49,142 @@ */ public class PHbyModeCalculator { - 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(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))); - phtPerIteration.put(iteration,phtbyMode); - } - - - void writeOutput() { - writePHTText(); - - } - - 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)); + private final Map> phtPerIteration = new TreeMap<>(); + 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(OutputDirectoryHierarchy controllerIO, GlobalConfigGroup globalConfig) { + 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) { + 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; } - csvPrinter.println(); - } + 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)); + } + + 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)); + } - } catch (IOException e) { + void writeOutput(boolean writePng) { + writeCsv(); + if(writePng){ + new Thread(this::writePng).start(); + } + } + + 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 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 TravelTimeAndWaitTime(double travelTime, double waitTime) { - this.travelTime = travelTime; - this.waitTime = waitTime; - } - - private static TravelTimeAndWaitTime sum(TravelTimeAndWaitTime object1, TravelTimeAndWaitTime object2) { - return new TravelTimeAndWaitTime(object1.travelTime + object2.travelTime, object1.waitTime + object2.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 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(); + } + + 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); + } + } } 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/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/analysis/TravelDistanceStats.java b/matsim/src/main/java/org/matsim/analysis/TravelDistanceStats.java index cc46e9434fd..9099b8f67f6 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 MAX_ITERATIONS_IN_GRAPH = 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 Map legStats; + private Map tripStats; private final static Logger log = LogManager.getLogger(TravelDistanceStats.class); @@ -75,136 +75,160 @@ 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 expectedIterations = controllerConfigGroup.getLastIteration() - controllerConfigGroup.getFirstIteration(); + this.legStats = new HashMap<>(expectedIterations+1); + this.tripStats = new HashMap<>(expectedIterations+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? + 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(writePngs && this.legStats.size() < MAX_ITERATIONS_IN_GRAPH){ + 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.get(iteration); + DoubleSummaryStatistics tripStats = this.tripStats.get(iteration); + try { this.out.write(iteration + this.delimiter + legStats.getAverage() + this.delimiter + tripStats.getAverage() + "\n"); this.out.flush(); } catch (IOException e) { e.printStackTrace(); } + } - 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; - } + void writePngs(int iteration){ + writeLegStatsPng(iteration); + writeTripStatsPng(iteration); + } + + private void writeTripStatsPng(int iteration) { + if (iteration == controllerConfigGroup.getFirstIteration()){ + return; + } + + // 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[iteration + 1]; + double[] values = new double[iteration + 1]; + + for (int i = 0; i <= iteration; i++) { + iterations[i] = i + controllerConfigGroup.getFirstIteration(); + values[i] = Optional.ofNullable(this.tripStats.get(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; } - 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; - } + // 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[iteration + 1]; + double[] values = new double[iteration + 1]; + + for (int i = 0; i <= iteration; i++) { + iterations[i] = i + controllerConfigGroup.getFirstIteration(); + values[i] = Optional.ofNullable(this.legStats.get(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(); } public void close() { @@ -215,6 +239,4 @@ public void close() { } } - - } 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/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 f1d8bae8b6a..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 @@ -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"; @@ -83,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; @@ -101,7 +101,7 @@ 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; private CompressionType compressionType = CompressionType.gzip; @@ -134,6 +134,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())); @@ -353,24 +358,34 @@ public void setWriteSnapshotsInterval(int writeSnapshotsInterval) { this.writeSnapshotsInterval = writeSnapshotsInterval; } - @StringGetter( CREATE_GRAPHS ) - public boolean isCreateGraphs() { - return createGraphs; + @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; } - /** - * 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) { - this.createGraphs = 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 ) 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/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/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(); 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(); } 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"}; 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 a1c50b1d46f..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 @@ -161,5 +161,22 @@ 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()); + } + }