Skip to content

Commit fd3d3a2

Browse files
Merge pull request #120 from moia-oss/master
Update MATSim CW48
2 parents 9a5f465 + 5cb44ba commit fd3d3a2

File tree

203 files changed

+2821
-1262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

203 files changed

+2821
-1262
lines changed

contribs/application/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
<dependency>
8888
<groupId>com.github.matsim-org</groupId>
8989
<artifactId>gtfs2matsim</artifactId>
90-
<version>0bd5850fd6</version>
90+
<version>47b0802a29</version>
9191
<exclusions>
9292
<!-- Exclude unneeded dependencies and these with known CVE -->
9393
<exclusion>

contribs/application/src/main/java/org/matsim/application/analysis/noise/NoiseAnalysis.java

+11-10
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public class NoiseAnalysis implements MATSimAppCommand {
6666

6767
@CommandLine.Option(names = "--consider-activities", split = ",", description = "Considered activities for noise calculation." +
6868
" Use asterisk ('*') for acttype prefixes, if all such acts shall be considered.", defaultValue = "home*,work*,educ*,leisure*")
69-
private Set<String> considerActivities;
69+
private Set<String> consideredActivities;
7070

7171
@CommandLine.Option(names = "--noise-barrier", description = "Path to the noise barrier File", defaultValue = "")
7272
private String noiseBarrierFile;
@@ -86,19 +86,20 @@ public Integer call() throws Exception {
8686
boolean overrideParameters = ! ConfigUtils.hasModule(config, NoiseConfigGroup.class);
8787
NoiseConfigGroup noiseParameters = ConfigUtils.addOrGetModule(config, NoiseConfigGroup.class);
8888

89-
if(overrideParameters){
89+
if (overrideParameters){
9090
log.warn("no NoiseConfigGroup was configured before. Will set some standards. You should check the next lines in the log file and the output_config.xml!");
91-
noiseParameters.setConsideredActivitiesForReceiverPointGridArray(considerActivities.toArray(String[]::new));
92-
noiseParameters.setConsideredActivitiesForDamageCalculationArray(considerActivities.toArray(String[]::new));
91+
noiseParameters.setConsideredActivitiesForReceiverPointGridArray(consideredActivities.toArray(String[]::new));
92+
noiseParameters.setConsideredActivitiesForDamageCalculationArray(consideredActivities.toArray(String[]::new));
9393

9494
{
95-
Set<String> set = CollectionUtils.stringArrayToSet( new String[]{TransportMode.bike, TransportMode.walk, TransportMode.transit_walk, TransportMode.non_network_walk} );
96-
noiseParameters.setNetworkModesToIgnoreSet( set );
97-
}
98-
{
99-
String[] set = new String[]{"freight"};
100-
noiseParameters.setHgvIdPrefixesArray( set );
95+
//the default settings are now actually the same as what we 'override' here, but let's leave it here for clarity.
96+
Set<String> ignoredNetworkModes = CollectionUtils.stringArrayToSet( new String[]{TransportMode.bike, TransportMode.walk, TransportMode.transit_walk, TransportMode.non_network_walk} );
97+
noiseParameters.setNetworkModesToIgnoreSet( ignoredNetworkModes );
98+
99+
String[] hgvIdPrefixes = {"lkw", "truck", "freight"};
100+
noiseParameters.setHgvIdPrefixesArray( hgvIdPrefixes );
101101
}
102+
102103
//use actual speed and not freespeed
103104
noiseParameters.setUseActualSpeedLevel(true);
104105
//use the valid speed range (recommended by IK)

contribs/application/src/main/java/org/matsim/application/analysis/population/TripAnalysis.java

+114-19
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
import it.unimi.dsi.fastutil.ints.IntList;
55
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
66
import it.unimi.dsi.fastutil.ints.IntSet;
7-
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
8-
import it.unimi.dsi.fastutil.objects.Object2IntMap;
9-
import it.unimi.dsi.fastutil.objects.Object2LongMap;
10-
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
7+
import it.unimi.dsi.fastutil.objects.*;
118
import org.apache.commons.csv.CSVFormat;
129
import org.apache.commons.csv.CSVPrinter;
1310
import org.apache.commons.math3.analysis.interpolation.LoessInterpolator;
@@ -27,6 +24,7 @@
2724
import org.matsim.core.utils.io.IOUtils;
2825
import picocli.CommandLine;
2926
import tech.tablesaw.api.*;
27+
import tech.tablesaw.columns.strings.AbstractStringColumn;
3028
import tech.tablesaw.io.csv.CsvReadOptions;
3129
import tech.tablesaw.joining.DataFrameJoiner;
3230
import tech.tablesaw.selection.Selection;
@@ -37,6 +35,7 @@
3735
import java.nio.file.Files;
3836
import java.nio.file.Path;
3937
import java.util.*;
38+
import java.util.stream.Collectors;
4039
import java.util.stream.IntStream;
4140

4241
import static tech.tablesaw.aggregate.AggregateFunctions.count;
@@ -46,8 +45,9 @@
4645
requires = {"trips.csv", "persons.csv"},
4746
produces = {
4847
"mode_share.csv", "mode_share_per_dist.csv", "mode_users.csv", "trip_stats.csv",
49-
"mode_share_per_%s.csv", "population_trip_stats.csv", "trip_purposes_by_hour.csv",
50-
"mode_share_distance_distribution.csv", "mode_shift.csv",
48+
"mode_share_per_purpose.csv", "mode_share_per_%s.csv",
49+
"population_trip_stats.csv", "trip_purposes_by_hour.csv",
50+
"mode_share_distance_distribution.csv", "mode_shift.csv", "mode_chains.csv",
5151
"mode_choices.csv", "mode_choice_evaluation.csv", "mode_choice_evaluation_per_mode.csv",
5252
"mode_confusion_matrix.csv", "mode_prediction_error.csv"
5353
}
@@ -283,10 +283,15 @@ public Integer call() throws Exception {
283283

284284
joined.addColumns(dist_group);
285285

286+
TextColumn purpose = joined.textColumn("end_activity_type");
287+
288+
// Remove suffix durations like _345
289+
purpose.set(Selection.withRange(0, purpose.size()), purpose.replaceAll("_[0-9]{2,}$", ""));
290+
286291
writeModeShare(joined, labels);
287292

288293
if (groups != null) {
289-
groups.analyzeModeShare(joined, labels, modeOrder, (g) -> output.getPath("mode_share_per_%s.csv", g));
294+
groups.writeModeShare(joined, labels, modeOrder, (g) -> output.getPath("mode_share_per_%s.csv", g));
290295
}
291296

292297
if (persons.containsColumn(ATTR_REF_MODES)) {
@@ -305,17 +310,24 @@ public Integer call() throws Exception {
305310

306311
writePopulationStats(persons, joined);
307312

308-
writeTripStats(joined);
309-
310-
writeTripPurposes(joined);
311-
312-
writeTripDistribution(joined);
313-
314-
writeModeShift(joined);
313+
tryRun(this::writeTripStats, joined);
314+
tryRun(this::writeTripPurposes, joined);
315+
tryRun(this::writeTripDistribution, joined);
316+
tryRun(this::writeModeShift, joined);
317+
tryRun(this::writeModeChains, joined);
318+
tryRun(this::writeModeStatsPerPurpose, joined);
315319

316320
return 0;
317321
}
318322

323+
private void tryRun(ThrowingConsumer<Table> f, Table df) {
324+
try {
325+
f.accept(df);
326+
} catch (IOException e) {
327+
log.error("Error while running method", e);
328+
}
329+
}
330+
319331
private void writeModeShare(Table trips, List<String> labels) {
320332

321333
Table aggr = trips.summarize("trip_id", count).by("dist_group", "main_mode");
@@ -502,11 +514,6 @@ private void writeTripPurposes(Table trips) {
502514
IntColumn.create("arrival_h", arrival.intStream().toArray())
503515
);
504516

505-
TextColumn purpose = trips.textColumn("end_activity_type");
506-
507-
// Remove suffix durations like _345
508-
purpose.set(Selection.withRange(0, purpose.size()), purpose.replaceAll("_[0-9]{2,}$", ""));
509-
510517
Table tArrival = trips.summarize("trip_id", count).by("end_activity_type", "arrival_h");
511518

512519
tArrival.column(0).setName("purpose");
@@ -610,6 +617,89 @@ private void writeModeShift(Table trips) throws IOException {
610617
aggr.write().csv(output.getPath("mode_shift.csv").toFile());
611618
}
612619

620+
/**
621+
* Collects information about all modes used during one day.
622+
*/
623+
private void writeModeChains(Table trips) throws IOException {
624+
625+
Map<String, List<String>> modesPerPerson = new LinkedHashMap<>();
626+
627+
for (Row trip : trips) {
628+
String id = trip.getString("person");
629+
String mode = trip.getString("main_mode");
630+
modesPerPerson.computeIfAbsent(id, s -> new LinkedList<>()).add(mode);
631+
}
632+
633+
// Store other values explicitly
634+
ObjectDoubleMutablePair<String> other = ObjectDoubleMutablePair.of("other", 0);
635+
Object2DoubleMap<String> chains = new Object2DoubleOpenHashMap<>();
636+
for (List<String> modes : modesPerPerson.values()) {
637+
String key;
638+
if (modes.size() == 1)
639+
key = modes.getFirst();
640+
else if (modes.size() > 6) {
641+
other.right(other.rightDouble() + 1);
642+
continue;
643+
} else
644+
key = String.join("-", modes);
645+
646+
chains.mergeDouble(key, 1, Double::sum);
647+
}
648+
649+
650+
List<ObjectDoubleMutablePair<String>> counts = chains.object2DoubleEntrySet().stream()
651+
.map(e -> ObjectDoubleMutablePair.of(e.getKey(), (int) e.getDoubleValue()))
652+
.sorted(Comparator.comparingDouble(p -> -p.rightDouble()))
653+
.collect(Collectors.toList());
654+
655+
// Aggregate entries to prevent file from getting too large
656+
for (int i = 250; i < counts.size(); i++) {
657+
other.right(other.rightDouble() + counts.get(i).rightDouble());
658+
}
659+
counts = counts.subList(0, Math.min(counts.size(), 250));
660+
counts.add(other);
661+
662+
counts.sort(Comparator.comparingDouble(p -> -p.rightDouble()));
663+
664+
665+
try (CSVPrinter printer = new CSVPrinter(Files.newBufferedWriter(output.getPath("mode_chains.csv")), CSVFormat.DEFAULT)) {
666+
667+
printer.printRecord("modes", "count", "share");
668+
669+
double total = counts.stream().mapToDouble(ObjectDoubleMutablePair::rightDouble).sum();
670+
for (ObjectDoubleMutablePair<String> p : counts) {
671+
printer.printRecord(p.left(), (int) p.rightDouble(), p.rightDouble() / total);
672+
}
673+
}
674+
}
675+
676+
@SuppressWarnings("unchecked")
677+
private void writeModeStatsPerPurpose(Table trips) {
678+
679+
Table aggr = trips.summarize("trip_id", count).by("end_activity_type", "main_mode");
680+
681+
Comparator<Row> cmp = Comparator.comparing(row -> row.getString("end_activity_type"));
682+
aggr = aggr.sortOn(cmp.thenComparing(row -> row.getString("main_mode")));
683+
684+
aggr.doubleColumn(aggr.columnCount() - 1).setName("share");
685+
aggr.column("end_activity_type").setName("purpose");
686+
687+
Set<String> purposes = (Set<String>) aggr.column("purpose").asSet();
688+
689+
// Norm each purpose to 1
690+
// It was not clear if the purpose is a string or text colum, therefor this code uses the abstract version
691+
for (String label : purposes) {
692+
DoubleColumn all = aggr.doubleColumn("share");
693+
Selection sel = ((AbstractStringColumn<?>) aggr.column("purpose")).isEqualTo(label);
694+
695+
double total = all.where(sel).sum();
696+
if (total > 0)
697+
all.set(sel, all.divide(total));
698+
}
699+
700+
aggr.write().csv(output.getPath("mode_share_per_purpose.csv").toFile());
701+
}
702+
613703
/**
614704
* How shape file filtering should be applied.
615705
*/
@@ -619,4 +709,9 @@ enum LocationFilter {
619709
home,
620710
none
621711
}
712+
713+
@FunctionalInterface
714+
private interface ThrowingConsumer<T> {
715+
void accept(T t) throws IOException;
716+
}
622717
}

contribs/application/src/main/java/org/matsim/application/analysis/population/TripByGroupAnalysis.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ final class TripByGroupAnalysis {
100100
}
101101
}
102102

103-
void analyzeModeShare(Table trips, List<String> dists, List<String> modeOrder, Function<String, Path> output) {
103+
void writeModeShare(Table trips, List<String> dists, List<String> modeOrder, Function<String, Path> output) {
104104

105105
for (Group group : groups) {
106106

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* *********************************************************************** *
2+
* project: org.matsim.*
3+
* *
4+
* *********************************************************************** *
5+
* *
6+
* copyright : (C) 2024 by the members listed in the COPYING, *
7+
* LICENSE and WARRANTY file. *
8+
* email : info at matsim dot org *
9+
* *
10+
* *********************************************************************** *
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* See also COPYING, LICENSE and WARRANTY file *
17+
* *
18+
* *********************************************************************** */
19+
20+
package org.matsim.application.analysis.pt;
21+
22+
import org.apache.logging.log4j.LogManager;
23+
import org.apache.logging.log4j.Logger;
24+
import org.matsim.analysis.pt.stop2stop.PtStop2StopAnalysis;
25+
import org.matsim.api.core.v01.Scenario;
26+
import org.matsim.application.ApplicationUtils;
27+
import org.matsim.application.CommandSpec;
28+
import org.matsim.application.MATSimAppCommand;
29+
import org.matsim.application.analysis.emissions.AirPollutionAnalysis;
30+
import org.matsim.application.options.InputOptions;
31+
import org.matsim.application.options.OutputOptions;
32+
import org.matsim.application.options.SampleOptions;
33+
import org.matsim.core.api.experimental.events.EventsManager;
34+
import org.matsim.core.config.Config;
35+
import org.matsim.core.config.ConfigUtils;
36+
import org.matsim.core.events.EventsUtils;
37+
import org.matsim.core.events.MatsimEventsReader;
38+
import org.matsim.core.scenario.ScenarioUtils;
39+
import picocli.CommandLine;
40+
41+
@CommandLine.Command(
42+
name = "transit", description = "General public transit analysis.",
43+
mixinStandardHelpOptions = true, showDefaultValues = true
44+
)
45+
@CommandSpec(requireRunDirectory = true,
46+
produces = {
47+
"pt_pax_volumes.csv.gz",
48+
}
49+
)
50+
public class PublicTransitAnalysis implements MATSimAppCommand {
51+
52+
private static final Logger log = LogManager.getLogger(PublicTransitAnalysis.class);
53+
54+
@CommandLine.Mixin
55+
private final InputOptions input = InputOptions.ofCommand(PublicTransitAnalysis.class);
56+
@CommandLine.Mixin
57+
private final OutputOptions output = OutputOptions.ofCommand(PublicTransitAnalysis.class);
58+
@CommandLine.Mixin
59+
private SampleOptions sample;
60+
61+
public static void main(String[] args) {
62+
new PublicTransitAnalysis().execute(args);
63+
}
64+
65+
@Override
66+
public Integer call() throws Exception {
67+
68+
Config config = prepareConfig();
69+
Scenario scenario = ScenarioUtils.loadScenario(config);
70+
EventsManager eventsManager = EventsUtils.createEventsManager();
71+
72+
String eventsFile = ApplicationUtils.matchInput("events", input.getRunDirectory()).toString();
73+
74+
PtStop2StopAnalysis ptStop2StopEventHandler = new PtStop2StopAnalysis(scenario.getTransitVehicles(), sample.getUpscaleFactor());
75+
eventsManager.addHandler(ptStop2StopEventHandler);
76+
eventsManager.initProcessing();
77+
MatsimEventsReader matsimEventsReader = new MatsimEventsReader(eventsManager);
78+
matsimEventsReader.readFile(eventsFile);
79+
80+
log.info("Done reading the events file.");
81+
log.info("Finish processing...");
82+
eventsManager.finishProcessing();
83+
84+
ptStop2StopEventHandler.writeStop2StopEntriesByDepartureCsv(output.getPath("pt_pax_volumes.csv.gz"),
85+
",", ";");
86+
87+
return 0;
88+
}
89+
90+
private Config prepareConfig() {
91+
Config config = ConfigUtils.loadConfig(ApplicationUtils.matchInput("config.xml", input.getRunDirectory()).toAbsolutePath().toString());
92+
93+
config.vehicles().setVehiclesFile(ApplicationUtils.matchInput("vehicles", input.getRunDirectory()).toAbsolutePath().toString());
94+
config.network().setInputFile(ApplicationUtils.matchInput("network", input.getRunDirectory()).toAbsolutePath().toString());
95+
config.transit().setTransitScheduleFile(ApplicationUtils.matchInput("transitSchedule", input.getRunDirectory()).toAbsolutePath().toString());
96+
config.transit().setVehiclesFile(ApplicationUtils.matchInput("transitVehicles", input.getRunDirectory()).toAbsolutePath().toString());
97+
config.plans().setInputFile(null);
98+
config.facilities().setInputFile(null);
99+
config.eventsManager().setNumberOfThreads(null);
100+
config.eventsManager().setEstimatedNumberOfEvents(null);
101+
config.global().setNumberOfThreads(1);
102+
103+
return config;
104+
}
105+
}

contribs/application/src/main/java/org/matsim/application/prepare/network/CreateAvroNetwork.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,11 @@ public Integer call() throws Exception {
108108
filter = filter.and(link -> p.matcher(link.getId().toString()).matches());
109109
}
110110

111+
// Strings that could have been added in the list, due to command line parsing
112+
modes.removeIf(m -> m.isBlank() || m.equals("none") || m.equals("\"") || m.equals("\"\""));
113+
111114
// At least one of the specified modes needs to be contained
112-
if (!modes.isEmpty() && !modes.equals(Set.of("none"))) {
115+
if (!modes.isEmpty()) {
113116
filter = filter.and(link -> modes.stream().anyMatch(m -> link.getAllowedModes().contains(m)));
114117
}
115118

contribs/application/src/main/java/org/matsim/application/prepare/network/params/ApplyNetworkParams.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,10 @@ private boolean applyFreeSpeed(Link link, Feature ft) {
257257
return false;
258258

259259
link.setFreespeed(freeSpeed);
260-
link.getAttributes().putAttribute("speed_factor", freeSpeed);
260+
link.getAttributes().putAttribute(
261+
"speed_factor",
262+
BigDecimal.valueOf(freeSpeed).setScale(5, RoundingMode.HALF_EVEN).doubleValue()
263+
);
261264

262265
return modified;
263266
}

0 commit comments

Comments
 (0)