Skip to content

Add Schedule dataclass and refactor EventGenerator#3250

Merged
EwoutH merged 4 commits intomesa:mainfrom
EwoutH:Schedule
Feb 7, 2026
Merged

Add Schedule dataclass and refactor EventGenerator#3250
EwoutH merged 4 commits intomesa:mainfrom
EwoutH:Schedule

Conversation

@EwoutH
Copy link
Copy Markdown
Member

@EwoutH EwoutH commented Feb 6, 2026

Summary

This PR introduces a Schedule dataclass to centralize recurring event configuration and refactors EventGenerator to use it. The change simplifies the event-generation API, consolidates stop/start behavior, and improves clarity around recurring execution rules.

A new stable API for creating events and running will still be introduced in another PR, this PR just builds the foundation for that. See EwoutH@440fdbe for an initial concept.

Motive

Previously, EventGenerator handled interval logic, start timing, and stop conditions (at, after, count) internally. This created a more complex API surface and scattered scheduling logic across multiple methods.

Implementation

A new Schedule dataclass was introduced to define recurring execution behavior (interval, start, end, and count). The interval field supports both fixed numeric values and callables evaluated against the model at runtime.

EventGenerator was refactored to depend on Schedule instead of storing interval and stop-condition state internally. The generator now reads execution limits directly from the schedule when determining whether to reschedule events. The start() method was simplified to use schedule.start or default to model.time + interval, and stop() now performs only immediate cancellation.

Tests were updated to reflect the new API, and a new test suite for Schedule was added. Existing behavior such as callable intervals, execution counting, and priority ordering remains covered.

Usage Examples

# Basic recurring generator
gen = EventGenerator(model, fn, Schedule(interval=1.0, start=0.0))
gen.start()

# schedules
schedule = Schedule(interval=1.0, start=0.0, count=5)
schedule = Schedule(interval=1.0, start=0.0, end=10.0)
schedule = Schedule(interval=lambda m: m.time * 0.5, start=0.0)

# using schedule
EventGenerator(model, fn, schedule).start()

Additional Notes

  • The Schedule object can be used for other things too, like data collection schedules (see Add DataRecorder for reactive Data Storage and DatasetConfig for Configuration #3145 (comment), cc @codebreaker32).
  • This PR introduces a small API break in EventGenerator by replacing the interval argument with Schedule.
  • Stop conditions are now declarative via Schedule instead of configured at runtime.
  • The generator lifecycle is now simpler: start() activates scheduling, stop() cancels immediately.
  • Test coverage for DEVS scheduling behavior has been expanded.

Introduce a Schedule dataclass (interval, start, end, count) to represent recurring timing and refactor EventGenerator to consume a Schedule instead of separate interval/stop parameters.

EventGenerator now stores schedule, uses schedule.interval (callable supported), and evaluates schedule.count/end for stop conditions. Simplified start() and stop() to immediate start/stop behavior (start/after/at and stop(at/after/count) parameters removed) and removed internal _max_count/_end_time fields.

Also updated event enqueueing to use model._event_list.add_event.

This centralizes scheduling logic and simplifies the API: callers must be updated for the changed constructor and start/stop semantics.
Add Schedule import and a new TestSchedule suite to validate default, custom, and callable interval behavior.

Refactor EventGenerator tests to accept a Schedule object instead of raw interval/at/after/stop args: update initialization, start/stop semantics, schedule end/count handling, callable-interval evaluation, functools.partial usage, and priority ordering.

Rename and reorganize several test cases and adjust simulation timings accordingly; remove some legacy error cases now superseded by Schedule-based API.
@EwoutH EwoutH requested a review from quaquel February 6, 2026 14:05
@EwoutH EwoutH added the feature Release notes label label Feb 6, 2026
@github-actions

This comment was marked as off-topic.

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Feb 6, 2026

As indicated also in #3145, I am not convinced by this for data collection. But I agree that this might be cleaner to use over a long list of keyword arguments. I'll try to review later this weekend.

@EwoutH
Copy link
Copy Markdown
Member Author

EwoutH commented Feb 7, 2026

Even if not useful for anything other than event scheduling, from this refactor a Scheduling object (of which instances can be reused) seems to clean up both source and user code nicely.

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Feb 7, 2026

Even if not useful for anything other than event scheduling, from this refactor a Scheduling object (of which instances can be reused) seems to clean up both source and user code nicely.

Similar to Scenario, so this is for me the prime reason why I still like this PR.

Copy link
Copy Markdown
Member

@quaquel quaquel left a comment

Choose a reason for hiding this comment

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

one clarifying question, but looks fine to me otherwise.

@EwoutH EwoutH added the experimental Release notes label label Feb 7, 2026
@EwoutH EwoutH merged commit d6a26f0 into mesa:main Feb 7, 2026
14 of 15 checks passed
@EwoutH
Copy link
Copy Markdown
Member Author

EwoutH commented Feb 8, 2026

@quaquel conceptually, do you think priority should be part of the Schedule? Because it basically defines in which order you do things at a certain time, and thus is part of the ordering of events.

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Feb 8, 2026

@quaquel conceptually, do you think priority should be part of the Schedule? Because it basically defines in which order you do things at a certain time, and thus is part of the ordering of events.

I can see arguments for this but also against this. It depends a bit on how we wan tot use Schedule. At the moment, it is purely used for repeated scheduling of events on the event list. If you want to reuse a schedule object, having the priority frozen in it might be a bit restrictive. At the same time, your argument also holds and creating a Schedule instance is not a big deal, removing the need to extensively reuse them. So, I am fine either way, with a slight preference for including it.

@EwoutH
Copy link
Copy Markdown
Member Author

EwoutH commented Feb 8, 2026

You could essentially do stuff like:

step_start = Schedule(interval=1.0, priority=10)
step_end = Schedule(interval=1.0, priority=0)

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

Labels

experimental Release notes label feature Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants