args = parser.parse_args() print("\nArguments:", args) #NUM PARTICLES AND BOX n_ionpairs = 100 n_part = n_ionpairs * 2 density_si = 0.5 # g/cm^3 rho_factor_bmim_pf6 = 0.003931 box_volume = n_ionpairs / rho_factor_bmim_pf6 / density_si box_l = box_volume**(1. / 3.) print("\n-->Ion pairs:", n_ionpairs, "Box size:", box_l) system = espressomd.System(box_l=[box_l, box_l, box_l]) system.virtual_sites = VirtualSitesRelative(have_velocity=True) system.set_random_state_PRNG() if args.visu: d_scale = 0.988 * 0.5 c_ani = [1, 0, 0, 1] c_dru = [0, 0, 1, 1] c_com = [0, 0, 0, 1] c_cat = [0, 1, 0, 1] visualizer = espressomd.visualization_opengl.openGLLive( system, background_color=[1, 1, 1], drag_enabled=True, ext_force_arrows=True, drag_force=10, draw_bonds=False,
class CollisionDetection(ut.TestCase): """Tests interface and functionality of the collision detection / dynamic binding""" s = espressomd.System(box_l=[1.0, 1.0, 1.0]) s.seed = s.cell_system.get_state()['n_nodes'] * [1234] np.random.seed(seed=s.seed) if espressomd.has_features("VIRTUAL_SITES"): from espressomd.virtual_sites import VirtualSitesRelative s.virtual_sites = VirtualSitesRelative() H = HarmonicBond(k=5000, r_0=0.1) H2 = HarmonicBond(k=25000, r_0=0.02) s.bonded_inter.add(H) s.bonded_inter.add(H2) s.time_step = 0.001 s.cell_system.skin = 0.05 s.min_global_cut = 0.112 part_type_to_attach_vs_to = 0 part_type_vs = 1 part_type_to_be_glued = 2 part_type_after_glueing = 3 other_type = 5 def get_state_set_state_consistency(self): state = self.s.collision_detection.get_params() self.s.collision_detection.set_params(**state) self.assertEqual(state, self.s.collision_detection.get_params()) def test_00_interface_and_defaults(self): # Is it off by default self.assertEqual(self.s.collision_detection.mode, "off") # Make sure params cannot be set individually with self.assertRaises(Exception): self.s.collision_detection.mode = "bind_centers" # Verify exception throwing for unknown collision modes with self.assertRaises(Exception): self.s.collision_detection.set_params(mode=0) self.s.collision_detection.set_params(mode="blahblah") # That should work self.s.collision_detection.set_params(mode="off") self.assertEqual(self.s.collision_detection.mode, "off") def test_bind_centers(self): # Check that it leaves particles alone, wehn off self.s.collision_detection.set_params(mode="off") self.s.part.clear() self.s.part.add(pos=(0, 0, 0), id=0) self.s.part.add(pos=(0.1, 0, 0), id=1) self.s.part.add(pos=(0.1, 0.3, 0), id=2) self.s.integrator.run(0) self.assertEqual(self.s.part[0].bonds, ()) self.assertEqual(self.s.part[1].bonds, ()) self.assertEqual(self.s.part[2].bonds, ()) # Check that it cannot be activated self.s.collision_detection.set_params( mode="bind_centers", distance=0.11, bond_centers=self.H) self.get_state_set_state_consistency() self.s.integrator.run(1, recalc_forces=True) bond0 = ((self.s.bonded_inter[0], 1),) bond1 = ((self.s.bonded_inter[0], 0),) self.assertTrue( self.s.part[0].bonds == bond0 or self.s.part[1].bonds == bond1) self.assertEqual(self.s.part[2].bonds, ()) # Check that no additional bonds appear self.s.integrator.run(1) self.assertTrue( self.s.part[0].bonds == bond0 or self.s.part[1].bonds == bond1) self.assertEqual(self.s.part[2].bonds, ()) # Check turning it off self.s.collision_detection.set_params(mode="off") self.get_state_set_state_consistency() self.assertEqual(self.s.collision_detection.mode, "off") def run_test_bind_at_point_of_collision_for_pos(self, *positions): positions = list(positions) shuffle(positions) self.s.part.clear() # Place particle which should not take part in collisions p = self.s.part.add(pos=(0.1, 0.3, 0)) for pos in positions: p1 = self.s.part.add(pos=pos + (0, 0, 0)) p2 = self.s.part.add(pos=pos + (0.1, 0, 0)) if self.s.distance(p1, p) < 0.12 or self.s.distance(p2, p) < 0.12: raise Exception( "Test particle too close to particle, which should not take part in collision") # 2 non-virtual + 2 virtual + one that doesn't tkae part expected_np = 4 * len(positions) + 1 self.s.collision_detection.set_params( mode="bind_at_point_of_collision", distance=0.11, bond_centers=self.H, bond_vs=self.H2, part_type_vs=1, vs_placement=0.4) self.get_state_set_state_consistency() self.s.integrator.run(0, recalc_forces=True) self.verify_state_after_bind_at_poc(expected_np) # Integrate again and check that nothing has changed self.s.integrator.run(0, recalc_forces=True) self.verify_state_after_bind_at_poc(expected_np) # Check that nothing explodes, when the particles are moved. # In particular for parallel simulations self.s.thermostat.set_langevin(kT=0, gamma=0.01) self.s.part[:].v = 0.05, 0.01, 0.15 self.s.integrator.run(3000) self.verify_state_after_bind_at_poc(expected_np) def verify_state_after_bind_at_poc(self, expected_np): self.assertEqual(len(self.s.part), expected_np) # At the end of test, this list should be empty parts_not_accounted_for = list(range(expected_np)) # Collect pairs of non-virtual-particles found non_virtual_pairs = [] # We traverse particles. We look for a vs with a bond to find the other vs. # From the two vs we find the two non-virtual particles for p in self.s.part: # Skip non-virtual if p.virtual == 0: continue # Skip vs that doesn't have a bond if p.bonds == (): continue # Parse the bond self.assertEqual(len(p.bonds), 1) # Bond type self.assertEqual(p.bonds[0][0], self.H2) # get partner p2 = self.s.part[p.bonds[0][1]] # Is that really a vs self.assertEqual(p2.virtual, 1) # Get base particles base_p1 = self.s.part[p.vs_relative[0]] base_p2 = self.s.part[p2.vs_relative[0]] # Take note of accounted-for particles for _p in p, p2, base_p1, base_p2: parts_not_accounted_for.remove(_p.id) self.verify_bind_at_poc_pair(base_p1, base_p2, p, p2) # Check particle that did not take part in collision. self.assertEqual(len(parts_not_accounted_for), 1) p = self.s.part[parts_not_accounted_for[0]] self.assertEqual(p.virtual, 0) self.assertEqual(p.bonds, ()) parts_not_accounted_for.remove(p.id) self.assertEqual(parts_not_accounted_for, []) def verify_bind_at_poc_pair(self, p1, p2, vs1, vs2): bond_p1 = ((self.s.bonded_inter[0], p2.id),) bond_p2 = ((self.s.bonded_inter[0], p1.id),) self.assertTrue(p1.bonds == bond_p1 or p2.bonds == bond_p2) # Check for presence of vs # Check for bond betwen vs bond_vs1 = ((self.s.bonded_inter[1], vs2.id),) bond_vs2 = ((self.s.bonded_inter[1], vs1.id),) self.assertTrue(vs1.bonds == bond_vs1 or vs2.bonds == bond_vs2) # Vs properties self.assertEqual(vs1.virtual, 1) self.assertEqual(vs2.virtual, 1) # vs_relative properties seen = [] for p in vs1, vs2: r = p.vs_relative rel_to = r[0] dist = r[1] # Vs is related to one of the particles self.assertTrue(rel_to == p1.id or rel_to == p2.id) # The two vs relate to two different particles self.assertNotIn(rel_to, seen) seen.append(rel_to) # Check placement if rel_to == p1.id: dist_centers = np.copy(p2.pos - p1.pos) else: dist_centers = p1.pos - p2.pos expected_pos = self.s.part[rel_to].pos_folded + \ self.s.collision_detection.vs_placement * dist_centers dist = expected_pos - p.pos_folded dist -= np.round(dist / self.s.box_l) * self.s.box_l self.assertLess(np.linalg.norm(dist), 1E-12) @ut.skipIf(not espressomd.has_features("VIRTUAL_SITES_RELATIVE"), "VIRTUAL_SITES not compiled in") def test_bind_at_point_of_collision(self): # Single collision head node self.run_test_bind_at_point_of_collision_for_pos(np.array((0, 0, 0))) # Single collision, mixed self.run_test_bind_at_point_of_collision_for_pos( np.array((0.45, 0, 0))) # Single collision, non-head-node self.run_test_bind_at_point_of_collision_for_pos(np.array((0.7, 0, 0))) # head-node + mixed self.run_test_bind_at_point_of_collision_for_pos( np.array((0, 0, 0)), np.array((0.45, 0, 0))) # Mixed + other node self.run_test_bind_at_point_of_collision_for_pos( np.array((0.45, 0, 0)), np.array((0.7, 0, 0))) # Head + other self.run_test_bind_at_point_of_collision_for_pos( np.array((0.0, 0, 0)), np.array((0.7, 0, 0))) # Head + mixed + other self.run_test_bind_at_point_of_collision_for_pos( np.array((0.2, 0, 0)), np.array((0.95, 0, 0)), np.array((0.7, 0, 0))) @ut.skipIf(not espressomd.has_features("LENNARD_JONES", "VIRTUAL_SITES"), "Skipping for lack of LJ potential") def test_bind_at_point_of_collision_random(self): """Integrate lj liquid and check that no double bonds are formed and the number of bonds fits the number of virtual sites """ self.s.part.clear() # Add randomly placed particles self.s.part.add(pos=np.random.random((200, 3))) # Setup Lennard-Jones self.s.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1, sigma=0.1, cutoff=2**(1. / 6) * 0.1, shift="auto") # Remove overalp between particles self.s.integrator.set_steepest_descent( f_max=0, gamma=1, max_displacement=0.001) while self.s.analysis.energy()["total"] > len(self.s.part): self.s.integrator.run(10) # Collision detection self.s.collision_detection.set_params( mode="bind_at_point_of_collision", distance=0.11, bond_centers=self.H, bond_vs=self.H2, part_type_vs=1, vs_placement=0.4) self.get_state_set_state_consistency() # Integrate lj liquid self.s.integrator.set_vv() self.s.integrator.run(5000) # Analysis virtual_sites = self.s.part.select(virtual=1) non_virtual = self.s.part.select(virtual=0) # Check bonds on non-virtual particles bonds = [] for p in non_virtual: for bond in p.bonds: # Sort bond partners to make them unique independently of # which particle got the bond bonds.append(tuple(sorted([p.id, bond[1]]))) # No duplicate bonds? self.assertEqual(len(bonds), len(set(bonds))) # 2 virtual sites per bond? self.assertEqual(2 * len(bonds), len(virtual_sites)) # Find pairs of bonded virtual sites vs_pairs = [] for p in virtual_sites: # 0 or 1 bond on vs? self.assertTrue(len(p.bonds) in [0, 1]) if len(p.bonds) == 1: vs_pairs.append((p.id, p.bonds[0][1])) # Number of vs pairs = number of bonds? self.assertEqual(len(vs_pairs), len(bonds)) # Che3ck that vs pairs and bonds agree for vs_pair in vs_pairs: # Get corresponding non-virtual particles base_particles = tuple(sorted( [self.s.part[vs_pair[0]].vs_relative[0], self.s.part[vs_pair[1]].vs_relative[0]])) # Is there a corresponding bond? self.assertTrue(base_particles in bonds) # Tidy self.s.non_bonded_inter[ 0, 0].lennard_jones.set_params( epsilon=0, sigma=0, cutoff=0) def run_test_glue_to_surface_for_pos(self, *positions): positions = list(positions) shuffle(positions) self.s.part.clear() # Place particle which should not take part in collisions # In this case, it is skipped, because it is of the wrong type, # even if it is within range for a collision p = self.s.part.add(pos=positions[0], type=self.other_type) for pos in positions: # Since this is non-symmetric, we randomize order if np.random.random() > .5: p1 = self.s.part.add( pos=pos + (0, 0, 0), type=self.part_type_to_attach_vs_to) p2 = self.s.part.add( pos=pos + (0.1, 0, 0), type=self.part_type_to_be_glued) else: p2 = self.s.part.add( pos=pos + (0.1, 0, 0), type=self.part_type_to_be_glued) p1 = self.s.part.add( pos=pos + (0, 0, 0), type=self.part_type_to_attach_vs_to) # 2 non-virtual + 1 virtual + one that doesn't takekae part expected_np = 3 * len(positions) + 1 self.s.collision_detection.set_params( mode="glue_to_surface", distance=0.11, distance_glued_particle_to_vs=0.02, bond_centers=self.H, bond_vs=self.H2, part_type_vs=self.part_type_vs, part_type_to_attach_vs_to=self.part_type_to_attach_vs_to, part_type_to_be_glued=self.part_type_to_be_glued, part_type_after_glueing=self.part_type_after_glueing) self.get_state_set_state_consistency() self.s.integrator.run(0, recalc_forces=True) self.verify_state_after_glue_to_surface(expected_np) # Integrate again and check that nothing has changed self.s.integrator.run(0, recalc_forces=True) self.verify_state_after_glue_to_surface(expected_np) # Check that nothing explodes, when the particles are moved. # In particular for parallel simulations self.s.thermostat.set_langevin(kT=0, gamma=0.01) self.s.part[:].v = 0.05, 0.01, 0.15 self.s.integrator.run(3000) self.verify_state_after_glue_to_surface(expected_np) def verify_state_after_glue_to_surface(self, expected_np): self.assertEqual(len(self.s.part), expected_np) # At the end of test, this list should be empty parts_not_accounted_for = list(range(expected_np)) # We traverse particles. We look for a vs, get base particle from there # and prtner particle via bonds for p in self.s.part: # Skip non-virtual if p.virtual == 0: continue # The vs shouldn't have bonds self.assertEqual(p.bonds, ()) # Get base particles base_p = self.s.part[p.vs_relative[0]] # Get bound particle # There is a bond between the base particle and the bound particle # but we have no guarantee, on where its stored # 1. On the base particle of the vs p2 = None if len(base_p.bonds) == 1: self.assertEqual(base_p.bonds[0][0], self.H) p2 = self.s.part[base_p.bonds[0][1]] else: # We need to go through all particles to find it for candidate in self.s.part: if candidate.id not in parts_not_accounted_for: continue if len(candidate.bonds) >= 1: for b in candidate.bonds: if b[0] == self.H and b[1] == base_p.id: p2 = candidate if p2 is None: raise Exception("Bound particle not found") # Take note of accounted-for particles parts_not_accounted_for.remove(base_p.id) parts_not_accounted_for.remove(p.id) parts_not_accounted_for.remove(p2.id) self.verify_glue_to_surface_pair(base_p, p, p2) # Check particle that did not take part in collision. self.assertEqual(len(parts_not_accounted_for), 1) p = self.s.part[parts_not_accounted_for[0]] self.assertEqual(p.virtual, 0) self.assertEqual(p.type, self.other_type) self.assertEqual(p.bonds, ()) parts_not_accounted_for.remove(p.id) self.assertEqual(parts_not_accounted_for, []) def verify_glue_to_surface_pair(self, base_p, vs, bound_p): # Check all types self.assertEqual(base_p.type, self.part_type_to_attach_vs_to) self.assertEqual(vs.type, self.part_type_vs) self.assertEqual(bound_p.type, self.part_type_after_glueing) # Bound particle should have a bond to vs. It can additionally have a bond # to the base particle bond_to_vs_found = 0 for b in bound_p.bonds: if b[0] == self.H2: # bond to vs self.assertEqual(b, (self.H2, vs.id)) bond_to_vs_found += 1 self.assertEqual(bond_to_vs_found, 1) # Vs should not have a bond self.assertEqual(vs.bonds, ()) # Vs properties self.assertEqual(vs.virtual, 1) self.assertEqual(vs.vs_relative[0], base_p.id) # Distance vs,bound_p self.assertAlmostEqual(self.s.distance(vs, bound_p), 0.02, places=3) self.assertAlmostEqual(self.s.distance(base_p, bound_p), 0.1, places=3) self.assertAlmostEqual(self.s.distance(base_p, vs), 0.08, places=3) # base_p,vs,bound_p on a line self.assertGreater(np.dot(self.s.distance_vec(base_p, vs), self.s.distance_vec(base_p, bound_p)) / self.s.distance(base_p, vs) / self.s.distance(base_p, bound_p), 0.99) @ut.skipIf(not espressomd.has_features("VIRTUAL_SITES_RELATIVE"), "Skipped due to missing VIRTUAL_SITES_RELATIVE") def test_glue_to_surface(self): # Single collision head node self.run_test_glue_to_surface_for_pos(np.array((0, 0, 0))) # Single collision, mixed self.run_test_glue_to_surface_for_pos(np.array((0.45, 0, 0))) # Single collision, non-head-node self.run_test_glue_to_surface_for_pos(np.array((0.7, 0, 0))) # head-node + mixed self.run_test_glue_to_surface_for_pos( np.array((0, 0, 0)), np.array((0.45, 0, 0))) # Mixed + other node self.run_test_glue_to_surface_for_pos( np.array((0.45, 0, 0)), np.array((0.7, 0, 0))) # Head + other self.run_test_glue_to_surface_for_pos( np.array((0.0, 0, 0)), np.array((0.7, 0, 0))) # Head + mixed + other self.run_test_glue_to_surface_for_pos( np.array((0.2, 0, 0)), np.array((0.95, 0, 0)), np.array((0.7, 0, 0))) @ut.skipIf(not espressomd.has_features("VIRTUAL_SITES_RELATIVE"), "VIRTUAL_SITES not compiled in") def test_glue_to_surface_random(self): """Integrate lj liquid and check that no double bonds are formed and the number of bonds fits the number of virtual sites """ self.s.part.clear() # Add randomly placed particles self.s.part.add(pos=np.random.random((100, 3)), type=100 * [self.part_type_to_attach_vs_to]) self.s.part.add(pos=np.random.random( (100, 3)), type=100 * [self.part_type_to_be_glued]) self.s.part.add(pos=np.random.random( (100, 3)), type=100 * [self.other_type]) # Setup Lennard-Jones self.s.non_bonded_inter[0, 0].lennard_jones.set_params( epsilon=1, sigma=0.1, cutoff=2**(1. / 6) * 0.1, shift="auto") # Remove overalp between particles self.s.integrator.set_steepest_descent( f_max=0, gamma=1, max_displacement=0.001) while self.s.analysis.energy()["total"] > len(self.s.part): self.s.integrator.run(10) # Collision detection self.s.collision_detection.set_params( mode="glue_to_surface", distance=0.11, distance_glued_particle_to_vs=0.02, bond_centers=self.H, bond_vs=self.H2, part_type_vs=self.part_type_vs, part_type_to_attach_vs_to=self.part_type_to_attach_vs_to, part_type_to_be_glued=self.part_type_to_be_glued, part_type_after_glueing=self.part_type_after_glueing) self.get_state_set_state_consistency() # Integrate lj liquid self.s.integrator.set_vv() self.s.integrator.run(500) # Analysis virtual_sites = self.s.part.select(virtual=1) non_virtual = self.s.part.select(virtual=0) to_be_glued = self.s.part.select(type=self.part_type_to_be_glued) after_glueing = self.s.part.select(type=self.part_type_after_glueing) # One virtual site per glued particle? self.assertEqual(len(after_glueing), len(virtual_sites)) # Check bonds on non-virtual particles bonds_centers = [] bonds_virtual = [] for p in non_virtual: # Inert particles should not have bonds if p.type == self.other_type: self.assertEqual(len(p.bonds), 0) # Particles that have not yet collided should not have a bond if p.type == self.part_type_to_be_glued: self.assertEqual(len(p.bonds), 0) for bond in p.bonds: # Bond type and partner type # part_type_after_glueing can have a bond to a vs or to a # non_virtual particle if p.type == self.part_type_after_glueing: self.assertTrue(bond[0] in (self.H, self.H2)) # Bonds to virtual sites: if bond[0] == self.H2: self.assertEqual( self.s.part[bond[1]].type, self.part_type_vs) else: self.assertEqual( self.s.part[bond[1]].type, self.part_type_to_attach_vs_to) elif p.type == self.part_type_to_attach_vs_to: self.assertEqual(bond[0], self.H) self.assertEqual( self.s.part[bond[1]].type, self.part_type_after_glueing) else: print(p.id, p.type, p.bonds) raise Exception("Particle should not have bonds. ") # Collect bonds # Sort bond partners to make them unique independently of # which particle got the bond if bond[0] == self.H: bonds_centers.append(tuple(sorted([p.id, bond[1]]))) else: bonds_virtual.append(tuple(sorted([p.id, bond[1]]))) # No duplicate bonds? self.assertEqual(len(bonds_centers), len(set(bonds_centers))) self.assertEqual(len(bonds_virtual), len(set(bonds_virtual))) # 1 bond between centers and one between vs and glued particle # per collision self.assertEqual(len(bonds_virtual), len(bonds_centers)) # 1 virtual sites per bond? self.assertEqual(len(bonds_centers), len(virtual_sites)) # no bonds on vs and vs particle type for p in virtual_sites: self.assertEqual(len(p.bonds), 0) self.assertEqual(p.type, self.part_type_vs) # Tidy self.s.non_bonded_inter[ 0, 0].lennard_jones.set_params( epsilon=0, sigma=0, cutoff=0) @ut.skipIf(not espressomd.has_features("BOND_ANGLE"), "Tests skipped because AngleHarmonic not compiled in") def test_bind_three_particles(self): # Setup particles self.s.part.clear() dx = np.array((1, 0, 0)) dy = np.array((0, 1, 0)) dz = np.array((0, 0, 1)) a = np.array((0.499, 0.499, 0.499)) b = a + 0.1 * dx c = a + 0.03 * dx + 0.03 * dy d = a + 0.03 * dx - 0.03 * dy e = a - 0.1 * dx self.s.part.add(id=0, pos=a) self.s.part.add(id=1, pos=b) self.s.part.add(id=2, pos=c) self.s.part.add(id=3, pos=d) self.s.part.add(id=4, pos=e) # Setup bonds res = 181 for i in range(0, res, 1): self.s.bonded_inter[i + 2] = AngleHarmonic( bend=1, phi0=float(i) / (res - 1) * np.pi) cutoff = 0.11 self.s.collision_detection.set_params( mode="bind_three_particles", bond_centers=self.H, bond_three_particles=2, three_particle_binding_angle_resolution=res, distance=cutoff) self.get_state_set_state_consistency() self.s.integrator.run(0, recalc_forces=True) self.verify_triangle_binding(cutoff, self.s.bonded_inter[2], res) # Make sure no extra bonds appear self.s.integrator.run(0, recalc_forces=True) self.verify_triangle_binding(cutoff, self.s.bonded_inter[2], res) # Place the particles in two steps and make sure, the bonds are the # same self.s.part.clear() self.s.part.add(id=0, pos=a) self.s.part.add(id=2, pos=c) self.s.part.add(id=3, pos=d) self.s.integrator.run(0, recalc_forces=True) self.s.part.add(id=4, pos=e) self.s.part.add(id=1, pos=b) self.s.cell_system.set_domain_decomposition() self.s.integrator.run(0, recalc_forces=True) self.verify_triangle_binding(cutoff, self.s.bonded_inter[2], res) self.s.cell_system.set_n_square() self.s.part[:].bonds = () self.s.integrator.run(0, recalc_forces=True) self.verify_triangle_binding(cutoff, self.s.bonded_inter[2], res) def verify_triangle_binding(self, distance, first_bond, angle_res): # Gather pairs n = len(self.s.part) angle_res = angle_res - 1 expected_pairs = [] for i in range(n): for j in range(i + 1, n, 1): if self.s.distance(self.s.part[i], self.s.part[j]) <= distance: expected_pairs.append((i, j)) # Find triangles # Each elemtn is a particle id, a bond id and two bond partners in # ascending order expected_angle_bonds = [] for i in range(n): for j in range(i + 1, n, 1): for k in range(j + 1, n, 1): # Ref to particles p_i = self.s.part[i] p_j = self.s.part[j] p_k = self.s.part[k] # Normalized distnace vectors d_ij = np.copy(p_j.pos - p_i.pos) d_ik = np.copy(p_k.pos - p_i.pos) d_jk = np.copy(p_k.pos - p_j.pos) d_ij /= np.sqrt(np.sum(d_ij**2)) d_ik /= np.sqrt(np.sum(d_ik**2)) d_jk /= np.sqrt(np.sum(d_jk**2)) if self.s.distance(p_i, p_j) <= distance and self.s.distance(p_i, p_k) <= distance: id_i = first_bond._bond_id + \ int(np.round( np.arccos(np.dot(d_ij, d_ik)) * angle_res / np.pi)) expected_angle_bonds.append((i, id_i, j, k)) if self.s.distance(p_i, p_j) <= distance and self.s.distance(p_j, p_k) <= distance: id_j = first_bond._bond_id + \ int(np.round( np.arccos(np.dot(-d_ij, d_jk)) * angle_res / np.pi)) expected_angle_bonds.append((j, id_j, i, k)) if self.s.distance(p_i, p_k) <= distance and self.s.distance(p_j, p_k) <= distance: id_k = first_bond._bond_id + \ int(np.round( np.arccos(np.dot(-d_ik, -d_jk)) * angle_res / np.pi)) expected_angle_bonds.append((k, id_k, i, j)) # Gather actual pairs and actual triangles found_pairs = [] found_angle_bonds = [] for i in range(n): for b in self.s.part[i].bonds: if len(b) == 2: self.assertEqual(b[0]._bond_id, self.H._bond_id) found_pairs.append(tuple(sorted((i, b[1])))) elif len(b) == 3: partners = sorted(b[1:]) found_angle_bonds.append( (i, b[0]._bond_id, partners[0], partners[1])) else: raise Exception( "There should be only 2 and three particle bonds") # The order between expected and found bonds does not malways match # because collisions occur in random order. Sort stuff found_pairs = sorted(found_pairs) found_angle_bonds = sorted(found_angle_bonds) expected_angle_bonds = sorted(expected_angle_bonds) self.assertEqual(expected_pairs, found_pairs) if not expected_angle_bonds == found_angle_bonds: # Verbose info print("expected:", expected_angle_bonds) missing = [] for b in expected_angle_bonds: if b in found_angle_bonds: found_angle_bonds.remove(b) else: missing.append(b) print("missing", missing) print("extra:", found_angle_bonds) print() self.assertEqual(expected_angle_bonds, found_angle_bonds) def test_zz_serialization(self): self.s.collision_detection.set_params( mode="bind_centers", distance=0.11, bond_centers=self.H) reduce = self.s.collision_detection.__reduce__() res = reduce[0](reduce[1][0]) self.assertEqual(res.__class__.__name__, "CollisionDetection") self.assertEqual(res.mode, "bind_centers") self.assertAlmostEqual(res.distance, 0.11, delta=1E-12) self.assertEqual(res.bond_centers, self.H)
def test_pos_vel_forces(self): system = self.system system.cell_system.skin = 0.3 system.virtual_sites = VirtualSitesRelative(have_velocity=True) system.box_l = [10, 10, 10] system.part.clear() system.time_step = 0.004 system.part.clear() system.thermostat.turn_off() system.non_bonded_inter[0, 0].lennard_jones.set_params(epsilon=0, sigma=0, cutoff=0, shift=0) # Check setting of min_global_cut system.min_global_cut = 0.23 self.assertEqual(system.min_global_cut, 0.23) # Place central particle + 3 vs system.part.add(rotation=(1, 1, 1), pos=(0.5, 0.5, 0.5), id=1, quat=(1, 0, 0, 0), omega_lab=(1, 2, 3)) pos2 = (0.5, 0.4, 0.5) pos3 = (0.3, 0.5, 0.4) pos4 = (0.5, 0.5, 0.5) cur_id = 2 for pos in pos2, pos3, pos4: system.part.add(rotation=(1, 1, 1), pos=pos, id=cur_id) system.part[cur_id].vs_auto_relate_to(1) # Was the particle made virtual self.assertEqual(system.part[cur_id].virtual, True) # Are vs relative to id and vs_r = system.part[cur_id].vs_relative # id self.assertEqual(vs_r[0], 1) # distance self.assertAlmostEqual(vs_r[1], system.distance(system.part[1], system.part[cur_id]), places=6) cur_id += 1 # Move central particle and Check vs placement system.part[1].pos = (0.22, 0.22, 0.22) # linear and rotation velocity on central particle system.part[1].v = (0.45, 0.14, 0.447) system.part[1].omega_lab = (0.45, 0.14, 0.447) system.integrator.run(0, recalc_forces=True) for i in [2, 3, 4]: self.verify_vs(system.part[i]) # Check if still true, when non-virtual particle has rotated and a # linear motion system.part[1].omega_lab = [-5, 3, 8.4] system.integrator.run(10) for i in [2, 3, 4]: self.verify_vs(system.part[i]) if espressomd.has_features("EXTERNAL_FORCES"): # Test transfer of forces accumulating on virtual sites # to central particle f2 = np.array((3, 4, 5)) f3 = np.array((-4, 5, 6)) # Add forces to vs system.part[2].ext_force = f2 system.part[3].ext_force = f3 system.integrator.run(0) # get force/torques on non-vs f = system.part[1].f t = system.part[1].torque_lab # Expected force = sum of the forces on the vs self.assertLess(np.linalg.norm(f - f2 - f3), 1E-6) # Expected torque # Radial components of forces on a rigid body add to the torque t_exp = np.cross( system.distance_vec(system.part[1], system.part[2]), f2) t_exp += np.cross( system.distance_vec(system.part[1], system.part[3]), f3) # Check self.assertLessEqual(np.linalg.norm(t_exp - t), 1E-6) # Check virtual sites without velocity system.virtual_sites.have_velocity = False v2 = system.part[2].v system.part[1].v = 17, -13.5, 2 system.integrator.run(0, recalc_forces=True) self.assertLess(np.linalg.norm(v2 - system.part[2].v), 1E-6)
def run_test_lj(self): """This fills the system with vs-based dumbells, adds a lj potential, integrates and verifies forces. This is to make sure that no pairs get lost or are outdated in the short range loop""" system = self.system system.virtual_sites = VirtualSitesRelative(have_velocity=True) # Parameters n = 90 phi = 0.6 sigma = 1. eps = .025 cut = sigma * 2**(1. / 6.) kT = 2 gamma = .5 # box l = (n / 6. * np.pi * sigma**3 / phi)**(1. / 3.) # Setup system.box_l = l, l, l system.min_global_cut = 0.501 system.part.clear() system.time_step = 0.01 system.thermostat.turn_off() # Dumbells consist of 2 virtual lj spheres + central particle w/o interactions # For n spheres, n/2 dumbells. for i in range(int(n / 2)): # Type=1, i.e., no lj ia for the center of mass particles system.part.add(rotation=(1, 1, 1), id=3 * i, pos=random.random(3) * l, type=1, omega_lab=0.3 * random.random(3), v=random.random(3)) # lj spheres system.part.add(rotation=(1, 1, 1), id=3 * i + 1, pos=system.part[3 * i].pos + system.part[3 * i].director / 2., type=0) system.part.add(rotation=(1, 1, 1), id=3 * i + 2, pos=system.part[3 * i].pos - system.part[3 * i].director / 2., type=0) system.part[3 * i + 1].vs_auto_relate_to(3 * i) self.verify_vs(system.part[3 * i + 1], verify_velocity=False) system.part[3 * i + 2].vs_auto_relate_to(3 * i) self.verify_vs(system.part[3 * i + 2], verify_velocity=False) system.integrator.run(0, recalc_forces=True) # interactions system.non_bonded_inter[0, 0].lennard_jones.set_params(epsilon=eps, sigma=sigma, cutoff=cut, shift="auto") # Remove overlap system.integrator.set_steepest_descent(f_max=0, gamma=0.1, max_displacement=0.1) while system.analysis.energy()["total"] > 10 * n: system.integrator.run(20) # Integrate system.integrator.set_vv() for i in range(10): # Langevin to maintain stability system.thermostat.set_langevin(kT=kT, gamma=gamma, seed=42) system.integrator.run(50) system.thermostat.turn_off() # Constant energy to get rid of thermostat forces in the # verification system.integrator.run(2) # Check the virtual sites config,pos and vel of the lj spheres for j in range(int(n / 2)): self.verify_vs(system.part[3 * j + 1]) self.verify_vs(system.part[3 * j + 2]) # Verify lj forces on the particles. The non-virtual particles are # skipped because the forces on them originate from the vss and not # the lj interaction verify_lj_forces(system, 1E-10, 3 * np.arange(int(n / 2), dtype=int)) # Test applying changes enegry_pre_change = system.analysis.energy()['total'] pressure_pre_change = system.analysis.pressure()['total'] system.part[0].pos = system.part[0].pos + (2.2, -1.4, 4.2) enegry_post_change = system.analysis.energy()['total'] pressure_post_change = system.analysis.pressure()['total'] self.assertNotAlmostEqual(enegry_pre_change, enegry_post_change) self.assertNotAlmostEqual(pressure_pre_change, pressure_post_change) # Turn off lj interaction system.non_bonded_inter[0, 0].lennard_jones.set_params(epsilon=0, sigma=0, cutoff=0, shift=0)
print("\nArguments:", args) np.random.seed(42) # NUM PARTICLES AND BOX n_ionpairs = 100 n_part = n_ionpairs * 2 density_si = 0.5 # g/cm^3 rho_factor_bmim_pf6 = 0.003931 box_volume = n_ionpairs / rho_factor_bmim_pf6 / density_si box_l = box_volume**(1. / 3.) print("\n-->Ion pairs:", n_ionpairs, "Box size:", box_l) system = espressomd.System(box_l=[box_l, box_l, box_l]) system.virtual_sites = VirtualSitesRelative() if args.visu: d_scale = 0.988 * 0.5 c_ani = [1, 0, 0, 1] c_dru = [0, 0, 1, 1] c_com = [0, 0, 0, 1] c_cat = [0, 1, 0, 1] visualizer = espressomd.visualization_opengl.openGLLive( system, background_color=[1, 1, 1], drag_enabled=True, ext_force_arrows=True, drag_force=10, draw_bonds=False, quality_particles=32,