-
Notifications
You must be signed in to change notification settings - Fork 456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Capped Road Pricing via a TollFactor - thread safety questions #1898
Comments
It might be different in Hermes, but I think the event handling in Hermes and Qsim works the same way. Events are handled in separate threads which fetch events which need processing form a queue. Your config indicates, that you have 32 threads fetching from that queue. Hermes is issuing Event handlers, in the default implementation of I guess the way you are doing this will not work, since the toll factor is fetched when a |
Thanks @Janekdererste That confirms my suspicions - ultimately the problem here is that the processing of the From looking through the MATSim code, I can see that As I mentioned above, my initial thought was to have my |
You should be fine by querying the If you don't want to work within the matsim repository directly, it is possible to name a package in your downstream project |
So my plan seemed like a good one until I realised that From reading these comments, it looks like the toll record as stored internally by |
My intuition would be to program it yourself. The way to do this is always the same with MATSim contribs:
You should end up with replacements for these three classes in your own space. But not more. Otherwise, please let us know (also see below). A bit more philosophical: Evidently, one should reuse as much code as possible, since otherwise we will never arrive at certifiable simulation results. On the other hand, it is nearly impossible to make everything replaceable by injection. In consequence, if requirements go significantly beyond what the existing code is able to process, one may have to replace the injected module. I would say that you own code should be shorter than 1000 lines of code, otherwise something is wrong, either with our or with your design. If, at some point, you notice that you need access to something that is not accessible (non-public, or final), please let us know. Normally, we will not make it public or non-final, but hopefully either point to ways to proceed anyways, or find some other solution to address the problem. In particular the latter sometimes takes some time; then please have patience with us. |
Hi
Following this discussion, we have created an implementation of
TollFactor
that tracks each agent's progress towards a daily toll cap. Once an agent reaches the cap, we return a toll factor of 0 so that they pay no more tolls in the current iteration.We initially tested this mechanism using very small and simple datasets with a simple and minimal MATSim config, and it seemed to do exactly what we want.
We have now progressed to using this
TollFactor
with some of our real models, and we're seeing some unexpected results. Although the toll cap greatly reduces the overall amount of toll revenue, as we would expect, we find that a significant minority of agents, in some cases 30 - 35%, have paid well over the cap value in tolls, often exceeding the cap by hundreds or even thousands.It feels to me like a race condition between threads (for reasons I will explain below), but I don't have a good enough understanding of the MATSim/Hermes threading or event handling model to know exactly how this is happening or how to fix it. I will start logging out timestamps and thread IDs in an attempt to piece things together, but I wondered if anybody could immediately confirm my race condition theory if I describe my
TollFactor
class.Description of our TollFactor class
Initially I was attempting to track tolls paid by agents by updating a map in the
TollFactor
whenevergetTollFactor()
is called. My assumption was that this method is called when and only when a toll is about to be paid by an agent. This assumption proved incorrect; the method seemed to be called in many cases without a toll being paid subsequently by the agent.From stepping through the MATSim code I found one such example when an agent moves between links inside a cordon (we were using MATSim 12 and cordon based road pricing schemes at the time). My
getTollFactor()
method would be called in response to an agent entering a tolled link and would return a toll factor value, then later on in the call stack there was a check inRoadPricingTollCalculator
to determine whether or not the agent was moving from an untolled link to a tolled link. If this was not the case, noPersonMoneyEvent
was fired and the agent (correctly) paid no toll. I thus had a mismatch between the (correct) end-of-iteration toll statistics reported byRoadPricingControlerListener
, and the toll stats in my ownTollFactor
, for example 28.0 (correct) versus 406 (incorrect):For this reason, I made my
CappedTollFactor
aPersonMoneyEventHandler
; it now updates its own toll record whenever it receives aPersonMoneyEvent
inhandleEvent()
. My reasoning was that these events are guaranteed to fire only when an agent actually pays a toll. NB I would have liked to injectRoadPricingTollCalculator
in to myTollFactor
and delegate to it, but the method I need from it,getAgentToll()
is not public.My
CappedTollFactor
now reports the same end-of-iteration "sum of all paid tolls" value asRoadPricingControlerListener
, so I'm satisfied it doesn't miss anyPersonMoneyEvents
. What I'm unsure about is the chronology of when it sees these events.The parts that query and update the toll record look like this:
I made no attempt to make this code thread safe. I am now wondering if the thread that ultimately handles
PersonMoneyEvents
and updates the toll record is different from the thread that is responding toLinkEnterEvents
(which ultimately leads to a call togetTollFactor
on myCappedTollFactor
). In our MATSim config we have:Is this causing a race condition between two or more threads? In fact, thinking about this some more, it may not even require a race between threads - it could just be that the
LinkEnterEvents
all fire and are processed together, whereas thePersonMoneyEvents
that result from some of these movements along tolled links are not handled until much later, meaning that the toll record is not up-to-date when we query it ingetTollFactor
.If so, this would seem to require a fundamentally different approach to supporting capped road pricing - do you have any ideas on what that approach would look like?
The text was updated successfully, but these errors were encountered: