-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add model.time as universal source of truth for simulation time
#2903
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
base: main
Are you sure you want to change the base?
Conversation
This commit introduces `model.time` as the single, canonical source for simulation time in Mesa, addressing the long-standing issue of having multiple competing time sources (model.steps, simulator.time) that caused confusion and required workarounds in components like DataCollector, visualization, and the experimental ContinuousObservable. Previously, time information was scattered: basic models only had `model.steps`, while models using simulators had to access `simulator.time`. This forced components to implement fallback chains like checking for simulator.time, then model.time, then model.steps. The new design establishes `model.time` as the ground truth that all components can rely on. Key changes to mesa/model.py: - Add `time: float = 0.0` attribute that tracks simulation time - Add `step_duration: float = 1.0` parameter allowing customization of how much time passes per step (useful for staged activation where you might want 0.25 per stage) - Add `_simulator: Simulator | None` to track whether a simulator controls time progression - Modify `_wrapped_step()` to only auto-increment time when no simulator is attached Key changes to mesa/experimental/devs/simulator.py: - Remove `self.time` from Simulator, now writes directly to `model.time` - Add deprecated `time` property that delegates to `model.time` with a DeprecationWarning for backward compatibility - Register simulator with model via `model._simulator = self` in setup() - Update all time reads/writes to use `model.time` instead of `self.time` - Improve error messages to be more descriptive The design intentionally keeps things minimal: no RunControl abstraction, no complex time management classes. Simulators simply write to `model.time` directly, and the default step wrapper increments time by `step_duration` when no simulator is attached. This provides a single way to do things while remaining backward compatible.
Replace MagicMock(spec=Model) with real Model instances since mocks lack the new time attribute. Update assertions to use model.time instead of simulator.time. Add tests for time increment, step_duration, simulator attachment, and deprecation warning. Fix AgentSet constructor calls to use random= keyword argument.
2433e3f to
d295f96
Compare
|
Performance benchmarks:
|
model = Model()
simulator = DEVSimulator()
simulator.setup(model)However, in the Wolfsheep example, I have moved As an aside: what are people's thoughts on stabilizing the simulator stuff? |
|
Thanks for getting back.
Maybe we need to think this a bit further through before going one way or the other. I also want to add Do what do you think about a separate
I just copied (previous) best-practices. It can be that's now outdated. Should I update it?
Perfectly ok with it. Used it intensively without problems during my thesis. |
|
Make Model._step_duration private allows us to update it's working in the future without breaking compatibility. For example if changes are needed for variable durations or datetime support.
Done. |
Summary
Introduces
model.timeas the single, canonical source of simulation time in Mesa. All components (DataCollector, visualization, user code, simulators) can now rely onmodel.timeto get the current simulation time, regardless of whether the model uses simple stepping or discrete event simulation.Motive
Previously, time in Mesa was fragmented across different locations depending on the simulation mode:
model.stepsas a proxy for timesimulator.timeThis caused problems for features that need consistent time access, such as the
ContinuousObservablein #2851 and the conceptual model of space discussed in #2585. The discussion in #2228 (which originated in #2223) established consensus that Mesa needs a universal truth for time.Implementation
Model class changes:
time: floatattribute initialized to0.0step_duration: floatparameter (default1.0) controlling time advancement per step_simulator: Simulator | Noneto track if a simulator controls time_wrapped_step()to only auto-increment time when no simulator is attachedSimulator class changes:
self.timeattributemodel.timeduring event executiontimeproperty delegating tomodel.timefor backward compatibilitymodel._simulator = selfinsetup()model.timetostart_timeinreset()Design kept minimal: a simple attribute rather than a property or
RunControlclass, following YAGNI principles while solving the immediate problem.Usage Examples
Basic usage (time auto-increments with steps):
Custom step duration (useful for staged activation):
With discrete event simulator (simulator controls time):
Components can now simply use
model.time:Compatibility
simulator.timestill works but emits aDeprecationWarningdirecting users tomodel.time(considering DEVS is still experimental, this is quite gentle)simulator.timewithout triggering the property (unlikely) would break (again, experimental, and all test pass)Additional Notes
ContinuousObservable)datetimevalues fortime.