From bab579ff85baee9b2301ad0ca4b200d8cee380da Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 4 Dec 2020 18:43:33 -0500 Subject: [PATCH 01/36] Make triggers pickable Also add a __eq__ methods for triggers --- hoomd/Trigger.cc | 75 ++++++++++++++++++++++++-------------- hoomd/Trigger.h | 16 ++++----- hoomd/trigger.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 146 insertions(+), 38 deletions(-) diff --git a/hoomd/Trigger.cc b/hoomd/Trigger.cc index 85b0bf8166..dc73ad2257 100644 --- a/hoomd/Trigger.cc +++ b/hoomd/Trigger.cc @@ -5,8 +5,6 @@ #include #include -PYBIND11_MAKE_OPAQUE(std::vector >); - //* Method to enable unit testing of C++ trigger calls from pytest bool testTriggerCall(std::shared_ptr t, uint64_t step) { @@ -37,6 +35,9 @@ void export_Trigger(pybind11::module& m) .def(pybind11::init<>()) .def("__call__", &Trigger::operator()) .def("compute", &Trigger::compute) + /* .def(pybind11::pickle( */ + /* [](const TriggerPy& trigger){ return pybind11::tuple(); }, */ + /* [](pybind11::tuple params){ return TriggerPy(); })) */ ; pybind11::class_(), pybind11::arg("period")) - .def_property("phase", - &PeriodicTrigger::getPhase, - &PeriodicTrigger::setPhase) - .def_property("period", - &PeriodicTrigger::getPeriod, - &PeriodicTrigger::setPeriod) + .def_property_readonly("phase", &PeriodicTrigger::getPhase) + .def_property_readonly("period", &PeriodicTrigger::getPeriod) + .def(pybind11::pickle( + [](const PeriodicTrigger& trigger) + { + return pybind11::make_tuple( + trigger.getPeriod(), trigger.getPhase()); + }, + [](pybind11::tuple params) + { + return PeriodicTrigger(params[0].cast(), + params[1].cast()); + })) ; pybind11::class_ >(m, "BeforeTrigger") .def(pybind11::init(), pybind11::arg("timestep")) - .def_property("timestep", - &BeforeTrigger::getTimestep, - &BeforeTrigger::setTimestep) + .def_property_readonly("timestep", &BeforeTrigger::getTimestep) + .def(pybind11::pickle( + [](const BeforeTrigger& trigger) + { + return pybind11::make_tuple(trigger.getTimestep()); + }, + [](pybind11::tuple params) + { + return BeforeTrigger(params[0].cast()); + })) ; pybind11::class_ >(m, "OnTrigger") .def(pybind11::init(), pybind11::arg("timestep")) - .def_property("timestep", - &OnTrigger::getTimestep, - &OnTrigger::setTimestep) + .def_property_readonly("timestep", &OnTrigger::getTimestep) + .def(pybind11::pickle( + [](const OnTrigger& trigger) + { + return pybind11::make_tuple(trigger.getTimestep()); + }, + [](pybind11::tuple params) + { + return OnTrigger(params[0].cast()); + })) ; pybind11::class_ >(m, "AfterTrigger") .def(pybind11::init(), pybind11::arg("timestep")) - .def_property("timestep", - &AfterTrigger::getTimestep, - &AfterTrigger::setTimestep) + .def_property_readonly("timestep", &AfterTrigger::getTimestep) + .def(pybind11::pickle( + [](const AfterTrigger& trigger) + { + return pybind11::make_tuple(trigger.getTimestep()); + }, + [](pybind11::tuple params) + { + return AfterTrigger(params[0].cast()); + })) ; pybind11::class_ >(m, "NotTrigger") .def(pybind11::init >(), pybind11::arg("trigger")) - .def_property("trigger", - &NotTrigger::getTrigger, - &NotTrigger::setTrigger) + .def_property_readonly("trigger", &NotTrigger::getTrigger) ; - pybind11::bind_vector > - >(m, "trigger_list"); - pybind11::class_ >(m, "AndTrigger") - .def(pybind11::init(), - pybind11::arg("triggers")) - .def_property_readonly("triggers", &AndTrigger::getTriggers) + .def(pybind11::init(), pybind11::arg("triggers")) ; pybind11::class_ >(m, "OrTrigger") .def(pybind11::init(), pybind11::arg("triggers")) - .def_property_readonly("triggers", &OrTrigger::getTriggers) ; m.def("_test_trigger_call", &testTriggerCall); diff --git a/hoomd/Trigger.h b/hoomd/Trigger.h index 1cfc8f3ecb..ccc15a9d62 100644 --- a/hoomd/Trigger.h +++ b/hoomd/Trigger.h @@ -84,7 +84,7 @@ class PYBIND11_EXPORT PeriodicTrigger : public Trigger } /// Get the period - uint64_t getPeriod() + uint64_t getPeriod() const { return m_period; } @@ -96,7 +96,7 @@ class PYBIND11_EXPORT PeriodicTrigger : public Trigger } /// Get the phase - uint64_t getPhase() + uint64_t getPhase() const { return m_phase; } @@ -124,7 +124,7 @@ class PYBIND11_EXPORT BeforeTrigger : public Trigger } /// Get the timestep before which the trigger is active. - uint64_t getTimestep() {return m_timestep;} + uint64_t getTimestep() const {return m_timestep;} const /// Set the timestep before which the trigger is active. void setTimestep(uint64_t timestep) {m_timestep = timestep;} @@ -149,7 +149,7 @@ class PYBIND11_EXPORT OnTrigger : public Trigger } /// Get the timestep when the trigger is active. - uint64_t getTimestep() {return m_timestep;} + uint64_t getTimestep() const {return m_timestep;} const /// Set the timestep when the trigger is active. void setTimestep(uint64_t timestep) {m_timestep = timestep;} @@ -174,7 +174,7 @@ class PYBIND11_EXPORT AfterTrigger : public Trigger } /// Get the timestep after which the trigger is active. - uint64_t getTimestep() {return m_timestep;} + uint64_t getTimestep() const {return m_timestep;} const /// Set the timestep after which the trigger is active. void setTimestep(uint64_t timestep) {m_timestep = timestep;} @@ -199,7 +199,7 @@ class PYBIND11_EXPORT NotTrigger : public Trigger } /// Get the trigger that is negated - std::shared_ptr getTrigger() {return m_trigger;} + std::shared_ptr getTrigger() const {return m_trigger;} /// Set the trigger to negate void setTrigger(std::shared_ptr trigger) {m_trigger = trigger;} @@ -237,7 +237,7 @@ class PYBIND11_EXPORT AndTrigger : public Trigger }); } - std::vector >& getTriggers() + const std::vector >& getTriggers() const { return m_triggers; } @@ -276,7 +276,7 @@ class PYBIND11_EXPORT OrTrigger : public Trigger }); } - std::vector >& getTriggers() + const std::vector >& getTriggers() const { return m_triggers; } diff --git a/hoomd/trigger.py b/hoomd/trigger.py index 2ec6cf8658..d0e0c113cf 100644 --- a/hoomd/trigger.py +++ b/hoomd/trigger.py @@ -29,6 +29,23 @@ def compute(self, timestep): from hoomd import _hoomd +# Note: We use pybind11's pickling infrastructure for simple triggers like +# Periodic, Before, After, and On. However, we use __reduce__ for classes with +# that are composed by other triggers. We do this not because we can't do this +# in pybind11, we can, but because we would upon unpickling downcast the +# triggers to their pybind11 defined type. This happens as all references to the +# composing triggers besides the C++ class are gone, the Python garbage +# collector removes them. If we store the triggers on the Python side as well, +# since __init__ is not called in unpickling, the attributes are not +# initialized if we use pybind11 pickling. + +# For the base class Trigger, we also create __getstate__ and __setstate__ +# methods which should allow for pickling Python subclasses. pybind11's +# facilities do not work as they prevent us from getting the attributes of the +# class be pickled and unpickled. We manual pass and set the instance __dict__ +# instead and instantiate _hoomd.Trigger in __setstate__ (which as not already +# been called as __init__ was not called). + class Trigger(_hoomd.Trigger): # noqa D214 """Base class trigger. @@ -62,7 +79,14 @@ class Trigger(_hoomd.Trigger): # noqa D214 Returns: bool: `True` when the trigger is active, `False` when it is not. """ - pass + def __getstate__(self): + """Get the state of the trigger object.""" + return self.__dict__ + + def __setstate__(self, state): + """Set the state of the trigger object.""" + _hoomd.Trigger.__init__(self) + self.__dict__ = state class Periodic(_hoomd.PeriodicTrigger, Trigger): @@ -95,6 +119,15 @@ def __str__(self): return f"hoomd.trigger.Periodic(period={self.period}, " \ f"phase={self.phase})" + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return ( + isinstance(other, Periodic) + and self.period == other.period + and self.phase == other.phase + ) + class Before(_hoomd.BeforeTrigger, Trigger): """Trigger on all steps before a given step. @@ -128,6 +161,11 @@ def __str__(self): """Human readable representation of the trigger as a string.""" return f"hoomd.trigger.Before(timestep={self.timestep})" + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, Before) and self.timestep == other.timestep + class On(_hoomd.OnTrigger, Trigger): """Trigger on a specific timestep. @@ -159,6 +197,11 @@ def __str__(self): """Human readable representation of the trigger as a string.""" return f"hoomd.trigger.On(timestep={self.timestep})" + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, On) and self.timestep == other.timestep + class After(_hoomd.AfterTrigger, Trigger): """Trigger on all steps after a given step. @@ -192,6 +235,11 @@ def __str__(self): """Human readable representation of the trigger as a string.""" return f"hoomd.trigger.After(timestep={self.timestep})" + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, After) and self.timestep == other.timestep + class Not(_hoomd.NotTrigger, Trigger): """Negate a trigger. @@ -213,11 +261,24 @@ class Not(_hoomd.NotTrigger, Trigger): def __init__(self, trigger): Trigger.__init__(self) _hoomd.NotTrigger.__init__(self, trigger) + self._trigger = trigger def __str__(self): """Human readable representation of the trigger as a string.""" return f"hoomd.trigger.Not(trigger={self.trigger})" + @property + def trigger(self): + return self._trigger + + def __reduce__(self): + return (type(self), (self._trigger,)) + + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, Not) and self.trigger == other.trigger + class And(_hoomd.AndTrigger, Trigger): """Boolean and operation. @@ -243,10 +304,11 @@ class And(_hoomd.AndTrigger, Trigger): def __init__(self, triggers): Trigger.__init__(self) - triggers = list(triggers) + triggers = tuple(triggers) if not all(isinstance(t, Trigger) for t in triggers): raise ValueError("triggers must an iterable of Triggers.") _hoomd.AndTrigger.__init__(self, triggers) + self._triggers = triggers def __str__(self): """Human readable representation of the trigger as a string.""" @@ -255,6 +317,18 @@ def __str__(self): result += "])" return result + @property + def triggers(self): + return self._triggers + + def __reduce__(self): + return (type(self), (self._triggers,)) + + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, And) and self.triggers == other.triggers + class Or(_hoomd.OrTrigger, Trigger): """Boolean or operation. @@ -284,10 +358,11 @@ class Or(_hoomd.OrTrigger, Trigger): """ def __init__(self, triggers): Trigger.__init__(self) - triggers = list(triggers) + triggers = tuple(triggers) if not all(isinstance(t, Trigger) for t in triggers): raise ValueError("triggers must an iterable of Triggers.") _hoomd.OrTrigger.__init__(self, triggers) + self._triggers = triggers def __str__(self): """Human readable representation of the trigger as a string.""" @@ -295,3 +370,15 @@ def __str__(self): result += ", ".join(str(trigger) for trigger in self.triggers) result += "])" return result + + @property + def triggers(self): + return self._triggers + + def __reduce__(self): + return (type(self), (self._triggers,)) + + def __eq__(self, other): + """Return a Boolean indicating whether the two triggers are equivalent. + """ + return isinstance(other, Or) and self.triggers == other.triggers From c4ef492cc7fb61ce74bcdf3b9e67d5f85f3ebff4 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 4 Dec 2020 18:43:56 -0500 Subject: [PATCH 02/36] Add tests for pickling triggers Also implement extensible testing framework for trigger tests --- hoomd/pytest/test_trigger.py | 310 ++++++++++++++--------------------- 1 file changed, 121 insertions(+), 189 deletions(-) diff --git a/hoomd/pytest/test_trigger.py b/hoomd/pytest/test_trigger.py index 5bfa7da5f6..1c8719d6f4 100644 --- a/hoomd/pytest/test_trigger.py +++ b/hoomd/pytest/test_trigger.py @@ -3,208 +3,140 @@ # License. """Test the Trigger classes.""" +import itertools +from inspect import isclass +import pickle + +import pytest import hoomd import hoomd.trigger -def test_periodic_properties(): - """Test construction and properties of Periodic.""" - a = hoomd.trigger.Periodic(123) - - assert a.period == 123 - assert a.phase == 0 - - a.period = 10000000000 - - assert a.period == 10000000000 - assert a.phase == 0 - - a.phase = 6000000000 - - assert a.period == 10000000000 - assert a.phase == 6000000000 - - b = hoomd.trigger.Periodic(phase=3, period=456) - - assert b.period == 456 - assert b.phase == 3 - - -def test_periodic_str(): - """Test the Periodic __str__ method.""" - b = hoomd.trigger.Periodic(phase=456, period=3) - assert str(b) == "hoomd.trigger.Periodic(period=3, phase=456)" - - -def test_periodic_eval(): - """Test the Periodic trigger evaluation.""" - a = hoomd.trigger.Periodic(period=456, phase=18) - +class CustomTrigger(hoomd.trigger.Trigger): + def __init__(self): + hoomd.trigger.Trigger.__init__(self) + + def compute(self, timestep): + return (timestep**(1 / 2)).is_integer() + + def __str__(self): + return "CustomTrigger()" + + def __eq__(self, other): + return isinstance(other, CustomTrigger) + + +# List of trigger classes +_classes = [ + hoomd.trigger.Periodic, + hoomd.trigger.Before, + hoomd.trigger.After, + hoomd.trigger.On, + hoomd.trigger.Not, + hoomd.trigger.And, + hoomd.trigger.Or, + CustomTrigger +] + + +# List of kwargs for the class constructors +_kwargs = [ + {'period': (456, 10000000000), 'phase': (18, 60000000000)}, + {'timestep': (100, 10000000000)}, + {'timestep': (100, 10000000000)}, + {'timestep': (100, 10000000000)}, + {'trigger': (hoomd.trigger.Periodic(10, 1), hoomd.trigger.Before(100))}, + {'triggers': ((hoomd.trigger.Periodic(10, 1), hoomd.trigger.Before(100)), + (hoomd.trigger.After(100), hoomd.trigger.On(101)))}, + {'triggers': ((hoomd.trigger.Periodic(10, 1), hoomd.trigger.Before(100)), + (hoomd.trigger.After(100), hoomd.trigger.On(101)))}, + {} +] + + +def _cartesian(grid): + for values in itertools.product(*grid.values()): + yield dict(zip(grid.keys(), values)) + + +def _test_name(arg): + if not isinstance(arg, hoomd.trigger.Trigger): + return None + else: + if isclass(arg): + return arg.__name__ + else: + return arg.__class__.__name__ + + +# Go over all class and constructor pairs +@pytest.mark.parametrize( + 'cls, kwargs', + ((cls, kwarg) for cls, kwargs in zip(_classes, _kwargs) + for kwarg in _cartesian(kwargs)), + ids=_test_name) +def test_properties(cls, kwargs): + instance = cls(**kwargs) + for key, value in kwargs.items(): + assert getattr(instance, key) == value + + +_strings_beginning = ( + "hoomd.trigger.Periodic(", + "hoomd.trigger.Before(", + "hoomd.trigger.After(", + "hoomd.trigger.On(", + "hoomd.trigger.Not(", + "hoomd.trigger.And(", + "hoomd.trigger.Or(", + "CustomTrigger()" +) + + +# Trigger instanace for the first arguments in _kwargs +def triggers(): + _single_kwargs = [next(_cartesian(kwargs)) for kwargs in _kwargs] + return (cls(**kwargs) for cls, kwargs in zip(_classes, _single_kwargs)) + + +@pytest.mark.parametrize('trigger, instance_string', + zip(triggers(), _strings_beginning), + ids=_test_name) +def test_str(trigger, instance_string): + assert str(trigger).startswith(instance_string) + + +_eval_funcs = [ + lambda x: (x - 18) % 456 == 0, # periodic + lambda x: x < 100, # before + lambda x: x > 100, # after + lambda x: x == 100, # on + lambda x: not (x - 1) % 10 == 0, # not + lambda x: (x - 1) % 10 == 0 and x < 100, # and + lambda x: (x - 1) % 10 == 0 or x < 100, # or + lambda x: (x ** (1 / 2)).is_integer() +] + + +@pytest.mark.parametrize('trigger, eval_func', + zip(triggers(), _eval_funcs), + ids=_test_name) +def test_eval(trigger, eval_func): for i in range(10000): - assert a(i) == ((i - 18) % 456 == 0) - + assert trigger(i) == eval_func(i) # Test values greater than 2^32 for i in range(10000000000, 10000010000): - assert a(i) == ((i - 18) % 456 == 0) - - # Test trigger with values greater than 2^32 - b = hoomd.trigger.Periodic(period=10000000000, phase=6000000000) - - assert b(6000000000) - assert not b(6000000001) - assert b(16000000000) - assert not b(16000000001) - - -def test_before_str(): - """Test the Before __str__ method.""" - a = hoomd.trigger.Before(1000) - assert str(a) == "hoomd.trigger.Before(timestep=1000)" - - -def test_before_eval(): - """Test the Before trigger.""" - a = hoomd.trigger.Before(1000) - - assert all(a(i) for i in range(1000)) - assert not any(a(i) for i in range(1000, 10000)) - - # tests for values greater than 2^32 - assert not any(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.Before(10000000000) - assert all(a(i) for i in range(9999990000, 10000000000)) - assert not any(a(i) for i in range(10000000000, 10000010000)) - - -def test_after_str(): - """Test the After __str__ method.""" - a = hoomd.trigger.After(1000) - assert str(a) == "hoomd.trigger.After(timestep=1000)" - - -def test_after_eval(): - """Test the After trigger.""" - a = hoomd.trigger.After(1000) - - assert not any(a(i) for i in range(1001)) - assert all(a(i) for i in range(1001, 10000)) + assert trigger(i) == eval_func(i) - # tests for values greater than 2^32 - assert all(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.After(10000000000) - assert not any(a(i) for i in range(9999990000, 10000000001)) - assert all(a(i) for i in range(10000000001, 10000010000)) - -def test_on_str(): - """Test the On __str__ method.""" - a = hoomd.trigger.On(1000) - assert str(a) == "hoomd.trigger.On(timestep=1000)" - - -def test_on_eval(): - """Test the On trigger.""" - a = hoomd.trigger.On(1000) - - assert not any(a(i) for i in range(1000)) - assert a(1000) - assert not any(a(i) for i in range(1001, 10000)) - - # tests for values greater than 2^32 - assert not any(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.On(10000000000) - assert not any(a(i) for i in range(9999990000, 10000000000)) - assert a(10000000000) - assert not any(a(i) for i in range(10000000001, 10000010000)) - - -def test_not_str(): - """Test the Not __str__ method.""" - a = hoomd.trigger.Not(hoomd.trigger.After(1000)) - assert str(a).startswith("hoomd.trigger.Not(") - - -def test_not_eval(): - """Test the Not Trigger.""" - a = hoomd.trigger.Not(hoomd.trigger.After(1000)) - assert str(a).startswith("hoomd.trigger.Not(") - - assert all(a(i) for i in range(1001)) - assert not any(a(i) for i in range(1001, 10000)) - - # tests for values greater than 2^32 - assert not any(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.Not(hoomd.trigger.After(10000000000)) - assert all(a(i) for i in range(9999990000, 10000000001)) - assert not any(a(i) for i in range(10000000001, 10000010000)) - - -def test_and_str(): - """Test the And __str__ method.""" - a = hoomd.trigger.And( - [hoomd.trigger.Before(1000), - hoomd.trigger.After(1000)]) - assert str(a).startswith("hoomd.trigger.And(") - - -def test_and_eval(): - """Test the And trigger.""" - a = hoomd.trigger.And( - [hoomd.trigger.Before(1000), - hoomd.trigger.After(1000)]) - - assert not any(a(i) for i in range(1000)) - assert not any(a(i) for i in range(1000, 10000)) - - # tests for values greater than 2^32 - assert not any(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.And( - [hoomd.trigger.Before(10000000000), - hoomd.trigger.After(10000000000)]) - assert not any(a(i) for i in range(9999990000, 10000010000)) - - -def test_or_str(): - """Test the Or __str__ method.""" - a = hoomd.trigger.Or([ - hoomd.trigger.Before(1000), - hoomd.trigger.On(1000), - hoomd.trigger.After(1000) - ]) - assert str(a).startswith("hoomd.trigger.Or(") - - -def test_or_eval(): - """Test the Or trigger.""" - a = hoomd.trigger.Or([ - hoomd.trigger.Before(1000), - hoomd.trigger.On(1000), - hoomd.trigger.After(1000) - ]) - - assert all(a(i) for i in range(10000)) - - # tests for values greater than 2^32 - assert all(a(i) for i in range(10000000000, 10000010000)) - a = hoomd.trigger.Or([ - hoomd.trigger.Before(10000000000), - hoomd.trigger.On(10000000000), - hoomd.trigger.After(10000000000) - ]) - assert all(a(i) for i in range(9999990000, 10000010000)) +@pytest.mark.parametrize('trigger', triggers(), ids=_test_name) +def test_pickling(trigger): + pkled_trigger = pickle.loads(pickle.dumps(trigger)) + assert trigger == pkled_trigger def test_custom(): - """Test CustomTrigger.""" - class CustomTrigger(hoomd.trigger.Trigger): - - def __init__(self): - hoomd.trigger.Trigger.__init__(self) - - def compute(self, timestep): - return (timestep**(1 / 2)).is_integer() - c = CustomTrigger() # test that the custom trigger can be called from c++ From 0d3b97f8ef25f90fca4aad991173938b25b1334f Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 4 Dec 2020 18:49:24 -0500 Subject: [PATCH 03/36] Remove some asserts in custom trigger test Since the CustomTrigger is tested in the other tests too, we only need to test the C++ callback a few times. --- hoomd/pytest/test_trigger.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hoomd/pytest/test_trigger.py b/hoomd/pytest/test_trigger.py index 1c8719d6f4..9d4c542ebc 100644 --- a/hoomd/pytest/test_trigger.py +++ b/hoomd/pytest/test_trigger.py @@ -141,14 +141,4 @@ def test_custom(): # test that the custom trigger can be called from c++ assert hoomd._hoomd._test_trigger_call(c, 0) - assert hoomd._hoomd._test_trigger_call(c, 1) - assert not hoomd._hoomd._test_trigger_call(c, 2) - assert not hoomd._hoomd._test_trigger_call(c, 3) - assert hoomd._hoomd._test_trigger_call(c, 4) - assert not hoomd._hoomd._test_trigger_call(c, 5) - assert not hoomd._hoomd._test_trigger_call(c, 6) - assert not hoomd._hoomd._test_trigger_call(c, 7) - assert not hoomd._hoomd._test_trigger_call(c, 8) - assert hoomd._hoomd._test_trigger_call(c, 9) - assert hoomd._hoomd._test_trigger_call(c, 250000000000) assert not hoomd._hoomd._test_trigger_call(c, 250000000001) From 7ffdaa16d35bd409b41be7c45c70a7b50cf87724 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 4 Dec 2020 18:50:56 -0500 Subject: [PATCH 04/36] Add changelong entry for pickable Triggers --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4e01265629..a2a1e2594c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,7 @@ v3.0.0-beta.2 (not yet released) *Added* +- ``hoomd.trigger.Trigger`` objects are pickable. - Support pybind11 2.6.0 - Exclusive creation file mode for ``write.GSD``. - ``hpmc.update.BoxMC``. From 573c4f182edddc42d0fd64d13789b92fc2f3fd9a Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 4 Dec 2020 19:00:28 -0500 Subject: [PATCH 05/36] Fix typo in changelog --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a2a1e2594c..e762a6ba15 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,7 +9,7 @@ v3.0.0-beta.2 (not yet released) *Added* -- ``hoomd.trigger.Trigger`` objects are pickable. +- ``hoomd.trigger.Trigger`` objects are picklable. - Support pybind11 2.6.0 - Exclusive creation file mode for ``write.GSD``. - ``hpmc.update.BoxMC``. From 333275d8c23ff493d50590eebe038513b1feeaad Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 7 Dec 2020 08:08:33 -0500 Subject: [PATCH 06/36] Fix ``hoomd.data.typeconverter.TypeConverterValue`` constructor We checked for the class inclusion in ``TypeConverterValue._conversion_func_dict`` instead of looking at the subclass or instance status of the class/instance passed to the constructor. This commit fixes this and does some rearrangement (for efficiency considerations) to the constructor. We know correctly check for the subclass or is instance status of all values passed. Also rearranges the _conversion_func_dict to be in probable class likelihood order to improve speed of iteration. Removes list from special preprocessing type. --- hoomd/data/typeconverter.py | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/hoomd/data/typeconverter.py b/hoomd/data/typeconverter.py index 230b2c8e67..7feb201b93 100644 --- a/hoomd/data/typeconverter.py +++ b/hoomd/data/typeconverter.py @@ -20,14 +20,6 @@ class TypeConversionError(ValueError): pass -def preprocess_list(value): - """Function for TypeConverterValue to exclude `str` and `dict` objects.""" - if is_iterable: - return value - else: - raise ValueError("Expected an iterable (excluding str and dict).") - - def trigger_preprocessing(trigger): if isinstance(trigger, Trigger): return trigger @@ -60,6 +52,7 @@ def box_preprocessing(box): "{} is not convertible into a hoomd.Box object. " "using hoomd.Box.from_box".format(box)) + class _HelpValidate(ABC): """Base class for classes that perform validation on an inputed value. @@ -264,28 +257,36 @@ def natural_number(value): TypeConverterValue(OnlyType(int, postprocess=natural_number)) """ _conversion_func_dict = { - list: OnlyType(list, preprocess=preprocess_list), - ndarray: OnlyType(ndarray, preprocess=array), - str: OnlyType(str, strict=True), Variant: OnlyType(Variant, preprocess=variant_preprocessing), + ParticleFilter: OnlyType(ParticleFilter, strict=True), + str: OnlyType(str, strict=True), Trigger: OnlyType(Trigger, preprocess=trigger_preprocessing), - ParticleFilter: OnlyType(ParticleFilter, strict=True) + ndarray: OnlyType(ndarray, preprocess=array), } def __init__(self, value): - # if constructor with special default setting logic - if value in self._conversion_func_dict.keys(): - self.converter = self._conversion_func_dict[value] - # if type with special value setting logic - elif type(value) in self._conversion_func_dict.keys(): - self.converter = self._conversion_func_dict[type(value)] - # if object constructor - elif isclass(value): + # If the value is a class object + if isclass(value): + # if constructor with special default setting logic + for cls in self._conversion_func_dict: + if issubclass(value, cls): + self.converter = self._conversion_func_dict[cls] + return None + # constructor with no special logic self.converter = OnlyType(value) - # if callable - elif callable(value): + return None + + # If the value is a class instance + # if value is a subtype of a type with special value setting logic + for cls in self._conversion_func_dict: + if isinstance(value, cls): + self.converter = self._conversion_func_dict[cls] + return None + + # if value is a callable assume that it is the validation function + if callable(value): self.converter = value - # if other object + # if any other object else: self.converter = OnlyType(type(value)) From d4b69cf0e0fcfb920ec3129ec9965230e9555b73 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 7 Dec 2020 10:10:49 -0500 Subject: [PATCH 07/36] Make hoomd.variant.Variant subclasses picklable --- hoomd/Variant.cc | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ hoomd/Variant.h | 34 +++++++++++----------- hoomd/variant.py | 7 +++++ 3 files changed, 98 insertions(+), 17 deletions(-) diff --git a/hoomd/Variant.cc b/hoomd/Variant.cc index f6e4afd193..8f3e6add9a 100644 --- a/hoomd/Variant.cc +++ b/hoomd/Variant.cc @@ -75,6 +75,15 @@ void export_Variant(pybind11::module& m) pybind11::class_ >(m, "VariantConstant") .def(pybind11::init< Scalar >(), pybind11::arg("value")) .def_property("value", &VariantConstant::getValue, &VariantConstant::setValue) + .def(pybind11::pickle( + [](const VariantConstant& variant) + { + return pybind11::make_tuple(variant.getValue()); + }, + [](pybind11::tuple params) + { + return VariantConstant(params[0].cast()); + })) ; pybind11::class_ >(m, "VariantRamp") @@ -86,6 +95,25 @@ void export_Variant(pybind11::module& m) .def_property("B", &VariantRamp::getB, &VariantRamp::setB) .def_property("t_start", &VariantRamp::getTStart, &VariantRamp::setTStart) .def_property("t_ramp", &VariantRamp::getTRamp, &VariantRamp::setTRamp) + .def(pybind11::pickle( + [](const VariantRamp& variant) + { + return pybind11::make_tuple( + variant.getA(), + variant.getB(), + variant.getTStart(), + variant.getTRamp() + ); + }, + [](pybind11::tuple params) + { + return VariantRamp( + params[0].cast(), + params[1].cast(), + params[2].cast(), + params[3].cast() + ); + })) ; pybind11::class_ >(m, "VariantCycle") @@ -112,6 +140,31 @@ void export_Variant(pybind11::module& m) .def_property("t_AB", &VariantCycle::getTAB, &VariantCycle::setTAB) .def_property("t_B", &VariantCycle::getTB, &VariantCycle::setTB) .def_property("t_BA", &VariantCycle::getTBA, &VariantCycle::setTBA) + .def(pybind11::pickle( + [](const VariantCycle& variant) + { + return pybind11::make_tuple( + variant.getA(), + variant.getB(), + variant.getTStart(), + variant.getTA(), + variant.getTAB(), + variant.getTB(), + variant.getTBA() + ); + }, + [](pybind11::tuple params) + { + return VariantCycle( + params[0].cast(), + params[1].cast(), + params[2].cast(), + params[3].cast(), + params[4].cast(), + params[5].cast(), + params[6].cast() + ); + })) ; pybind11::class_(), + params[1].cast(), + params[2].cast(), + params[3].cast(), + params[4].cast() + ); + })) ; m.def("_test_variant_call", &testVariantCall); diff --git a/hoomd/Variant.h b/hoomd/Variant.h index de64ac5b02..7da986e367 100644 --- a/hoomd/Variant.h +++ b/hoomd/Variant.h @@ -76,7 +76,7 @@ class PYBIND11_EXPORT VariantConstant : public Variant } /// Get the value. - Scalar getValue() + Scalar getValue() const { return m_value; } @@ -141,7 +141,7 @@ class PYBIND11_EXPORT VariantRamp : public Variant } /// Get the starting value. - Scalar getA() + Scalar getA() const { return m_A; } @@ -153,7 +153,7 @@ class PYBIND11_EXPORT VariantRamp : public Variant } /// Get the ending value. - Scalar getB() + Scalar getB() const { return m_B; } @@ -165,7 +165,7 @@ class PYBIND11_EXPORT VariantRamp : public Variant } /// Get the starting time step. - uint64_t getTStart() + uint64_t getTStart() const { return m_t_start; } @@ -182,7 +182,7 @@ class PYBIND11_EXPORT VariantRamp : public Variant } /// Get the length of the ramp. - uint64_t getTRamp() + uint64_t getTRamp() const { return m_t_ramp; } @@ -288,7 +288,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get A. - Scalar getA() + Scalar getA() const { return m_A; } @@ -300,7 +300,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get B. - Scalar getB() + Scalar getB() const { return m_B; } @@ -312,7 +312,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get the starting time step. - uint64_t getTStart() + uint64_t getTStart() const { return m_t_start; } @@ -324,7 +324,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get the holding time at A. - uint64_t getTA() + uint64_t getTA() const { return m_t_A; } @@ -341,7 +341,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get the length of the AB ramp. - uint64_t getTAB() + uint64_t getTAB() const { return m_t_AB; } @@ -353,7 +353,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get the holding time at B. - uint64_t getTB() + uint64_t getTB() const { return m_t_B; } @@ -370,7 +370,7 @@ class PYBIND11_EXPORT VariantCycle : public Variant } /// Get the length of the BA ramp. - uint64_t getTBA() + uint64_t getTBA() const { return m_t_BA; } @@ -460,7 +460,7 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Get the starting value. - Scalar getA() + Scalar getA() const { return m_A; } @@ -473,7 +473,7 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Get the ending value. - Scalar getB() + Scalar getB() const { return m_B; } @@ -486,7 +486,7 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Get the ending value. - Scalar getPower() + Scalar getPower() const { return m_power; } @@ -498,7 +498,7 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Get the starting time step. - uint64_t getTStart() + uint64_t getTStart() const { return m_t_start; } @@ -515,7 +515,7 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Get the length of the ramp. - uint64_t getTSize() + uint64_t getTSize() const { return m_t_size; } diff --git a/hoomd/variant.py b/hoomd/variant.py index 2c3455e1dc..b302970a29 100644 --- a/hoomd/variant.py +++ b/hoomd/variant.py @@ -47,6 +47,13 @@ def max(self): """The maximum value of this variant.""" return self._max() + def __getstate__(self): + return self.__dict__ + + def __setstate__(self, state): + _hoomd.Variant.__init__(self) + self.__dict__ = state + class Constant(_hoomd.VariantConstant, Variant): """A constant value. From e24d8892c50181f89528b48b4a1afa90b4f629ca Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 7 Dec 2020 10:12:55 -0500 Subject: [PATCH 08/36] Fix ``hoomd.variant.Power.t_ramp`` attribute We had exported an attribute ``t_size`` which represented ``t_ramp``. This is a bug left from the initial creation of the Power variant. --- hoomd/Variant.cc | 6 +++--- hoomd/Variant.h | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hoomd/Variant.cc b/hoomd/Variant.cc index 8f3e6add9a..d690828ae8 100644 --- a/hoomd/Variant.cc +++ b/hoomd/Variant.cc @@ -183,8 +183,8 @@ void export_Variant(pybind11::module& m) &VariantPower::setPower) .def_property("t_start", &VariantPower::getTStart, &VariantPower::setTStart) - .def_property("t_size", &VariantPower::getTSize, - &VariantPower::setTSize) + .def_property("t_ramp", &VariantPower::getTRamp, + &VariantPower::setTRamp) .def(pybind11::pickle( [](const VariantPower& variant) { @@ -193,7 +193,7 @@ void export_Variant(pybind11::module& m) variant.getB(), variant.getPower(), variant.getTStart(), - variant.getTSize() + variant.getTRamp() ); }, [](pybind11::tuple params) diff --git a/hoomd/Variant.h b/hoomd/Variant.h index 7da986e367..b732a43764 100644 --- a/hoomd/Variant.h +++ b/hoomd/Variant.h @@ -419,15 +419,15 @@ class PYBIND11_EXPORT VariantPower : public Variant @param B the final value @param power the power to approach as @param t_start the first timestep - @param t_size the length of the approach + @param t_ramp the length of the approach */ VariantPower(Scalar A, Scalar B, double power, - uint64_t t_start, uint64_t t_size) + uint64_t t_start, uint64_t t_ramp) : m_A(A), m_B(B), m_power(power), m_t_start(t_start), - m_t_size(t_size) + m_t_ramp(t_ramp) { m_offset = computeOffset(m_A, m_B); setStartEnd(); @@ -440,9 +440,9 @@ class PYBIND11_EXPORT VariantPower : public Variant { return m_A; } - else if (timestep < m_t_start + m_t_size) + else if (timestep < m_t_start + m_t_ramp) { - double s = double(timestep - m_t_start) / double(m_t_size); + double s = double(timestep - m_t_start) / double(m_t_ramp); double inv_result = m_inv_end * s + m_inv_start * (1.0 - s); return pow(inv_result, m_power) - m_offset; } @@ -504,20 +504,20 @@ class PYBIND11_EXPORT VariantPower : public Variant } /// Set the length of the ramp. - void setTSize(uint64_t t_size) + void setTRamp(uint64_t t_ramp) { // Doubles can only represent integers accurately up to 2**53. - if (t_size >= 9007199254740992ull) + if (t_ramp >= 9007199254740992ull) { - throw std::invalid_argument("t_size must be less than 2**53"); + throw std::invalid_argument("t_ramp must be less than 2**53"); } - m_t_size = t_size; + m_t_ramp = t_ramp; } /// Get the length of the ramp. - uint64_t getTSize() const + uint64_t getTRamp() const { - return m_t_size; + return m_t_ramp; } /// Return min @@ -570,7 +570,7 @@ class PYBIND11_EXPORT VariantPower : public Variant uint64_t m_t_start; /// length of apporach to m_B - uint64_t m_t_size; + uint64_t m_t_ramp; /// offset from given positions allows for negative values double m_offset; From ca187d3b4077a638083115c84032373412a360fe Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 7 Dec 2020 10:14:20 -0500 Subject: [PATCH 09/36] Add pickling tests for variants Also use pytest parametrize API to reduce LOC --- hoomd/pytest/test_variant.py | 477 ++++++++++++++--------------------- 1 file changed, 193 insertions(+), 284 deletions(-) diff --git a/hoomd/pytest/test_variant.py b/hoomd/pytest/test_variant.py index 7ec2b285fe..409c384ea4 100644 --- a/hoomd/pytest/test_variant.py +++ b/hoomd/pytest/test_variant.py @@ -2,51 +2,203 @@ # This file is part of the HOOMD-blue project, released under the BSD 3-Clause # License. -import numpy +from inspect import isclass +import pickle + +import numpy as np import hoomd import hoomd.variant import pytest -def test_constant(): - """ Test construction and properties of variant.constant - """ - - a = hoomd.variant.Constant(10.0) - - assert a.value == 10.0 - - a.value = 0.125 - - assert a.min == 0.125 - assert a.max == 0.125 - assert a.range == (0.125, 0.125) - - -def test_constant_eval(): - a = hoomd.variant.Constant(10.0) - - for i in range(10000): - assert a(i) == 10.0 - - for i in range(10000000000, 10000010000): - assert a(i) == 10.0 +_classes = [ + hoomd.variant.Constant, + hoomd.variant.Ramp, + hoomd.variant.Cycle, + hoomd.variant.Power +] + +_test_kwargs = [ + # Constant: first args value=1 + {'value': np.linspace(1, 10, 3)}, + # Ramp: first args A=1, B=3, t_start=0, t_ramp=10 + {'A': np.linspace(1, 10, 3), 'B': np.linspace(3, 10, 3), + 't_start': (0, 10, 10000000000), 't_ramp': (10, 20, 2000000000000)}, + # Cycle: first args A=2, B=5, t_start=0, t_A=10, t_AB=15, t_B=10, t_BA_20 + {'A': np.linspace(2, 10, 3), 'B': np.linspace(5, 10, 3), + 't_start': (0, 10, 10000000000), 't_A': (10, 20, 2000000000000), + 't_AB': (15, 30, 40000000000), 't_B': (10, 20, 2000000000000), + 't_BA': (20, 40, 560000000000)}, + # Power: first args A=1, B=10, t_start=0, t_ramp=10 + {'A': np.linspace(1, 10, 3), 'B': np.linspace(10, 100, 3), + 'power': np.linspace(2, 5, 3), 't_start': (0, 10, 10000000000), + 't_ramp': (10, 20, 2000000000000)} +] + + +def _to_kwargs(specs): + """Take a dictionary of iterables into a generator of dicts.""" + for value in zip(*specs.values()): + yield dict(zip(specs.keys(), value)) + + +def _test_id(value): + if isinstance(value, hoomd.variant.Variant): + if isclass(value): + return value.__name__ + else: + return value.__class__.__name__ + else: + return None + + +@pytest.mark.parametrize( + 'cls, kwargs', + ((cls, kwarg) for cls, kwargs in zip(_classes, _test_kwargs) + for kwarg in _to_kwargs(kwargs)), + ids=_test_id) +def test_construction(cls, kwargs): + variant = cls(**kwargs) + for key, value in kwargs.items(): + assert getattr(variant, key) == value + + +_expected_min_max = [ + (1., 1.), + (1., 3.), + (2., 5.), + (1., 10.), +] + + +_single_kwargs = [next(_to_kwargs(kwargs)) for kwargs in _test_kwargs] + + +def variants(): + return (cls(**kwargs) for cls, kwargs in zip(_classes, _single_kwargs)) + + +@pytest.mark.parametrize( + 'variant, expected_min_max', + zip(variants(), _expected_min_max), + ids=_test_id) +def test_min_max(variant, expected_min_max): + assert np.isclose(variant.min, expected_min_max[0]) + assert np.isclose(variant.max, expected_min_max[1]) + assert np.allclose(variant.range, expected_min_max) + + +@pytest.mark.parametrize( + 'variant, attrs', + ((variant, kwarg) for variant, kwargs in zip(variants(), _test_kwargs) + for kwarg in _to_kwargs(kwargs)), + ids=_test_id) +def test_setattr(variant, attrs): + for attr, value in attrs.items(): + setattr(variant, attr, value) + assert getattr(variant, attr) == value + + +def constant_eval(value): + def expected_value(timestep): + return value + return expected_value + + +def power_eval(A, B, power, t_start, t_ramp): + def expected_value(timestep): + if timestep < t_start: + return A + elif timestep < t_start + t_ramp: + inv_a, inv_b = (A ** (1 / power)), (B ** (1 / power)) + frac = (timestep - t_start) / t_ramp + return ((inv_b * frac) + ((1 - frac) * inv_a)) ** power + else: + return B + return expected_value + + +def ramp_eval(A, B, t_start, t_ramp): + def expected_value(timestep): + if timestep < t_start: + return A + elif timestep < t_start + t_ramp: + frac = (timestep - t_start) / t_ramp + return (B * frac) + ((1 - frac) * A) + else: + return B + return expected_value + + +def cycle_eval(A, B, t_start, t_A, t_AB, t_BA, t_B): + period = t_A + t_B + t_AB + t_BA + + def expected_value(timestep): + delta = (timestep - t_start) % period + if timestep < t_start or delta < t_A: + return A + elif delta < t_A + t_AB: + scale = (delta - t_A) / t_AB + return (scale * B) + ((1 - scale) * A) + elif delta < t_A + t_AB + t_B: + return B + else: + scale = (delta - (t_A + t_AB + t_B)) / t_BA + return (scale * A) + ((1 - scale) * B) + return expected_value + + +_eval_constructors = [ + constant_eval, + ramp_eval, + cycle_eval, + power_eval +] + + +@pytest.mark.parametrize( + 'variant, evaluator, kwargs', + ((variant, evaluator, kwarg) for variant, evaluator, kwargs in zip( + variants(), _eval_constructors, _test_kwargs) + for kwarg in _to_kwargs(kwargs)), + ids=_test_id) +def test_evaulation(variant, evaluator, kwargs): + for attr, value in kwargs.items(): + setattr(variant, attr, value) + eval_func = evaluator(**kwargs) + for i in range(1000): + assert np.isclose(eval_func(i), variant(i)) + for i in range(1000000000000, 1000000001000): + assert np.isclose(eval_func(i), variant(i)) + + +@pytest.mark.parametrize( + 'variant, attrs', + ((variant, list(kwarg.keys())) for variant, kwarg in zip( + variants(), _single_kwargs)), + ids=_test_id) +def test_pickling(variant, attrs): + pkled_variant = pickle.loads(pickle.dumps(variant)) + for attr in attrs: + assert getattr(pkled_variant, attr) == getattr(variant, attr) + + +class CustomVariant(hoomd.variant.Variant): + def __init__(self): + hoomd.variant.Variant.__init__(self) + self._a = 1 + + def __call__(self, timestep): + return (float(timestep)**(1 / 2)) + + def _min(self): + return 0.0 + + def _max(self): + return 1.0 def test_custom(): - class CustomVariant(hoomd.variant.Variant): - def __init__(self): - hoomd.variant.Variant.__init__(self) - - def __call__(self, timestep): - return (float(timestep)**(1 / 2)) - - def _min(self): - return 0.0 - - def _max(self): - return 1.0 - c = CustomVariant() # test that the custom variant can be called from c++ @@ -60,250 +212,7 @@ def _max(self): assert hoomd._hoomd._test_variant_min(c) == 0.0 assert hoomd._hoomd._test_variant_max(c) == 1.0 - -def test_ramp(): - a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000) - - for i in range(100): - assert a(i) == 1.0 - - assert a(100) == 1.0 - numpy.testing.assert_allclose(a(2600), 3.5) - numpy.testing.assert_allclose(a(5100), 6.0) - numpy.testing.assert_allclose(a(7600), 8.5) - assert a(10100) == 11.0 - - for i in range(10000000000, 10000010000): - assert a(i) == 11.0 - - -def test_ramp_properties(): - a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000) - assert a.A == 1.0 - assert a.B == 11.0 - assert a.t_start == 100 - assert a.t_ramp == 10000 - - a.A = 100 - assert a.A == 100.0 - assert a(0) == 100.0 - - a.B = -25 - assert a.B == -25.0 - assert a(10100) == -25.0 - - a.t_start = 1000 - assert a.t_start == 1000 - assert a(1000) == 100.0 - assert a(1001) < 100.0 - - a.t_ramp = int(1e6) - assert a.t_ramp == int(1e6) - assert a(1001000) == -25.0 - assert a(1000999) > -25.0 - - with pytest.raises(ValueError): - a.t_ramp = int(2**53 + 1) - - -def test_ramp_min_max(): - a = hoomd.variant.Ramp(1.0, 11.0, 100, 10000) - assert a.min == 1. - assert a.max == 11. - assert a.range == (1., 11.) - a.B = -5 - assert a.min == -5 - assert a.max == 1. - assert a.range == (-5., 1.) - - -def test_cycle(): - A = 0.0 - B = 10.0 - t_start = 100 - t_A = 200 - t_AB = 400 - t_B = 300 - t_BA = 100 - - a = hoomd.variant.Cycle(A=A, - B=B, - t_start=t_start, - t_A=t_A, - t_AB=t_AB, - t_B=t_B, - t_BA=t_BA) - - # t_start - for i in range(t_start): - assert a(i) == A - - period = t_A + t_AB + t_B + t_BA - for cycle in [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 10000000000]: - offset = period * cycle + t_start - - # t_A - for i in range(offset, offset + t_A): - assert a(i) == A - - # t_AB ramp - numpy.testing.assert_allclose(a(offset + t_A + t_AB // 2), (A + B) / 2) - - # t_B - for i in range(offset + t_A + t_AB, offset + t_A + t_AB + t_B): - assert a(i) == B - - # t_BA ramp - numpy.testing.assert_allclose(a(offset + t_A + t_AB + t_B + t_BA // 2), - (A + B) / 2) - - -def test_cycle_properties(): - a = hoomd.variant.Cycle(A=1.0, - B=12.0, - t_start=100, - t_A=200, - t_AB=300, - t_B=400, - t_BA=500) - assert a.A == 1.0 - assert a.B == 12.0 - assert a.t_start == 100 - assert a.t_A == 200 - assert a.t_AB == 300 - assert a.t_B == 400 - assert a.t_BA == 500 - - a.A = 100 - assert a.A == 100.0 - assert a(0) == 100.0 - - a.B = -25 - assert a.B == -25.0 - assert a(600) == -25.0 - - a.t_start = 1000 - assert a.t_start == 1000 - assert a(1000) == 100.0 - assert a(1201) < 100.0 - - a.t_A = 400 - assert a.t_A == 400 - assert a(1400) == 100.0 - assert a(1401) < 100.0 - - a.t_AB = 600 - assert a.t_AB == int(600) - assert a(2000) == -25.0 - assert a(1999) > -25.0 - - a.t_B = 1000 - assert a.t_B == 1000 - assert a(3000) == -25.0 - assert a(3001) > -25.0 - - a.t_BA = 10000 - assert a.t_BA == 10000 - assert a(13000) == 100.0 - assert a(12999) < 100.0 - - with pytest.raises(ValueError): - a.t_AB = int(2**53 + 1) - - with pytest.raises(ValueError): - a.t_BA = int(2**53 + 1) - - -def test_cycle_min_max(): - A = 0.0 - B = 10.0 - t_start = 100 - t_A = 200 - t_AB = 400 - t_B = 300 - t_BA = 100 - - a = hoomd.variant.Cycle(A, B, t_start, t_A, t_AB, t_B, t_BA) - assert a.min == 0. - assert a.max == 10. - assert a.range == (0., 10.) - a.B = -5 - assert a.min == -5 - assert a.max == 0. - assert a.range == (-5., 0.) - - -def test_power(): - args = (1.0, 100.0, 2., 100, 1000) - a = hoomd.variant.Power(*args) - - def power(init, final, power, start, length): - def expected_value(timestep): - if timestep < start: - return init - elif timestep < start + length: - inv_a, inv_b = (init ** (1 / power)), (final ** (1 / power)) - frac = (timestep - start) / length - return ((inv_b * frac) + ((1 - frac) * inv_a)) ** power - else: - return final - return expected_value - - expected_value = power(*args) - - for i in range(100): - assert a(i) == 1.0 - - assert a(100) == 1.0 - for i in range(101, 1000): - numpy.testing.assert_allclose(a(i), expected_value(i)) - assert a(10100) == 100.0 - - for i in range(10000000000, 10000010000): - assert a(i) == 100.0 - - -def test_power_properties(): - a = hoomd.variant.Power(1.0, 100.0, 2., 100, 1000) - assert a.A == 1.0 - assert a.B == 100.0 - assert a.power == 2. - assert a.t_start == 100 - assert a.t_size == 1000 - - a.A = 100 - assert a.A == 100.0 - assert a(0) == 100.0 - - a.B = -25 - assert a.B == -25.0 - assert a(10100) == -25.0 - - pow_2 = a(101) - a.power = 1 / 10 - assert a.power == 1 / 10 - assert a(101) > pow_2 - - a.t_start = 1000 - assert a.t_start == 1000 - assert a(1000) == 100.0 - assert a(1001) < 100.0 - - a.t_size = int(1e6) - assert a.t_size == int(1e6) - assert a(1001000) == -25.0 - assert a(1000990) > -25.0 - - with pytest.raises(ValueError): - a.t_size = int(2**53 + 1) - - -def test_power_min_max(): - a = hoomd.variant.Power(1.0, 11.0, 2, 100, 10000) - assert a.min == 1. - assert a.max == 11. - assert a.range == (1., 11.) - a.B = -5 - assert a.min == -5 - assert a.max == 1. - assert a.range == (-5., 1.) + pkled_variant = pickle.loads(pickle.dumps(c)) + assert pkled_variant._a == 1 + for i in range(0, 10000, 100): + assert hoomd._hoomd._test_variant_call(pkled_variant, i) == float(i)**(1 / 2) From e671a2c3365aa1f6bf5735c8cf5ad467d7d899fb Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 7 Dec 2020 10:26:19 -0500 Subject: [PATCH 10/36] Add entry to changelog for variant picklability --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4e01265629..9e20ef97b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,7 @@ v3.0.0-beta.2 (not yet released) *Added* +- ``hoomd.variant.Variant`` subclasses are picklable. - Support pybind11 2.6.0 - Exclusive creation file mode for ``write.GSD``. - ``hpmc.update.BoxMC``. @@ -29,6 +30,7 @@ v3.0.0-beta.2 (not yet released) *Fixed* +- ``hoomd.variant.Power`` objects now have a ``t_ramp`` attribute as documented. - ``Simulation.run`` now ends with a ``KeyboardInterrupt`` exception when Jupyter interrupts the kernel. - Logging the state of specific objects with nested attributes. From a735ebf18c21c2a6d08baa09bb6aa27a185137ac Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Sun, 13 Dec 2020 22:07:20 -0500 Subject: [PATCH 11/36] moved environment variable warning before cmake --- INSTALLING.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 433a959e97..b150f200c5 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -180,12 +180,6 @@ Run ``python3 install-prereq-headers.py -h`` to see a list of the command line o Compile HOOMD-blue ------------------ -Configure:: - - $ cd /path/to/hoomd-blue - $ cmake -B build - $ cd build - .. warning:: Make certain you point ``CMAKE_PREFIX_PATH`` at your virtual environment so that CMake can find @@ -193,6 +187,12 @@ Configure:: $ export CMAKE_PREFIX_PATH=/path/to/environment +Configure:: + + $ cd /path/to/hoomd-blue + $ cmake -B build + $ cd build + By default, **HOOMD-blue** configures a *Release* optimized build type for a generic CPU architecture and with no optional libraries. Pass these options to cmake to enable optimizations specific to your CPU:: From b5414877fd36fae5be134a5a9d6d5ad874e39424 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 22:36:47 -0500 Subject: [PATCH 12/36] move virtual environment config after install prereqs --- INSTALLING.rst | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index b150f200c5..f2094b1abf 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -66,28 +66,6 @@ Or clone using Git: **HOOMD-blue** uses Git submodules. Either clone with the ``--recursive`` option, or execute ``git submodule update --init`` to fetch the submodules. -Configure a virtual environment -------------------------------- - -When using a shared Python installation, create a `virtual environment -`_ where you can install -**HOOMD-blue**:: - - $ python3 -m venv /path/to/environment --system-site-packages - -Activate the environment before configuring and before executing -**HOOMD-blue** scripts:: - - $ source /path/to/environment/bin/activate - -Tell CMake to search for packages in the virtual environment first:: - - $ export CMAKE_PREFIX_PATH=/path/to/environment - -.. note:: - - Other types of virtual environments (such as *conda*) may work, but are not thoroughly tested. - Install prerequisites --------------------- @@ -177,6 +155,25 @@ Some package managers (such as *pip*) and most clusters are missing some or all Run ``python3 install-prereq-headers.py -h`` to see a list of the command line options. +Configure a virtual environment +------------------------------- + +When using a shared Python installation, create a `virtual environment +`_ where you can install +**HOOMD-blue** Note that other types of virtual environments +(such as *conda*) may work, but are not thoroughly tested.:: + + $ python3 -m venv /path/to/environment --system-site-packages + +Activate the environment before configuring and before executing +**HOOMD-blue** scripts:: + + $ source /path/to/environment/bin/activate + +Tell CMake to search for packages in the virtual environment first:: + + $ export CMAKE_PREFIX_PATH=/path/to/environment + Compile HOOMD-blue ------------------ From a416b166be5de380487c134e434217640ceb368d Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 22:38:21 -0500 Subject: [PATCH 13/36] remove cmake prefix warning --- INSTALLING.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index f2094b1abf..73e5ec7e3e 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -177,13 +177,6 @@ Tell CMake to search for packages in the virtual environment first:: Compile HOOMD-blue ------------------ -.. warning:: - - Make certain you point ``CMAKE_PREFIX_PATH`` at your virtual environment so that CMake can find - packages there and correctly determine the installation location.:: - - $ export CMAKE_PREFIX_PATH=/path/to/environment - Configure:: $ cd /path/to/hoomd-blue From 29f738887d486763b19811f417180874d6c9c84f Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 22:38:46 -0500 Subject: [PATCH 14/36] introduce default build type separate from customization flags --- INSTALLING.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 73e5ec7e3e..80fd84863f 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -177,14 +177,14 @@ Tell CMake to search for packages in the virtual environment first:: Compile HOOMD-blue ------------------ -Configure:: +By default, **HOOMD-blue** configures a *Release* optimized build type for a +generic CPU architecture and with no optional libraries.:: $ cd /path/to/hoomd-blue $ cmake -B build $ cd build -By default, **HOOMD-blue** configures a *Release* optimized build type for a -generic CPU architecture and with no optional libraries. Pass these options to cmake +Pass these options to cmake to enable optimizations specific to your CPU:: -DCMAKE_CXX_FLAGS=-march=native -DCMAKE_C_FLAGS=-march=native From a9eb86aa4660675abe91ab193d8d523067eda5e4 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 22:39:10 -0500 Subject: [PATCH 15/36] add gentle reminder about prefix path before make install --- INSTALLING.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 80fd84863f..6f2d8f9510 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -215,7 +215,8 @@ Execute longer running validation tests:: On a cluster, run tests within a job on a GPU compute node. -To install **HOOMD-blue** into your Python environment, run:: +With ``CMAKE_PREFIX_PATH`` pointing to your desired python environment, +install **HOOMD-blue** into your Python environment:: $ make install From 6d92149decde28aa5c84187d1c7f83860d9f773e Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 22:41:47 -0500 Subject: [PATCH 16/36] Replace top level bullet w/paragraphs of the features w/dependencies --- INSTALLING.rst | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 433a959e97..c0c2e95d66 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -93,8 +93,7 @@ Install prerequisites **HOOMD-blue** requires a number of libraries to build. -- Required: - +**General requirements** - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, 8, 9, ``clang`` 5, 6, 7, 8) - Python >= 3.5 @@ -102,20 +101,16 @@ Install prerequisites - pybind11 >= 2.2 - Eigen >= 3.2 - CMake >= 3.9 - - For MPI parallel execution (required when ``ENABLE_MPI=on``): - +**For MPI parallel execution** (required when ``ENABLE_MPI=on``): - MPI (tested with OpenMPI, MVAPICH) - cereal >= 1.1 (required when ``ENABLE_MPI=on``) - - For GPU execution (required when ``ENABLE_GPU=on``): - +**For GPU execution** (required when ``ENABLE_GPU=on``): - NVIDIA CUDA Toolkit >= 9.0 - **OR** + *OR* - - `AMD ROCm >= 2.9 `_ - - Additional dependencies: + - `AMD ROCm >= 2.9 `_ with additional dependencies: - HIP [with ``hipcc`` and ``hcc`` as backend] - rocFFT - rocPRIM @@ -131,21 +126,16 @@ Install prerequisites A `temporary bugfix branch of HIP `_ addresses these problems. When using a custom HIP version, other libraries used by HOOMD-blue (``rocfft``) need to be compiled against that same HIP version. - 2. The `mpcd` component is disabled on AMD GPUs. - 3. Multi-GPU execution via unified memory is not available. - - For threaded parallelism on the CPU (required when ``ENABLE_TBB=on``): - +**For threaded parallelism on the CPU** (required when ``ENABLE_TBB=on``): - Intel Threading Building Blocks >= 4.3 - - For runtime code generation (required when ``BUILD_JIT=on``): - +**For runtime code generation** (required when ``BUILD_JIT=on``): - LLVM >= 5.0 - - To build documentation: - +**To build documentation** - Doxygen >= 1.8.5 - Sphinx >= 1.6 From 857dc8bb9d1ebd031435f937fd26baadae1e7907 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 23:06:03 -0500 Subject: [PATCH 17/36] intro note on additional flags --- INSTALLING.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index c0c2e95d66..6b756e6598 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -91,7 +91,8 @@ Tell CMake to search for packages in the virtual environment first:: Install prerequisites --------------------- -**HOOMD-blue** requires a number of libraries to build. +**HOOMD-blue** requires a number of libraries to build. The flags ``ENABLE_MPI``, +``ENABLE_GPU``, ``ENABLE_TBB``, and ``BUILD_JIT`` each require additional libraries. **General requirements** - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, From 468fa6bd25812292296103a2884ae2e850b97ecc Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 23:11:50 -0500 Subject: [PATCH 18/36] spacing and remove extraneoux bold in list heading --- INSTALLING.rst | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 6b756e6598..960a2701a9 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -94,19 +94,20 @@ Install prerequisites **HOOMD-blue** requires a number of libraries to build. The flags ``ENABLE_MPI``, ``ENABLE_GPU``, ``ENABLE_TBB``, and ``BUILD_JIT`` each require additional libraries. -**General requirements** - - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, - 8, 9, ``clang`` 5, 6, 7, 8) - - Python >= 3.5 - - NumPy >= 1.7 - - pybind11 >= 2.2 - - Eigen >= 3.2 - - CMake >= 3.9 -**For MPI parallel execution** (required when ``ENABLE_MPI=on``): +General requirements + - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, + 8, 9, ``clang`` 5, 6, 7, 8) + - Python >= 3.5 + - NumPy >= 1.7 + - pybind11 >= 2.2 + - Eigen >= 3.2 + - CMake >= 3.9 + +For MPI parallel execution (required when ``ENABLE_MPI=on``): - MPI (tested with OpenMPI, MVAPICH) - cereal >= 1.1 (required when ``ENABLE_MPI=on``) -**For GPU execution** (required when ``ENABLE_GPU=on``): +For GPU execution (required when ``ENABLE_GPU=on``): - NVIDIA CUDA Toolkit >= 9.0 *OR* @@ -130,13 +131,13 @@ Install prerequisites 2. The `mpcd` component is disabled on AMD GPUs. 3. Multi-GPU execution via unified memory is not available. -**For threaded parallelism on the CPU** (required when ``ENABLE_TBB=on``): +For threaded parallelism on the CPU (required when ``ENABLE_TBB=on``) - Intel Threading Building Blocks >= 4.3 -**For runtime code generation** (required when ``BUILD_JIT=on``): +For runtime code generation (required when ``BUILD_JIT=on``) - LLVM >= 5.0 -**To build documentation** +To build documentation - Doxygen >= 1.8.5 - Sphinx >= 1.6 From 205488061e4b348ddd5a73a748860be24556a1bf Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Mon, 14 Dec 2020 23:12:08 -0500 Subject: [PATCH 19/36] remove redundant text --- INSTALLING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 960a2701a9..2d8ba71a67 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -105,7 +105,7 @@ General requirements For MPI parallel execution (required when ``ENABLE_MPI=on``): - MPI (tested with OpenMPI, MVAPICH) - - cereal >= 1.1 (required when ``ENABLE_MPI=on``) + - cereal >= 1.1 For GPU execution (required when ``ENABLE_GPU=on``): - NVIDIA CUDA Toolkit >= 9.0 From 4b188e51c1991c6062ce875241bcff24bc68fc99 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Tue, 15 Dec 2020 10:46:07 -0500 Subject: [PATCH 20/36] Add docs for hoomd.variant.Variant methods --- hoomd/variant.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hoomd/variant.py b/hoomd/variant.py index b302970a29..68826fa52b 100644 --- a/hoomd/variant.py +++ b/hoomd/variant.py @@ -48,9 +48,11 @@ def max(self): return self._max() def __getstate__(self): + """Get the variant's ``__dict__`` attributue.""" return self.__dict__ def __setstate__(self, state): + """Restore the state of the variant.""" _hoomd.Variant.__init__(self) self.__dict__ = state From c4d1c750b31f193be7b136bc1ffe071a727cdc13 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 10:58:32 -0500 Subject: [PATCH 21/36] first pass at fixing list syntax --- INSTALLING.rst | 55 ++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 2d8ba71a67..8e80c1d73d 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -95,34 +95,38 @@ Install prerequisites ``ENABLE_GPU``, ``ENABLE_TBB``, and ``BUILD_JIT`` each require additional libraries. General requirements - - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, - 8, 9, ``clang`` 5, 6, 7, 8) - - Python >= 3.5 - - NumPy >= 1.7 - - pybind11 >= 2.2 - - Eigen >= 3.2 - - CMake >= 3.9 + +- C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, + 8, 9, ``clang`` 5, 6, 7, 8) +- Python >= 3.5 +- NumPy >= 1.7 +- pybind11 >= 2.2 +- Eigen >= 3.2 +- CMake >= 3.9 For MPI parallel execution (required when ``ENABLE_MPI=on``): - - MPI (tested with OpenMPI, MVAPICH) - - cereal >= 1.1 + +- MPI (tested with OpenMPI, MVAPICH) +- cereal >= 1.1 For GPU execution (required when ``ENABLE_GPU=on``): - - NVIDIA CUDA Toolkit >= 9.0 + +- NVIDIA CUDA Toolkit >= 9.0 *OR* - - `AMD ROCm >= 2.9 `_ with additional dependencies: - - HIP [with ``hipcc`` and ``hcc`` as backend] - - rocFFT - - rocPRIM - - rocThrust - - hipCUB, included for NVIDIA GPU targets, but required as an - external dependency when building for AMD GPUs - - roctracer-dev - - Linux kernel >= 3.5.0 +- `AMD ROCm >= 2.9 `_ with additional dependencies: + + - HIP [with ``hipcc`` and ``hcc`` as backend] + - rocFFT + - rocPRIM + - rocThrust + - hipCUB, included for NVIDIA GPU targets, but required as an + external dependency when building for AMD GPUs + - roctracer-dev + - Linux kernel >= 3.5.0 - For HOOMD-blue on AMD GPUs, the following limitations currently apply. + For HOOMD-blue on AMD GPUs, the following limitations currently apply. 1. Certain HOOMD-blue kernels trigger a `unknown HSA error `_. A `temporary bugfix branch of HIP `_ @@ -132,14 +136,17 @@ For GPU execution (required when ``ENABLE_GPU=on``): 3. Multi-GPU execution via unified memory is not available. For threaded parallelism on the CPU (required when ``ENABLE_TBB=on``) - - Intel Threading Building Blocks >= 4.3 + +- Intel Threading Building Blocks >= 4.3 For runtime code generation (required when ``BUILD_JIT=on``) - - LLVM >= 5.0 + +- LLVM >= 5.0 To build documentation - - Doxygen >= 1.8.5 - - Sphinx >= 1.6 + +- Doxygen >= 1.8.5 +- Sphinx >= 1.6 Install these tools with your system or virtual environment package manager. HOOMD developers have had success with ``pacman`` (`arch linux `_), ``apt-get`` (`ubuntu `_), `Homebrew From 04c4def2d7cdd3409a26cd650897352b59a1df15 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 11:02:04 -0500 Subject: [PATCH 22/36] update ROCm requirement version --- INSTALLING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 8e80c1d73d..fe4fd40eec 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -115,7 +115,7 @@ For GPU execution (required when ``ENABLE_GPU=on``): *OR* -- `AMD ROCm >= 2.9 `_ with additional dependencies: +- `AMD ROCm >= 3.5.0 `_ with additional dependencies: - HIP [with ``hipcc`` and ``hcc`` as backend] - rocFFT From 7c4789ac4662a2a690687fffd7f857d31aa2e4ad Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 11:02:37 -0500 Subject: [PATCH 23/36] remove "temporary bugfix..." block --- INSTALLING.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index fe4fd40eec..065e6fc453 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -129,9 +129,6 @@ For GPU execution (required when ``ENABLE_GPU=on``): For HOOMD-blue on AMD GPUs, the following limitations currently apply. 1. Certain HOOMD-blue kernels trigger a `unknown HSA error `_. - A `temporary bugfix branch of HIP `_ - addresses these problems. When using a custom HIP version, other libraries used by HOOMD-blue (``rocfft``) need - to be compiled against that same HIP version. 2. The `mpcd` component is disabled on AMD GPUs. 3. Multi-GPU execution via unified memory is not available. From 312ef6159e5eeb21f4ed38d5b7eecfe65279b652 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 11:08:33 -0500 Subject: [PATCH 24/36] formatting of header paragraphs --- INSTALLING.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 065e6fc453..7c6fad4643 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -94,7 +94,7 @@ Install prerequisites **HOOMD-blue** requires a number of libraries to build. The flags ``ENABLE_MPI``, ``ENABLE_GPU``, ``ENABLE_TBB``, and ``BUILD_JIT`` each require additional libraries. -General requirements +**General requirements** - C++11 capable compiler (tested with ``gcc`` 4.8, 5.5, 6.4, 7, 8, 9, ``clang`` 5, 6, 7, 8) @@ -104,12 +104,12 @@ General requirements - Eigen >= 3.2 - CMake >= 3.9 -For MPI parallel execution (required when ``ENABLE_MPI=on``): +**For MPI parallel execution** (required when ``ENABLE_MPI=on``) - MPI (tested with OpenMPI, MVAPICH) - cereal >= 1.1 -For GPU execution (required when ``ENABLE_GPU=on``): +**For GPU execution** (required when ``ENABLE_GPU=on``) - NVIDIA CUDA Toolkit >= 9.0 @@ -132,15 +132,15 @@ For GPU execution (required when ``ENABLE_GPU=on``): 2. The `mpcd` component is disabled on AMD GPUs. 3. Multi-GPU execution via unified memory is not available. -For threaded parallelism on the CPU (required when ``ENABLE_TBB=on``) +**For threaded parallelism on the CPU** (required when ``ENABLE_TBB=on``) - Intel Threading Building Blocks >= 4.3 -For runtime code generation (required when ``BUILD_JIT=on``) +**For runtime code generation** (required when ``BUILD_JIT=on``) - LLVM >= 5.0 -To build documentation +**To build documentation** - Doxygen >= 1.8.5 - Sphinx >= 1.6 From 448cae944e1b82cfee960033ce9ac4b8dfeec367 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:23:33 -0500 Subject: [PATCH 25/36] remove tip and note callouts to integrate into text --- INSTALLING.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 6f2d8f9510..7824db088a 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -137,16 +137,6 @@ Typical HPC cluster environments provide python, numpy, cmake, cuda, and mpi, vi $ module load gcc python cuda cmake -.. note:: - - Packages may be named differently, check your system's package list. Install any ``-dev`` packages as needed. - -.. tip:: - - You can install numpy and other python packages into your virtual environment:: - - python3 -m pip install numpy - Some package managers (such as *pip*) and most clusters are missing some or all of pybind11, eigen, and cereal. ``install-prereq-headers.py`` will install the missing packages into your virtual environment:: From 98d3d3e24a093b683529fca9262cf8494d24cb1c Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:24:10 -0500 Subject: [PATCH 26/36] integrate "different name tip" also removes generic install command --- INSTALLING.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 7824db088a..80cd5d844c 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -127,11 +127,11 @@ Install prerequisites - Doxygen >= 1.8.5 - Sphinx >= 1.6 -Install these tools with your system or virtual environment package manager. HOOMD developers have had success with +Install these tools with your system or virtual environment package manager. +HOOMD developers have had success with ``pacman`` (`arch linux `_), ``apt-get`` (`ubuntu `_), `Homebrew -`_ (macOS), and `MacPorts `_ (macOS):: - - $ your-package-manager install python python-numpy pybind11 eigen cmake openmpi cereal cuda +`_ (macOS), and `MacPorts `_ (macOS). +Note that packages may be named differently, so check your system's package list and install any ``-dev`` packages as needed. Typical HPC cluster environments provide python, numpy, cmake, cuda, and mpi, via a module system:: From d02c233fad2f7de4ca0f3f06e8902f73c48d649e Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:24:39 -0500 Subject: [PATCH 27/36] integrate note about pip install --- INSTALLING.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 80cd5d844c..633acccc65 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -149,9 +149,11 @@ Configure a virtual environment ------------------------------- When using a shared Python installation, create a `virtual environment -`_ where you can install -**HOOMD-blue** Note that other types of virtual environments -(such as *conda*) may work, but are not thoroughly tested.:: +`_ where you can install the dependencies and +**HOOMD-blue**. +You can install numpy and other python packages into your virtual environment using, *e.g.*, ``python3 -m pip install numpy``. +Note that other types of virtual environments +(such as *conda*) may work, but are not thoroughly tested. :: $ python3 -m venv /path/to/environment --system-site-packages From 6eb4f8717a2e4479960e742ab5773fb9631be508 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:25:15 -0500 Subject: [PATCH 28/36] Combine two virtual environment commands into one block --- INSTALLING.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 633acccc65..4f010bb1a0 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -157,13 +157,10 @@ Note that other types of virtual environments $ python3 -m venv /path/to/environment --system-site-packages -Activate the environment before configuring and before executing -**HOOMD-blue** scripts:: - - $ source /path/to/environment/bin/activate - -Tell CMake to search for packages in the virtual environment first:: +Activate the environment and tell CMake to search for packages there +before configuring and installing **HOOMD-blue**. :: + $ source /path/to/environment/bin/activate $ export CMAKE_PREFIX_PATH=/path/to/environment Compile HOOMD-blue From 0869e0e8f98b54b933d430efdad44b52b571e3fd Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:26:52 -0500 Subject: [PATCH 29/36] fix double punctuation before block --- INSTALLING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 4f010bb1a0..08f3539828 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -167,7 +167,7 @@ Compile HOOMD-blue ------------------ By default, **HOOMD-blue** configures a *Release* optimized build type for a -generic CPU architecture and with no optional libraries.:: +generic CPU architecture and with no optional libraries:: $ cd /path/to/hoomd-blue $ cmake -B build From 30f14caa9ceadfe43abdfa12b3a9c1ab28f71629 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:32:15 -0500 Subject: [PATCH 30/36] add back generic package manager install statement --- INSTALLING.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 08f3539828..c95c6dfbfa 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -131,7 +131,9 @@ Install these tools with your system or virtual environment package manager. HOOMD developers have had success with ``pacman`` (`arch linux `_), ``apt-get`` (`ubuntu `_), `Homebrew `_ (macOS), and `MacPorts `_ (macOS). -Note that packages may be named differently, so check your system's package list and install any ``-dev`` packages as needed. +Note that packages may be named differently, so check your system's package list and install any ``-dev`` packages as needed. :: + + $ your-package-manager install python python-numpy pybind11 eigen cmake openmpi cereal cuda Typical HPC cluster environments provide python, numpy, cmake, cuda, and mpi, via a module system:: From 307e5cc06af3ce81f50336975d38fe037e735480 Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 12:45:39 -0500 Subject: [PATCH 31/36] decrease some indenting just in case and fix typo --- INSTALLING.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index 7c6fad4643..7887837974 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -113,7 +113,7 @@ Install prerequisites - NVIDIA CUDA Toolkit >= 9.0 - *OR* + *OR* - `AMD ROCm >= 3.5.0 `_ with additional dependencies: @@ -128,9 +128,9 @@ Install prerequisites For HOOMD-blue on AMD GPUs, the following limitations currently apply. - 1. Certain HOOMD-blue kernels trigger a `unknown HSA error `_. - 2. The `mpcd` component is disabled on AMD GPUs. - 3. Multi-GPU execution via unified memory is not available. + 1. Certain HOOMD-blue kernels trigger an `unknown HSA error `_. + 2. The `mpcd` component is disabled on AMD GPUs. + 3. Multi-GPU execution via unified memory is not available. **For threaded parallelism on the CPU** (required when ``ENABLE_TBB=on``) From e61ce073c8c32853266758b585b0e7977c46b0bd Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Tue, 15 Dec 2020 13:17:40 -0500 Subject: [PATCH 32/36] move activating virtual env & exporting CMAKE_PREFIX to compile sect --- INSTALLING.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/INSTALLING.rst b/INSTALLING.rst index c95c6dfbfa..3195a56a01 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -159,15 +159,16 @@ Note that other types of virtual environments $ python3 -m venv /path/to/environment --system-site-packages + +Compile HOOMD-blue +------------------ + Activate the environment and tell CMake to search for packages there before configuring and installing **HOOMD-blue**. :: $ source /path/to/environment/bin/activate $ export CMAKE_PREFIX_PATH=/path/to/environment -Compile HOOMD-blue ------------------- - By default, **HOOMD-blue** configures a *Release* optimized build type for a generic CPU architecture and with no optional libraries:: From e81e5e9a9a5ecfbdbf729259f5d944e2d2222876 Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Tue, 15 Dec 2020 14:09:40 -0500 Subject: [PATCH 33/36] Removed commented code --- hoomd/Trigger.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/hoomd/Trigger.cc b/hoomd/Trigger.cc index dc73ad2257..00ed418640 100644 --- a/hoomd/Trigger.cc +++ b/hoomd/Trigger.cc @@ -35,9 +35,6 @@ void export_Trigger(pybind11::module& m) .def(pybind11::init<>()) .def("__call__", &Trigger::operator()) .def("compute", &Trigger::compute) - /* .def(pybind11::pickle( */ - /* [](const TriggerPy& trigger){ return pybind11::tuple(); }, */ - /* [](pybind11::tuple params){ return TriggerPy(); })) */ ; pybind11::class_ Date: Tue, 15 Dec 2020 17:34:13 -0500 Subject: [PATCH 34/36] Update change log --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea80836dce..f01555c641 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,12 @@ v3.0.0-beta.3 (not yet released) - ``hoomd.filter.ParticleFilter`` objects are picklable. - ``hoomd.trigger.Trigger`` objects are picklable. +*Changed* + +*Fixed* + +- ``hoomd.variant.Power`` objects now have a ``t_ramp`` attribute as documented. + v3.0.0-beta.2 (2020-12-15) ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 607ec5d26a42dfc865fca7ce05ee622bac840de5 Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Tue, 15 Dec 2020 17:35:17 -0500 Subject: [PATCH 35/36] Fix incorrect conflict resolution in change log --- CHANGELOG.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f01555c641..5594c45975 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -48,7 +48,6 @@ v3.0.0-beta.2 (2020-12-15) *Fixed* -- ``hoomd.variant.Power`` objects now have a ``t_ramp`` attribute as documented. - ``Simulation.run`` now ends with a ``KeyboardInterrupt`` exception when Jupyter interrupts the kernel. - Logging the state of specific objects with nested attributes. From b69175cf694bdc36a6e0e602b5171cf1c7943f2e Mon Sep 17 00:00:00 2001 From: "Joshua A. Anderson" Date: Tue, 15 Dec 2020 19:28:55 -0500 Subject: [PATCH 36/36] Update change log --- CHANGELOG.rst | 2 ++ INSTALLING.rst | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5594c45975..9b7521f03b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,8 @@ v3.0.0-beta.3 (not yet released) *Changed* +- Improved compilation docs. + *Fixed* - ``hoomd.variant.Power`` objects now have a ``t_ramp`` attribute as documented. diff --git a/INSTALLING.rst b/INSTALLING.rst index f0bedd6c78..00a0b98c8e 100644 --- a/INSTALLING.rst +++ b/INSTALLING.rst @@ -203,8 +203,7 @@ Execute longer running validation tests:: On a cluster, run tests within a job on a GPU compute node. -With ``CMAKE_PREFIX_PATH`` pointing to your desired python environment, -install **HOOMD-blue** into your Python environment:: +Install **HOOMD-blue** into your Python environment:: $ make install