Skip to content

Add iteration counts to MPP protocol - #378

Open
IrisvdWerf wants to merge 89 commits into
multiscale:developfrom
IrisvdWerf:timelines
Open

Add iteration counts to MPP protocol#378
IrisvdWerf wants to merge 89 commits into
multiscale:developfrom
IrisvdWerf:timelines

Conversation

@IrisvdWerf

@IrisvdWerf IrisvdWerf commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

See issue #373

@IrisvdWerf
IrisvdWerf requested a review from maarten-ic July 28, 2026 15:47

@maarten-ic maarten-ic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See comments below. Let me know if you have any questions!

Comment thread libmuscle/python/libmuscle/port_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_mpp_message.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_mpp_message.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_port_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_snapshot_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_timeline_manager.py

@LourensVeen LourensVeen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've left a few comments, but I mainly want to say that this is very, very nice. Clear, clean, well-structured, well-documented, despite the complexity of the algorithm. Couldn't be happier 😄

Comment thread libmuscle/python/libmuscle/instance.py
Comment thread libmuscle/python/libmuscle/instance.py
Comment thread libmuscle/python/libmuscle/timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/timeline_manager.py Outdated
Comment thread libmuscle/python/libmuscle/communicator.py Outdated
Comment thread libmuscle/python/libmuscle/communicator.py Outdated
Comment thread libmuscle/python/libmuscle/test/test_instance.py
Comment thread libmuscle/python/libmuscle/test/test_port_manager.py Outdated
@IrisvdWerf
IrisvdWerf marked this pull request as ready for review August 3, 2026 16:50

@maarten-ic maarten-ic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Part 1 of the C++ review -- I didn't look at the tests yet. Nice work!

Comment thread libmuscle/cpp/src/libmuscle/communicator.cpp
Comment thread libmuscle/cpp/src/libmuscle/communicator.cpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/communicator.cpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/mpp_message.cpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/snapshot_manager.cpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/timeline_manager.hpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/timeline_manager.cpp
Comment thread libmuscle/cpp/src/libmuscle/timeline_manager.cpp Outdated
Comment thread libmuscle/cpp/src/libmuscle/timeline_manager.cpp
Comment thread libmuscle/cpp/src/libmuscle/timeline_manager.cpp Outdated
@multiscale multiscale deleted a comment from maarten-ic Aug 4, 2026
if (first_run_.is_set()
#ifdef MUSCLE_ENABLE_MPI
&& mpi_barrier_.is_root()
#endif

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Only do this check on the root? @maarten-ic

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree this check should be done on the root process only.
My latest commit changes the logic slightly, such that any thrown exceptions are handled with the existing try/except logic for MPI-enabled instances.

Comment thread pyproject.toml Outdated

@maarten-ic maarten-ic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

To ensure TimelineStates can be reliably compared for equality.

Since the list of ports should be fairly small (and checkpoints are not commonly generated), I don't expect a performance problem because of this.
Ensure any thrown exceptions in `communicator_->finish_reuse_iteration()` are handled correctly with MPI.

Also updated the Python code to follow the same flow as C++.

@LourensVeen LourensVeen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You know, C++ gets criticised sometimes, but there's really not much wrong with it if you just use it right... 😄 Looks really nice, I didn't check the logic again in detail because I trust it matches the Python side, but I found a few style things you may want to look at. It's pretty much ready to go though.


bool TimelinePorts::has_participated(std::string const & port_name, Optional<int> slot) const {
PortAndSlot key(port_name, slot);
for (auto const & p : participated)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

std::find would make that a one-liner I think?

@@ -17,7 +18,7 @@ namespace libmuscle { namespace _MUSCLE_IMPL_NS {
class Snapshot {
public:
enum class VersionByte : char {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should probably be inline static const char version_byte = '2' in C++17 to match the Python side. Or could even be static const char version_byte already with a separate definition in snapshot.cpp. But we can also change that later.

resume_overlay_ = snapshot.settings_overlay;

port_manager_.restore_message_counts(snapshot.port_message_counts);
if (!snapshot.is_final_snapshot) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Python side has a comment here, maybe good to replicate? I've already forgotten what this was for...

* @param our_ndims Number of dimensions of our instance set.
* @param peer_dims Dimensions of the peer instance set of this port.
*/
Port(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Now that we have a ymmsl::Port, should this derive from it like in Python?

Comment on lines +14 to +16
// Note: this header is only ever pulled in via the LIBMUSCLE_MOCK_TIMELINE_MANAGER
// swap point inside timeline_manager.hpp, which is already inside the
// libmuscle::_MUSCLE_IMPL_NS namespace at that point, so it isn't opened here again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The other mocks do reopen the namespace. Is there a reason to deviate from that here? Seems good to be consistent, and safer in case something changes?

settings["b"] = true;
Message message(1.0, 2.0, "test_data", settings);
Snapshot snapshot ({}, 0, {}, false, message, {});
TimelineStateFixture timeline_state_fixture;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to use a fixture class here, like we're doing in e.g. test_communicator.cpp?

mock_port_manager_.get_message_counts.return_value = port_message_counts;
mock_port_manager_.settings_in_connected.return_value = false;

TimelineStateFixture timeline_state_fixture;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here, it could be in the fixture and then we could just use it, like communicator_.

ExpectedActions: TypeAlias = list[tuple[str, Port, list[int]]]


class SubTimelineState(TypedDict):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking at this again, why is this a TypedDict and TimelineState below a dataclass?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants