-
Notifications
You must be signed in to change notification settings - Fork 11
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
Synchronised variable transfer #189
Synchronised variable transfer #189
Conversation
This fixes issue #181, "Only transfer variable values at common communication points".
Sounds like it would be quite straightforward to verify this functionality with a test? For example two mock slaves with decimation factors of 2 and 3, connect real out->real in, verify that values are transferred every 6 base steps. Or are you saying such a test would not be sufficient? |
What happens here if we have slaves: |
I was thinking more like verifying that it doesn't have subtle unwanted consequences for the simulation results, but you are right, just testing the basic functionality should be simple enough. And it should be done, so I'll add a test for it! |
This has nothing to do with when to step, that is controlled solely by the step size and decimation factor of each individual slave. This only has to do with when we transfer variable values. And with the change proposed here, we transfer from
Not really, but it doesn't harm either, and it's easier to just update the variables than to special-case it. It probably won't affect performance that much, because it only updates the local cache. Nothing is actually sent to the slave until |
I suppose this is OK, but when would |
I think it helps to ask "when must a connection/bond step?" and not "when must a slave step?". It actually makes little sense IMHO to talk about when one slave should step/synchronize, it always is a matter of when a connection/bond should "step" and synchronize the slaves it connects.
Thoughts? |
This means that at a given |
In my opinion, this is not the case. I base this on two observations (or rather lack thereof) of things that are not mentioned in the FMI standard:
|
Note that this is currently not what happens; the inputs always get set due to the way we do caching. This is what @hplatou mentioned in today's meeting. |
More concisely, the following (pseudo-code):
should be equivalent to this:
|
Aah, ok. I thought we agreed on the exact opposite in the previous mob session xD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me.
Well, any disagreements definitely need to be laid on the table now. I encourage you all to double-check the relevant parts of the FMI (2.0) spec and give me your interpretations of it. I may very well be wrong in this, I don't know it all by heart even if I sometimes pretend to. ;) PS: I accidentally edited @mrindar's post to write this reply, instead of starting a new comment. Apologies, and reverted now. |
From the FMI 2.0 standard:
I interpret this to mean that it is the master algorithms responsibility to ensure 'input correctness'. So if we have the following stepping scenario:
where Thoughts? Edit: Edit Edit: |
I don't. As this is a standards document, and hence must be expected to be precise, I don't think we should interpret anything into it that isn't explicitly stated. And I don't see it stated that the master algorithm must provide all inputs, only that it may do so.
Consider the connection from B to C, and assume that B sends derivatives with its outputs. Hey, that is awesome, now C can extrapolate its corresponding input between communication points for improved accuracy. However, if a simplistic master algorithm then goes and "resets" the value of the input at |
I yield |
As stated, in the most simple case (0th order interpolation) you would just hold the last input and continue to use it unless an updated one becomes available. I think both of @mrindar and @kyllingstad 's latest comments are correct as far as I can tell. Just an academic side note (not really important):
No, they actually could be in an iterative scheme or when first stepping B, and then stepping C (slaves don't have to step in parallel in all cases). |
I am working both on a test for this as well as a fix for the caching issue, so I'll close this for now. I'll reopen when the fix is ready to merge. |
I started changing things so that we only set the input variables where we have received an updated value (as opposed to setting all connected inputs at all time steps). But I've hit a snag and would like some feedback: We now have the ability to set I see two solutions:
Any comments or preferences here? |
That’s a good question. I think that the ability to override unconnected inputs is required so we need to find some solution for that. In that sense I think I will be voting for the second solution. |
I believe I have fixed the caching issue now, so I am reopening. This required separating the "get cache" and "set cache" into two different helper classes in I've also added a test that verifies the functionality. |
f134121
to
e04e26e
Compare
This ensures that only the variables that have actually been set() will be cached in `slave_simulator` and passed to `async_slave::set_variables()`. Previously, all *exposed* variables would always be in the cache.
This test verifies that issue #181 has been fixed, i.e., that variable values are only transfered between simulators at common synchronisation points.
e04e26e
to
9c78d76
Compare
I've made it so that when a manipulator is set for a variable, it will always be run on the last set value for that variable. If no value has been set yet, it will use the default value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! This looks good to me.
From reading the code, it seems the manipulator will only run once, and after that only if/when a new value is set with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I like the updated test, I am missing a test case for the original predicament leading to this PR;
A fast slave has an input connected to the output from a slow slave, and we want to verify that the fast slave only gets its input updated at "matching" steps. Right now we only test the opposite case (fast -> slow), but that might be sufficient?
// specifies whether they have been run on the values currently in | ||
// `values_`. | ||
std::unordered_map<variable_index, std::function<T(T)>> manipulators_; | ||
bool hasRunManipulators_ = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is hasRunManipulators_
mainly a guard against setting values and manipulators during slave_simulator::set_variables()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partly, but that's not its primary purpose. It is used in set_variable_cache::manipulate_and_get()
to check whether the manipulators have already been run, to avoid running them multiple times on the same values if the function is called again. (That never happens now, but could easily happen in the future.)
Compare with how it was done before, and still is in get_variable_cache
, where you first call run_manipulators()
and then access the manipulatedValues
vector directly. In the new class, I've made the member variables private
and tried to offer a less error prone interface.
Can we use the ramp as a case? Will it be sufficient that the "manipulator-function" takes delta-t as an argument? |
Correct.
Dang, you're right. I didn't think of that use case at all. Good catch! Possible solutions, off the top of my head:
|
I agree. I'll add a test.
Technically, it's sufficient to test the current implementation because |
The manipulator functions don't get the current time, nor the step size, at the moment, so more changes are needed to support that case. |
This is starting to feel like a design choice. Until we hash out a master plan for the intended usage of these manipulator functions - i.e. how often will they be called, how good of an idea is statefulness etc., and as long as there are no current stateful manipulator function implementations, I'm OK with tackling this in a future issue.
Another input to this debate is the |
Done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! There was a seemingly random test failure on Jenkins which had me worried, so we might have to to keep our eyes open here 😃
Hm, that is worrisome indeed. And given that it fails in exactly the same way on two different platforms makes it seem less random. I'll look into it a bit more before merging the PR. |
Found the problem: #213 It is really unrelated to this PR, so I'll just go ahead and merge now. (It was detected by an |
This fixes issue #181, "Only transfer variable values at common communication points".
Please have a good look at this and give it some consideration before approving, as it's a bit difficult to verify with tests that this is the right way to go about it. (Fixing #186 would help.)