diff --git a/doc/sphinx/io.rst b/doc/sphinx/io.rst
index 66f07f5b6ab..520b06b6fe6 100644
--- a/doc/sphinx/io.rst
+++ b/doc/sphinx/io.rst
@@ -111,11 +111,6 @@ Be aware of the following limitations:
for a specific combination of features, please share your findings
with the |es| community.
-* Checkpointing only supports recursion on the head node. It is therefore
- impossible to checkpoint a :class:`espressomd.system.System` instance that
- contains LB boundaries, constraint unions or auto-update accumulators when the
- simulation is running with 2 or more MPI nodes.
-
* The active actors, i.e., the content of ``system.actors``, are checkpointed.
For lattice-Boltzmann fluids, this only includes the parameters such as the
lattice constant (``agrid``). The actual flow field has to be saved
diff --git a/src/python/espressomd/interactions.py b/src/python/espressomd/interactions.py
index f6e9578cf80..c765f4bfea6 100644
--- a/src/python/espressomd/interactions.py
+++ b/src/python/espressomd/interactions.py
@@ -1546,8 +1546,6 @@ def add(self, *args):
return bond_id
def __getitem__(self, bond_id):
- self._assert_key_type(bond_id)
-
if self.call_method('has_bond', bond_id=bond_id):
bond_obj = self.call_method('get_bond', bond_id=bond_id)
bond_obj._bond_id = bond_id
@@ -1590,7 +1588,6 @@ def _insert_bond(self, bond_id, bond_obj):
bond_id = self.call_method("insert", object=bond_obj)
else:
# Throw error if attempting to overwrite a bond of different type
- self._assert_key_type(bond_id)
if self.call_method("contains", key=bond_id):
old_type = self._bond_classes[
self.call_method("get_zero_based_type", bond_id=bond_id)]
@@ -1625,3 +1622,14 @@ def __getstate__(self):
def __setstate__(self, params):
for bond_id, (type_number, bond_params) in params.items():
self[bond_id] = self._bond_classes[type_number](**bond_params)
+
+ def __reduce__(self):
+ so_callback, (so_name, so_bytestring) = super().__reduce__()
+ return (BondedInteractions._restore_object,
+ (so_callback, (so_name, so_bytestring), self.__getstate__()))
+
+ @classmethod
+ def _restore_object(cls, so_callback, so_callback_args, state):
+ so = so_callback(*so_callback_args)
+ so.__setstate__(state)
+ return so
diff --git a/src/python/espressomd/script_interface.pyx b/src/python/espressomd/script_interface.pyx
index 940c1f3e2cb..39a2121c845 100644
--- a/src/python/espressomd/script_interface.pyx
+++ b/src/python/espressomd/script_interface.pyx
@@ -457,15 +457,6 @@ class ScriptObjectList(ScriptInterfaceHelper):
"""
- def __init__(self, *args, **kwargs):
- if args:
- params, (_unpickle_so_class, (_so_name, bytestring)) = args
- assert _so_name == self._so_name
- self = _unpickle_so_class(_so_name, bytestring)
- self.__setstate__(params)
- else:
- super().__init__(**kwargs)
-
def __getitem__(self, key):
return self.call_method("get_elements")[key]
@@ -477,24 +468,6 @@ class ScriptObjectList(ScriptInterfaceHelper):
def __len__(self):
return self.call_method("size")
- @classmethod
- def _restore_object(cls, so_callback, so_callback_args, state):
- so = so_callback(*so_callback_args)
- so.__setstate__(state)
- return so
-
- def __reduce__(self):
- so_callback, (so_name, so_bytestring) = super().__reduce__()
- return (ScriptObjectList._restore_object,
- (so_callback, (so_name, so_bytestring), self.__getstate__()))
-
- def __getstate__(self):
- return self.call_method("get_elements")
-
- def __setstate__(self, object_list):
- for item in object_list:
- self.add(item)
-
class ScriptObjectMap(ScriptInterfaceHelper):
"""
@@ -507,17 +480,6 @@ class ScriptObjectMap(ScriptInterfaceHelper):
"""
- _key_type = int
-
- def __init__(self, *args, **kwargs):
- if args:
- params, (_unpickle_so_class, (_so_name, bytestring)) = args
- assert _so_name == self._so_name
- self = _unpickle_so_class(_so_name, bytestring)
- self.__setstate__(params)
- else:
- super().__init__(**kwargs)
-
def remove(self, key):
"""
Remove the element with the given key.
@@ -536,15 +498,12 @@ class ScriptObjectMap(ScriptInterfaceHelper):
return self.call_method("size")
def __getitem__(self, key):
- self._assert_key_type(key)
return self.call_method("get", key=key)
def __setitem__(self, key, value):
- self._assert_key_type(key)
self.call_method("insert", key=key, object=value)
def __delitem__(self, key):
- self._assert_key_type(key)
self.call_method("erase", key=key)
def keys(self):
@@ -556,28 +515,6 @@ class ScriptObjectMap(ScriptInterfaceHelper):
def items(self):
for k in self.keys(): yield k, self[k]
- def _assert_key_type(self, key):
- if not utils.is_valid_type(key, self._key_type):
- raise TypeError(f"Key has to be of type {self._key_type.__name__}")
-
- @classmethod
- def _restore_object(cls, so_callback, so_callback_args, state):
- so = so_callback(*so_callback_args)
- so.__setstate__(state)
- return so
-
- def __reduce__(self):
- so_callback, (so_name, so_bytestring) = super().__reduce__()
- return (ScriptObjectMap._restore_object,
- (so_callback, (so_name, so_bytestring), self.__getstate__()))
-
- def __getstate__(self):
- return dict(self.items())
-
- def __setstate__(self, params):
- for key, val in params.items():
- self[key] = val
-
# Map from script object names to their corresponding python classes
_python_class_by_so_name = {}
diff --git a/src/script_interface/CMakeLists.txt b/src/script_interface/CMakeLists.txt
index f53e6c85e88..13736ca4090 100644
--- a/src/script_interface/CMakeLists.txt
+++ b/src/script_interface/CMakeLists.txt
@@ -19,8 +19,8 @@
add_library(
espresso_script_interface SHARED
- initialize.cpp ObjectHandle.cpp object_container_mpi_guard.cpp
- GlobalContext.cpp ContextManager.cpp ParallelExceptionHandler.cpp)
+ initialize.cpp ObjectHandle.cpp GlobalContext.cpp ContextManager.cpp
+ ParallelExceptionHandler.cpp)
add_library(espresso::script_interface ALIAS espresso_script_interface)
set_target_properties(espresso_script_interface
PROPERTIES CXX_CLANG_TIDY "${ESPRESSO_CXX_CLANG_TIDY}")
diff --git a/src/script_interface/ObjectContainer.hpp b/src/script_interface/ObjectContainer.hpp
new file mode 100644
index 00000000000..f5a8e89e2da
--- /dev/null
+++ b/src/script_interface/ObjectContainer.hpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The ESPResSo project
+ *
+ * This file is part of ESPResSo.
+ *
+ * ESPResSo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ESPResSo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef SCRIPT_INTERFACE_OBJECT_CONTAINER_HPP
+#define SCRIPT_INTERFACE_OBJECT_CONTAINER_HPP
+
+#include "script_interface/auto_parameters/AutoParameters.hpp"
+
+#include
+
+namespace ScriptInterface {
+
+/**
+ * @brief Base class for containers whose @c BaseType might be a full
+ * specialization of @ref AutoParameters.
+ */
+template class Container, typename ManagedType,
+ class BaseType,
+ class =
+ std::enable_if_t>>
+using ObjectContainer = std::conditional_t<
+ std::is_same_v,
+ AutoParameters, BaseType>, BaseType>;
+
+} // namespace ScriptInterface
+
+#endif
diff --git a/src/script_interface/ObjectList.hpp b/src/script_interface/ObjectList.hpp
index 65e9332a8b6..72b7bb8f9b7 100644
--- a/src/script_interface/ObjectList.hpp
+++ b/src/script_interface/ObjectList.hpp
@@ -22,9 +22,9 @@
#ifndef SCRIPT_INTERFACE_OBJECT_LIST_HPP
#define SCRIPT_INTERFACE_OBJECT_LIST_HPP
+#include "script_interface/ObjectContainer.hpp"
#include "script_interface/ScriptInterface.hpp"
#include "script_interface/get_value.hpp"
-#include "script_interface/object_container_mpi_guard.hpp"
#include
@@ -35,20 +35,39 @@
#include
namespace ScriptInterface {
+
/**
* @brief Owning list of ObjectHandles
* @tparam ManagedType Type of the managed objects, needs to be
- * derived from ObjectHandle
+ * derived from @ref ObjectHandle
*/
-template >>
-class ObjectList : public BaseType {
+template
+class ObjectList : public ObjectContainer {
+public:
+ using Base = ObjectContainer;
+ using Base::add_parameters;
+
private:
+ std::vector> m_elements;
+
virtual void add_in_core(const std::shared_ptr &obj_ptr) = 0;
virtual void remove_in_core(const std::shared_ptr &obj_ptr) = 0;
public:
+ ObjectList() {
+ add_parameters({
+ {"_objects", AutoParameter::read_only,
+ [this]() { return make_vector_of_variants(m_elements); }},
+ });
+ }
+
+ void do_construct(VariantMap const ¶ms) override {
+ m_elements = get_value_or(params, "_objects", {});
+ for (auto const &object : m_elements) {
+ add_in_core(object);
+ }
+ }
+
/**
* @brief Add an element to the list.
*
@@ -107,12 +126,7 @@ class ObjectList : public BaseType {
}
if (method == "get_elements") {
- std::vector ret;
- ret.reserve(m_elements.size());
- for (auto const &e : m_elements)
- ret.emplace_back(e);
-
- return ret;
+ return make_vector_of_variants(m_elements);
}
if (method == "clear") {
@@ -128,33 +142,8 @@ class ObjectList : public BaseType {
return m_elements.empty();
}
- return BaseType::do_call_method(method, parameters);
+ return Base::do_call_method(method, parameters);
}
-
-private:
- std::string get_internal_state() const override {
- object_container_mpi_guard(BaseType::name(), m_elements.size(),
- BaseType::context()->get_comm().size());
-
- std::vector object_states(m_elements.size());
-
- boost::transform(m_elements, object_states.begin(),
- [](auto const &e) { return e->serialize(); });
-
- return Utils::pack(object_states);
- }
-
- void set_internal_state(std::string const &state) override {
- auto const object_states = Utils::unpack>(state);
-
- for (auto const &packed_object : object_states) {
- auto o = std::dynamic_pointer_cast(
- BaseType::deserialize(packed_object, *BaseType::context()));
- add(std::move(o));
- }
- }
-
- std::vector> m_elements;
};
} // Namespace ScriptInterface
#endif
diff --git a/src/script_interface/ObjectMap.hpp b/src/script_interface/ObjectMap.hpp
index ee763c4f9d7..d62e76b11fb 100644
--- a/src/script_interface/ObjectMap.hpp
+++ b/src/script_interface/ObjectMap.hpp
@@ -22,11 +22,10 @@
#ifndef SCRIPT_INTERFACE_OBJECT_MAP_HPP
#define SCRIPT_INTERFACE_OBJECT_MAP_HPP
+#include "script_interface/ObjectContainer.hpp"
#include "script_interface/ScriptInterface.hpp"
+#include "script_interface/Variant.hpp"
#include "script_interface/get_value.hpp"
-#include "script_interface/object_container_mpi_guard.hpp"
-
-#include
#include
#include
@@ -38,13 +37,18 @@ namespace ScriptInterface {
/**
* @brief Owning map of ObjectHandles
* @tparam ManagedType Type of the managed objects, needs to be
- * derived from ObjectHandle
+ * derived from @ref ObjectHandle
*/
-template <
- typename ManagedType, class BaseType = ObjectHandle, class KeyType = int,
- class = std::enable_if_t>>
-class ObjectMap : public BaseType {
+template
+class ObjectMap : public ObjectContainer {
+public:
+ using Base = ObjectContainer;
+ using Base::add_parameters;
+
private:
+ std::unordered_map> m_elements;
+
virtual KeyType
insert_in_core(std::shared_ptr const &obj_ptr) = 0;
virtual void insert_in_core(KeyType const &key,
@@ -52,6 +56,20 @@ class ObjectMap : public BaseType {
virtual void erase_in_core(KeyType const &key) = 0;
public:
+ ObjectMap() {
+ add_parameters({
+ {"_objects", AutoParameter::read_only,
+ [this]() { return make_unordered_map_of_variants(m_elements); }},
+ });
+ }
+
+ void do_construct(VariantMap const ¶ms) override {
+ m_elements = get_value_or(params, "_objects", {});
+ for (auto const &[key, element] : m_elements) {
+ insert_in_core(key, element);
+ }
+ }
+
/**
* @brief Add an element to the map.
*
@@ -110,7 +128,7 @@ class ObjectMap : public BaseType {
get_value>(parameters.at("object"));
if (parameters.count("key")) {
- auto key = get_value(parameters.at("key"));
+ auto const key = get_key(parameters.at("key"));
insert(key, obj_ptr);
return none;
}
@@ -118,14 +136,13 @@ class ObjectMap : public BaseType {
}
if (method == "erase") {
- auto key = get_value(parameters.at("key"));
-
+ auto const key = get_key(parameters.at("key"));
erase(key);
return none;
}
if (method == "get") {
- auto key = get_value(parameters.at("key"));
+ auto const key = get_key(parameters.at("key"));
return Variant{m_elements.at(key)};
}
@@ -155,40 +172,26 @@ class ObjectMap : public BaseType {
}
if (method == "contains") {
- return m_elements.find(get_value(parameters.at("key"))) !=
- m_elements.end();
+ return m_elements.find(get_key(parameters.at("key"))) != m_elements.end();
}
- return BaseType::do_call_method(method, parameters);
+ return Base::do_call_method(method, parameters);
}
-private:
- std::string get_internal_state() const override {
- object_container_mpi_guard(BaseType::name(), m_elements.size(),
- BaseType::context()->get_comm().size());
-
- using packed_type = std::pair;
- std::vector object_states(m_elements.size());
-
- boost::transform(m_elements, object_states.begin(), [](auto const &kv) {
- return std::make_pair(kv.first, kv.second->serialize());
- });
-
- return Utils::pack(object_states);
- }
-
- void set_internal_state(std::string const &state) override {
- using packed_type = std::pair;
- auto const object_states = Utils::unpack>(state);
-
- for (auto const &packed_object : object_states) {
- auto o = std::dynamic_pointer_cast(
- BaseType::deserialize(packed_object.second, *BaseType::context()));
- insert(packed_object.first, std::move(o));
+ KeyType get_key(Variant const &key) const {
+ try {
+ return get_value(key);
+ } catch (...) {
+ using namespace detail::demangle;
+ auto const actual = simplify_symbol_variant(key);
+ auto const target = simplify_symbol(static_cast(nullptr));
+ if (Base::context()->is_head_node()) {
+ throw std::invalid_argument("Key has to be of type '" + target +
+ "', got type '" + actual + "'");
+ }
+ throw;
}
}
-
- std::unordered_map> m_elements;
};
} // Namespace ScriptInterface
#endif
diff --git a/src/script_interface/interactions/BondedInteractions.hpp b/src/script_interface/interactions/BondedInteractions.hpp
index fe92ef1e196..92b65880354 100644
--- a/src/script_interface/interactions/BondedInteractions.hpp
+++ b/src/script_interface/interactions/BondedInteractions.hpp
@@ -29,6 +29,7 @@
#include
#include
+#include
#include
#include
#include
@@ -44,6 +45,16 @@ class BondedInteractions : public ObjectMap {
using key_type = typename container_type::key_type;
using mapped_type = typename container_type::mapped_type;
+ BondedInteractions() : ObjectMap::ObjectMap() {
+ add_parameters({
+ {"_objects", AutoParameter::read_only,
+ []() {
+ // deactivate serialization (done at the Python level)
+ return make_unordered_map_of_variants(container_type{});
+ }},
+ });
+ }
+
key_type insert_in_core(mapped_type const &obj_ptr) override {
auto const key = ::bonded_ia_params.insert(obj_ptr->bonded_ia());
m_bonds[key] = std::move(obj_ptr);
@@ -64,6 +75,7 @@ class BondedInteractions : public ObjectMap {
mpi_update_cell_system_ia_range_local();
}
+protected:
Variant do_call_method(std::string const &name,
VariantMap const ¶ms) override {
if (name == "get_size") {
@@ -78,12 +90,12 @@ class BondedInteractions : public ObjectMap {
}
if (name == "has_bond") {
- auto const bond_id = get_value(params, "bond_id");
+ auto const bond_id = get_key(params.at("bond_id"));
return {m_bonds.count(bond_id) != 0};
}
if (name == "get_bond") {
- auto const bond_id = get_value(params, "bond_id");
+ auto const bond_id = get_key(params.at("bond_id"));
// core and script interface must agree
assert(m_bonds.count(bond_id) == ::bonded_ia_params.count(bond_id));
if (not context()->is_head_node())
@@ -97,7 +109,7 @@ class BondedInteractions : public ObjectMap {
}
if (name == "get_zero_based_type") {
- auto const bond_id = get_value(params, "bond_id");
+ auto const bond_id = get_key(params.at("bond_id"));
return ::bonded_ia_params.get_zero_based_type(bond_id);
}
@@ -105,9 +117,6 @@ class BondedInteractions : public ObjectMap {
}
private:
- // disable serialization: pickling done by the python interface
- std::string get_internal_state() const override { return {}; }
- void set_internal_state(std::string const &state) override {}
container_type m_bonds;
};
} // namespace Interactions
diff --git a/src/script_interface/object_container_mpi_guard.cpp b/src/script_interface/object_container_mpi_guard.cpp
deleted file mode 100644
index da62287b3f2..00000000000
--- a/src/script_interface/object_container_mpi_guard.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021-2022 The ESPResSo project
- *
- * This file is part of ESPResSo.
- *
- * ESPResSo is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ESPResSo is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include "script_interface/object_container_mpi_guard.hpp"
-
-#include
-#include
-
-#include
-#include
-#include
-
-void object_container_mpi_guard(boost::string_ref const &name,
- std::size_t n_elements, int world_size) {
- if (world_size > 1 and n_elements) {
- std::stringstream error_msg;
- error_msg << "Non-empty object containers do not support checkpointing in "
- << "MPI environments. Container " << name << " contains "
- << n_elements << " elements.";
- throw std::runtime_error(error_msg.str());
- }
-}
diff --git a/src/script_interface/object_container_mpi_guard.hpp b/src/script_interface/object_container_mpi_guard.hpp
deleted file mode 100644
index bb9723c5e6f..00000000000
--- a/src/script_interface/object_container_mpi_guard.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021-2022 The ESPResSo project
- *
- * This file is part of ESPResSo.
- *
- * ESPResSo is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ESPResSo is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-#ifndef ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP
-#define ESPRESSO_OBJECT_CONTAINER_MPI_GUARD_HPP
-
-#include
-#include
-
-#include
-
-/**
- * @brief Prevent object container serialization.
- *
- * The @ref ScriptInterface::ObjectHandle framework doesn't support
- * recursive deserialization. When an object container such as
- * @ref ScriptInterface::ObjectList is deserialized, the contained
- * objects are deserialized on the head node only, which leads to
- * silent bugs in simulations.
- *
- * This function needs to be called from an object container
- * get_internal_state() method to throw a runtime error
- * when the container is not empty and the MPI world size is
- * greater than 1.
- *
- * @param name Name of the object container
- * @param n_elements Number of elements in the container
- * @param world_size MPI world size
- */
-void object_container_mpi_guard(boost::string_ref const &name,
- std::size_t n_elements, int world_size);
-
-#endif
diff --git a/src/script_interface/tests/CMakeLists.txt b/src/script_interface/tests/CMakeLists.txt
index 35b14fed086..0fc25729ec1 100644
--- a/src/script_interface/tests/CMakeLists.txt
+++ b/src/script_interface/tests/CMakeLists.txt
@@ -47,9 +47,6 @@ unit_test(NAME ObjectList_test SRC ObjectList_test.cpp DEPENDS
espresso::script_interface espresso::core Boost::mpi)
unit_test(NAME ObjectMap_test SRC ObjectMap_test.cpp DEPENDS
espresso::script_interface espresso::core Boost::mpi)
-unit_test(NAME serialization_mpi_guard_test SRC
- serialization_mpi_guard_test.cpp DEPENDS espresso::script_interface
- Boost::mpi MPI::MPI_CXX NUM_PROC 2)
unit_test(NAME Accumulators_test SRC Accumulators_test.cpp DEPENDS
espresso::script_interface espresso::core)
unit_test(NAME Constraints_test SRC Constraints_test.cpp DEPENDS
diff --git a/src/script_interface/tests/serialization_mpi_guard_test.cpp b/src/script_interface/tests/serialization_mpi_guard_test.cpp
deleted file mode 100644
index 72749046f97..00000000000
--- a/src/script_interface/tests/serialization_mpi_guard_test.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2022 The ESPResSo project
- *
- * This file is part of ESPResSo.
- *
- * ESPResSo is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ESPResSo is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#define BOOST_TEST_NO_MAIN
-#define BOOST_TEST_MODULE Object container MPI guard test
-#define BOOST_TEST_DYN_LINK
-#include
-
-#include "script_interface/GlobalContext.hpp"
-#include "script_interface/ObjectList.hpp"
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-using ScriptInterface::ObjectHandle;
-
-namespace Testing {
-struct ObjectContainer : ScriptInterface::ObjectList {
- std::vector> objects;
-
-private:
- void add_in_core(std::shared_ptr const &obj_ptr) override {
- objects.push_back(obj_ptr);
- }
- void remove_in_core(std::shared_ptr const &obj_ptr) override {
- objects.erase(std::remove(objects.begin(), objects.end(), obj_ptr),
- objects.end());
- }
-};
-} // namespace Testing
-
-BOOST_AUTO_TEST_CASE(parallel_exception) {
- boost::mpi::communicator world;
- Utils::Factory factory;
- factory.register_new("ObjectContainer");
- Communication::MpiCallbacks cb{world};
- auto ctx = std::make_shared(
- cb, std::make_shared(factory, world));
-
- if (world.rank() == 0) {
- auto const obj_ptr = std::make_shared();
- auto const predicate = [](std::exception const &ex) {
- std::string message =
- "Non-empty object containers do not support checkpointing in MPI "
- "environments. Container ObjectContainer contains 1 elements.";
- return ex.what() == message;
- };
-
- auto list_so = ctx->make_shared("ObjectContainer", {});
- auto &list = dynamic_cast(*list_so);
- BOOST_CHECK_NO_THROW(list.serialize());
-
- list.add(obj_ptr);
- if (world.size() > 1) {
- BOOST_CHECK_EXCEPTION(list.serialize(), std::runtime_error, predicate);
- }
-
- list.remove(obj_ptr);
- BOOST_CHECK_NO_THROW(list.serialize());
- } else {
- cb.loop();
- }
-}
-
-int main(int argc, char **argv) {
- boost::mpi::environment mpi_env(argc, argv);
-
- return boost::unit_test::unit_test_main(init_unit_test, argc, argv);
-}
diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py
index 9a374c40c91..c0565641d8a 100644
--- a/testsuite/python/bond_breakage.py
+++ b/testsuite/python/bond_breakage.py
@@ -84,12 +84,12 @@ def test_00_interface(self):
self.system.bond_breakage.clear()
self.assertEqual(len(self.system.bond_breakage), 0)
self.assertEqual(self.system.bond_breakage.keys(), [])
- with self.assertRaisesRegex(TypeError, "Key has to be of type int"):
+ with self.assertRaisesRegex(ValueError, "Key has to be of type 'int'"):
self.system.bond_breakage[None]
- with self.assertRaisesRegex(TypeError, "Key has to be of type int"):
- self.system.bond_breakage[None] = None
- with self.assertRaisesRegex(TypeError, "Key has to be of type int"):
- self.system.bond_breakage.remove(None)
+ with self.assertRaisesRegex(ValueError, "Key has to be of type 'int'"):
+ self.system.bond_breakage[None] = spec2
+ with self.assertRaisesRegex(ValueError, "Key has to be of type 'int', got type 'double'"):
+ self.system.bond_breakage.remove(1.)
with self.assertRaisesRegex(ValueError, "Bond needs to be added to the system first"):
self.system.bond_breakage[HarmonicBond(k=1, r_0=0)]
with self.assertRaisesRegex(RuntimeError, "Inserting breakage spec without a bond type is not permitted"):
diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py
index 38b2b09df86..e7d3d07e210 100644
--- a/testsuite/python/save_checkpoint.py
+++ b/testsuite/python/save_checkpoint.py
@@ -64,8 +64,6 @@
for filepath in path_cpt_root.iterdir():
filepath.unlink(missing_ok=True)
-n_nodes = system.cell_system.get_state()["n_nodes"]
-
# Lees-Edwards boundary conditions
if 'INT.NPT' not in modes:
protocol = espressomd.lees_edwards.LinearShear(
@@ -186,8 +184,7 @@
union = espressomd.shapes.Union()
union.add([espressomd.shapes.Wall(normal=[1., 0., 0.], dist=0.5),
espressomd.shapes.Wall(normal=[0., 1., 0.], dist=1.5)])
-if n_nodes == 1:
- system.constraints.add(shape=union, particle_type=2)
+system.constraints.add(shape=union, particle_type=2)
if espressomd.has_features("ELECTROSTATICS"):
system.constraints.add(espressomd.constraints.ElectricPlaneWave(
E0=[1., -2., 3.], k=[-.1, .2, .3], omega=5., phi=1.4))
diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py
index 4c99af5bd01..18e3088e050 100644
--- a/testsuite/python/test_checkpoint.py
+++ b/testsuite/python/test_checkpoint.py
@@ -55,7 +55,6 @@ class CheckpointTest(ut.TestCase):
**config.get_checkpoint_params())
checkpoint.load(0)
path_cpt_root = pathlib.Path(checkpoint.checkpoint_dir)
- n_nodes = system.cell_system.get_state()["n_nodes"]
@classmethod
def setUpClass(cls):
@@ -720,9 +719,7 @@ def check_lb_boundaries(self, remove_boundaries=False):
np.testing.assert_equal(np.copy(lbf[:, :, :].boundary.astype(int)), 0)
def test_constraints(self):
- n_contraints = 7
- if self.n_nodes == 1:
- n_contraints += 1
+ n_contraints = 8
if espressomd.has_features("ELECTROSTATICS"):
n_contraints += 1
self.assertEqual(len(system.constraints), n_contraints)
@@ -771,20 +768,19 @@ def test_constraints(self):
np.testing.assert_allclose(np.copy(c[6].field), np.copy(ref_vec.field),
atol=1e-10)
- if self.n_nodes == 1:
- union = c[7].shape
- self.assertIsInstance(union, espressomd.shapes.Union)
- self.assertEqual(c[7].particle_type, 2)
- self.assertEqual(len(union), 2)
- wall1, wall2 = union.call_method('get_elements')
- self.assertIsInstance(wall1, espressomd.shapes.Wall)
- self.assertIsInstance(wall2, espressomd.shapes.Wall)
- np.testing.assert_allclose(np.copy(wall1.normal),
- [1., 0., 0.], atol=1e-10)
- np.testing.assert_allclose(np.copy(wall2.normal),
- [0., 1., 0.], atol=1e-10)
- np.testing.assert_allclose(wall1.dist, 0.5, atol=1e-10)
- np.testing.assert_allclose(wall2.dist, 1.5, atol=1e-10)
+ union = c[7].shape
+ self.assertIsInstance(union, espressomd.shapes.Union)
+ self.assertEqual(c[7].particle_type, 2)
+ self.assertEqual(len(union), 2)
+ wall1, wall2 = union.call_method('get_elements')
+ self.assertIsInstance(wall1, espressomd.shapes.Wall)
+ self.assertIsInstance(wall2, espressomd.shapes.Wall)
+ np.testing.assert_allclose(np.copy(wall1.normal),
+ [1., 0., 0.], atol=1e-10)
+ np.testing.assert_allclose(np.copy(wall2.normal),
+ [0., 1., 0.], atol=1e-10)
+ np.testing.assert_allclose(wall1.dist, 0.5, atol=1e-10)
+ np.testing.assert_allclose(wall2.dist, 1.5, atol=1e-10)
if espressomd.has_features("ELECTROSTATICS"):
wave = c[n_contraints - 1]
@@ -795,6 +791,23 @@ def test_constraints(self):
self.assertAlmostEqual(wave.omega, 5., delta=1E-10)
self.assertAlmostEqual(wave.phi, 1.4, delta=1E-10)
+ @utx.skipIfMissingFeatures("WCA")
+ @ut.skipIf("INT.SDM" in modes, "Stokesian integrator not supported")
+ @ut.skipIf("INT.BD" in modes, "Brownian integrator not supported")
+ @ut.skipIf("INT.SD" in modes, "Steepest descent not supported")
+ def test_union(self):
+ # the union shape is an object list, and should be properly
+ # deserialized on all MPI ranks
+ system.non_bonded_inter[2, 6].wca.set_params(epsilon=1., sigma=1.)
+ p1 = system.part.add(pos=[1., 1.6, 0.], type=6)
+ p2 = system.part.add(pos=[system.box_l[0] - 1., 1.6, 0.], type=6)
+ system.integrator.run(0, recalc_forces=True)
+ np.testing.assert_allclose(p1.f, [0., 1e8, 0.], atol=1e-3)
+ np.testing.assert_allclose(p2.f, [0., 1e8, 0.], atol=1e-3)
+ p1.remove()
+ p2.remove()
+ system.non_bonded_inter[2, 6].reset()
+
if __name__ == '__main__':
config.bind_test_class(CheckpointTest)