def test_points_on_sphere(): points = geom.get_points_on_sphere(n_points=4) # 4 points on a sphere equally spaced should be roughly √2 apart assert len(points) == 4 assert np.abs(np.linalg.norm(points[0] - points[1]) - np.sqrt(2)) < 1E-6 points = geom.get_points_on_sphere(n_points=2) # The algorithm isn't great at generated small numbers of points so 2 -> 3 # 3 points on a sphere equally spaced should be roughly the diameter assert len(points) == 3 assert np.abs(np.linalg.norm(points[0] - points[1]) - np.sqrt(3)) < 1E-6
def _generate_conformers(self): """ Generate rigid body conformers of a complex by (1) Fixing the first m olecule, (2) initialising the second molecule's COM evenly on the points of a sphere around the first with a random rotation and (3) iterating until all molecules in the complex have been added """ if len(self.molecules) < 2: # Single (or zero) molecule complex only has a single *rigid body* # conformer self.conformers = [get_conformer(name=self.name, species=self)] return None n_molecules = len(self.molecules) # Number of molecules in the complex self.conformers = [] n = 0 # Current conformer number points_on_sphere = get_points_on_sphere( n_points=Config.num_complex_sphere_points) for _ in iterprod(range(Config.num_complex_random_rotations), repeat=n_molecules - 1): # Generate the rotation thetas and axes rotations = [ np.random.uniform(-np.pi, np.pi, size=4) for _ in range(n_molecules - 1) ] for points in iterprod(points_on_sphere, repeat=n_molecules - 1): conformer = get_conformer(species=self, name=f'{self.name}_conf{n}') atoms = get_complex_conformer_atoms(self.molecules, rotations, points) conformer.set_atoms(atoms) self.conformers.append(conformer) n += 1 if n == Config.max_num_complex_conformers: logger.warning( f'Generated the maximum number of complex conformers ({n})' ) return None logger.info(f'Generated {n} conformers') return None
def get_active_atom_neighbour_lists(self, mol, depth): """ Get neighbour lists of all the active atoms in the molecule (reactant complex) Arguments: mol (autode.species.Species): depth (int): Depth of the neighbour list to consider Returns: (list(list(int))): """ connected_molecules = connected_components(mol.graph) n_molecules = len(connected_molecules) def shift_molecules(vectors): for i, molecule_nodes in enumerate(connected_molecules): for j in molecule_nodes: mol.atoms[j].translate(vec=vectors[i]) # For every molecule in the complex shift so they are far away, thus # the neighbour lists only include atoms in the same molecule vecs = get_points_on_sphere(n_points=n_molecules + 1) shift_vectors = [100 * vec for vec in vecs] shift_molecules(vectors=shift_vectors) # Calculate the neighbour lists while the molecules are all far away def nl(atom): return get_neighbour_list(species=mol, atom_i=atom)[:depth] if self.active_atom_nl is None: self.active_atom_nl = [nl(atom) for atom in self.active_atoms] # Shift the molecules back to where they were shift_molecules(vectors=[-vector for vector in shift_vectors]) return self.active_atom_nl