Skip to content

Commit 7bb99a7

Browse files
authored
Documentation mode choice + make mode choice analysis standard (matsim-org#3285)
* add comments to clarify configs * add ModeChoiceCoverageControlerListener as default analysis * fix tests * fix tests
1 parent 4796104 commit 7bb99a7

File tree

6 files changed

+136
-106
lines changed

6 files changed

+136
-106
lines changed

matsim/src/main/java/org/matsim/analysis/ModeChoiceCoverageControlerListener.java

Lines changed: 96 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -98,95 +98,102 @@ public void notifyIterationEnds(final IterationEndsEvent event) {
9898

9999
updateModesUsedPerPerson();
100100

101-
102-
/*
103-
* Looks through modesUsedPerPersonTrip at each person-trip. How many of those person trips have used each mode more than the
104-
* predefined limits.
105-
*/
106-
int totalPersonTripCount = 0;
107-
Map<Integer, Map<String, Double>> modeCountCurrentIteration = new TreeMap<>();
108-
//Map<Limit, Map<Mode , TotalTripCount >>
109-
110-
for (Map<Integer, Map<String, Integer>> mapForPerson : modesUsedPerPersonTrip.values()) {
111-
//Map<Trip # , Map<Mode , Count >>
112-
for (Map<String, Integer> mapForPersonTrip : mapForPerson.values()) {
113-
//Map<Mode , Count >
114-
totalPersonTripCount++;
115-
for (String mode : mapForPersonTrip.keySet()) {
116-
Integer realCount = mapForPersonTrip.get(mode);
117-
for (Integer limit : limits) {
118-
Map<String, Double> modeCountMap = modeCountCurrentIteration.computeIfAbsent(limit, k -> new TreeMap<>());
119-
Double modeCount = modeCountMap.computeIfAbsent(mode, k -> 0.);
120-
if (realCount >= limit) {
121-
modeCount++;
122-
}
123-
modeCountMap.put(mode, modeCount);
124-
modeCountCurrentIteration.put(limit, modeCountMap);
125-
}
126-
}
127-
}
128-
}
129-
// Calculates mcc share for each mode in current iteration, and updates modeCCHistory accordingly
130-
for (Integer limit : limits) {
131-
Map<String, Double> modeCnt = modeCountCurrentIteration.get(limit);
132-
this.modes.addAll(modeCnt.keySet()); // potentially adds new modes to setthat just showed up in current iter
133-
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.computeIfAbsent(limit, k -> new HashMap<>());
134-
for (String mode : modes) {
135-
Double cnt = modeCnt.get(mode);
136-
double share = 0.;
137-
if (cnt != null) {
138-
share = cnt / totalPersonTripCount;
139-
}
140-
141-
log.info("-- mode choice coverage (" + limit + "x) of mode " + mode + " = " + share);
142-
143-
Map<Integer, Double> iterationShareMap = modeIterationShareMap.get(mode);
144-
145-
// If this is the first iteration where the mode shows up, add zeros to all previous iterations in history
146-
if (iterationShareMap == null) {
147-
iterationShareMap = new TreeMap<>();
148-
for (int iter = firstIteration; iter < event.getIteration(); iter++) {
149-
iterationShareMap.put(iter, 0.0);
150-
}
151-
modeIterationShareMap.put(mode, iterationShareMap);
152-
}
153-
154-
iterationShareMap.put(event.getIteration(), share);
155-
}
156-
}
157-
158-
159-
// Print MCC Stats to output file
160-
for (Integer limit : limits) {
161-
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.get(limit);
162-
163-
BufferedWriter modeOut = IOUtils.getBufferedWriter(this.modeFileName + limit + "x.txt");
164-
try {
165-
modeOut.write("Iteration");
166-
for (String mode : modes) {
167-
modeOut.write("\t" + mode);
168-
}
169-
modeOut.write("\n");
170-
for (int iter = firstIteration; iter <= event.getIteration(); iter++) {
171-
modeOut.write(String.valueOf(iter));
172-
for (String mode : modes) {
173-
modeOut.write("\t" + modeIterationShareMap.get(mode).get(iter));
174-
}
175-
modeOut.write("\n");
176-
}
177-
178-
modeOut.flush();
179-
modeOut.close();
180-
} catch (IOException e) {
181-
e.printStackTrace();
182-
}
183-
}
184-
185-
// Produce Graphs
186-
if (this.createPNG && event.getIteration() > this.minIteration) {
187-
produceGraphs();
188-
}
189-
101+
/*
102+
* Looks through modesUsedPerPersonTrip at each person-trip. How many of those person trips have used each mode more than the
103+
* predefined limits.
104+
*/
105+
int totalPersonTripCount = 0;
106+
Map<Integer, Map<String, Double>> modeCountCurrentIteration = new TreeMap<>();
107+
//Map<Limit, Map<Mode , TotalTripCount >>
108+
109+
for (Map<Integer, Map<String, Integer>> mapForPerson : modesUsedPerPersonTrip.values()) {
110+
//Map<Trip # , Map<Mode , Count >>
111+
for (Map<String, Integer> mapForPersonTrip : mapForPerson.values()) {
112+
//Map<Mode , Count >
113+
totalPersonTripCount++;
114+
for (String mode : mapForPersonTrip.keySet()) {
115+
Integer realCount = mapForPersonTrip.get(mode);
116+
for (Integer limit : limits) {
117+
Map<String, Double> modeCountMap = modeCountCurrentIteration.computeIfAbsent(limit, k -> new TreeMap<>());
118+
Double modeCount = modeCountMap.computeIfAbsent(mode, k -> 0.);
119+
if (realCount >= limit) {
120+
modeCount++;
121+
}
122+
modeCountMap.put(mode, modeCount);
123+
modeCountCurrentIteration.put(limit, modeCountMap);
124+
}
125+
}
126+
}
127+
}
128+
129+
130+
// for testing purposes: if there are any trips, do analysis. If not, it is probably a test or a faulty / empty population. -sme0524
131+
if (!modeCountCurrentIteration.isEmpty()) {
132+
// Calculates mcc share for each mode in current iteration, and updates modeCCHistory accordingly
133+
for (Integer limit : limits) {
134+
Map<String, Double> modeCnt = modeCountCurrentIteration.get(limit);
135+
this.modes.addAll(modeCnt.keySet()); // potentially adds new modes to setthat just showed up in current iter
136+
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.computeIfAbsent(limit, k -> new HashMap<>());
137+
for (String mode : modes) {
138+
Double cnt = modeCnt.get(mode);
139+
double share = 0.;
140+
if (cnt != null) {
141+
share = cnt / totalPersonTripCount;
142+
}
143+
144+
log.info("-- mode choice coverage (" + limit + "x) of mode " + mode + " = " + share);
145+
146+
Map<Integer, Double> iterationShareMap = modeIterationShareMap.get(mode);
147+
148+
// If this is the first iteration where the mode shows up, add zeros to all previous iterations in history
149+
if (iterationShareMap == null) {
150+
iterationShareMap = new TreeMap<>();
151+
for (int iter = firstIteration; iter < event.getIteration(); iter++) {
152+
iterationShareMap.put(iter, 0.0);
153+
}
154+
modeIterationShareMap.put(mode, iterationShareMap);
155+
}
156+
157+
iterationShareMap.put(event.getIteration(), share);
158+
}
159+
}
160+
161+
162+
// Print MCC Stats to output file
163+
for (Integer limit : limits) {
164+
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.get(limit);
165+
166+
BufferedWriter modeOut = IOUtils.getBufferedWriter(this.modeFileName + limit + "x.txt");
167+
try {
168+
modeOut.write("Iteration");
169+
for (String mode : modes) {
170+
modeOut.write("\t" + mode);
171+
}
172+
modeOut.write("\n");
173+
for (int iter = firstIteration; iter <= event.getIteration(); iter++) {
174+
modeOut.write(String.valueOf(iter));
175+
for (String mode : modes) {
176+
modeOut.write("\t" + modeIterationShareMap.get(mode).get(iter));
177+
}
178+
modeOut.write("\n");
179+
}
180+
181+
modeOut.flush();
182+
modeOut.close();
183+
} catch (IOException e) {
184+
e.printStackTrace();
185+
}
186+
}
187+
188+
// Produce Graphs
189+
if (this.createPNG && event.getIteration() > this.minIteration) {
190+
produceGraphs();
191+
}
192+
193+
} else {
194+
log.warn("There are no trips conducted by the analyzed population. This should only be the case for tests. If you are running a simulation run, " +
195+
" this should not happen. Check your population.");
196+
}
190197
}
191198

192199
private void updateModesUsedPerPerson() {

matsim/src/main/java/org/matsim/analysis/ModeStatsModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ public class ModeStatsModule extends AbstractModule {
3030
public void install() {
3131
bind(ModeStatsControlerListener.class).in(Singleton.class);
3232
addControlerListenerBinding().to(ModeStatsControlerListener.class);
33+
// KN: if this is a somewhat standard analysis it should be added by default rather than adding it in every scenario run class
34+
addControlerListenerBinding().to(ModeChoiceCoverageControlerListener.class);
3335
}
3436
}

matsim/src/main/java/org/matsim/core/config/groups/ReplanningConfigGroup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ private StrategySettings getStrategySettings(final Id<StrategySettings> index, f
275275
@Override
276276
public final Map<String, String> getComments() {
277277
Map<String,String> map = super.getComments();
278-
map.put(ReflectiveDelegate.ITERATION_FRACTION_TO_DISABLE_INNOVATION, "fraction of iterations where innovative strategies are switched off. Something like 0.8 should be good. E.g. if you run from iteration 400 to iteration 500, innovation is switched off at iteration 480" ) ;
278+
map.put(ReflectiveDelegate.ITERATION_FRACTION_TO_DISABLE_INNOVATION, "fraction of iterations where innovative strategies are switched off. Something like 0.8 should be good. E.g. if you run from iteration 400 to iteration 500, innovation is switched off at iteration 480. If the ReplanningAnnealer is used, it will also be switched off." ) ;
279279
map.put(ReflectiveDelegate.MAX_AGENT_PLAN_MEMORY_SIZE, "maximum number of plans per agent. ``0'' means ``infinity''. Currently (2010), ``5'' is a good number");
280280

281281
StringBuilder strb = new StringBuilder() ;

matsim/src/main/java/org/matsim/core/config/groups/SubtourModeChoiceConfigGroup.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,25 @@
3232
public final class SubtourModeChoiceConfigGroup extends ReflectiveConfigGroup {
3333

3434
public static final String GROUP_NAME = "subtourModeChoice";
35-
35+
3636
public final static String MODES = "modes";
3737
public final static String CHAINBASEDMODES = "chainBasedModes";
3838
public final static String CARAVAIL = "considerCarAvailability";
3939
public final static String SINGLE_PROBA = "probaForRandomSingleTripMode";
4040
public final static String COORD_DISTANCE = "coordDistance";
4141

4242
private static final String BEHAVIOR = "behavior";
43-
43+
4444
private String[] chainBasedModes = new String[] { TransportMode.car, TransportMode.bike };
4545
private String[] allModes = new String[] { TransportMode.car, TransportMode.pt, TransportMode.bike, TransportMode.walk };
4646
// default is false for backward compatibility
4747
private boolean considerCarAvailability = false;
4848
private SubtourModeChoice.Behavior behavior = SubtourModeChoice.Behavior.fromSpecifiedModesToSpecifiedModes ;
49-
49+
5050
private double probaForRandomSingleTripMode = 0. ; // yyyyyy backwards compatibility setting; should be change. kai, may'18
5151

5252
private double coordDistance = 0;
53-
53+
5454
public SubtourModeChoiceConfigGroup() {
5555
super(GROUP_NAME);
5656
}
@@ -67,7 +67,7 @@ private String getChainBaseModesString() {
6767

6868
private static String toString( final String[] modes ) {
6969
// (not same as toString() because of argument!)
70-
70+
7171
StringBuilder b = new StringBuilder();
7272

7373
if (modes.length > 0) b.append( modes[ 0 ] );
@@ -106,7 +106,8 @@ public Map<String, String> getComments() {
106106
comments.put(CHAINBASEDMODES, "Defines the chain-based modes, seperated by commas" );
107107
comments.put(CARAVAIL, "Defines whether car availability must be considered or not. A agent has no car only if it has no license, or never access to a car" );
108108
comments.put(SINGLE_PROBA, "Defines the probability of changing a single trip for a unchained mode instead of subtour.");
109-
comments.put(COORD_DISTANCE, "If greater than 0, subtours will also consider coordinates to be at the same location when smaller than set distance.");
109+
comments.put(COORD_DISTANCE, "If greater than 0, activities that are closer than coordDistance, to each other, will be considered part of the same subtour." +
110+
"i.e. if two activities are close to each other, the agent is allowed to use the same 'chain-based' vehicle for both subtours.");
110111

111112
{
112113
StringBuilder msg = new StringBuilder("Only for backwards compatibility. Defines if only trips from modes list should change mode, or all trips. Options: ");

matsim/src/main/java/org/matsim/core/replanning/annealing/ReplanningAnnealerConfigGroup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public Map<String, String> getComments() {
229229
map.put(HALFLIFE,
230230
"this parameter enters the exponential and sigmoid formulas. May be an iteration or a share, i.e. 0.5 for halfLife at 50% of iterations.");
231231
map.put(SHAPE_FACTOR, "see comment of parameter annealType.");
232-
map.put(ANNEAL_TYPE, "options: linear, exponential, geometric, msa, sigmoid and disabled (no annealing)." + " sigmoid: 1/(1+e^(shapeFactor*(it - halfLife))); geometric: startValue * shapeFactor^it; msa: startValue / it^shapeFactor. Exponential: startValue / exp(it/halfLife)");
232+
map.put(ANNEAL_TYPE, "options: linear, exponential, geometric, msa, sigmoid and disabled (no annealing). sigmoid: 1/(1+e^(shapeFactor*(it - halfLife))); geometric: startValue * shapeFactor^it; msa: startValue / it^shapeFactor. Exponential: startValue / exp(it/halfLife)");
233233
map.put(ANNEAL_PARAM,
234234
"list of config parameters that shall be annealed. Currently supported: globalInnovationRate, BrainExpBeta, PathSizeLogitBeta, learningRate. Default is globalInnovationRate");
235235
map.put(SUBPOPULATION, "subpopulation to have the global innovation rate adjusted. Not applicable when annealing with other parameters.");

matsim/src/main/java/org/matsim/core/replanning/modules/SubtourModeChoice.java

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@
3333
* different mode given a list of possible modes.
3434
*
3535
* A subtour is a consecutive subset of a plan which starts and ends at the same link.
36-
*
36+
*
3737
* Certain modes are considered only if the choice would not require some resource to appear
3838
* out of thin air. For example, you can only drive your car back from work if you have previously parked it
3939
* there. These are called chain-based modes.
40-
*
40+
*
4141
* The assumption is that each chain-based mode requires one resource (car, bike, ...) and that this
4242
* resource is initially positioned at home. Home is the location of the first activity in the plan.
43-
*
44-
* If the plan initially violates this constraint, this module may (!) repair it.
45-
*
43+
*
44+
* If the plan initially violates this constraint, this module may (!) repair it.
45+
*
4646
* @author michaz
4747
*
4848
*/
@@ -51,7 +51,27 @@ public class SubtourModeChoice extends AbstractMultithreadedModule {
5151
private final double probaForChangeSingleTripMode;
5252
private final double coordDist;
5353

54-
public enum Behavior {fromAllModesToSpecifiedModes, fromSpecifiedModesToSpecifiedModes, betweenAllAndFewerConstraints}
54+
public enum Behavior {
55+
/**
56+
* Allow agents to switch to specified modes from all other modes.
57+
* This implies that agents might switch to a specified mode, but won't be able to switch back
58+
* to their original mode. This option should not be used.
59+
*/
60+
@Deprecated
61+
fromAllModesToSpecifiedModes,
62+
63+
/**
64+
* Allow agents switching from one of a specified mode to another specified mode.
65+
* Note, that agents that have an unclosed subtour, are not able to switch mode.
66+
* If you have unclosed/open subtours in your data, consider using {@link #betweenAllAndFewerConstraints}.
67+
*/
68+
fromSpecifiedModesToSpecifiedModes,
69+
70+
/**
71+
* Same as "fromSpecifiedModesToSpecifiedModes", but also allow agents with open subtours to switch modes.
72+
*/
73+
betweenAllAndFewerConstraints
74+
}
5575

5676
private Behavior behavior = Behavior.fromSpecifiedModesToSpecifiedModes;
5777

@@ -85,19 +105,19 @@ public SubtourModeChoice(GlobalConfigGroup globalConfigGroup,
85105
this.probaForChangeSingleTripMode = probaForChangeSingleTripMode;
86106
this.coordDist = coordDist;
87107
}
88-
108+
89109
@Deprecated // only use when backwards compatibility is needed. kai, may'18
90110
public final void setBehavior ( Behavior behavior ) {
91111
this.behavior = behavior ;
92112
}
93-
113+
94114
protected String[] getModes() {
95115
return modes.clone();
96116
}
97117

98118
@Override
99119
public PlanAlgorithm getPlanAlgoInstance() {
100-
120+
101121
final ChooseRandomLegModeForSubtour chooseRandomLegMode =
102122
new ChooseRandomLegModeForSubtour(
103123
TripStructureUtils.getRoutingModeIdentifier(),

0 commit comments

Comments
 (0)