diff --git a/CMakeLists.txt b/CMakeLists.txt
index 43f5a7ef8a3..6e887417044 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@
# along with this program. If not, see .
#
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.11)
message(STATUS "CMake version: ${CMAKE_VERSION}")
if(POLICY CMP0076)
# make target_sources() convert relative paths to absolute
@@ -166,7 +166,7 @@ if(WITH_CUDA)
endif()
endif(WITH_CUDA)
-find_package(PythonInterp 3.5 REQUIRED)
+find_package(PythonInterp 3.6 REQUIRED)
if(WITH_PYTHON)
find_package(Cython 0.26 REQUIRED)
@@ -278,7 +278,6 @@ endif(WITH_STOKESIAN_DYNAMICS)
if(WITH_STOKESIAN_DYNAMICS)
set(CMAKE_INSTALL_LIBDIR
"${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTDIR}/espressomd")
- cmake_minimum_required(VERSION 3.11)
include(FetchContent)
FetchContent_Declare(
stokesian_dynamics
diff --git a/src/python/espressomd/actors.pyx b/src/python/espressomd/actors.pyx
index 53747d92948..30d6d53998d 100644
--- a/src/python/espressomd/actors.pyx
+++ b/src/python/espressomd/actors.pyx
@@ -57,7 +57,7 @@ cdef class Actor:
if k in self.valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
def _activate(self):
inter = self._get_interaction_type()
diff --git a/src/python/espressomd/analyze.pyx b/src/python/espressomd/analyze.pyx
index d1f80e9c9ce..163e3fff588 100644
--- a/src/python/espressomd/analyze.pyx
+++ b/src/python/espressomd/analyze.pyx
@@ -186,12 +186,12 @@ class Analysis:
for i in range(len(p1)):
if not is_valid_type(p1[i], int):
raise TypeError(
- "Particle types in p1 have to be of type int, got: " + repr(p1[i]))
+ f"Particle types in p1 have to be of type int, got: {repr(p1[i])}")
for i in range(len(p2)):
if not is_valid_type(p2[i], int):
raise TypeError(
- "Particle types in p2 have to be of type int, got: " + repr(p2[i]))
+ f"Particle types in p2 have to be of type int, got: {repr(p2[i])}")
return analyze.mindist(analyze.partCfg(), p1, p2)
@@ -249,7 +249,7 @@ class Analysis:
"The p_type keyword argument must be provided (particle type)")
check_type_or_throw_except(p_type, 1, int, "p_type has to be an int")
if p_type < 0 or p_type >= analyze.max_seen_particle_type:
- raise ValueError("Particle type {} does not exist!".format(p_type))
+ raise ValueError(f"Particle type {p_type} does not exist!")
return analyze.centerofmass(analyze.partCfg(), p_type)
@@ -736,8 +736,7 @@ class Analysis:
check_type_or_throw_except(
ptype, 1, int, "particle type has to be an int")
if ptype < 0 or ptype >= analyze.max_seen_particle_type:
- raise ValueError(
- "Particle type {} does not exist!".format(ptype))
+ raise ValueError(f"Particle type {ptype} does not exist!")
selection = self._system.part.select(lambda p: (p.type in p_type))
cm = np.mean(selection.pos, axis=0)
mat = np.zeros(shape=(3, 3))
@@ -787,7 +786,7 @@ class Analysis:
"The p_type keyword argument must be provided (particle type)")
check_type_or_throw_except(p_type, 1, int, "p_type has to be an int")
if p_type < 0 or p_type >= analyze.max_seen_particle_type:
- raise ValueError("Particle type {} does not exist!".format(p_type))
+ raise ValueError(f"Particle type {p_type} does not exist!")
analyze.momentofinertiamatrix(
analyze.partCfg(), p_type, MofImatrix)
diff --git a/src/python/espressomd/cellsystem.pyx b/src/python/espressomd/cellsystem.pyx
index 02cfc4ea458..48eadba1185 100644
--- a/src/python/espressomd/cellsystem.pyx
+++ b/src/python/espressomd/cellsystem.pyx
@@ -137,8 +137,8 @@ cdef class CellSystem:
def __set__(self, _node_grid):
if not np.prod(_node_grid) == n_nodes:
- raise ValueError("Number of available nodes " + str(
- n_nodes) + " and imposed node grid " + str(_node_grid) + " do not agree.")
+ raise ValueError(
+ f"Number of available nodes {n_nodes} and imposed node grid {_node_grid} do not agree.")
else:
node_grid[0] = _node_grid[0]
node_grid[1] = _node_grid[1]
diff --git a/src/python/espressomd/checkpointing.py b/src/python/espressomd/checkpointing.py
index 57141e10896..37023468dae 100644
--- a/src/python/espressomd/checkpointing.py
+++ b/src/python/espressomd/checkpointing.py
@@ -67,7 +67,7 @@ def __init__(self, checkpoint_id=None, checkpoint_path="."):
# update checkpoint counter
self.counter = 0
while os.path.isfile(os.path.join(
- self.checkpoint_dir, "{}.checkpoint".format(self.counter))):
+ self.checkpoint_dir, f"{self.counter}.checkpoint")):
self.counter += 1
# init signals
@@ -134,11 +134,11 @@ def register(self, *args):
# if not a in dir(self.calling_module):
if not self.__hasattr_submodule(self.calling_module, a):
raise KeyError(
- "The given object '{}' was not found in the current scope.".format(a))
+ f"The given object '{a}' was not found in the current scope.")
if a in self.checkpoint_objects:
raise KeyError(
- "The given object '{}' is already registered for checkpointing.".format(a))
+ f"The given object '{a}' is already registered for checkpointing.")
self.checkpoint_objects.append(a)
@@ -154,7 +154,7 @@ def unregister(self, *args):
for a in args:
if not isinstance(a, str) or a not in self.checkpoint_objects:
raise KeyError(
- "The given object '{}' was not registered for checkpointing yet.".format(a))
+ f"The given object '{a}' was not registered for checkpointing yet.")
self.checkpoint_objects.remove(a)
@@ -204,7 +204,7 @@ def save(self, checkpoint_index=None):
if checkpoint_index is None:
checkpoint_index = self.counter
filename = os.path.join(
- self.checkpoint_dir, "{}.checkpoint".format(checkpoint_index))
+ self.checkpoint_dir, f"{checkpoint_index}.checkpoint")
tmpname = filename + ".__tmp__"
with open(tmpname, "wb") as checkpoint_file:
@@ -226,7 +226,7 @@ def load(self, checkpoint_index=None):
checkpoint_index = self.get_last_checkpoint_index()
filename = os.path.join(
- self.checkpoint_dir, "{}.checkpoint".format(checkpoint_index))
+ self.checkpoint_dir, f"{checkpoint_index}.checkpoint")
with open(filename, "rb") as f:
checkpoint_data = pickle.load(f)
@@ -288,7 +288,7 @@ def register_signal(self, signum=None):
if signum in self.checkpoint_signals:
raise KeyError(
- "The signal {} is already registered for checkpointing.".format(signum))
+ f"The signal {signum} is already registered for checkpointing.")
signal.signal(signum, self.__signal_handler)
self.checkpoint_signals.append(signum)
diff --git a/src/python/espressomd/collision_detection.pyx b/src/python/espressomd/collision_detection.pyx
index 9bd28373acf..4674f84b789 100644
--- a/src/python/espressomd/collision_detection.pyx
+++ b/src/python/espressomd/collision_detection.pyx
@@ -216,7 +216,7 @@ class CollisionDetection(ScriptInterfaceHelper):
for key in self._int_mode:
if self._int_mode[key] == int_mode:
return key
- raise Exception("Unknown integer collision mode %d" % int_mode)
+ raise Exception(f"Unknown integer collision mode {int_mode}")
# Pickle support
def __reduce__(self):
diff --git a/src/python/espressomd/electrokinetics.pyx b/src/python/espressomd/electrokinetics.pyx
index ad24bf1242f..162016df24a 100644
--- a/src/python/espressomd/electrokinetics.pyx
+++ b/src/python/espressomd/electrokinetics.pyx
@@ -43,7 +43,7 @@ IF ELECTROKINETICS:
return ElectrokineticsRoutines(np.array(key))
else:
raise Exception(
- "%s is not a valid key. Should be a point on the nodegrid e.g. ek[0,0,0]," % key)
+ f"{key} is not a valid key. Should be a point on the nodegrid e.g. ek[0,0,0].")
def validate_params(self):
"""
@@ -431,7 +431,7 @@ IF ELECTROKINETICS:
return SpecieRoutines(np.array(key), self.id)
else:
raise Exception(
- "%s is not a valid key. Should be a point on the nodegrid e.g. species[0,0,0]," % key)
+ f"{key} is not a valid key. Should be a point on the nodegrid e.g. species[0,0,0].")
def __init__(self, **kwargs):
Species.py_number_of_species += 1
@@ -449,7 +449,7 @@ IF ELECTROKINETICS:
if k in self.valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
def valid_keys(self):
"""
diff --git a/src/python/espressomd/highlander.py b/src/python/espressomd/highlander.py
index f27d110062c..d170442bded 100644
--- a/src/python/espressomd/highlander.py
+++ b/src/python/espressomd/highlander.py
@@ -25,8 +25,7 @@ def __init__(self, cls):
self._cls = cls
def __str__(self):
- return "There can only be one instance of '{}' at any time.".format(
- self._cls)
+ return f"There can only be one instance of '{self._cls}' at any time."
def highlander(klass):
diff --git a/src/python/espressomd/interactions.pyx b/src/python/espressomd/interactions.pyx
index 791cd43fac6..ce67701e173 100644
--- a/src/python/espressomd/interactions.pyx
+++ b/src/python/espressomd/interactions.pyx
@@ -1825,37 +1825,37 @@ class BondedInteractionNotDefined:
self.__class__.__name__ + " not compiled into ESPResSo core")
def type_number(self):
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def type_name(self):
"""Name of interaction type.
"""
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def valid_keys(self):
"""All parameters that can be set.
"""
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def required_keys(self):
"""Parameters that have to be set.
"""
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def set_default_params(self):
"""Sets parameters that are not required to their default value.
"""
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def _get_params_from_es_core(self):
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
def _set_params_in_es_core(self):
- raise Exception(("%s has to be defined in myconfig.hpp.") % self.name)
+ raise Exception(f"{self.name} has to be defined in myconfig.hpp.")
class FeneBond(BondedInteraction):
@@ -2591,8 +2591,8 @@ class TabulatedAngle(_TabulatedBase):
"""
phi = [self._params["min"], self._params["max"]]
if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - self.pi) > 1e-5:
- raise ValueError("Tabulated angle expects forces/energies "
- "within the range [0, pi], got " + str(phi))
+ raise ValueError(f"Tabulated angle expects forces/energies "
+ f"within the range [0, pi], got {phi}")
class TabulatedDihedral(_TabulatedBase):
@@ -2631,8 +2631,8 @@ class TabulatedDihedral(_TabulatedBase):
"""
phi = [self._params["min"], self._params["max"]]
if abs(phi[0] - 0.) > 1e-5 or abs(phi[1] - 2 * self.pi) > 1e-5:
- raise ValueError("Tabulated dihedral expects forces/energies "
- "within the range [0, 2*pi], got " + str(phi))
+ raise ValueError(f"Tabulated dihedral expects forces/energies "
+ f"within the range [0, 2*pi], got {phi}")
IF TABULATED == 1:
@@ -3313,7 +3313,7 @@ class BondedInteractions:
# Check if the bonded interaction exists in ESPResSo core
if bond_type == -1:
raise ValueError(
- "The bonded interaction with the id " + str(key) + " is not yet defined.")
+ f"The bonded interaction with the id {key} is not yet defined.")
# Find the appropriate class representing such a bond
bond_class = bonded_interaction_classes[bond_type]
diff --git a/src/python/espressomd/lb.pyx b/src/python/espressomd/lb.pyx
index bb774784fd1..9fd7697e50d 100644
--- a/src/python/espressomd/lb.pyx
+++ b/src/python/espressomd/lb.pyx
@@ -57,7 +57,7 @@ cdef class HydrodynamicInteraction(Actor):
return LBFluidRoutines(np.array(key))
else:
raise Exception(
- "%s is not a valid key. Should be a point on the nodegrid e.g. lbf[0,0,0]," % key)
+ f"{key} is not a valid key. Should be a point on the nodegrid e.g. lbf[0,0,0].")
# validate the given parameters on actor initialization
####################################################
diff --git a/src/python/espressomd/particle_data.pyx b/src/python/espressomd/particle_data.pyx
index 4227de0fe71..0f11b0c04c1 100644
--- a/src/python/espressomd/particle_data.pyx
+++ b/src/python/espressomd/particle_data.pyx
@@ -1067,27 +1067,26 @@ cdef class ParticleHandle:
"""
if _partner in self.exclusions:
- raise Exception("Exclusion id {} already in exclusion list of particle {}".format(
- _partner, self._id))
+ raise Exception(
+ f"Exclusion id {_partner} already in exclusion list of particle {self._id}")
check_type_or_throw_except(
_partner, 1, int, "PID of partner has to be an int.")
if self._id == _partner:
raise Exception(
- "Cannot exclude of a particle with itself!\n->particle id %i, partner %i." % (self._id, _partner))
+ "Cannot exclude of a particle with itself!\n"
+ f"->particle id {self._id}, partner {_partner}.")
if change_exclusion(self._id, _partner, 0) == 1:
- raise Exception("Particle with id " +
- str(_partner) + " does not exist.")
+ raise Exception(f"Particle with id {_partner} does not exist.")
def delete_exclusion(self, _partner):
check_type_or_throw_except(
_partner, 1, int, "PID of partner has to be an int.")
if _partner not in self.exclusions:
- raise Exception("Particle with id " +
- str(_partner) + " is not in exclusion list.")
+ raise Exception(
+ f"Particle with id {_partner} is not in exclusion list.")
if change_exclusion(self._id, _partner, 1) == 1:
- raise Exception("Particle with id " +
- str(_partner) + " does not exist.")
+ raise Exception(f"Particle with id {_partner} does not exist.")
IF ENGINE:
property swimming:
@@ -1379,8 +1378,8 @@ cdef class ParticleHandle:
"""
if tuple(_bond) in self.bonds:
- raise Exception("Bond {} already exists on particle {}.".format(
- tuple(_bond), self._id))
+ raise Exception(
+ f"Bond {tuple(_bond)} already exists on particle {self._id}.")
bond = list(_bond) # As we will modify it
self.check_bond_or_throw_exception(bond)
@@ -1523,14 +1522,16 @@ cdef class _ParticleSliceImpl:
self.id_selection = np.array(slice_, dtype=int)
else:
raise TypeError(
- "ParticleSlice must be initialized with an instance of slice or range, or with a list, tuple, or ndarray of ints, but got {} of type {}".format((str(slice_), str(type(slice_)))))
+ f"ParticleSlice must be initialized with an instance of "
+ f"slice or range, or with a list, tuple, or ndarray of ints, "
+ f"but got {slice_} of type {type(slice_)}")
def _id_selection_from_slice(self, slice_):
"""Returns an ndarray of particle ids to be included in the
ParticleSlice for a given range or slice object.
"""
# Prevent negative bounds
- if (not slice_.start is None and slice_.start < 0) or\
+ if (not slice_.start is None and slice_.start < 0) or \
(not slice_.stop is None and slice_.stop < 0):
raise IndexError(
"Negative start and end ids are not supported on ParticleSlice")
@@ -1555,9 +1556,9 @@ cdef class _ParticleSliceImpl:
for pid in pid_list:
if not is_valid_type(pid, int):
raise TypeError(
- "Particle id must be an integer but got " + str(pid))
+ f"Particle id must be an integer but got {pid}")
if not particle_exists(pid):
- raise IndexError("Particle does not exist " + str(pid))
+ raise IndexError(f"Particle does not exist {pid}")
def __iter__(self):
return self._id_gen()
@@ -1606,9 +1607,9 @@ cdef class _ParticleSliceImpl:
pl = ParticleList()
for i in self.id_selection:
if pl.exists(i):
- res += str(pl[i]) + ", "
+ res += f"{pl[i]}, "
# Remove final comma
- return "ParticleSlice([" + res[:-2] + "])"
+ return f"ParticleSlice([{res[:-2]}])"
def update(self, P):
if "id" in P:
@@ -1662,7 +1663,7 @@ class ParticleSlice(_ParticleSliceImpl):
def __setattr__(self, name, value):
if name != "_chunk_size" and not hasattr(ParticleHandle, name):
raise AttributeError(
- "ParticleHandle does not have the attribute {}.".format(name))
+ f"ParticleHandle does not have the attribute {name}.")
super().__setattr__(name, value)
@@ -1796,7 +1797,7 @@ cdef class ParticleList:
P["id"] = get_maximal_particle_id() + 1
else:
if particle_exists(P["id"]):
- raise Exception("Particle %d already exists." % P["id"])
+ raise Exception(f"Particle {P['id']} already exists.")
# Prevent setting of contradicting attributes
IF DIPOLES:
@@ -2119,20 +2120,19 @@ def _add_particle_slice_properties():
elif np.shape(values)[0] == N:
set_slice_one_for_each(particle_slice, attribute, values)
else:
- raise Exception("Shape of value (%s) does not broadcast to shape of attribute (%s)." % (
- np.shape(values), target_shape))
+ raise Exception(
+ f"Value shape {np.shape(values)} does not broadcast to attribute shape {target_shape}.")
return
else: # fixed length vector quantity
-
if target_shape == np.shape(values):
set_slice_one_for_all(particle_slice, attribute, values)
elif target_shape == tuple(np.shape(values)[1:]) and np.shape(values)[0] == N:
set_slice_one_for_each(particle_slice, attribute, values)
else:
- raise Exception("Shape of value (%s) does not broadcast to shape of attribute (%s)." % (
- np.shape(values), target_shape))
+ raise Exception(
+ f"Value shape {np.shape(values)} does not broadcast to attribute shape {target_shape}.")
return
diff --git a/src/python/espressomd/polymer.pyx b/src/python/espressomd/polymer.pyx
index e6884aedbca..8a312171bcc 100644
--- a/src/python/espressomd/polymer.pyx
+++ b/src/python/espressomd/polymer.pyx
@@ -131,7 +131,7 @@ def linear_polymer_positions(**kwargs):
for k in kwargs:
if k not in valid_keys:
- raise ValueError("Unknown parameter '%s'" % k)
+ raise ValueError(f"Unknown parameter '{k}'")
params[k] = kwargs[k]
for k in required_keys:
diff --git a/src/python/espressomd/reaction_ensemble.pyx b/src/python/espressomd/reaction_ensemble.pyx
index 4f6f0698919..474a03b660a 100644
--- a/src/python/espressomd/reaction_ensemble.pyx
+++ b/src/python/espressomd/reaction_ensemble.pyx
@@ -426,7 +426,7 @@ cdef class ReactionEnsemble(ReactionAlgorithm):
if k in self._valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
self._set_params_in_es_core()
@@ -449,7 +449,7 @@ cdef class ConstantpHEnsemble(ReactionAlgorithm):
if k in self._valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
self._set_params_in_es_core()
@@ -495,7 +495,7 @@ cdef class WangLandauReactionEnsemble(ReactionAlgorithm):
if k in self._valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
self.WLRptr.reset(new CWangLandauReactionEnsemble(int(self._params["seed"])))
self.RE = self.WLRptr.get()
@@ -539,7 +539,7 @@ cdef class WangLandauReactionEnsemble(ReactionAlgorithm):
if k in self._valid_keys_add_collective_variable_degree_of_association():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
for k in self._required_keys_add_collective_variable_degree_of_association():
if k not in kwargs:
@@ -591,7 +591,7 @@ cdef class WangLandauReactionEnsemble(ReactionAlgorithm):
if k in self._valid_keys_add_collective_variable_potential_energy():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
for k in self._required_keys_add_collective_variable_potential_energy():
if k not in kwargs:
@@ -633,7 +633,7 @@ cdef class WangLandauReactionEnsemble(ReactionAlgorithm):
if k in self._valid_keys_set_wang_landau_parameters():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
deref(self.WLRptr).final_wang_landau_parameter = self._params[
"final_wang_landau_parameter"]
@@ -760,7 +760,7 @@ cdef class WidomInsertion(ReactionAlgorithm):
if k in self._valid_keys():
self._params[k] = kwargs[k]
else:
- raise KeyError("%s is not a valid key" % k)
+ raise KeyError(f"{k} is not a valid key")
self._set_params_in_es_core()
diff --git a/src/python/espressomd/system.pyx b/src/python/espressomd/system.pyx
index 6de9d7bb60f..5ddc28cc110 100644
--- a/src/python/espressomd/system.pyx
+++ b/src/python/espressomd/system.pyx
@@ -112,7 +112,7 @@ cdef class System:
def __init__(self, **kwargs):
global _system_created
- if (not _system_created):
+ if not _system_created:
self.globals = Globals()
if 'box_l' not in kwargs:
raise ValueError("Required argument box_l not provided.")
@@ -123,7 +123,7 @@ cdef class System:
System.__setattr__(self, arg, kwargs.get(arg))
else:
raise ValueError(
- "Property {} can not be set via argument to System class.".format(arg))
+ f"Property {arg} can not be set via argument to System class.")
self.actors = Actors()
self.analysis = Analysis(self)
self.auto_update_accumulators = AutoUpdateAccumulators()
diff --git a/src/python/espressomd/utils.pyx b/src/python/espressomd/utils.pyx
index 492c158206d..5246b29b1ee 100644
--- a/src/python/espressomd/utils.pyx
+++ b/src/python/espressomd/utils.pyx
@@ -33,23 +33,26 @@ cpdef check_type_or_throw_except(x, n, t, msg):
# Check whether x is an array/list/tuple or a single value
if n > 1:
if hasattr(x, "__getitem__"):
+ if len(x) != n:
+ raise ValueError(
+ msg + f" -- {len(x)} values were given but {n} were expected.")
for i in range(len(x)):
if not isinstance(x[i], t):
if not ((t == float and is_valid_type(x[i], int))
or (t == float and issubclass(type(x[i]), np.integer))) \
and not (t == int and issubclass(type(x[i]), np.integer)):
raise ValueError(
- msg + " -- Item " + str(i) + " was of type " + type(x[i]).__name__)
+ msg + f" -- Item {i} was of type {type(x[i]).__name__}")
else:
# if n>1, but the user passed a single value, also throw exception
raise ValueError(
- msg + " -- A single value was given but " + str(n) + " were expected.")
+ msg + f" -- A single value was given but {n} were expected.")
else:
# N=1 and a single value
if not isinstance(x, t):
if not (t == float and is_valid_type(x, int)) and not (
t == int and issubclass(type(x), np.integer)):
- raise ValueError(msg + " -- Got an " + type(x).__name__)
+ raise ValueError(msg + f" -- Got an {type(x).__name__}")
cdef np.ndarray create_nparray_from_double_array(double * x, int len_x):
@@ -93,14 +96,16 @@ cdef check_range_or_except(D, name, v_min, incl_min, v_max, incl_max):
or (not incl_min and not all(v > v_min for v in x)))) or \
(v_max != "inf" and ((incl_max and not all(v <= v_max for v in x))
or (not incl_max and not all(v < v_max for v in x)))):
- raise ValueError("In " + name + ": Some values in " + str(x) + "are out of range " +
- ("[" if incl_min else "]") + str(v_min) + "," + str(v_max) + ("]" if incl_max else "["))
+ raise ValueError(f"In {name}: Some values in {x} are out of"
+ f" range {'[' if incl_min else ']'}{v_min},"
+ f"{v_max}{']' if incl_max else '['}")
# Single Value
else:
if (v_min != "inf" and ((incl_min and x < v_min) or (not incl_min and x <= v_min)) or
v_max != "inf" and ((incl_max and x > v_max) or (not incl_max and x >= v_max))):
- raise ValueError("In " + name + ": Value " + str(x) + " is out of range " + ("[" if incl_min else "]") +
- str(v_min) + "," + str(v_max) + ("]" if incl_max else "["))
+ raise ValueError(f"In {name}: Value {x} is out of"
+ f" range {'[' if incl_min else ']'}{v_min},"
+ f"{v_max}{']' if incl_max else '['}")
def to_char_pointer(s):
@@ -286,10 +291,9 @@ def requires_experimental_features(reason):
def exception_raiser(self, *args, **kwargs):
raise Exception(
- "Class " +
- self.__class__.__name__ +
- " is experimental. Define EXPERIMENTAL_FEATURES in myconfig.hpp to use it.\nReason: " +
- reason)
+ f"Class {self.__class__.__name__} is experimental. Define "
+ "EXPERIMENTAL_FEATURES in myconfig.hpp to use it.\n"
+ f"Reason: {reason}")
def modifier(cls):
cls.__init__ = exception_raiser
diff --git a/src/python/espressomd/version.pyx b/src/python/espressomd/version.pyx
index 63c440befa5..6b93e7a39fa 100644
--- a/src/python/espressomd/version.pyx
+++ b/src/python/espressomd/version.pyx
@@ -33,7 +33,7 @@ def minor():
def friendly():
"""Dot version of the version.
"""
- return "{}.{}".format(major(), minor())
+ return f"{major()}.{minor()}"
def git_branch():
diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt
index 514645e27fe..bee190337b7 100644
--- a/testsuite/python/CMakeLists.txt
+++ b/testsuite/python/CMakeLists.txt
@@ -217,6 +217,7 @@ python_test(FILE shapes.py MAX_NUM_PROC 1)
python_test(FILE h5md.py MAX_NUM_PROC 2)
python_test(FILE mdanalysis.py MAX_NUM_PROC 2)
python_test(FILE p3m_tuning_exceptions.py MAX_NUM_PROC 1 LABELS gpu)
+python_test(FILE utils.py MAX_NUM_PROC 1)
add_custom_target(
python_test_data
diff --git a/testsuite/python/interactions_bonded_interface.py b/testsuite/python/interactions_bonded_interface.py
index 58ca1d814ae..0753317b344 100644
--- a/testsuite/python/interactions_bonded_interface.py
+++ b/testsuite/python/interactions_bonded_interface.py
@@ -75,7 +75,7 @@ def parameterKeys(self, bondObject):
def setUp(self):
if not self.system.part.exists(self.pid):
- self.system.part.add(id=self.pid, pos=(0, 0, 0, 0))
+ self.system.part.add(id=self.pid, pos=(0, 0, 0))
def generateTestForBondParams(_bondId, _bondClass, _params):
"""Generates test cases for checking bond parameters set and gotten
diff --git a/testsuite/python/utils.py b/testsuite/python/utils.py
new file mode 100644
index 00000000000..dbd6e88d75a
--- /dev/null
+++ b/testsuite/python/utils.py
@@ -0,0 +1,74 @@
+#
+# Copyright (C) 2020 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 .
+#
+import unittest as ut
+import numpy as np
+
+import espressomd.utils as utils
+
+
+class espresso_utils(ut.TestCase):
+
+ def test_check_type_or_throw_except(self):
+ with self.assertRaisesRegex(
+ ValueError, 'A -- 4 values were given but 3 were expected.'):
+ utils.check_type_or_throw_except([1, 2, 3, 4], 3, float, 'A')
+ with self.assertRaisesRegex(
+ ValueError, 'B -- 2 values were given but 3 were expected.'):
+ utils.check_type_or_throw_except([1, 2], 3, float, 'B')
+ with self.assertRaisesRegex(
+ ValueError, 'C -- A single value was given but 3 were expected.'):
+ utils.check_type_or_throw_except(1, 3, float, 'C')
+ with self.assertRaisesRegex(
+ ValueError, 'D -- Item 1 was of type str'):
+ utils.check_type_or_throw_except([1, '2', '3'], 3, float, 'D')
+ try:
+ utils.check_type_or_throw_except([1, 2, 3], 3, float, 'E')
+ except ValueError as err:
+ self.fail(f'check_type_or_throw_except raised ValueError("{err}")')
+
+ def test_is_valid_type(self):
+ # basic types
+ self.assertFalse(utils.is_valid_type(None, int))
+ self.assertFalse(utils.is_valid_type('12', int))
+ self.assertFalse(utils.is_valid_type(0.99, int))
+ self.assertFalse(utils.is_valid_type(12, float))
+ self.assertFalse(utils.is_valid_type(1234, str))
+ self.assertTrue(utils.is_valid_type(1.0, float))
+ self.assertTrue(utils.is_valid_type(12345, int))
+ self.assertTrue(utils.is_valid_type('123', str))
+ # numpy types
+ self.assertTrue(utils.is_valid_type(
+ np.array([12], dtype=int)[0], int))
+ self.assertTrue(utils.is_valid_type(
+ np.array([12], dtype=np.long)[0], int))
+ self.assertTrue(utils.is_valid_type(
+ np.array([1.], dtype=float)[0], float))
+ self.assertTrue(utils.is_valid_type(
+ np.array([1.], dtype=np.float64)[0], float))
+
+ def test_nesting_level(self):
+ self.assertEqual(utils.nesting_level(12345), 0)
+ self.assertEqual(utils.nesting_level('123'), 0)
+ self.assertEqual(utils.nesting_level((1, )), 1)
+ self.assertEqual(utils.nesting_level([1, ]), 1)
+ self.assertEqual(utils.nesting_level([[1]]), 2)
+
+
+if __name__ == "__main__":
+ ut.main()