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

Update PushLogQuantity and add value setter for user-defined logging scalars #74

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions examples/log-mpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from random import uniform
from logpyle import (LogManager, add_general_quantities,
add_simulation_quantities, add_run_info, IntervalTimer,
LogQuantity, set_dt)
LogQuantity, set_dt, PushLogQuantity, set_quantity_value)

from warnings import warn
from mpi4py import MPI
Expand Down Expand Up @@ -43,14 +43,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()
set_quantity_value(logmgr, "q1", 2*istep)

dt = uniform(0.01, 0.1)
set_dt(logmgr, dt)
Expand Down
7 changes: 5 additions & 2 deletions examples/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from random import uniform
from logpyle import (LogManager, add_general_quantities,
add_simulation_quantities, add_run_info, IntervalTimer,
LogQuantity, set_dt)
LogQuantity, set_dt, PushLogQuantity, set_quantity_value)

from warnings import warn

Expand Down Expand Up @@ -33,13 +33,16 @@ 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"))
set_quantity_value(logmgr, "q1", -99)

# 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()
set_quantity_value(logmgr, "q1", 2*istep + 1)

dt = uniform(0.01, 0.1)
set_dt(logmgr, dt)
Expand Down
52 changes: 42 additions & 10 deletions logpyle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

.. autoclass:: LogQuantity
.. autoclass:: PostLogQuantity
.. autoclass:: PushLogQuantity
.. autoclass:: MultiLogQuantity
.. autoclass:: MultiPostLogQuantity

Expand Down Expand Up @@ -32,6 +33,7 @@
.. autoclass:: Timestep
.. autofunction:: set_dt
.. autofunction:: add_simulation_quantities
.. autofunction:: set_quantity_value
"""

__copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner"
Expand Down Expand Up @@ -97,6 +99,7 @@ class LogQuantity:
.. automethod:: tick
.. autoproperty:: default_aggregator
.. automethod:: __call__
.. automethod:: set_value
"""

sort_weight = 0
Expand Down Expand Up @@ -134,7 +137,15 @@ def __call__(self) -> Optional[float]:

This is only called if the invocation interval calls for it.
"""
raise NotImplementedError
raise NotImplementedError(
f"__call__ not implemented for log quantity {self.name}."
)

def set_value(self, value: Any) -> None:
"""Set the logged quantity value."""
raise NotImplementedError(
f"set_value not implemented for log quantity {self.name}."
)
Comment on lines +144 to +148
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it avoidable to have set_value in the LogQuantity interface instead of only in PushLogQuantity? Having it breeds an expectation that every LogQuantity support it, which isn't right.



class PostLogQuantity(LogQuantity):
Expand Down Expand Up @@ -251,11 +262,18 @@ def __init__(self, dt: Optional[float], name: str, unit: str = None,


class PushLogQuantity(LogQuantity):
"""A loggable scalar whose value can be updated.
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 comment a bit on how the LogQuantity interface is a "pull" model generically, and how this offers "push" capability over it. What happens to redundant "set" attempts? What if nothing is pushed in a cycle?


Quantity values are set by using :meth:`set_value`.

.. automethod:: set_value
.. automethod:: __call__
"""
def __init__(self, name: str, unit: str = None, description: str = None) -> None:
LogQuantity.__init__(self, name, unit, description)
self.value = None

def push_value(self, value) -> None:
def set_value(self, value) -> None:
if self.value is not None:
raise RuntimeError("can't push two values per cycle")
self.value = value
Expand Down Expand Up @@ -289,10 +307,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 @@ -867,12 +887,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, set_value=None):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now that we're standardizing set_value in PushLogQuantity, I'm no longer sold on the value of passing in set_value.

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,
set_value)

from pickle import dumps
self.db_conn.execute("""insert into quantities values (?,?,?,?)""", (
Expand All @@ -893,16 +914,18 @@ def add_internal(name, unit, description, def_agg):
gd_list.sort(key=lambda gd: gd.quantity.sort_weight)

if isinstance(quantity, MultiLogQuantity):
for name, unit, description, def_agg in zip(
for name, unit, description, def_agg, set_value in zip(
quantity.names,
quantity.units,
quantity.descriptions,
quantity.default_aggregators):
add_internal(name, unit, description, def_agg)
quantity.default_aggregators,
quantity.set_value):
add_internal(name, unit, description, def_agg, set_value)
else:
add_internal(quantity.name,
quantity.unit, quantity.description,
quantity.default_aggregator)
quantity.unit, quantity.description,
quantity.default_aggregator,
quantity.set_value)

def get_expr_dataset(self, expression, description=None, unit=None):
"""Prepare a time-series dataset for a given expression.
Expand Down Expand Up @@ -1498,6 +1521,15 @@ def add_run_info(mgr: LogManager) -> None:
mgr.set_constant("date", strftime("%a, %d %b %Y %H:%M:%S %Z", localtime()))
mgr.set_constant("unixtime", time())


def set_quantity_value(mgr: LogManager, name: str, value: Any) -> None:
"""Set a the value of a named LogQuantity.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should narrow this to erroring if that quantity is not a push quantity.


:param name: the name of the logged quantity.
:param value: the value of the logged quantity.
"""
mgr.quantity_data[name].value_setter(value)

# }}}

# vim: foldmethod=marker