Skip to content

Commit

Permalink
AdrioProgress events include attribute name. (#225)
Browse files Browse the repository at this point in the history
Related refactoring.
  • Loading branch information
JavadocMD authored Jan 10, 2025
1 parent e92d05e commit 22e487f
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 35 deletions.
14 changes: 7 additions & 7 deletions USAGE.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Loading epymorph.adrio.commuting_flows.Commuters:\n",
" |####################| 100% (9.727s)\n",
"Loading epymorph.adrio.acs5.Population:\n",
" |####################| 100% (1.296s)\n",
"Loading epymorph.adrio.us_tiger.PostalCode:\n",
" |####################| 100% (0.171s)\n",
"Loading gpm:all::mm::commuters (epymorph.adrio.commuting_flows.Commuters):\n",
" |####################| 100% (9.563s)\n",
"Loading gpm:all::init::population (epymorph.adrio.acs5.Population):\n",
" |####################| 100% (1.101s)\n",
"Loading meta::geo::label (epymorph.adrio.us_tiger.PostalCode):\n",
" |####################| 100% (0.173s)\n",
"Running simulation (BasicSimulator):\n",
"• 2015-01-01 to 2015-05-30 (150 days)\n",
"• 6 geo nodes\n",
" |####################| 100% \n",
"Runtime: 0.313s\n"
"Runtime: 0.336s\n"
]
}
],
Expand Down
26 changes: 12 additions & 14 deletions epymorph/adrio/adrio.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,16 @@ class Adrio(SimulationFunction[NDArray[ResultDType]]):
web APIs, local files or database, or anything imaginable.
"""

@property
def full_name(self) -> str:
return f"{self.__class__.__module__}.{self.__class__.__qualname__}"

def estimate_data(self) -> DataEstimate:
"""Estimate the data usage of this ADRIO in a RUME.
If a reasonable estimate cannot be made, None is returned."""
return EmptyDataEstimate(self.full_name)
"""Estimate the data usage for this ADRIO in a RUME.
If a reasonable estimate cannot be made, return EmptyDataEstimate."""
return EmptyDataEstimate(self.class_name)

@abstractmethod
def evaluate_adrio(self) -> NDArray[ResultDType]:
"""
Implement this method to provide logic for the function.
Your implementation is free to use `data`, `dim`, and `rng`.
You can also use `defer` to utilize another SimulationFunction instance.
"""Implement this method to provide logic for the function.
Use self methods and properties to access the simulation context or defer
processing to another function.
"""

@override
Expand All @@ -65,7 +60,8 @@ def evaluate(self) -> NDArray[ResultDType]:
functionality. ADRIO implementations should override `evaluate_adrio`."""
_events.on_adrio_progress.publish(
AdrioProgress(
adrio_name=self.full_name,
adrio_name=self.class_name,
attribute=self.name,
final=False,
ratio_complete=0,
download=None,
Expand All @@ -77,7 +73,8 @@ def evaluate(self) -> NDArray[ResultDType]:
t1 = perf_counter()
_events.on_adrio_progress.publish(
AdrioProgress(
adrio_name=self.full_name,
adrio_name=self.class_name,
attribute=self.name,
final=True,
ratio_complete=1,
download=None,
Expand All @@ -95,7 +92,8 @@ def progress(
"""Emit a progress event."""
_events.on_adrio_progress.publish(
AdrioProgress(
adrio_name=self.full_name,
adrio_name=self.class_name,
attribute=self.name,
final=False,
ratio_complete=ratio_complete,
download=download,
Expand Down
2 changes: 1 addition & 1 deletion epymorph/adrio/commuting_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def estimate_data(self) -> DataEstimate:

key = f"commflows:{year}"
return AvailableDataEstimate(
name=self.full_name,
name=self.class_name,
cache_key=key,
new_network_bytes=est.missing_cache_size,
new_cache_bytes=est.missing_cache_size,
Expand Down
2 changes: 1 addition & 1 deletion epymorph/adrio/us_tiger.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def estimate_data(self) -> DataEstimate:
)
key = f"us_tiger:{scope.granularity}:{year}"
return AvailableDataEstimate(
name=self.full_name,
name=self.class_name,
cache_key=key,
new_network_bytes=est.missing_cache_size,
new_cache_bytes=est.missing_cache_size,
Expand Down
11 changes: 10 additions & 1 deletion epymorph/data_usage.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from abc import abstractmethod
from dataclasses import dataclass
from functools import partial
from math import floor, inf
from pathlib import Path
from shutil import disk_usage
from typing import Sequence
from typing import Protocol, Sequence, runtime_checkable

from humanize import naturaldelta, naturalsize

Expand Down Expand Up @@ -55,6 +56,14 @@ class AvailableDataEstimate:
DataEstimate = EmptyDataEstimate | AvailableDataEstimate


@runtime_checkable
class CanEstimateData(Protocol):
@abstractmethod
def estimate_data(self) -> DataEstimate:
"""Estimate the data usage for this entity.
If a reasonable estimate cannot be made, return EmptyDataEstimate."""


@dataclass(frozen=True)
class DataEstimateTotal:
new_network_bytes: int
Expand Down
7 changes: 4 additions & 3 deletions epymorph/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from numpy.typing import NDArray

from epymorph.attribute import AbsoluteName
from epymorph.data_type import SimDType
from epymorph.rume import Rume
from epymorph.util import Event, Singleton
Expand Down Expand Up @@ -125,9 +126,9 @@ class AdrioProgress(NamedTuple):
to report as many intermediate progress events as they like."""

adrio_name: str
"""The name of the ADRIO."""
# attribute: AbsoluteName # TODO: see if we can get AbsoluteName here...
# """The name of the attribute."""
"""The full name of the ADRIO class."""
attribute: AbsoluteName
"""The name of the attribute being evaluated."""
final: bool
"""Is this the last progress update for this ADRIO?"""
ratio_complete: float
Expand Down
2 changes: 1 addition & 1 deletion epymorph/log/messaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def on_finish(_: None) -> None:
def on_adrio_progress(e: AdrioProgress) -> None:
nonlocal last_progress_length
if e.ratio_complete == 0:
print(f"Loading {e.adrio_name}:")
print(f"Loading {e.attribute} ({e.adrio_name}):")
if not e.final:
if e.download is None:
dl = ""
Expand Down
2 changes: 1 addition & 1 deletion epymorph/movement_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def is_active(self, tick: Tick) -> bool:
return self.leaves.step == tick.step and self.predicate.evaluate(tick)

@property
def name(self) -> str:
def clause_name(self) -> str:
return self.__class__.__name__

@abstractmethod
Expand Down
7 changes: 3 additions & 4 deletions epymorph/rume.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
)
from epymorph.data_shape import Shapes
from epymorph.data_type import SimArray, dtype_str
from epymorph.data_usage import estimate_report
from epymorph.data_usage import CanEstimateData, estimate_report
from epymorph.database import (
Database,
DatabaseWithFallback,
Expand Down Expand Up @@ -429,10 +429,9 @@ def estimate_data(
scope=self.scope,
time_frame=self.time_frame,
ipm=self.ipm,
).estimate_data() # type: ignore
).estimate_data()
for p in self.params.values()
if isinstance(p, SimulationFunction) and hasattr(p, "estimate_data")
# TODO: this is a bit of a hack to avoid importing Adrio here...
if isinstance(p, SimulationFunction) and isinstance(p, CanEstimateData)
]

lines = list[str]()
Expand Down
11 changes: 11 additions & 0 deletions epymorph/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,11 @@ class BaseSimulationFunction(ABC, Generic[ResultT], metaclass=SimulationFunction

_ctx: _FullContext | _PartialContext = _EMPTY_CONTEXT

@property
def class_name(self) -> str:
"""The class name of the SimulationFunction."""
return f"{self.__class__.__module__}.{self.__class__.__qualname__}"

def validate(self, result: ResultT) -> None:
"""Override this method to validate the evaluation result.
Implementations should raise an appropriate error if results
Expand Down Expand Up @@ -567,6 +572,12 @@ def defer_context(
rng=self._ctx._rng,
)

@final
@property
def name(self) -> AbsoluteName:
"""The name under which this attribute is being evaluated."""
return self._ctx.name

@final
def data(self, attribute: AttributeDef | str) -> NDArray:
"""Retrieve the value of a specific attribute."""
Expand Down
4 changes: 2 additions & 2 deletions epymorph/simulator/basic/mm_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def __init__(
namespace = ModuleNamespace(gpm_strata(strata), "mm")
for clause in model.clauses:
c = clause.with_context_internal(
namespace.to_absolute(clause.name),
namespace.to_absolute(clause.clause_name),
data,
rume.scope,
rume.time_frame,
Expand Down Expand Up @@ -155,7 +155,7 @@ def apply(self, tick: Tick) -> None:

available_movers = self._world.get_local_array()
clause_event = calculate_travelers(
clause.name,
clause.clause_name,
self._rume.compartment_mobility[strata],
requested_movers,
available_movers,
Expand Down

0 comments on commit 22e487f

Please sign in to comment.