def setUpClass(cls): box_l = 20.0 # start with a small box cls.system.box_l = np.array([box_l, box_l, box_l]) cls.system.cell_system.set_n_square(use_verlet_lists=False) fene = FeneBond(k=30, d_r_max=2) cls.system.bonded_inter.add(fene) positions = polymer.positions(n_polymers=cls.num_poly, bond_length=0.9, beads_per_chain=cls.num_mono, seed=42) for p in positions: for ndx, m in enumerate(p): part_id = len(cls.system.part) cls.system.part.add(id=part_id, pos=m) if ndx > 0: cls.system.part[part_id].add_bond((fene, part_id - 1)) # bring two polymers to opposite corners: # far in cell centre, but mirror images are close head_id = 0 tail_id = head_id + cls.num_mono cm = np.mean(cls.system.part[head_id:tail_id].pos, axis=0) cls.system.part[head_id:tail_id].pos = cls.system.part[ head_id:tail_id].pos - cm + cls.system.box_l head_id = cls.num_mono + 1 tail_id = head_id + cls.num_mono cm = np.mean(cls.system.part[head_id:tail_id].pos, axis=0) cls.system.part[head_id:tail_id].pos -= cm
def setUpClass(self): box_l = 20.0 # start with a small bo self.system.box_l = np.array([box_l, box_l, box_l]) self.system.cell_system.set_n_square(use_verlet_lists=False) fene = FeneBond(k=30, d_r_max=2) self.fene = fene self.system.bonded_inter.add(fene)
def test_fene(self): # system parameters box_l = 10.0 system.box_l = [box_l, box_l, box_l] skin = 0.4 time_step = 0.01 system.time_step = time_step # thermostat and cell system system.cell_system.skin = skin system.periodicity = [1, 1, 1] # particles and bond system.part.add( id=0, pos=[9.9, 9.75, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) system.part.add( id=1, pos=[9.9, 10.25, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) k = 1e4 d_r_max = 1.5 r_0 = 0.1 fene = FeneBond(k=k, d_r_max=d_r_max, r_0=r_0) system.bonded_inter.add(fene) system.part[0].add_bond((fene, 1)) system.integrator.run(steps=0) sim_stress_bonded = system.analysis.stress_tensor()['bonded'] sim_stress_fene = system.analysis.stress_tensor()[ 'bonded', len(system.bonded_inter) - 1] total_bonded_stresses = np.zeros([3, 3]) for i in range(len(system.bonded_inter)): total_bonded_stresses = np.add( total_bonded_stresses, system.analysis.stress_tensor()['bonded', i]) anal_stress_fene = self.get_anal_stress_fene( system.part[0].pos, system.part[1].pos, k, d_r_max, r_0) self.assertTrue(np.max(np.abs(sim_stress_bonded - anal_stress_fene)) < tol, 'bonded stress does not match analytical result') self.assertTrue(np.max(np.abs(sim_stress_fene - anal_stress_fene)) < tol, 'bonded stress for fene does not match analytical result') self.assertTrue(np.max(np.abs(sim_stress_bonded - total_bonded_stresses)) < tol, 'bonded stresses do not sum up to the total value') sim_pressure_fene = system.analysis.pressure()[ 'bonded', len(system.bonded_inter) - 1] anal_pressure_fene = np.einsum("ii", anal_stress_fene) / 3.0 self.assertTrue(np.max(np.abs(sim_pressure_fene - anal_pressure_fene)) < tol, 'bonded pressure for fene does not match analytical result') # Compare stress tensor observable to stress tensor from analysis np.testing.assert_allclose( StressTensor().calculate(), system.analysis.stress_tensor()["total"].reshape(9), atol=1E-10) system.part.clear()
def test(self): system = espressomd.System(box_l=[15.0, 15.0, 15.0]) system.cell_system.skin = 1 # Initial state. Skin does not influence cutoffs as long as there are # no interactions self.assertEqual(system.cell_system.max_cut_nonbonded, -1) self.assertEqual(system.cell_system.max_cut_bonded, -1) self.assertEqual(system.cell_system.interaction_range, -1) # Bonded interaction fene = FeneBond(r_0=1, d_r_max=2, k=1) system.bonded_inter.add(fene) self.assertEqual(system.cell_system.max_cut_bonded, 3) n_nodes = np.product(system.cell_system.node_grid) if n_nodes == 1: # Bonds don't influence interaction range self.assertEqual(system.cell_system.interaction_range, -1) else: self.assertEqual( system.cell_system.interaction_range, system.cell_system.max_cut_bonded + system.cell_system.skin) system.bonded_inter.remove(fene._bond_id) self.assertEqual(system.cell_system.max_cut_bonded, -1) self.assertEqual(system.cell_system.interaction_range, -1) if espressomd.has_features("LENNARD_JONES"): lj_off_params = system.non_bonded_inter[ 0, 0].lennard_jones.get_params() system.non_bonded_inter[0, 0].lennard_jones.set_params(sigma=1, epsilon=1, cutoff=2.5, shift="auto") self.assertEqual(system.cell_system.max_cut_nonbonded, 2.5) self.assertEqual( system.cell_system.interaction_range, system.cell_system.max_cut_nonbonded + system.cell_system.skin) system.non_bonded_inter[0, 0].lennard_jones.set_params( **lj_off_params) self.assertEqual(system.cell_system.max_cut_nonbonded, -1) self.assertEqual(system.cell_system.interaction_range, -1)
def setUpClass(self): box_l = 20.0 # start with a small bo self.system.box_l = np.array([box_l, box_l, box_l]) self.system.cell_system.set_n_square(use_verlet_lists=False) fene = FeneBond(k=30, d_r_max=2) self.system.bonded_inter.add(fene) polymer.create_polymer(N_P=self.num_poly, bond_length=0.9, MPC=self.num_mono, bond=fene) # bring two polymers to opposite corners: # far in centre cell, but mirror images are close head_id = 0 tail_id = head_id + self.num_mono cm = np.mean(self.system.part[head_id:tail_id].pos, axis=0) self.system.part[head_id:tail_id].pos = self.system.part[ head_id:tail_id].pos - cm + self.system.box_l head_id = self.num_mono + 1 tail_id = head_id + self.num_mono cm = np.mean(self.system.part[head_id:tail_id].pos, axis=0) self.system.part[head_id:tail_id].pos -= cm
def test_bonds(self): """Tests bond addition and removal.""" p1 = self.system.part[self.pid] p2 = self.system.part.add(pos=p1.pos) inactive_bond = FeneBond(k=1, d_r_max=2) p2.add_bond([self.f1, p1]) with self.assertRaisesRegex(RuntimeError, "already exists on particle"): p2.add_bond([self.f1, p1.id]) with self.assertRaisesRegex(RuntimeError, "already exists on particle"): p2.add_bond((self.f1, p1)) with self.assertRaisesRegex(Exception, "1st element of Bond has to be of type BondedInteraction or int"): p2.add_bond(('self.f1', p1)) with self.assertRaisesRegex(ValueError, "Bond partners have to be of type integer or ParticleHandle"): p2.add_bond((self.f1, '1')) with self.assertRaisesRegex(ValueError, r"Bond FeneBond\(.+?\) needs 1 partner"): p2.add_bond((self.f1, p1, p2)) with self.assertRaisesRegex(Exception, "The bonded interaction has not yet been added to the list of active bonds in ESPResSo"): p2.add_bond((inactive_bond, p1)) p2.delete_bond([self.f1, p1]) with self.assertRaisesRegex(RuntimeError, "doesn't exist on particle"): p2.delete_bond([self.f1, p1]) with self.assertRaisesRegex(ValueError, "Bond partners have to be of type integer or ParticleHandle"): p2.delete_bond((self.f1, 'p1'))
class PairCriteria(ut.TestCase): """Tests interface and implementation of pair criteria""" es = espressomd.System(box_l=[1., 1., 1.]) f1 = FeneBond(k=1, d_r_max=0.05) es.bonded_inter.add(f1) f2 = FeneBond(k=1, d_r_max=0.05) es.bonded_inter.add(f2) es.part.add(id=0, pos=(0, 0, 0)) es.part.add(id=1, pos=(0.91, 0, 0)) p1 = es.part[0] p2 = es.part[1] epsilon = 1E-8 def test_distance_crit_periodic(self): dc = pair_criteria.DistanceCriterion(cut_off=0.1) # Interface self.assertEqual(list(dc.get_params().keys()), ["cut_off", ]) self.assertTrue(abs(dc.get_params()["cut_off"] - 0.1) < self.epsilon) # Decisions # Periodic system. Particles in range via minimum image convention self.es.periodicity = (1, 1, 1) self.assertTrue(dc.decide(self.p1, self.p2)) self.assertTrue(dc.decide(self.p1.id, self.p2.id)) @utx.skipIfMissingFeatures("PARTIAL_PERIODIC") def test_distance_crit_non_periodic(self): dc = pair_criteria.DistanceCriterion(cut_off=0.1) # Non-periodic system. Particles out of range self.es.periodicity = (0, 0, 0) self.assertTrue(not dc.decide(self.p1, self.p2)) self.assertTrue(not dc.decide(self.p1.id, self.p2.id)) @utx.skipIfMissingFeatures("LENNARD_JONES") def test_energy_crit(self): # Setup purely repulsive lj self.es.non_bonded_inter[0, 0].lennard_jones.set_params( sigma=0.11, epsilon=1, cutoff=2**(1. / 6.) * 0.11, shift="auto") ec = pair_criteria.EnergyCriterion(cut_off=0.001) # Interface self.assertEqual(list(ec.get_params().keys()), ["cut_off", ]) self.assertTrue(abs(ec.get_params()["cut_off"] - 0.001) < self.epsilon) # Decisions # Periodic system. Particles in range via minimum image convention self.es.periodicity = (1, 1, 1) self.assertTrue(ec.decide(self.p1, self.p2)) self.assertTrue(ec.decide(self.p1.id, self.p2.id)) @utx.skipIfMissingFeatures(["LENNARD_JONES", "PARTIAL_PERIODIC"]) def test_energy_crit_non_periodic(self): # Setup purely repulsive lj self.es.non_bonded_inter[0, 0].lennard_jones.set_params( sigma=0.11, epsilon=1, cutoff=2**(1. / 6.) * 0.11, shift="auto") ec = pair_criteria.EnergyCriterion(cut_off=0.001) # Interface self.assertEqual(list(ec.get_params().keys()), ["cut_off", ]) self.assertTrue(abs(ec.get_params()["cut_off"] - 0.001) < self.epsilon) # Non-periodic system. Particles out of range self.es.periodicity = (0, 0, 0) self.assertTrue(not ec.decide(self.p1, self.p2)) self.assertTrue(not ec.decide(self.p1.id, self.p2.id)) def test_bond_crit(self): bc = pair_criteria.BondCriterion(bond_type=0) # Interface self.assertEqual(list(bc.get_params().keys()), ["bond_type", ]) self.assertEqual(bc.get_params()["bond_type"], 0) # Decisions # No bond yet. Should return false self.assertTrue(not bc.decide(self.p1, self.p2)) self.assertTrue(not bc.decide(self.p1.id, self.p2.id)) # Add bond. Then the criterion should match self.es.part[0].bonds = ((0, 1),) self.assertTrue(bc.decide(self.p1, self.p2)) self.assertTrue(bc.decide(self.p1.id, self.p2.id)) # Place bond on the 2nd particle. The criterion should still match self.es.part[0].bonds = () self.es.part[1].bonds = ((0, 0),) self.assertTrue(bc.decide(self.p1, self.p2)) self.assertTrue(bc.decide(self.p1.id, self.p2.id))
class ParticleProperties(ut.TestCase): # Particle id to work on pid = 17 # Error tolerance when comparing arrays/tuples... tol = 1E-9 # Handle for espresso system system = espressomd.System(box_l=[100.0, 100.0, 100.0]) system.cell_system.skin = 0 system.time_step = 0.01 f1 = FeneBond(k=1, d_r_max=2) system.bonded_inter.add(f1) f2 = FeneBond(k=1, d_r_max=2) system.bonded_inter.add(f2) def setUp(self): if not self.system.part.exists(self.pid): self.system.part.add(id=self.pid, pos=(0, 0, 0)) def tearDown(self): self.system.part.clear() def generateTestForVectorProperty(_propName, _value): """Generates test cases for vectorial particle properties such as position, velocity... 1st arg: name of the property (e.g., "pos"), 2nd array: value to be used for testing. Has to be numpy.array of floats """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.system.part[self.pid], propName, value) np.testing.assert_allclose( np.array(getattr(self.system.part[self.pid], propName)), value, err_msg=propName + ": value set and value gotten back differ.", atol=self.tol) return func def generateTestForScalarProperty(_propName, _value): """Generates test cases for scalar particle properties such as type, mass, charge... 1st arg: name of the property (e.g., "type"), 2nd array: value to be used for testing. int or float """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.system.part[self.pid], propName, value) self.assertEqual( getattr(self.system.part[self.pid], propName), value, propName + ": value set and value gotten back differ.") return func test_pos = generateTestForVectorProperty("pos", np.array([0.1, 0.2, 0.3])) test_v = generateTestForVectorProperty("v", np.array([0.2, 0.3, 0.4])) test_f = generateTestForVectorProperty("f", np.array([0.2, 0.3, 0.7])) test_type = generateTestForScalarProperty("type", int(3)) test_mol_id = generateTestForScalarProperty("mol_id", int(3)) test_bonds_property = generateTestForScalarProperty( "bonds", ((f1, 1), (f2, 2))) if espressomd.has_features(["MASS"]): test_mass = generateTestForScalarProperty("mass", 1.3) if espressomd.has_features(["ROTATION"]): for x in 0, 1: for y in 0, 1: for z in 0, 1: test_rotation = generateTestForVectorProperty( "rotation", np.array([x, y, z], dtype=int)) test_omega_lab = generateTestForVectorProperty("omega_lab", np.array([4., 2., 1.])) test_omega_body = generateTestForVectorProperty( "omega_body", np.array([4., 72., 1.])) test_torque_lab = generateTestForVectorProperty( "torque_lab", np.array([4., 72., 3.7])) # The tested value has to be normalized! test_quat = generateTestForVectorProperty( "quat", np.array([0.5, 0.5, 0.5, 0.5])) if espressomd.has_features(["LANGEVIN_PER_PARTICLE"]): if espressomd.has_features(["PARTICLE_ANISOTROPY"]): test_gamma = generateTestForVectorProperty( "gamma", np.array([2., 9., 0.23])) def test_gamma_single(self): self.system.part[self.pid].gamma = 17.4 np.testing.assert_array_equal( np.copy(self.system.part[self.pid].gamma), np.array([17.4, 17.4, 17.4]), "gamma: value set and value gotten back differ.") else: test_gamma = generateTestForScalarProperty("gamma", 17.3) if espressomd.has_features(["PARTICLE_ANISOTROPY"]): test_gamma_rot = generateTestForVectorProperty( "gamma_rot", np.array([5., 10., 0.33])) def test_gamma_rot_single(self): self.system.part[self.pid].gamma_rot = 15.4 np.testing.assert_array_equal( np.copy(self.system.part[self.pid].gamma_rot), np.array([15.4, 15.4, 15.4]), "gamma_rot: value set and value gotten back differ.") else: test_gamma_rot = generateTestForScalarProperty( "gamma_rot", 14.23) # test_director=generateTestForVectorProperty("director", # np.array([0.5,0.4,0.3])) if espressomd.has_features(["ELECTROSTATICS"]): test_charge = generateTestForScalarProperty("q", -19.7) if espressomd.has_features(["EXTERNAL_FORCES"]): test_ext_force = generateTestForVectorProperty("ext_force", [0.1, 0.2, 0.3]) test_fix = generateTestForVectorProperty("fix", [True, False, True]) if espressomd.has_features(["EXTERNAL_FORCES", "ROTATION"]): test_ext_torque = generateTestForVectorProperty( "ext_torque", [.4, .5, .6]) if espressomd.has_features(["DIPOLES"]): test_dip = generateTestForVectorProperty("dip", np.array([0.5, -0.5, 3])) test_dipm = generateTestForScalarProperty("dipm", -9.7) if espressomd.has_features(["VIRTUAL_SITES"]): test_virtual = generateTestForScalarProperty("virtual", 1) if espressomd.has_features(["VIRTUAL_SITES_RELATIVE"]): def test_yy_vs_relative(self): self.system.part.add(id=0, pos=(0, 0, 0)) self.system.part.add(id=1, pos=(0, 0, 0)) self.system.part[1].vs_relative = (0, 5.0, (0.5, -0.5, -0.5, -0.5)) self.system.part[1].vs_quat = [1, 2, 3, 4] np.testing.assert_array_equal(self.system.part[1].vs_quat, [1, 2, 3, 4]) res = self.system.part[1].vs_relative self.assertEqual(res[0], 0, "vs_relative: " + res.__str__()) self.assertEqual(res[1], 5.0, "vs_relative: " + res.__str__()) np.testing.assert_allclose(res[2], np.array((0.5, -0.5, -0.5, -0.5)), err_msg="vs_relative: " + res.__str__(), atol=self.tol) @utx.skipIfMissingFeatures("DIPOLES") def test_contradicting_properties_dip_dipm(self): with self.assertRaises(ValueError): self.system.part.add(pos=[0, 0, 0], dip=[1, 1, 1], dipm=1.0) @utx.skipIfMissingFeatures(["DIPOLES", "ROTATION"]) def test_contradicting_properties_dip_quat(self): with self.assertRaises(ValueError): self.system.part.add(pos=[0, 0, 0], dip=[1, 1, 1], quat=[1.0, 1.0, 1.0, 1.0]) @utx.skipIfMissingFeatures("ELECTROSTATICS") def test_particle_selection(self): s = self.system s.part.clear() positions = ((0.2, 0.3, 0.4), (0.4, 0.2, 0.3), (0.7, 0.7, 0.7)) charges = [0, 1E-6, -1, 1] # Place particles i = 0 for pos in positions: for q in charges: s.part.add(pos=pos, q=q, id=i) i += 1 # Scalar property res = s.part.select(q=0) self.assertEqual(len(res.id), len(positions)) for p in res: self.assertAlmostEqual(p.q, 0, places=13) # Vectorial property res = s.part.select(pos=(0.2, 0.3, 0.4)) self.assertEqual(len(res.id), len(charges)) for p in res: np.testing.assert_allclose((0.2, 0.3, 0.4), np.copy(p.pos), atol=1E-12) # Two criteria res = s.part.select(pos=(0.2, 0.3, 0.4), q=0) self.assertEqual(tuple(res.id), (0, )) # Empty result res = s.part.select(q=17) self.assertEqual(tuple(res.id), ()) # User-specified criterion res = s.part.select(lambda p: p.pos[0] < 0.5) self.assertEqual(tuple(sorted(res.id)), (0, 1, 2, 3, 4, 5, 6, 7)) def test_image_box(self): s = self.system s.part.clear() pos = 1.5 * s.box_l s.part.add(pos=pos) np.testing.assert_equal(np.copy(s.part[0].image_box), [1, 1, 1]) def test_accessing_invalid_id_raises(self): self.system.part.clear() handle_to_non_existing_particle = self.system.part[42] with self.assertRaises(RuntimeError): handle_to_non_existing_particle.id def test_parallel_property_setters(self): s = self.system s.part.clear() s.part.add(pos=s.box_l * np.random.random((100, 3))) # Copy individual properties of particle 0 print( "If this test hangs, there is an mpi deadlock in a particle property setter." ) for p in espressomd.particle_data.particle_attributes: # Uncomment to identify guilty property # print( p) if not hasattr(s.part[0], p): raise Exception( "Inconsistency between ParticleHandle and particle_data.particle_attributes" ) try: setattr(s.part[:], p, getattr(s.part[0], p)) except AttributeError: print("Skipping read-only", p) # Cause a different mpi callback to uncover deadlock immediately x = getattr(s.part[:], p) def test_zz_remove_all(self): for id in self.system.part[:].id: self.system.part[id].remove() self.system.part.add(pos=np.random.random( (100, 3)) * self.system.box_l, id=np.arange(100, dtype=int)) ids = self.system.part[:].id np.random.shuffle(ids) for id in ids: self.system.part[id].remove() with self.assertRaises(Exception): self.system.part[17].remove() def test_coord_fold_corner_cases(self): system = self.system system.time_step = .5 system.cell_system.set_domain_decomposition(use_verlet_lists=False) system.cell_system.skin = 0 system.min_global_cut = 3 system.part.clear() p1 = system.part.add(pos=3 * [np.nextafter(0., -1.)], v=system.box_l / 3) print(p1.pos) p2 = system.part.add(pos=np.nextafter(system.box_l, 2 * system.box_l), v=system.box_l / 3) print(p2.pos) p3 = system.part.add(pos=np.nextafter(system.box_l, (0, 0, 0)), v=system.box_l / 3) print(p3.pos) p4 = system.part.add(pos=3 * [np.nextafter(0., 1.)], v=system.box_l / 3) print(p4.pos) system.integrator.run(3) for p in system.part: for i in range(3): self.assertGreaterEqual(p.pos_folded[i], 0) self.assertLess(p.pos_folded[i], system.box_l[i]) # Force resort system.part.add(pos=(0, 0, 0)) system.integrator.run(9) for p in system.part: for i in range(3): self.assertGreaterEqual(p.pos_folded[i], 0) self.assertLess(p.pos_folded[i], system.box_l[i])
class ClusterAnalysis(ut.TestCase): """Tests the cluster analysis""" es = espressomd.System(box_l=(1, 1, 1)) f = FeneBond(k=1, d_r_max=0.05) es.bonded_inter.add(f) # Firt cluster es.part.add(id=0, pos=(0, 0, 0)) es.part.add(id=1, pos=(0.91, 0, 0), bonds=((0, 0), )) es.part.add(id=2, pos=(0, 0.2, 0)) es.part.add(id=3, pos=(0, 0.1, 0)) # 2nd cluster es.part.add(id=4, pos=(0.5, 0.5, 0.5)) es.part.add(id=5, pos=(0.55, 0.5, 0.5)) cs = ClusterStructure() np.random.seed(1) # Setup check handle_errors("") def test_00_fails_without_criterion_set(self): with (self.assertRaises(Exception)): self.cs.run_for_all_pairs() def test_set_criterion(self): # Test setters/getters for criteria dc = DistanceCriterion(cut_off=0.11) self.cs.set_params(pair_criterion=dc) # Do we get back the right criterion dc_ret = self.cs.get_params()["pair_criterion"] # Note: This work around the fact that the script interface does not # yet assign the correct derived class when returning an object self.assertEqual(dc_ret.name(), "PairCriteria::DistanceCriterion") self.assertAlmostEqual(dc_ret.get_params()["cut_off"], 0.11, places=7) # Is the cluster structure empty before being used def test_analysis_for_all_pairs(self): # Run cluster analysis self.cs.set_params(pair_criterion=DistanceCriterion(cut_off=0.12)) self.cs.run_for_all_pairs() # Number of clusters self.assertEqual(len(self.cs.clusters), 2) cids = self.cs.cluster_ids() # Sizes of individual clusters l2 = self.cs.clusters[cids[1]].size() l1 = len(self.cs.clusters[cids[0]].particle_ids()) # Clusters should contain 2 and 4 particles self.assertEqual(min(l1, l2), 2) self.assertEqual(max(l1, l2), 4) # Verify particle ids smaller_cluster = None bigger_cluster = None if l1 < l2: smaller_cluster = self.cs.clusters[cids[0]] bigger_cluster = self.cs.clusters[cids[1]] else: smaller_cluster = self.cs.clusters[cids[1]] bigger_cluster = self.cs.clusters[cids[0]] self.assertEqual(bigger_cluster.particle_ids(), [0, 1, 2, 3]) self.assertEqual(smaller_cluster.particle_ids(), [4, 5]) # Test obtaining a ParticleSlice for a cluster pids = bigger_cluster.particle_ids() particles = bigger_cluster.particles() # Do the number of entries match self.assertEqual(len(pids), len(particles.id_selection)) # Compare ids of particles in the slice self.assertEqual(all(particles.id_selection), all(pids)) # Test iteration over clusters visited_sizes = [] for c in self.cs.clusters: visited_sizes.append(c[1].size()) visited_sizes = sorted(visited_sizes) self.assertEqual(visited_sizes, [2, 4]) def test_zz_single_cluster_analysis(self): self.es.part.clear() # Place particles on a line (crossing periodic boundaries) for x in np.arange(-0.2, 0.21, 0.01): self.es.part.add(pos=(x, 1.1 * x, 1.2 * x)) self.cs.pair_criterion = DistanceCriterion(cut_off=0.13) self.cs.run_for_all_pairs() self.assertEqual(len(self.cs.clusters), 1) for c in self.cs.clusters: # Discard cluster id c = c[1] # Center of mass should be at origin self.assertTrue( np.sqrt(np.sum(np.array(c.center_of_mass())**2)) <= 1E-8) # Longest distance self.assertTrue( abs(c.longest_distance() - self.es.distance(self.es.part[0], self.es.part[ len(self.es.part) - 1])) <= 1E-8) # Radius of gyration rg = 0. com_particle = self.es.part[len(self.es.part) / 2] for p in c.particles(): rg += self.es.distance(p, com_particle)**2 rg /= len(self.es.part) rg = np.sqrt(rg) self.assertTrue(abs(c.radius_of_gyration() - rg) <= 1E-6) # Fractal dimension calc require gsl if not espressomd.has_features("GSL"): print("Skipping fractal dimension tests for lack of GSL") return # The fractal dimension of a line should be 1 dr = 0. self.assertAlmostEqual(c.fractal_dimension(dr=dr)[0], 1, delta=0.05) # Fractal dimension of a disk should be close to 2 self.es.part.clear() center = np.array((0.1, .02, 0.15)) for i in range(3000): r_inv, phi = np.random.random(2) * np.array((0.2, 2 * np.pi)) r = 1 / r_inv self.es.part.add(pos=center + r * np.array((np.sin(phi), np.cos(phi), 0))) self.cs.clear() self.cs.run_for_all_pairs() cid = self.cs.cluster_ids()[0] df = self.cs.clusters[cid].fractal_dimension(dr=0.001) self.assertAlmostEqual(df[0], 2, delta=0.08) def test_analysis_for_bonded_particles(self): # Run cluster analysis self.cs.set_params(pair_criterion=BondCriterion(bond_type=0)) self.cs.run_for_bonded_particles() # There should be one cluster containing particles 0 and 1 self.assertEqual(len(self.cs.clusters), 1) self.assertEqual( self.cs.clusters[self.cs.cluster_ids()[0]].particle_ids(), [0, 1]) # Check particle to cluster id mapping, once by ParticleHandle, once by id self.assertEqual(self.cs.cid_for_particle(self.es.part[0]), self.cs.cluster_ids()[0]) self.assertEqual(self.cs.cid_for_particle(1), self.cs.cluster_ids()[0])
def setUp(self): self.es.part[self.pid].pos = 0, 0, 0 self.es.bondedInter[0] = FeneBond(k=1, d_r_max=5) self.es.bondedInter[1] = FeneBond(k=1, d_r_max=5)
class ParticleProperties(ut.TestCase): # Particle id to work on pid = 17 # Error tolerance when comparing arrays/tuples... tol = 1E-9 # Handle for espresso system es = espressomd.System() f1 = FeneBond(k=1, d_r_max=5) es.bonded_inter.add(f1) f2 = FeneBond(k=1, d_r_max=5) es.bonded_inter.add(f2) def arraysNearlyEqual(self, a, b): """Test, if the magnitude of the difference between two arrays is smaller than the tolerance""" # Check length if len(a) != len(b): return False # We have to use a loop, since we can't be sure, we're getting numpy # arrays sum = 0. for i in range(len(a)): sum += abs(a[i] - b[i]) if sum > self.tol: return False return True def setUp(self): if not self.es.part.exists(self.pid): self.es.part.add(id=self.pid, pos=(0, 0, 0)) def generateTestForVectorProperty(_propName, _value): """Generates test cases for vectorial particle properties such as position, velocity... 1st arg: name of the property (e.g., "pos"), 2nd array: value to be used for testing. Has to be numpy.array of floats """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.es.part[self.pid], propName, value) self.assertTrue( self.arraysNearlyEqual( getattr(self.es.part[self.pid], propName), value), propName + ": value set and value gotten back differ.") return func def generateTestForScalarProperty(_propName, _value): """Generates test cases for scalar particle properties such as type, mass, charge... 1st arg: name of the property (e.g., "type"), 2nd array: value to be used for testing. int or float """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.es.part[self.pid], propName, value) self.assertTrue( getattr(self.es.part[self.pid], propName) == value, propName + ": value set and value gotten back differ.") return func test_pos = generateTestForVectorProperty("pos", np.array([0.1, 0.2, 0.3])) test_v = generateTestForVectorProperty("v", np.array([0.2, 0.3, 0.4])) test_f = generateTestForVectorProperty("f", np.array([0.2, 0.3, 0.7])) test_type = generateTestForScalarProperty("type", int(3)) test_bonds_property = generateTestForScalarProperty( "bonds", ((f1, 1), (f2, 2))) if "MASS" in espressomd.features(): test_mass = generateTestForScalarProperty("mass", 1.3) if "ROTATION" in espressomd.features(): test_omega_lab = generateTestForVectorProperty("omega_lab", np.array([4., 2., 1.])) test_omega_body = generateTestForVectorProperty( "omega_body", np.array([4., 72., 1.])) test_torque_lab = generateTestForVectorProperty( "torque_lab", np.array([4., 72., 3.7])) # The tested value has to be normalized! test_quat = generateTestForVectorProperty( "quat", np.array([0.5, 0.5, 0.5, 0.5])) if "ROTATIONAL_INERTIA" in espressomd.features(): test_gamma_rot = generateTestForVectorProperty( "gamma_rot", np.array([5., 10., 0.33])) else: test_gamma_rot = generateTestForScalarProperty("gamma_rot", 14.23) # test_director=generateTestForVectorProperty("director",np.array([0.5,0.4,0.3])) if "ELECTROSTATICS" in espressomd.features(): test_charge = generateTestForScalarProperty("q", -19.7) if "DIPOLES" in espressomd.features(): test_dip = generateTestForVectorProperty("dip", np.array([0.5, -0.5, 3])) test_dipm = generateTestForScalarProperty("dipm", -9.7) if "VIRTUAL_SITES" in espressomd.features(): test_virtual = generateTestForScalarProperty("virtual", 1) if "VIRTUAL_SITES_RELATIVE" in espressomd.features(): def test_zz_vs_relative(self): self.es.part.add(id=0, pos=(0, 0, 0)) self.es.part.add(id=1, pos=(0, 0, 0)) self.es.part[1].vs_relative = (0, 5.0, (0.5, -0.5, -0.5, -0.5)) res = self.es.part[1].vs_relative self.assertTrue( res[0] == 0 and res[1] == 5.0 and self.arraysNearlyEqual( res[2], np.array((0.5, -0.5, -0.5, -0.5))), "vs_relative: " + res.__str__())
def test_fene(self): # system parameters system.box_l = 3 * [10.0] skin = 0.4 time_step = 0.01 system.time_step = time_step # thermostat and cell system system.cell_system.skin = skin system.periodicity = [1, 1, 1] # particles and bond p0 = system.part.add(pos=[9.9, 9.75, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) p1 = system.part.add(pos=[9.9, 10.25, 9.9], type=0, mol_id=0, fix=[1, 1, 1]) k = 1e4 d_r_max = 1.5 r_0 = 0.1 fene = FeneBond(k=k, d_r_max=d_r_max, r_0=r_0) system.bonded_inter.add(fene) p0.add_bond((fene, p1)) system.integrator.run(steps=0) sim_pressure_tensor = system.analysis.pressure_tensor() sim_pressure_tensor_bonded = sim_pressure_tensor['bonded'] sim_pressure_tensor_fene = sim_pressure_tensor[ 'bonded', len(system.bonded_inter) - 1] total_bonded_pressure_tensor = np.zeros([3, 3]) for i in range(len(system.bonded_inter)): total_bonded_pressure_tensor += sim_pressure_tensor['bonded', i] anal_pressure_tensor_fene = self.get_anal_pressure_tensor_fene( p0.pos, p1.pos, k, d_r_max, r_0) np.testing.assert_allclose( sim_pressure_tensor_bonded, anal_pressure_tensor_fene, atol=tol, err_msg='bonded pressure tensor does not match analytical result') np.testing.assert_allclose( sim_pressure_tensor_fene, anal_pressure_tensor_fene, atol=tol, err_msg= 'bonded pressure tensor for fene does not match analytical result') np.testing.assert_allclose( sim_pressure_tensor_bonded, total_bonded_pressure_tensor, atol=tol, err_msg='bonded pressure tensor do not sum up to the total value') sim_pressure = system.analysis.pressure() sim_pressure_fene = sim_pressure['bonded', len(system.bonded_inter) - 1] anal_pressure_fene = np.einsum("ii", anal_pressure_tensor_fene) / 3.0 np.testing.assert_allclose( sim_pressure_fene, anal_pressure_fene, atol=tol, err_msg='bonded pressure for fene does not match analytical result' ) # Compare pressure observables to pressure from analysis np.testing.assert_allclose(PressureTensor().calculate(), sim_pressure_tensor["total"], rtol=0, atol=1E-10) self.assertAlmostEqual(Pressure().calculate(), sim_pressure["total"], delta=tol)
class ParticleProperties(ut.TestCase): # Particle id to work on pid = 17 # Error tolerance when comparing arrays/tuples... tol = 1E-9 # Handle for espresso system system = espressomd.System(box_l=[100.0, 100.0, 100.0]) system.cell_system.skin = 0 system.time_step = 0.01 f1 = FeneBond(k=1, d_r_max=2) system.bonded_inter.add(f1) f2 = FeneBond(k=1, d_r_max=2) system.bonded_inter.add(f2) def setUp(self): if not self.system.part.exists(self.pid): self.system.part.add(id=self.pid, pos=(0, 0, 0)) def tearDown(self): self.system.part.clear() def generateTestForVectorProperty(_propName, _value): """Generates test cases for vectorial particle properties such as position, velocity... 1st arg: name of the property (e.g., "pos"), 2nd array: value to be used for testing. Has to be numpy.array of floats """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.system.part[self.pid], propName, value) np.testing.assert_allclose( np.array(getattr(self.system.part[self.pid], propName)), value, err_msg=propName + ": value set and value gotten back differ.", atol=self.tol) return func def generateTestForScalarProperty(_propName, _value): """Generates test cases for scalar particle properties such as type, mass, charge... 1st arg: name of the property (e.g., "type"), 2nd array: value to be used for testing. int or float """ # This is executed, when generateTestForVectorProperty() is called propName = _propName value = _value def func(self): # This code is run at the execution of the generated function. # It will use the state of the variables in the outer function, # which was there, when the outer function was called setattr(self.system.part[self.pid], propName, value) self.assertEqual(getattr(self.system.part[self.pid], propName), value, propName + ": value set and value gotten back differ.") return func test_pos = generateTestForVectorProperty("pos", np.array([0.1, 0.2, 0.3])) test_v = generateTestForVectorProperty("v", np.array([0.2, 0.3, 0.4])) test_f = generateTestForVectorProperty("f", np.array([0.2, 0.3, 0.7])) test_type = generateTestForScalarProperty("type", int(3)) test_mol_id = generateTestForScalarProperty("mol_id", int(3)) test_bonds_property = generateTestForScalarProperty( "bonds", ((f1, 1), (f2, 2))) if espressomd.has_features(["MASS"]): test_mass = generateTestForScalarProperty("mass", 1.3) if espressomd.has_features(["ROTATION"]): for x in 0, 1: for y in 0, 1: for z in 0, 1: test_rotation = generateTestForVectorProperty( "rotation", np.array([x, y, z], dtype=int)) test_omega_lab = generateTestForVectorProperty( "omega_lab", np.array([4., 2., 1.])) test_omega_body = generateTestForVectorProperty( "omega_body", np.array([4., 72., 1.])) test_torque_lab = generateTestForVectorProperty( "torque_lab", np.array([4., 72., 3.7])) # The tested value has to be normalized! test_quat = generateTestForVectorProperty( "quat", np.array([0.5, 0.5, 0.5, 0.5])) def test_director(self): """ Test `director`. When set, it should get normalized. """ sample_vector = np.array([0.5, -0.4, 1.3]) sample_vector_normalized = sample_vector / \ np.linalg.norm(sample_vector) setattr(self.system.part[self.pid], "director", sample_vector) np.testing.assert_allclose( np.array(getattr(self.system.part[self.pid], "director")), sample_vector_normalized ) if espressomd.has_features(["THERMOSTAT_PER_PARTICLE"]): if espressomd.has_features(["PARTICLE_ANISOTROPY"]): test_gamma = generateTestForVectorProperty( "gamma", np.array([2., 9., 0.23])) def test_gamma_single(self): self.system.part[self.pid].gamma = 17.4 np.testing.assert_array_equal( np.copy(self.system.part[self.pid].gamma), np.array([17.4, 17.4, 17.4]), "gamma: value set and value gotten back differ.") else: test_gamma = generateTestForScalarProperty("gamma", 17.3) if espressomd.has_features(["PARTICLE_ANISOTROPY"]): test_gamma_rot = generateTestForVectorProperty( "gamma_rot", np.array([5., 10., 0.33])) def test_gamma_rot_single(self): self.system.part[self.pid].gamma_rot = 15.4 np.testing.assert_array_equal( np.copy(self.system.part[self.pid].gamma_rot), np.array([15.4, 15.4, 15.4]), "gamma_rot: value set and value gotten back differ.") else: test_gamma_rot = generateTestForScalarProperty( "gamma_rot", 14.23) if espressomd.has_features(["ELECTROSTATICS"]): test_charge = generateTestForScalarProperty("q", -19.7) if espressomd.has_features(["EXTERNAL_FORCES"]): test_ext_force = generateTestForVectorProperty( "ext_force", [0.1, 0.2, 0.3]) test_fix = generateTestForVectorProperty("fix", [True, False, True]) if espressomd.has_features(["EXTERNAL_FORCES", "ROTATION"]): test_ext_torque = generateTestForVectorProperty( "ext_torque", [.4, .5, .6]) if espressomd.has_features(["DIPOLES"]): test_dip = generateTestForVectorProperty( "dip", np.array([0.5, -0.5, 3])) test_dipm = generateTestForScalarProperty("dipm", -9.7) if espressomd.has_features(["VIRTUAL_SITES"]): test_virtual = generateTestForScalarProperty("virtual", 1) if espressomd.has_features(["VIRTUAL_SITES_RELATIVE"]): def test_yy_vs_relative(self): self.system.part.add(id=0, pos=(0, 0, 0)) self.system.part.add(id=1, pos=(0, 0, 0)) self.system.part[1].vs_relative = (0, 5.0, (0.5, -0.5, -0.5, -0.5)) self.system.part[1].vs_quat = [1, 2, 3, 4] np.testing.assert_array_equal( self.system.part[1].vs_quat, [1, 2, 3, 4]) res = self.system.part[1].vs_relative self.assertEqual(res[0], 0, "vs_relative: " + res.__str__()) self.assertEqual(res[1], 5.0, "vs_relative: " + res.__str__()) np.testing.assert_allclose( res[2], np.array((0.5, -0.5, -0.5, -0.5)), err_msg="vs_relative: " + res.__str__(), atol=self.tol) # check exceptions with self.assertRaisesRegex(ValueError, "needs input in the form"): self.system.part[1].vs_relative = (0, 5.0) with self.assertRaisesRegex(ValueError, "particle id has to be given as an int"): self.system.part[1].vs_relative = ('0', 5.0, (1, 0, 0, 0)) with self.assertRaisesRegex(ValueError, "distance has to be given as a float"): self.system.part[1].vs_relative = (0, '5', (1, 0, 0, 0)) with self.assertRaisesRegex(ValueError, "quaternion has to be given as a tuple of 4 floats"): self.system.part[1].vs_relative = (0, 5.0, (1, 0, 0)) @utx.skipIfMissingFeatures("DIPOLES") def test_contradicting_properties_dip_dipm(self): with self.assertRaises(ValueError): self.system.part.add(pos=[0, 0, 0], dip=[1, 1, 1], dipm=1.0) @utx.skipIfMissingFeatures(["DIPOLES", "ROTATION"]) def test_contradicting_properties_dip_quat(self): with self.assertRaises(ValueError): self.system.part.add(pos=[0, 0, 0], dip=[1, 1, 1], quat=[1.0, 1.0, 1.0, 1.0]) @utx.skipIfMissingFeatures("ELECTROSTATICS") def test_particle_selection(self): s = self.system s.part.clear() positions = ((0.2, 0.3, 0.4), (0.4, 0.2, 0.3), (0.7, 0.7, 0.7)) charges = [0, 1E-6, -1, 1] # Place particles i = 0 for pos in positions: for q in charges: s.part.add(pos=pos, q=q, id=i) i += 2 # Scalar property res = s.part.select(q=0) self.assertEqual(len(res.id), len(positions)) for p in res: self.assertAlmostEqual(p.q, 0, places=13) # Vectorial property res = s.part.select(pos=(0.2, 0.3, 0.4)) self.assertEqual(len(res.id), len(charges)) for p in res: np.testing.assert_allclose( (0.2, 0.3, 0.4), np.copy(p.pos), atol=1E-12) # Two criteria res = s.part.select(pos=(0.2, 0.3, 0.4), q=0) self.assertEqual(tuple(res.id), (0,)) # Empty result res = s.part.select(q=17) self.assertEqual(tuple(res.id), ()) # User-specified criterion res = s.part.select(lambda p: p.pos[0] < 0.5) np.testing.assert_equal(sorted(res.id), np.arange(0, 16, 2, dtype=int)) def test_image_box(self): s = self.system s.part.clear() pos = 1.5 * s.box_l s.part.add(pos=pos) np.testing.assert_equal(np.copy(s.part[0].image_box), [1, 1, 1]) def test_accessing_invalid_id_raises(self): self.system.part.clear() handle_to_non_existing_particle = self.system.part[42] with self.assertRaises(RuntimeError): handle_to_non_existing_particle.id def test_parallel_property_setters(self): s = self.system s.part.clear() s.part.add(pos=s.box_l * np.random.random((100, 3))) # Copy individual properties of particle 0 print( "If this test hangs, there is an mpi deadlock in a particle property setter.") for p in espressomd.particle_data.particle_attributes: # Uncomment to identify guilty property # print( p) if not hasattr(s.part[0], p): raise Exception( "Inconsistency between ParticleHandle and particle_data.particle_attributes") try: setattr(s.part[:], p, getattr(s.part[0], p)) except AttributeError: print("Skipping read-only", p) # Cause a different mpi callback to uncover deadlock immediately _ = getattr(s.part[:], p) def test_remove_particle(self): """Tests that if a particle is removed, it no longer exists and bonds to the removed particle are also removed.""" p1 = self.system.part[self.pid] p2 = self.system.part.add(pos=p1.pos, bonds=[(self.f1, p1.id)]) p1.remove() self.assertFalse(self.system.part.exists(self.pid)) self.assertEqual(len(p2.bonds), 0) def test_bonds(self): """Tests bond addition and removal.""" p1 = self.system.part[self.pid] p2 = self.system.part.add(pos=p1.pos) inactive_bond = FeneBond(k=1, d_r_max=2) p2.add_bond([self.f1, p1]) with self.assertRaisesRegex(RuntimeError, "already exists on particle"): p2.add_bond([self.f1, p1.id]) with self.assertRaisesRegex(RuntimeError, "already exists on particle"): p2.add_bond((self.f1, p1)) with self.assertRaisesRegex(Exception, "1st element of Bond has to be of type BondedInteraction or int"): p2.add_bond(('self.f1', p1)) with self.assertRaisesRegex(ValueError, "Bond partners have to be of type integer or ParticleHandle"): p2.add_bond((self.f1, '1')) with self.assertRaisesRegex(ValueError, r"Bond FeneBond\(.+?\) needs 1 partner"): p2.add_bond((self.f1, p1, p2)) with self.assertRaisesRegex(Exception, "The bonded interaction has not yet been added to the list of active bonds in ESPResSo"): p2.add_bond((inactive_bond, p1)) p2.delete_bond([self.f1, p1]) with self.assertRaisesRegex(RuntimeError, "doesn't exist on particle"): p2.delete_bond([self.f1, p1]) with self.assertRaisesRegex(ValueError, "Bond partners have to be of type integer or ParticleHandle"): p2.delete_bond((self.f1, 'p1')) def test_zz_remove_all(self): for id in self.system.part[:].id: self.system.part[id].remove() self.system.part.add( pos=np.random.random((100, 3)) * self.system.box_l, id=np.arange(100, dtype=int)) ids = self.system.part[:].id np.random.shuffle(ids) for id in ids: self.system.part[id].remove() with self.assertRaises(Exception): self.system.part[17].remove() def test_coord_fold_corner_cases(self): system = self.system system.time_step = .5 system.cell_system.set_domain_decomposition(use_verlet_lists=False) system.cell_system.skin = 0 system.min_global_cut = 3 system.part.clear() p1 = system.part.add( pos=3 * [np.nextafter(0., -1.)], v=system.box_l / 3) print(p1.pos) p2 = system.part.add( pos=np.nextafter(system.box_l, 2 * system.box_l), v=system.box_l / 3) print(p2.pos) p3 = system.part.add( pos=np.nextafter(system.box_l, (0, 0, 0)), v=system.box_l / 3) print(p3.pos) p4 = system.part.add( pos=3 * [np.nextafter(0., 1.)], v=system.box_l / 3) print(p4.pos) system.integrator.run(3) for p in system.part: for i in range(3): self.assertGreaterEqual(p.pos_folded[i], 0) self.assertLess(p.pos_folded[i], system.box_l[i]) # Force resort system.part.add(pos=(0, 0, 0)) system.integrator.run(9) for p in system.part: for i in range(3): self.assertGreaterEqual(p.pos_folded[i], 0) self.assertLess(p.pos_folded[i], system.box_l[i]) def test_particle_slice(self): """Tests operations on slices of particles""" system = self.system # Empty slice system.part.clear() self.assertEqual(len(system.part[:]), 0) self.assertEqual(len(system.part[:].pos), 0) self.assertEqual(len(system.part[:].id), 0) with self.assertRaises(AttributeError): system.part[:].pos = ((1, 2, 3,),) # Slice containing particles ids = [1, 4, 6, 3, 8, 9] pos = np.random.random((len(ids), 3)) system.part.add(id=ids, pos=pos) # All particles self.assertEqual(len(system.part[:]), len(ids)) np.testing.assert_equal(system.part[:].id, sorted(ids)) np.testing.assert_equal(system.part[:].pos, pos[np.argsort(ids)]) # Access via slicing np.testing.assert_equal(system.part[4:9].id, [i for i in sorted(ids) if i >= 4 and i < 9]) np.testing.assert_equal(system.part[9:4:-1].id, [i for i in sorted(ids, key=lambda i:-i) if i > 4 and i <= 9]) # Check that negative start and end on slices are not accepted with self.assertRaises(IndexError): system.part[-1:] with self.assertRaises(IndexError): system.part[:-1] # Setting particle properties on a slice system.part[:5].pos = 0, 0, 0 np.testing.assert_equal(system.part[:].pos, [pos[i] if ids[i] >= 5 else [0, 0, 0] for i in np.argsort(ids)]) # Slice access via explicit list of ids np.testing.assert_equal(system.part[ids[1:4]].id, ids[1:4]) # Check that ids passed in an explicit list must exist with self.assertRaises(IndexError): system.part[99, 3] # Check that wrong types are not accepted with self.assertRaises(TypeError): system.part[[ids[0], 1.2]] def test_to_dict(self): self.system.part.clear() p = self.system.part.add( pos=np.random.uniform(size=(10, 3)) * self.system.box_l) pp = str(p) pdict = p.to_dict() p.remove() self.system.part.add(pdict) self.assertEqual(str(self.system.part.select()), pp)