Skip to content
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

Enable exporting and importing subsimulator state #769

Merged
merged 2 commits into from
Oct 10, 2024

Conversation

kyllingstad
Copy link
Member

@kyllingstad kyllingstad commented Jul 4, 2024

This is a follow-up to #765 and the second and final step to close #756. It is part of a PR series which will culminate in the implementation of #757, and until that point, I am targeting the dev/state-persistence branch rather than master.

Here, I've implemented functionality to export the internal state of individual subsimulators in a generic, structured form, and to import it again later.

This exported form is intended as an intermediate step before serialisation to disk. The idea was to create a type that can be inspected and serialised to almost any file format we'd like.

The type is defined by cosim::serialization::node in cosim/serialization.hpp. It is a hierarchical, dynamic data type with support for a variety of primitive scalar types and a few aggregate types: strings, arrays of nodes, dictionaries of nodes, and binary blobs. (Think JSON-like structure, only in-memory and with more types.)

Edit: node was originally a homebrew type. Now, it is based on Boost.PropertyTree. Otherwise, the description above still fits pretty well.

This is a follow-up to #765 and the second and final step to close #756.

Here, I've implemented functionality to export the internal state of
individual subsimulators in a generic, structured form, and to import
them again later.

This exported form is intended as an intermediate step before
serialisation and disk storage.  The idea was to create a type that can
be inspected and serialised to almost any file format we'd like.

The type is defined by `cosim::serialization::node` in
`cosim/serialization.hpp`.  It is a hierarchical, dynamic data type with
support for a variety of primitive scalar types and a few aggregate
types: strings, arrays of nodes, dictionaries of nodes, and binary
blobs. (Think JSON, only with more types.)
@kyllingstad kyllingstad added the enhancement New feature or request label Jul 4, 2024
@kyllingstad kyllingstad self-assigned this Jul 4, 2024
@kyllingstad kyllingstad marked this pull request as draft July 4, 2024 17:09
@kyllingstad
Copy link
Member Author

Hm. Seems like my little trick of storing an incomplete node type in an unordered_map doesn't work on GCC 9. It worked locally for me, but I use a more recent compiler. I've changed this PR to "draft" status while I figure out what to do about it.

@kyllingstad
Copy link
Member Author

kyllingstad commented Jul 9, 2024

Ok, I made it work now.

It seems that to make a recursive map-like data type in standard C++, one has to hide the internal type, for example by messing about with void* pointers. Rather than do this myself, I have replaced the homemade cosim::serialization::node class with an alias for boost::property_tree::basic_ptree. The downside of this is a slightly less elegant API, but this is more than outweighed by the confidence we get from using tried-and-true code. Plus, we're already using ptree elsewhere in libcosim, so there's no extra dependency baggage involved.

@kyllingstad kyllingstad marked this pull request as ready for review July 9, 2024 15:38
@@ -290,8 +291,6 @@ class slave
* `state_index`. The index is only valid for this particular slave.
*
* The function may be called at any point after `setup()` has been called.
*
* \pre `this->model_description().can_save_state`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the removal on lines 304–305 was just something I missed in #765.

Copy link
Contributor

@davidhjp01 davidhjp01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GW! One question - by looking at the visitor for byte type, are we going to export the FMU's serialized states into byte string?

tests/slave_simulator_unittest.cpp Show resolved Hide resolved
src/cosim/proxy/remote_slave.cpp Show resolved Hide resolved
src/cosim/serialization.cpp Show resolved Hide resolved
@kyllingstad kyllingstad merged commit efdbc87 into dev/state-persistence Oct 10, 2024
20 checks passed
@kyllingstad kyllingstad deleted the feature/756-subsim-state-export branch October 10, 2024 06:56
kyllingstad added a commit that referenced this pull request Nov 25, 2024
This closes #768. Some changes may warrant a bit of extra explanation:

**`execution` and `algorithm`:** I have taken one step towards prohibiting adding/removing sub-simulators after the co-simulation has begun, as decided in #771.  In particular, I've added the `execution::initialize()` function, which marks the point where such changes are no longer allowed.  (This is a backwards-compatible change, because it gets automatically called by `execution::step()` if it hasn't been called manually.)

**`slave_simulator`**: The changes in `slave_simulator.cpp` really ought to have been included in #769. Unfortunately, I didn't realise they were necessary before it was all put into the context of saving algorithm and execution state.  Basically, I thought I could get away with just saving each FMU's internal state, but it turns out that we also need to save the `slave_simulator` "get" and "set" caches.

(For those interested in the nitty-gritties, this is due to some subtleties regarding exactly when the cache values are set in the course of a co-simulation, relative to when the values are passed to the FMU. At the end of an "algorithm step", the "set cache" is out of sync with the FMUs input variables, and won't be synced before the next step. Simply performing an additional sync prior to saving the state is not sufficient, because that could have an effect on the FMUs output variables, thus invalidating the "get cache". That could in principle be updated too, but then the `slave_simulator` is in a whole different state from where it was when we started to save the state.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add interfaces to FMI functions for saving/serialising/deserialising/restoring FMU state
3 participants