From 0627fde6183272a27bf566f76fdfb55548dede52 Mon Sep 17 00:00:00 2001 From: Tyler Coles Date: Thu, 23 May 2024 09:48:41 -0700 Subject: [PATCH] Time-keeping removed from context. --- epymorph/engine/context.py | 28 +--------------- epymorph/engine/mm_exec.py | 4 +-- epymorph/engine/standard_sim.py | 4 +-- epymorph/movement/movement_model.py | 4 --- epymorph/simulation.py | 32 +++++++++++++++---- .../simulation_test.py} | 6 ++-- 6 files changed, 32 insertions(+), 46 deletions(-) rename epymorph/{engine/test/context_test.py => test/simulation_test.py} (86%) diff --git a/epymorph/engine/context.py b/epymorph/engine/context.py index 20c941e0..a375257f 100644 --- a/epymorph/engine/context.py +++ b/epymorph/engine/context.py @@ -3,10 +3,6 @@ with simulation data, for example, accessing geo and parameter attributes, calculating the simulation clock, initializing the world state, and so on. """ -from datetime import timedelta -from functools import cached_property -from typing import Iterable - import numpy as np from numpy.typing import NDArray @@ -15,7 +11,7 @@ from epymorph.geo.geo import Geo from epymorph.params import NormalizedParams, NormalizedParamsDict from epymorph.simulation import (CachingGetAttributeMixin, GeoData, - SimDimensions, Tick, TickDelta) + SimDimensions) class RumeContext(CachingGetAttributeMixin): @@ -47,15 +43,6 @@ def __init__( self._params = params CachingGetAttributeMixin.__init__(self, geo, params, dim) - def clock(self) -> Iterable[Tick]: - """Generate the simulation clock signal: a series of Tick objects describing each time step.""" - return _simulation_clock(self.dim) - - def resolve_tick(self, tick: Tick, delta: TickDelta) -> int: - """Add a delta to a tick to get the index of the resulting tick.""" - return -1 if delta.days == -1 else \ - tick.index - tick.step + (self.dim.tau_steps * delta.days) + delta.step - def update_param(self, attr_name: str, value: AttributeArray) -> None: """Updates a params value.""" self._params[attr_name] = value.copy() @@ -79,16 +66,3 @@ def compartment_mobility(self) -> NDArray[np.bool_]: def params(self) -> NormalizedParams: """The params values.""" return self._params - - -def _simulation_clock(dim: SimDimensions) -> Iterable[Tick]: - """Generator for the sequence of ticks which makes up the simulation clock.""" - one_day = timedelta(days=1) - tau_steps = list(enumerate(dim.tau_step_lengths)) - curr_index = 0 - curr_date = dim.start_date - for day in range(dim.days): - for step, tau in tau_steps: - yield Tick(curr_index, day, curr_date, step, tau) - curr_index += 1 - curr_date += one_day diff --git a/epymorph/engine/mm_exec.py b/epymorph/engine/mm_exec.py index 9fcd016b..278e1fc8 100644 --- a/epymorph/engine/mm_exec.py +++ b/epymorph/engine/mm_exec.py @@ -15,7 +15,7 @@ from epymorph.movement.movement_model import (MovementContext, MovementModel, PredefData, TravelClause) from epymorph.movement.parser import MovementSpec -from epymorph.simulation import Tick +from epymorph.simulation import Tick, resolve_tick from epymorph.util import row_normalize @@ -173,7 +173,7 @@ def apply(self, world: World, tick: Tick) -> None: travelers = clause_event.actual returns = clause.returns(self._ctx, tick) - return_tick = self._ctx.resolve_tick(tick, returns) + return_tick = resolve_tick(self._ctx.dim, tick, returns) world.apply_travel(travelers, return_tick) total += travelers.sum() diff --git a/epymorph/engine/standard_sim.py b/epymorph/engine/standard_sim.py index c76a3d21..cfbb8756 100644 --- a/epymorph/engine/standard_sim.py +++ b/epymorph/engine/standard_sim.py @@ -24,7 +24,7 @@ from epymorph.movement.movement_model import MovementModel, validate_mm from epymorph.movement.parser import MovementSpec from epymorph.params import NormalizedParamsDict, RawParams, normalize_params -from epymorph.simulation import TimeFrame +from epymorph.simulation import TimeFrame, simulation_clock from epymorph.util import Subscriber @@ -157,7 +157,7 @@ def run(self) -> Output: self.on_start.publish(OnStart(dim=ctx.dim, time_frame=self.time_frame)) - for tick in ctx.clock(): + for tick in simulation_clock(self.dim): # First do movement with error_gate("executing the movement model", MmSimException, AttributeException): movement_exec.apply(world, tick) diff --git a/epymorph/movement/movement_model.py b/epymorph/movement/movement_model.py index 096cf64b..1b4f8087 100644 --- a/epymorph/movement/movement_model.py +++ b/epymorph/movement/movement_model.py @@ -54,10 +54,6 @@ def version(self) -> int: """ raise NotImplementedError - @abstractmethod - def resolve_tick(self, tick: Tick, delta: TickDelta) -> int: - """Add a delta to a tick to get the index of the resulting tick.""" - PredefData = ParamsData PredefClause = Callable[[MovementContext], PredefData] diff --git a/epymorph/simulation.py b/epymorph/simulation.py index 52930d75..4879045c 100644 --- a/epymorph/simulation.py +++ b/epymorph/simulation.py @@ -3,24 +3,23 @@ import textwrap from abc import abstractmethod from dataclasses import dataclass, field -from datetime import date -from functools import partial +from datetime import date, timedelta from importlib import reload -from typing import Any, Callable, Literal, Mapping, NamedTuple, Protocol, Self +from typing import (Callable, Iterable, Literal, Mapping, NamedTuple, Protocol, + Self) import numpy as np import sympy from numpy.random import SeedSequence -from epymorph.code import ImmutableNamespace, base_namespace from epymorph.data_shape import (AttributeGetter, DataShape, Shapes, SimDimensions) from epymorph.data_type import (AttributeArray, AttributeScalar, DataDType, - DataPyScalar, SimDType, dtype_as_np, - dtype_check, dtype_str) + DataPyScalar, dtype_as_np, dtype_check, + dtype_str) from epymorph.error import AttributeException from epymorph.sympy_shim import to_symbol -from epymorph.util import MemoDict, pairwise_haversine, row_normalize +from epymorph.util import MemoDict class DataSource(Protocol): @@ -186,6 +185,25 @@ class TickDelta(NamedTuple): """ +def resolve_tick(dim: SimDimensions, tick: Tick, delta: TickDelta) -> int: + """Add a delta to a tick to get the index of the resulting tick.""" + return -1 if delta.days == -1 else \ + tick.index - tick.step + (dim.tau_steps * delta.days) + delta.step + + +def simulation_clock(dim: SimDimensions) -> Iterable[Tick]: + """Generator for the sequence of ticks which makes up the simulation clock.""" + one_day = timedelta(days=1) + tau_steps = list(enumerate(dim.tau_step_lengths)) + curr_index = 0 + curr_date = dim.start_date + for day in range(dim.days): + for step, tau in tau_steps: + yield Tick(curr_index, day, curr_date, step, tau) + curr_index += 1 + curr_date += one_day + + class CachingGetAttributeMixin: """ A mixin for adding cached attribute getter behavior to a context class. diff --git a/epymorph/engine/test/context_test.py b/epymorph/test/simulation_test.py similarity index 86% rename from epymorph/engine/test/context_test.py rename to epymorph/test/simulation_test.py index 30b231de..b2e958d1 100644 --- a/epymorph/engine/test/context_test.py +++ b/epymorph/test/simulation_test.py @@ -2,9 +2,7 @@ import unittest from datetime import date -from epymorph.data_shape import SimDimensions -from epymorph.engine.context import _simulation_clock -from epymorph.simulation import Tick +from epymorph.simulation import SimDimensions, Tick, simulation_clock class TestClock(unittest.TestCase): @@ -16,7 +14,7 @@ def test_clock(self): # sim clock doesn't depend on GEO/IPM dimensions nodes=99, compartments=99, events=99, ) - clock = _simulation_clock(dim) + clock = simulation_clock(dim) act = list(clock) exp = [ Tick(0, 0, date(2023, 1, 1), 0, 2 / 3),