matsim-org/matsim-code-examples

Unreasonably high travel speeds in simulation results

Closed this issue · 8 comments

I'm maintaining a parcel demand model, that uses MATSim for traffic assignment.
I ran a few simulations and upon further analysis of the results, I discovered strange spikes in the travel speeds.
Debugging reveals the maxVelocity of the vehicle and the link freespeed of the network are ignored during routing, leading to unreasonable spikes in travel speed.

Below is a chart showing the distribution of travel speed on the network for a small sample. The maxVelocity of the truck in this case is set to 150km/h (42m/s) and the freespeed is 45 km/h (12.5m/s) on majority of the links.

Truck speed

The spikes are even more pronounced in larger samples like the one below, with speeds reaching as high as 1200km/h.

Truck speed (large data)

I suspect this might have something to do with an incorrectly configured parameter in the config file but I can’t seem to pin down the issue.

I created a GitHub repo containing the config file, plans file, vehicles file and the network file for the small sample data
GitHub: speedspike-issue-v14

Please can you point me to where I should look for a solution @rewertvsp

kt86 commented

Dear Ibraheem, please take a look at my answer by email. There should be some hints for taking a more in-depth look into it.

In general, it would be very nice to avoid this double posting: GH issue (usually good!) and parallel same-wording e-mail — without link to the GH Issue — to another person. This causes unnecessary double-work. Thanks a lot!

And please report back here, when you find out a bit more.

Hi @kt86,

Thank you for your response via email. The pointers were very helpful for further investigation.
Also, I apologize for the double posting, I would make sure to avoid that next time.

I have some updates regarding this issue.
Contrary to my initial thoughts, the issue wasn't related to the config nor the router ignoring the vehicle's maxVelocity and link freespeed during routing. It also wasn't as a result of the inclusion of network mode bike because the issue persists when a single vehicleType and network mode car is used.

The issue appears to be related to how the distance of a route is calculated in MATSim and by extension the required travelTime for traversing the route.

Following your pointers, I decided to run a simulation with only one vehicle type car and I was able to replicate the issue. I also further reduced the sample data and the spike was narrowed to a single datapoint exceeding the maxVelocity of the vehicle (151.2km/h -- 42m/s).

image

I looked into the single datapoint and I was able to find the leg, and it's corresponding route within the plan file.
The travel time here is 3 seconds and the distance is ~164m. The speed here would be 54.67m/s -- 196.8km/h.

<leg mode="car" dep_time="08:58:07" trav_time="00:00:03">
	<attributes>
		<attribute name="routingMode" class="java.lang.String">car</attribute>
	</attributes>
	<route type="links" start_link="1141076970002f" end_link="1379264710003f" trav_time="00:00:03" distance="164.05003954068263" vehicleRefId="Dr4|0|0_car">1141076970002f 92528380001f 8129844180000f 1379264710003f</route>
</leg>

These are the four traversed links for the leg (extracted from the network)

<link id="1141076970002f" from="4209129467" to="84839649" length="42.76680076599318" freespeed="12.5" capacity="1200.0" permlanes="1.0" oneway="1" modes="bike,car" type="tertiary" />
<link id="92528380001f" from="84839649" to="81662342" length="18.686648650087104" freespeed="12.5" capacity="1200.0" permlanes="1.0" oneway="1" modes="bike,car" type="tertiary" />
<link id="8129844180000f" from="81662342" to="1574193799" length="23.29933698129051" freespeed="12.5" capacity="1200.0" permlanes="1.0" oneway="1" modes="bike,car" type="tertiary" />
<link id="1379264710003f" from="1574193799" to="276025086" length="122.064053909305" freespeed="12.5" capacity="600.0" permlanes="1.0" oneway="1" modes="bike,car" type="tertiary" />

Below is an analysis of the leg travel time and distance.
The cells highlighted green is the result obtained from leg.getTravelTime().seconds() and leg.getRoute().getDistance(). These values also match the leg distance in the snippet of the output plan file above. It seems the computation for the distance of a leg includes the length of the destination link but the travel time calculation excludes the destination link.
I believe this inconsistency is the reason for the strange spikes in travel speeds.

image

A few additional details:

  • The relevant MATSim version here is v14.0. So it makes sense to ask if this has been picked up and resolved in a later version.
  • I performed a similar analysis on simulation results from an older version of MATSim (v7) and the calculation for both travel time and distance exclude the origin and destination links. So, this behaviour wasn't always the case.

I traced the source of this behavior and it's from a method RouteUtils.calcDistance() in matsim-org: RouteUtils.calcRoute()

In the code block below, Line 170 adds the travelled distance on the endLink to the route distance, if the startLink and endLink are not the same. Correspondingly, line 173 deducts the distance not travelled on the endLink from the route distance, if start and end links are the same.
Since, for each leg, MATSim agents typically enter and leave traffic at a link’s end, I assume this was implemented to consider the entire distance travelled for the route, which should include the endLink. I believe this is correctly implemented.

163	public static double calcDistance(final NetworkRoute networkRoute, final double relPosOnDepartureLink, final double relPosOnArrivalLink, final Network network) {
164		// sum distance of all link besides departure and arrival link
165		double routeDistance = calcDistanceExcludingStartEndLink(networkRoute, network);
166		// add relative distance of departure link
167		routeDistance += network.getLinks().get(networkRoute.getStartLinkId()).getLength() * (1.0 - relPosOnDepartureLink);
168		if (!networkRoute.getStartLinkId().equals(networkRoute.getEndLinkId())){
169			// add relative distance of arrival link
170			routeDistance += network.getLinks().get(networkRoute.getEndLinkId()).getLength() * relPosOnArrivalLink;
171		} else { // i.e. departure = arrival link
172			// subtract relative distance of arrival link that is not traveled
173			routeDistance -= network.getLinks().get(networkRoute.getEndLinkId()).getLength() * (1.0 - relPosOnArrivalLink);
174		}
175		return routeDistance;
176	}

However, from the debugging screenshot below, the route does not contain the startLink and endLink. Only the intermediate links between start and end are contained in the route. This implies that the endLink is not considered for the calculation of travel time.

image

Here is an illustration of the relevant leg from the debugging screenshot
network drawio

In Line 105 below, the calculation of travTime is performed using the least cost path calculation of the routing algorithm and this requires arguments for startNode and endNode matsim-org: NetworkRoutingModule.calcRoute()

95	if (toLink != fromLink) {
96		// (a "true" route)
97		Node startNode = fromLink.getToNode(); // start at the end of the "current" link
98		Node endNode = toLink.getFromNode(); // the target is the start of the link
99
100		/* The NetworkInclAccessEgressModule actually looks up the vehicle in the scenario and passes it to the routeAlgo as well as to the resulting route.
101		 
105		Path path = this.routeAlgo.calcLeastCostPath(startNode, endNode, departureTime, person, null);
106		if (path == null)
107			throw new RuntimeException("No route found from node " + startNode.getId() + " to node " + endNode.getId() + " by mode " + this.mode + ".");
108		NetworkRoute route = this.populationFactory.getRouteFactories().createRoute(NetworkRoute.class, fromLink.getId(), toLink.getId());
109		route.setLinkIds(fromLink.getId(), NetworkUtils.getLinkIds(path.links), toLink.getId());
110		route.setTravelTime(path.travelTime);
111		route.setTravelCost(path.travelCost);
112		route.setDistance(RouteUtils.calcDistance(route, 1.0, 1.0, this.network));
113		newLeg.setRoute(route);
114		newLeg.setTravelTime(path.travelTime);
115	}

In line 98, the start of the endLink is used to initialize the endNode. This excludes the link from travel time calculation.
Considering the need to account for the endLink in the total distance of the leg, I imagine the relevant node should be the end of the endLink. This allows the algorithm to inlude the link during travel time calculation.

This is Line 98 currently:

98	Node endNode = toLink.getFromNode(); // the target is the start of the link

Here's my suggestion for a solution:

98	Node endNode = toLink.getToNode(); // the target is the end of the link

Before we continue with this: are your speed computations coming from events, or from something else? In general, only the events contain interpretable information. Plans, in contrast, are based on approximative models. This is the same difference as between genotype (= plan) and phenotype (= events) in genetics.

Hi @kainagel,

I just posted an update.
We might have posted within minutes of each other, so there's a chance you didn't see it.

kt86 commented

@Ibraheem-A But Kais question remains - also after your update: Are you doing your analysis / observation based on the events or just on the plans (as they are written out e.g. in the carriers file)?

My speed computations are from observations obtained using event handlers. I understand that this should produce the same output as the event file. Is this correct?

Following @kainagel's question, I checked the output event file and searched for the leg of the single datapoint in my second post here. I found the travel time in the event to be adequate for the leg.
Vehicle enters traffic at 32543 and leaves traffic at 32557, which means the travel time is 14 seconds for 164.05 metres (average speed ~11.7m/s -- 42.2km/h). This is reasonably less than the link freespeed and the maxVelocity of the vehicle

<event time="32543.0" type="actend" person="Dr4|0|0" link="1141076970002f" x="4348004.0044760555" y="5941272.86786586" actType="service"  />
<event time="32543.0" type="departure" person="Dr4|0|0" link="1141076970002f" legMode="car" computationalRoutingMode="car"  />
<event time="32543.0" type="PersonEntersVehicle" person="Dr4|0|0" vehicle="Dr4|0|0_car"  />
<event time="32543.0" type="vehicle enters traffic" person="Dr4|0|0" link="1141076970002f" vehicle="Dr4|0|0_car" networkMode="car" relativePosition="1.0"  />
<event time="32544.0" type="left link" link="1141076970002f" vehicle="Dr4|0|0_car"  />
<event time="32544.0" type="entered link" link="92528380001f" vehicle="Dr4|0|0_car"  />
<event time="32546.0" type="left link" link="92528380001f" vehicle="Dr4|0|0_car"  />
<event time="32546.0" type="entered link" link="8129844180000f" vehicle="Dr4|0|0_car"  />
<event time="32548.0" type="left link" link="8129844180000f" vehicle="Dr4|0|0_car"  />
<event time="32548.0" type="entered link" link="1379264710003f" vehicle="Dr4|0|0_car"  />
<event time="32557.0" type="vehicle leaves traffic" person="Dr4|0|0" link="1379264710003f" vehicle="Dr4|0|0_car" networkMode="car" relativePosition="1.0"  />
<event time="32557.0" type="PersonLeavesVehicle" person="Dr4|0|0" vehicle="Dr4|0|0_car"  />
<event time="32557.0" type="arrival" person="Dr4|0|0" link="1379264710003f" legMode="car"  />
<event time="32557.0" type="actstart" person="Dr4|0|0" link="1379264710003f" x="4348026.885355606" y="5941151.1690543825" actType="service"  />

I've uploaded the class handling the events for my analysis here speedspike-TravelTimeEvaluator perhaps there's an issue with the way I set up the handlers.

After digging further, I identified a bug in the way the observations from the event handler are consumed on my side.

In my analysis class, where the observations of the event handler are consumed, the distance of the destination link at the end of one leg is counted again at the begining of the following leg. This coincidentally produces values that are similar to the strange values in the plan's computation, hence my confusion.
Once I resolve this, my observations should have exactly the same values as the events file.

Many thanks for comments, pointers and questions @kainagel @kt86. They were very helpful in solving this mystery. :)

One last question for clarification before closing this, is the behaviour described here #1102 (comment) intended or is the computation of the plan's route simply irrelevant in the grand scheme and has no bearing on anything?