Skip to content

Commit

Permalink
Tidy up.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyler Coles committed Mar 14, 2024
1 parent 509e215 commit 544d1d4
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 27 deletions.
3 changes: 2 additions & 1 deletion epymorph/engine/mm_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class StandardMovementExecutor(MovementEventsMixin, MovementExecutor):
_predef_hash: int | None = None

def __init__(self, ctx: RumeContext):
super().__init__()
MovementEventsMixin.__init__(self)

# If we were given a MovementSpec, we need to compile it to get its clauses.
if isinstance(ctx.mm, MovementSpec):
self._model = compile_spec(ctx.mm, ctx.rng)
Expand Down
2 changes: 1 addition & 1 deletion epymorph/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class SimulationEvents(Protocol):
"""
Protocol for Simulations that support lifecycle events.
For correct operation, ensure that `on_start` is fired first,
then `on_tick` at least once, then finally `on_end`.
then `on_tick` any number of times, then finally `on_end`.
"""

on_start: Event[OnStart]
Expand Down
12 changes: 8 additions & 4 deletions epymorph/log/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def file_log(
) -> Generator[None, None, None]:
"""Attach file logging to a simulation."""

# Initialize the logging system and create some Loggers for epymorph subsystems.
log_handler = FileHandler(log_file, "w", "utf8")
log_handler.setFormatter(Formatter(BASIC_FORMAT))

Expand Down Expand Up @@ -80,15 +81,14 @@ def on_movement_finish(e: OnMovementFinish) -> None:
with subscriptions() as subs:
# Simulation logging
subs.subscribe(sim.on_start, on_start)
if sim.on_tick is not None:
subs.subscribe(sim.on_tick, on_tick)
subs.subscribe(sim.on_tick, on_tick)
subs.subscribe(sim.on_end, on_end)

# Geo logging will be attached if it makes sense.
sim_geo = getattr(sim, 'geo', None)
if isinstance(sim_geo, DynamicGeoEvents):
geo_log.info(
"Geo not loaded from cache; attributes will be lazily loaded during simulation run.")
geo_log.info("Geo not loaded from cache; "
"attributes will be lazily loaded during simulation run.")
subs.subscribe(sim_geo.adrio_start, adrio_start)

# Movement logging
Expand All @@ -98,5 +98,9 @@ def on_movement_finish(e: OnMovementFinish) -> None:

yield # to outer context

# Close out the log file.
# This isn't necessary if we're running on the CLI, but if we're in a Jupyter context,
# running the sim multiple times would keep appending to the file.
# For most use-cases, just having one sim run in the log file is preferable.
epy_log.removeHandler(log_handler)
epy_log.setLevel(NOTSET)
35 changes: 14 additions & 21 deletions epymorph/log/messaging.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Contexts which provide console messaging for epymorph processes like simulation runs and geo fetching.
It's nice to have some console output to show progress during long running tasks!
"""
from contextlib import contextmanager
from time import perf_counter
from typing import Generator
Expand All @@ -10,15 +14,12 @@
@contextmanager
def sim_messaging(sim: SimulationEvents, geo_messaging=False) -> Generator[None, None, None]:
"""
Attach fancy console messaging to a Simulation, e.g., a progress bar.
This creates subscriptions on `sim`'s events, so you only need to do it once
per sim. Returns `sim` as a convenience.
Attach fancy console messaging to a Simulation such as a progress bar.
If `geo_messaging` is true, provide verbose messaging about geo operations
(if applicable, e.g., when fetching external data).
"""

start_time: float | None = None
use_progress_bar = sim.on_tick is not None

# If geo_messaging is true, the user has requested verbose messaging re: geo operations.
# However we don't want to make a strong assertion that a sim has a geo, nor what type that geo is.
Expand All @@ -30,9 +31,6 @@ def sim_messaging(sim: SimulationEvents, geo_messaging=False) -> Generator[None,
if hasattr(sim, 'geo'):
sim_geo = getattr(sim, 'geo')

if geo_messaging and isinstance(sim_geo, DynamicGeoEvents):
print("Geo not loaded from cache; attributes will be lazily loaded during simulation run.")

def on_start(ctx: OnStart) -> None:
start_date = ctx.time_frame.start_date
duration_days = ctx.time_frame.duration_days
Expand All @@ -41,38 +39,33 @@ def on_start(ctx: OnStart) -> None:
print(f"Running simulation ({sim.__class__.__name__}):")
print(f"• {start_date} to {end_date} ({duration_days} days)")
print(f"• {ctx.dim.nodes} geo nodes")
if use_progress_bar:
print(progress(0.0), end='\r')
else:
print('Running...')
print(progress(0.0), end='\r')

nonlocal start_time
start_time = perf_counter()

def on_tick(tick: OnTick) -> None:
print(progress(tick.percent_complete), end='\r')

def adrio_start(adrio: AdrioStart) -> None:
print(f"Uncached geo attribute requested: {adrio.attribute}. Retreiving now...")

def on_end(_: None) -> None:
end_time = perf_counter()
if use_progress_bar:
print(progress(1.0))
else:
print('Complete.')
print(progress(1.0))
if start_time is not None:
print(f"Runtime: {(end_time - start_time):.3f}s")

def adrio_start(adrio: AdrioStart) -> None:
print(f"Uncached geo attribute requested: {adrio.attribute}. Retreiving now...")

# Set up a subscriptions context, subscribe our handlers,
# then yield to the outer context (ostensibly where the sim will be run).
with subscriptions() as subs:
subs.subscribe(sim.on_start, on_start)
if sim.on_tick is not None:
subs.subscribe(sim.on_tick, on_tick)
subs.subscribe(sim.on_tick, on_tick)
subs.subscribe(sim.on_end, on_end)
if geo_messaging and isinstance(sim_geo, DynamicGeoEvents):
print("Geo not loaded from cache; "
"attributes will be lazily loaded during simulation run.")
subs.subscribe(sim_geo.adrio_start, adrio_start)
subs.subscribe(sim.on_end, on_end)
yield # to outer context


Expand Down

0 comments on commit 544d1d4

Please sign in to comment.