{ "cells": [ { "cell_type": "markdown", "id": "a9c24a2decbdeb67", "metadata": {}, "source": "# Event Scheduling & Time" }, { "metadata": {}, "cell_type": "markdown", "source": [ "**Important:**\n", "- If you are just exploring Mesa and want the fastest way to execute the code we recommend executing this tutorial online in a Colab notebook. [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mesa/mesa/blob/main/docs/tutorials/3_event_scheduling.ipynb) or if you do not have a Google account you can use [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/mesa/mesa/main?labpath=docs%2Ftutorials%2F3_event_scheduling.ipynb) (This can take 30 seconds to 5 minutes to load)\n", "- If you are running locally, please ensure you have the latest Mesa version installed.\n", "## Tutorial Description\n", "In the previous tutorials, you created agents, queried them with AgentSet, and learned\n", "activation patterns. In this tutorial, we cover **time** — how Mesa models progress\n", "through time, how you run simulations, and how to schedule events that happen at specific\n", "moments.\n", "By the end of this tutorial you will know how to:\n", "- Understand the two time-tracking attributes: `model.steps` and `model.time`\n", "- Run models with `run_for()` and `run_until()`\n", "- Schedule one-off events at absolute or relative times\n", "- Schedule recurring events with `Schedule`\n", "- Control event priority\n", "- Cancel events and stop recurring generators\n", "- Combine step-based agent activation with discrete events in a single model" ], "id": "8ada7861000dfdb3" }, { "metadata": {}, "cell_type": "markdown", "source": "### IN COLAB? - Run the next cell", "id": "26778f34a6f7e715" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": "# %pip install --quiet mesa[rec]", "id": "d4c293787d09ede6" }, { "metadata": {}, "cell_type": "markdown", "source": "### Import Dependencies", "id": "dd4c89553e58e7a3" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "import numpy as np\n", "\n", "import mesa\n", "from mesa.time import Priority, Schedule" ], "id": "c7436140154088ae" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## How Time Works in Mesa\n", "Every Mesa model tracks two time-related attributes:\n", "- **`model.steps`** — An integer counting how many steps have been executed (starts at 0).\n", "- **`model.time`** — A float representing the current simulation time (starts at 0.0).\n", "### The default step mechanism\n", "When you define a `step()` method on your model and advance time, Mesa wraps your step\n", "in an internal event system. Each step:\n", "1. Advances `model.time` by 1.0\n", "2. Increments `model.steps` by 1\n", "3. Executes your `step()` method\n", "You never call `model.step()` directly to run a simulation. Instead, use:\n", "- **`model.run_for(n)`** — Advance time by `n` units (executing `n` steps for standard models)\n", "- **`model.run_until(t)`** — Advance time until `model.time` reaches `t`\n", "These methods are the primary way to run any Mesa model." ], "id": "a02833ef77fa765c" }, { "metadata": {}, "cell_type": "markdown", "source": "### A quick example", "id": "37f80876465f2db1" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class SimpleModel(mesa.Model):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " def step(self):\n", " print(f\" Step {self.steps} at time {self.time:.1f}\")\n", "\n", "\n", "model = SimpleModel()\n", "print(\"Initial state:\")\n", "print(f\" steps={model.steps}, time={model.time}\")\n", "print(\"\\nRunning for 3 time units:\")\n", "model.run_for(3)\n", "print(f\"\\nFinal state: steps={model.steps}, time={model.time}\")" ], "id": "1e12f1f25f5bf91f" }, { "metadata": {}, "cell_type": "markdown", "source": [ "Notice that after `run_for(3)`, both `steps` and `time` are at 3. For standard\n", "step-based models, they advance in lockstep. But as we'll see, events can fire\n", "at *any* time — including between steps." ], "id": "4aab057a3922985b" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## `run_for` vs `run_until`\n", "Both methods advance time and process any scheduled events (including the default step events)\n", "along the way. The difference is simple:\n", "- `run_for(duration)` advances time by the specified **duration** from the current time\n", "- `run_until(end_time)` advances time to the specified **absolute time**" ], "id": "43a7b45abb7b7a8e" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "model = SimpleModel()\n", "\n", "print(\"run_for(2):\")\n", "model.run_for(2)\n", "print(f\" → time is now {model.time}\\n\")\n", "\n", "print(\"run_until(5):\")\n", "model.run_until(5)\n", "print(f\" → time is now {model.time}\\n\")\n", "\n", "print(\"run_for(3) more:\")\n", "model.run_for(3)\n", "print(f\" → time is now {model.time}\")" ], "id": "6c0c6085d2799faa" }, { "metadata": {}, "cell_type": "markdown", "source": [ "`run_until` is particularly useful when you have a fixed end time for your simulation,\n", "or when coordinating with external time references:\n", "```python\n", "model.run_until(365) # Run for one simulated year\n", "```" ], "id": "c2369bb2435f2dee" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Scheduling One-Off Events\n", "Beyond the regular step cycle, Mesa lets you schedule **events** — functions that\n", "execute at specific times. This is useful for modeling things that don't happen every\n", "step: policy changes, natural disasters, market shocks, seasonal effects, or any\n", "occurrence that happens at a specific point in time.\n", "Use `model.schedule_event()` to schedule a one-off event:\n", "- **`at=`** — Schedule at an **absolute** time\n", "- **`after=`** — Schedule at a **relative** time from now (i.e., `model.time + after`)\n", "You must specify exactly one of `at` or `after`." ], "id": "12c85f79fc839a92" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class EconomyModel(mesa.Model):\n", " \"\"\"A simple economy where a tax reform happens at a specific time.\"\"\"\n", "\n", " def __init__(self, n=50):\n", " super().__init__()\n", " self.tax_rate = 0.1\n", " self.events_log = []\n", "\n", " # Create agents with wealth\n", " for _ in range(n):\n", " a = mesa.Agent(self)\n", " a.wealth = 10\n", "\n", " # Schedule a tax reform at time 5.0\n", " self.schedule_event(self.tax_reform, at=5.0)\n", "\n", " # Schedule a stimulus check 2 time units from now (so at time 2.0)\n", " self.schedule_event(self.stimulus, after=2.0)\n", "\n", " def tax_reform(self):\n", " self.tax_rate = 0.25\n", " self.events_log.append(\n", " f\"t={self.time:.1f}: Tax reform! Rate now {self.tax_rate}\"\n", " )\n", "\n", " def stimulus(self):\n", " for agent in self.agents:\n", " agent.wealth += 5\n", " self.events_log.append(f\"t={self.time:.1f}: Stimulus! Everyone gets 5 units\")\n", "\n", " def step(self):\n", " # Simple taxation each step\n", " for agent in self.agents:\n", " tax = int(agent.wealth * self.tax_rate)\n", " agent.wealth -= tax\n", "\n", "\n", "model = EconomyModel(10)\n", "model.run_for(7)\n", "\n", "print(\"Events that occurred:\")\n", "for event in model.events_log:\n", " print(f\" {event}\")\n", "\n", "print(f\"\\nFinal tax rate: {model.tax_rate}\")\n", "avg_wealth = model.agents.agg(\"wealth\", np.mean)\n", "print(f\"Average wealth: {avg_wealth:.1f}\")" ], "id": "b377625909a18473" }, { "metadata": {}, "cell_type": "markdown", "source": [ "**Key point:** Events fire *during* time advancement. When `run_for(7)` processes time,\n", "it encounters the stimulus event at t=2.0 and the tax reform at t=5.0, executing them\n", "at the correct moments. Your `step()` method also fires at t=1.0, 2.0, 3.0, etc. as\n", "scheduled events under the hood." ], "id": "2bf86a56299f25fe" }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Canceling events\n", "`schedule_event` returns an `Event` object. You can cancel it before it fires:" ], "id": "48826cba60a5de2c" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class CancelDemo(mesa.Model):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " # Schedule two events\n", " self.event_a = self.schedule_event(self.event_a_fn, at=3.0)\n", " self.event_b = self.schedule_event(self.event_b_fn, at=5.0)\n", "\n", " # Cancel event B before it fires\n", " self.event_b.cancel()\n", "\n", " def event_a_fn(self):\n", " print(f\" Event A fired at t={self.time:.1f}\")\n", "\n", " def event_b_fn(self):\n", " print(f\" Event B fired at t={self.time:.1f}\")\n", "\n", " def step(self):\n", " pass\n", "\n", "\n", "model = CancelDemo()\n", "print(\"Running — Event B was canceled:\")\n", "model.run_for(6)\n", "print(\" (Event B never fired)\")" ], "id": "2ce98529c3e64528" }, { "metadata": {}, "cell_type": "markdown", "source": [ "Cancellation is useful when model conditions change. For example, you might schedule\n", "a disaster event but cancel it if agents take preventive action." ], "id": "2f7445cb1c8b421a" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Scheduling Recurring Events\n", "Many models have things that happen repeatedly but not every step — quarterly reports,\n", "seasonal cycles, periodic inspections, or interest payments. Use `model.schedule_recurring()`\n", "with a `Schedule` to define these patterns.\n", "The `Schedule` dataclass specifies:\n", "- **`interval`** — Time between executions (required)\n", "- **`start`** — When to begin (default: current time + interval)\n", "- **`end`** — When to stop (default: never)\n", "- **`count`** — Maximum number of executions (default: unlimited)" ], "id": "1cc67641ec235c5a" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class SeasonalModel(mesa.Model):\n", " \"\"\"A model with regular seasonal events.\"\"\"\n", "\n", " def __init__(self, n=20):\n", " super().__init__()\n", " self.season = \"spring\"\n", " self.season_log = []\n", "\n", " for _ in range(n):\n", " a = mesa.Agent(self)\n", " a.wealth = 10\n", "\n", " # Change season every 3 time units, starting at t=3.0\n", " self.schedule_recurring(\n", " self.change_season,\n", " Schedule(interval=3.0),\n", " )\n", "\n", " # Collect taxes every 5 time units, but only 4 times\n", " self.schedule_recurring(\n", " self.collect_taxes,\n", " Schedule(interval=5.0, count=4),\n", " )\n", "\n", " def change_season(self):\n", " seasons = [\"spring\", \"summer\", \"autumn\", \"winter\"]\n", " idx = seasons.index(self.season)\n", " self.season = seasons[(idx + 1) % 4]\n", " self.season_log.append(f\"t={self.time:.1f}: Season → {self.season}\")\n", "\n", " def collect_taxes(self):\n", " for agent in self.agents:\n", " agent.wealth -= 1\n", " self.season_log.append(f\"t={self.time:.1f}: Taxes collected!\")\n", "\n", " def step(self):\n", " # Normal step — agents earn money\n", " for agent in self.agents:\n", " agent.wealth += self.random.randint(0, 2)\n", "\n", "\n", "model = SeasonalModel(10)\n", "model.run_for(20)\n", "\n", "print(\"Timeline:\")\n", "for entry in model.season_log:\n", " print(f\" {entry}\")\n", "print(f\"\\nFinal season: {model.season}\")\n", "print(f\"Average wealth: {model.agents.agg('wealth', np.mean):.1f}\")" ], "id": "570c44b3c841b335" }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Controlling when recurring events start\n", "By default, a recurring event first fires at `current_time + interval`. You can\n", "override this with the `start` parameter:" ], "id": "98454fd1b35dde05" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class StartDemo(mesa.Model):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " # Fires at t=5, t=10, t=15, ... (default start)\n", " self.schedule_recurring(\n", " self.default_start_event,\n", " Schedule(interval=5.0),\n", " )\n", "\n", " # Fires at t=0, t=5, t=10, ... (start immediately)\n", " self.schedule_recurring(\n", " self.start_at_zero_event,\n", " Schedule(interval=5.0, start=0.0),\n", " )\n", "\n", " # Fires at t=2, t=7, t=12, ... (custom start)\n", " self.schedule_recurring(\n", " self.start_at_two_event,\n", " Schedule(interval=5.0, start=2.0),\n", " )\n", "\n", " def default_start_event(self):\n", " print(f\" Default start: t={self.time:.1f}\")\n", "\n", " def start_at_zero_event(self):\n", " print(f\" Start at 0: t={self.time:.1f}\")\n", "\n", " def start_at_two_event(self):\n", " print(f\" Start at 2: t={self.time:.1f}\")\n", "\n", " def step(self):\n", " pass\n", "\n", "\n", "model = StartDemo()\n", "print(\"Events firing during first 12 time units:\")\n", "model.run_for(12)" ], "id": "4b725a337641f6de" }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Stopping recurring events\n", "`schedule_recurring` returns an `EventGenerator` that you can stop at any time:" ], "id": "bbb39854baf5cc05" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class StopDemo(mesa.Model):\n", " def __init__(self):\n", " super().__init__()\n", " self.counter = 0\n", "\n", " # Start a recurring event\n", " self.ticker = self.schedule_recurring(\n", " self.tick,\n", " Schedule(interval=1.0),\n", " )\n", "\n", " def tick(self):\n", " self.counter += 1\n", " print(f\" Tick #{self.counter} at t={self.time:.1f}\")\n", "\n", " def step(self):\n", " # Stop the ticker after 5 ticks\n", " if self.counter >= 5 and self.ticker.is_active:\n", " self.ticker.stop()\n", " print(f\" Ticker stopped at t={self.time:.1f}\")\n", "\n", "\n", "model = StopDemo()\n", "model.run_for(10)\n", "print(f\"\\nTotal ticks: {model.counter}\")" ], "id": "377f867e4292e975" }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Using `end` and `count` for automatic limits\n", "Instead of manually stopping a generator, you can set limits in the `Schedule` itself:\n", "```python\n", "# Stop after time 50.0\n", "Schedule(interval=5.0, end=50.0)\n", "# Execute at most 10 times\n", "Schedule(interval=5.0, count=10)\n", "# Both: stop after 10 executions OR after time 50.0, whichever comes first\n", "Schedule(interval=5.0, count=10, end=50.0)\n", "```" ], "id": "839d6d88db478d48" }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Dynamic intervals\n", "The `interval` parameter can be a callable that returns the next interval dynamically.\n", "The callable receives the model as its argument. This is useful for modeling processes\n", "where the frequency changes over time:" ], "id": "8ee37c615fd3e5b2" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class AcceleratingModel(mesa.Model):\n", " \"\"\"A model where events happen faster and faster.\"\"\"\n", "\n", " def __init__(self):\n", " super().__init__()\n", " self.event_times = []\n", "\n", " # Interval starts at 4.0 and shrinks each time\n", " self.schedule_recurring(\n", " self.record_event,\n", " Schedule(\n", " interval=lambda m: max(1.0, 4.0 - m.time * 0.3),\n", " count=8,\n", " ),\n", " )\n", "\n", " def record_event(self):\n", " self.event_times.append(self.time)\n", "\n", " def step(self):\n", " pass\n", "\n", "\n", "model = AcceleratingModel()\n", "model.run_for(25)\n", "\n", "print(\"Event times (accelerating intervals):\")\n", "for i, t in enumerate(model.event_times):\n", " if i > 0:\n", " gap = t - model.event_times[i - 1]\n", " print(f\" t={t:.1f} (gap: {gap:.1f})\")\n", " else:\n", " print(f\" t={t:.1f}\")" ], "id": "9b249ec9cf1034a" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Event Priority\n", "When multiple events are scheduled for the same time, **priority** determines execution\n", "order. Mesa provides three priority levels:\n", "- `Priority.HIGH` (1) — Executes first\n", "- `Priority.DEFAULT` (5) — Normal priority\n", "- `Priority.LOW` (10) — Executes last\n", "Lower numeric values mean higher priority. Note that the default model `step()` is\n", "scheduled at `Priority.HIGH`, so it runs before your custom events at the same time." ], "id": "cd732e2be342cd5" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class PriorityDemo(mesa.Model):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " # Schedule three events at the same time with different priorities\n", " self.schedule_event(\n", " self.low_priority_event,\n", " at=2.0,\n", " priority=Priority.LOW,\n", " )\n", " self.schedule_event(\n", " self.high_priority_event,\n", " at=2.0,\n", " priority=Priority.HIGH,\n", " )\n", " self.schedule_event(\n", " self.default_priority_event,\n", " at=2.0,\n", " priority=Priority.DEFAULT,\n", " )\n", "\n", " def low_priority_event(self):\n", " print(f\" LOW priority event at t={self.time:.1f}\")\n", "\n", " def high_priority_event(self):\n", " print(f\" HIGH priority event at t={self.time:.1f}\")\n", "\n", " def default_priority_event(self):\n", " print(f\" DEFAULT priority event at t={self.time:.1f}\")\n", "\n", " def step(self):\n", " if self.time == 2.0:\n", " print(f\" Model step (HIGH priority) at t={self.time:.1f}\")\n", "\n", "\n", "model = PriorityDemo()\n", "print(\"Events at t=2.0 in execution order:\")\n", "model.run_for(3)" ], "id": "b043452777b15d9a" }, { "metadata": {}, "cell_type": "markdown", "source": [ "Priority is useful when the order of operations matters. For example, you might want\n", "data collection (HIGH) to happen before agent actions (DEFAULT), or environmental\n", "updates (LOW) to happen after everything else." ], "id": "11a754b25470453e" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Putting It All Together: A Complete Example\n", "Let's build a more complete model that combines step-based agent activation with\n", "discrete events. This is a simple economy where:\n", "- Agents exchange money every step (standard activation)\n", "- A central bank adjusts interest rates every 10 time units (recurring event)\n", "- A one-time economic stimulus happens at t=25 (one-off event)\n", "- The simulation runs until t=50" ], "id": "adb73fe7876ff2bc" }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": [ "class Citizen(mesa.Agent):\n", " def __init__(self, model):\n", " super().__init__(model)\n", " self.wealth = 10\n", " self.savings = 0\n", "\n", " def exchange(self):\n", " \"\"\"Give 1 unit to a random other agent.\"\"\"\n", " if self.wealth > 0:\n", " other = self.random.choice(self.model.agents)\n", " other.wealth += 1\n", " self.wealth -= 1\n", "\n", " def earn_interest(self):\n", " \"\"\"Earn interest on savings based on current rate.\"\"\"\n", " interest = int(self.savings * self.model.interest_rate)\n", " self.savings += interest\n", "\n", "\n", "class CentralBankModel(mesa.Model):\n", " \"\"\"An economy with monetary policy events.\"\"\"\n", "\n", " def __init__(self, n_citizens=50):\n", " super().__init__()\n", " self.interest_rate = 0.05\n", " self.log = []\n", "\n", " Citizen.create_agents(model=self, n=n_citizens)\n", "\n", " # Distribute initial savings randomly\n", " for agent in self.agents:\n", " agent.savings = self.random.randint(0, 20)\n", "\n", " # Recurring: Central bank reviews interest rate every 10 time units\n", " self.rate_review = self.schedule_recurring(\n", " self.review_interest_rate,\n", " Schedule(interval=10.0, start=10.0),\n", " )\n", "\n", " # Recurring: Interest is paid every 5 time units\n", " self.schedule_recurring(\n", " self.pay_interest,\n", " Schedule(interval=5.0),\n", " )\n", "\n", " # One-off: Economic stimulus at t=25\n", " self.schedule_event(self.economic_stimulus, at=25.0)\n", "\n", " def review_interest_rate(self):\n", " \"\"\"Central bank adjusts rate based on average wealth.\"\"\"\n", " avg_wealth = self.agents.agg(\"wealth\", np.mean)\n", " if avg_wealth < 8:\n", " self.interest_rate = min(0.15, self.interest_rate + 0.02)\n", " action = \"raised\"\n", " elif avg_wealth > 12:\n", " self.interest_rate = max(0.01, self.interest_rate - 0.02)\n", " action = \"lowered\"\n", " else:\n", " action = \"held\"\n", " self.log.append(\n", " f\"t={self.time:5.1f} | Rate review: {action} to {self.interest_rate:.0%} \"\n", " f\"(avg wealth: {avg_wealth:.1f})\"\n", " )\n", "\n", " def pay_interest(self):\n", " \"\"\"Pay interest to all citizens.\"\"\"\n", " total_paid = 0\n", " for agent in self.agents:\n", " interest = int(agent.savings * self.interest_rate)\n", " agent.savings += interest\n", " total_paid += interest\n", " self.log.append(f\"t={self.time:5.1f} | Interest paid: {total_paid} total\")\n", "\n", " def economic_stimulus(self):\n", " \"\"\"One-time stimulus: every citizen gets 5 units.\"\"\"\n", " for agent in self.agents:\n", " agent.wealth += 5\n", " self.log.append(f\"t={self.time:5.1f} | *** STIMULUS: +5 to all citizens ***\")\n", "\n", " def step(self):\n", " \"\"\"Regular step: agents exchange money.\"\"\"\n", " self.agents.shuffle_do(\"exchange\")\n", "\n", " # Some agents save a portion of their wealth\n", " for agent in self.agents.select(lambda a: a.wealth > 3):\n", " save_amount = agent.wealth // 4\n", " agent.wealth -= save_amount\n", " agent.savings += save_amount\n", "\n", "\n", "# Run the simulation\n", "model = CentralBankModel(50)\n", "model.run_until(50)\n", "\n", "print(\"=== Central Bank Economy: Event Log ===\\n\")\n", "for entry in model.log:\n", " print(f\" {entry}\")\n", "\n", "print(f\"\\n=== Final State (t={model.time:.0f}, step {model.steps}) ===\")\n", "print(f\"Interest rate: {model.interest_rate:.0%}\")\n", "print(f\"Avg wealth: {model.agents.agg('wealth', np.mean):.1f}\")\n", "print(f\"Avg savings: {model.agents.agg('savings', np.mean):.1f}\")\n", "total = sum(a.wealth + a.savings for a in model.agents)\n", "print(f\"Total money in economy: {total}\")" ], "id": "d32dd6f6930ef868" }, { "metadata": {}, "cell_type": "markdown", "source": [ "This model demonstrates the core pattern of Mesa 3.5: the `step()` method handles\n", "regular per-step agent activation, while `schedule_event` and `schedule_recurring`\n", "handle things that happen at specific times or on different schedules. The event system\n", "manages all timing automatically — you just specify *what* should happen and *when*." ], "id": "55fa66e5b8f70922" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## When to Use Events vs Steps\n", "| Use case | Approach |\n", "|---|---|\n", "| Agents act every time unit | `step()` with `agents.shuffle_do()` |\n", "| Something happens once at a known time | `schedule_event(fn, at=...)` |\n", "| Something happens repeatedly on a schedule | `schedule_recurring(fn, Schedule(...))` |\n", "| Something happens after a delay from now | `schedule_event(fn, after=...)` |\n", "| Different processes run at different frequencies | Combine step + recurring events |\n", "| Pure discrete-event simulation (no regular steps) | Use only `schedule_event` / `schedule_recurring` |\n", "The step mechanism itself is implemented as a recurring event under the hood (with\n", "`Priority.HIGH`, interval 1.0, starting at t=1.0). This means steps and custom events\n", "coexist naturally in the same time-ordered event queue." ], "id": "4e7c9285c0e8c40b" }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Summary\n", "**Running models:**\n", "- `model.run_for(n)` — Advance time by `n` units\n", "- `model.run_until(t)` — Advance time to absolute time `t`\n", "**One-off events:**\n", "- `model.schedule_event(fn, at=t)` — Fire at absolute time `t`\n", "- `model.schedule_event(fn, after=d)` — Fire `d` time units from now\n", "- `event.cancel()` — Cancel before it fires\n", "**Recurring events:**\n", "- `model.schedule_recurring(fn, Schedule(interval=...))` — Repeat on a schedule\n", "- `Schedule(interval, start, end, count)` — Control timing, limits\n", "- `generator.stop()` — Stop a recurring event\n", "- `generator.is_active` — Check if still running\n", "**Priority:** `Priority.HIGH` → `Priority.DEFAULT` → `Priority.LOW`\n", "**Time tracking:** `model.steps` (integer count) and `model.time` (float)" ], "id": "d1c2c4bd660d1f53" }, { "cell_type": "markdown", "id": "e091daf1b0315256", "metadata": {}, "source": [ "## Next Steps\n", "\n", "Check out the [adding space tutorial](4_adding_space.ipynb) on how to add a space to your Mesa model." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }