Skip to content
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

Add value setter for simple logged quantities. #69

Closed
wants to merge 11 commits into from
29 changes: 27 additions & 2 deletions examples/log-mpi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3

from time import sleep
from typing import Any
from random import uniform
from logpyle import (LogManager, add_general_quantities,
add_simulation_quantities, add_run_info, IntervalTimer,
Expand All @@ -10,6 +11,26 @@
from mpi4py import MPI


class PushLogQuantity(LogQuantity):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than duplicating it, please move this somewhere and import from there.

"""Logging support for arbitrary user quantities."""
MTCam marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, name, value=None, unit=None,
description=None) -> None:
LogQuantity.__init__(self, name=name, unit=unit,
description=description)
self._quantity_value = value

def __call__(self) -> float:
"""Return the actual logged quantity."""
val = self._quantity_value
self._quantity_value = None
return val

def set_quantity_value(self, value: Any) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming? I think set_value would suffice.

"""Set the value of the logged quantity."""
self._quantity_value = value


class Fifteen(LogQuantity):
@property
def default_aggregator(self):
Expand Down Expand Up @@ -43,14 +64,18 @@ def main():
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
logmgr.add_quantity(Fifteen("fifteen"))
logmgr.add_quantity(PushLogQuantity("q1"))

# Watches are printed periodically during execution
logmgr.add_watches([("step.max", "step={value} "),
("t_step.min", "\nt_step({value:g},"), ("t_step.max", " {value:g})\n"),
"t_sim.max", "fifteen", "t_vis.max"])
("t_step.min", "\nt_step({value:g},"),
("t_step.max", " {value:g})\n"),
("q1.max", " UserQ1:({value:g}), "),
"t_sim.max", "fifteen", "t_vis.max"])

for istep in range(200):
logmgr.tick_before()
logmgr.set_quantity_value("q1", 2*istep)

dt = uniform(0.01, 0.1)
set_dt(logmgr, dt)
Expand Down
23 changes: 22 additions & 1 deletion examples/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,29 @@
from logpyle import (LogManager, add_general_quantities,
add_simulation_quantities, add_run_info, IntervalTimer,
LogQuantity, set_dt)
from typing import Any

from warnings import warn


class PushLogQuantity(LogQuantity):
"""Logging support for arbitrary user quantities."""

def __init__(self, name, value=None, unit=None,
description=None) -> None:
LogQuantity.__init__(self, name=name, unit=unit,
description=description)
self._quantity_value = value

def __call__(self) -> float:
"""Return the actual logged quantity."""
return self._quantity_value

def set_quantity_value(self, value: Any) -> None:
"""Set the value of the logged quantity."""
self._quantity_value = value


class Fifteen(LogQuantity):
def __call__(self):
return 15
Expand All @@ -33,13 +52,15 @@ def main():
vis_timer = IntervalTimer("t_vis", "Time spent visualizing")
logmgr.add_quantity(vis_timer)
logmgr.add_quantity(Fifteen("fifteen"))
logmgr.add_quantity(PushLogQuantity("q1", value=0))

# Watches are printed periodically during execution
logmgr.add_watches(["step.max", "t_sim.max", "t_step.max", "fifteen",
"t_vis", "t_log"])
"t_vis", "t_log", "q1"])

for istep in range(200):
logmgr.tick_before()
logmgr.set_quantity_value("q1", 2*istep + 1)

dt = uniform(0.01, 0.1)
set_dt(logmgr, dt)
Expand Down
33 changes: 28 additions & 5 deletions logpyle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class LogQuantity:
.. automethod:: tick
.. autoproperty:: default_aggregator
.. automethod:: __call__
.. automethod:: set_quantity_value
"""

sort_weight = 0
Expand Down Expand Up @@ -136,6 +137,10 @@ def __call__(self) -> Optional[float]:
"""
raise NotImplementedError

def set_quantity_value(self, value: Any) -> None:
"""Set the logged quantity value."""
raise NotImplementedError


class PostLogQuantity(LogQuantity):
"""A source of a loggable scalar that is gathered after each time step.
Expand Down Expand Up @@ -289,10 +294,12 @@ def __init__(self, quantity: LogQuantity, interval: int) -> None:

class _QuantityData:
def __init__(self, unit: str, description: str,
default_aggregator: Callable) -> None:
default_aggregator: Callable,
value_setter: Callable = None) -> None:
self.unit = unit
self.description = description
self.default_aggregator = default_aggregator
self.value_setter = value_setter


def _join_by_first_of_tuple(list_of_iterables):
Expand Down Expand Up @@ -741,6 +748,20 @@ def set_constant(self, name: str, value: Any) -> None:

self._commit()

def set_quantity_value(self, name: str, value: Any) -> None:
"""Set a the value of a named LogQuantity.

:param name: the name of the logged quantity.
:param value: the value of the logged quantity.
"""
if name in self.quantity_data:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just access it and handle the KeyError.

value_setter = self.quantity_data[name].value_setter
if value_setter:
value_setter(value)
else:
from warnings import warn
warn(f"No value_setter defined for log quantity (name={name}).")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be an error.


Comment on lines +751 to +764
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this a function instead.

def _insert_datapoint(self, name: str, value: Optional[float]) -> None:
if value is None:
return
Expand Down Expand Up @@ -867,12 +888,13 @@ def add_quantity(self, quantity: LogQuantity, interval: int = 1) -> None:
:param interval: interval (in time steps) when to gather this quantity.
"""

def add_internal(name, unit, description, def_agg):
def add_internal(name, unit, description, def_agg, val_set=None):
logger.debug("add log quantity '%s'" % name)

if name in self.quantity_data:
raise RuntimeError("cannot add the same quantity '%s' twice" % name)
self.quantity_data[name] = _QuantityData(unit, description, def_agg)
self.quantity_data[name] = _QuantityData(unit, description, def_agg,
val_set)

from pickle import dumps
self.db_conn.execute("""insert into quantities values (?,?,?,?)""", (
Expand Down Expand Up @@ -901,8 +923,9 @@ def add_internal(name, unit, description, def_agg):
add_internal(name, unit, description, def_agg)
else:
add_internal(quantity.name,
quantity.unit, quantity.description,
quantity.default_aggregator)
quantity.unit, quantity.description,
quantity.default_aggregator,
quantity.set_quantity_value)

def get_expr_dataset(self, expression, description=None, unit=None):
"""Prepare a time-series dataset for a given expression.
Expand Down