def setUp(self): mock_central_atom = Mock(spec=Atom) mock_central_atom.in_polyhedra = [] mock_central_atom.index = 0 mock_central_atom.label = 'A' mock_central_atom.__eq__ = mock_atom_eq mock_vertices = [Mock(spec=Atom) for i in range(6)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.index = i v.__lt__ = mock_atom_lt v.__eq__ = mock_atom_eq v.in_polyhedra = [] with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.construct_edge_graph' ) as mock_construct_edge_graph: mock_construct_edge_graph.return_value = { 0: [1, 2, 3, 4], 1: [0, 2, 3, 5], 2: [0, 1, 3, 5], 3: [0, 2, 4, 5], 4: [0, 1, 3, 5], 5: [1, 2, 3, 4] } with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.construct_abstract_geometry' ) as mock_construct_abstract_geometry: mock_construct_abstract_geometry.return_value = Mock( spec=AbstractGeometry) self.coordination_polyhedron = CoordinationPolyhedron( central_atom=mock_central_atom, vertices=mock_vertices)
def setUp(self): mock_central_atom = Mock(spec=Atom) mock_central_atom.in_polyhedra = [] mock_central_atom.index = 0 mock_central_atom.label = 'A' mock_central_atom.__eq__ = mock_atom_eq mock_vertices = [Mock(spec=Atom) for i in range(6)] for i, v in enumerate(mock_vertices, 1): v._neighbours = {} v.index = i v.__lt__ = mock_atom_lt v.__eq__ = mock_atom_eq v.in_polyhedra = [] self.coordination_polyhedron = CoordinationPolyhedron( central_atom=mock_central_atom, vertices=mock_vertices)
def opposite_vertex_pairs(polyhedron: CoordinationPolyhedron, check: bool = True) -> VertexPairs: """For an octahedral polyhedron, find the pairs of vertices opposite each other. Args: polyhedron (:obj:`CoordinationPolyhedron`): The polyhedron to be analysed. check: (Optional, :obj:`bool`): Optional flag to set whether to check that this polyhedron is an octahedron. Default is `True`. Returns: (tuple): 3 pairs of vertex atoms. """ if check: check_octahedra(polyhedron) assert {len(n) for n in polyhedron.edge_graph.values() } == {4}, "Edge graph does not describe an octahedron." vertex_pairs = [] seen_indices: List[int] = [] for v1 in polyhedron.vertices: if v1.index in seen_indices: continue v1_neighbours = polyhedron.vertices_by_indices( v1.neighbours[polyhedron.index]) v2 = next(v for v in polyhedron.vertices if v not in [v1, *v1_neighbours]) vertex_pairs.append((v1, v2)) seen_indices.extend([v1.index, v2.index]) return (vertex_pairs[0], vertex_pairs[1], vertex_pairs[2])
def polyhedra_from_atom_indices( central_atoms: List[Atom], vertex_atoms: List[Atom], central_indices: List[int], vertex_indices: List[List[int]], label: Optional[str] = None) -> List[CoordinationPolyhedron]: """Construct a set of polyhedra from lists of atom indices for central and vertex atoms. c Args: central_atoms (list(Atom)): List of Atom objects describing the set of possible centre atoms. vertex_atoms (list(Atom)): List of Atom objects describing the set of possible vertex atoms. central_indices (list(int)): List of integer indices specifying each central atom. vertex_indices (list(list(int)): Nested list of integer indices for the vertex atoms in each polyhedron. Returns: list(CoordinationPolyhedron) Raises: ValueError: If the lengths of the central_indices and vertex_indices lists are unequal. """ if len(central_indices) != len(vertex_indices): raise ValueError( 'central_indices and vertex_indices are different lengths: ' f'{len(central_indices)} vs. {len(vertex_indices)}.') polyhedra = [] for ic, iv in zip(central_indices, vertex_indices): central_atom = next(atom for atom in central_atoms if atom.index == ic) vertex_atoms = [atom for atom in vertex_atoms if atom.index in iv] polyhedra.append( CoordinationPolyhedron(central_atom=central_atom, vertices=vertex_atoms, label=label)) return polyhedra
def test_coordination_polyhedron_is_initialised(self): mock_central_atom = Mock(spec=Atom) mock_central_atom.in_polyhedra = [] mock_central_atom.index = 10 mock_central_atom.label = 'Li' mock_vertices = [Mock(spec=Atom) for i in range(6)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.__lt__ = mock_atom_lt v.index = i v.in_polyhedra = [] CoordinationPolyhedron(central_atom=mock_central_atom, vertices=mock_vertices)
def polyhedra_from_nearest_neighbours( central_atoms: List[Atom], vertex_atoms: List[Atom], nn: int, label: Optional[str] = None) -> List[CoordinationPolyhedron]: polyhedra = [] for c_atom in central_atoms: vertices = sorted(vertex_atoms, key=lambda atom: atom.distance(c_atom))[:nn] polyhedra.append( CoordinationPolyhedron(central_atom=c_atom, vertices=vertices, label=label)) return polyhedra
def polyhedra_from_closest_centre( central_atoms: List[Atom], vertex_atoms: List[Atom], label: Optional[str] = None) -> List[CoordinationPolyhedron]: coordination_coords = [co_atom.coords for co_atom in vertex_atoms] central_coords = [c_atom.coords for c_atom in central_atoms] closest_site_index = [ np.argmin([a.distance(c_atom) for c_atom in central_atoms]) for a in vertex_atoms ] polyhedra = [] for i, c_atom in enumerate(central_atoms): vertices = [ co for c, co in zip(closest_site_index, vertex_atoms) if c == i ] polyhedra.append( CoordinationPolyhedron(central_atom=c_atom, vertices=vertices, label=label)) return polyhedra
def polyhedra_from_distance_cutoff( central_atoms: List[Atom], vertex_atoms: List[Atom], cutoff: float, label: Optional[str] = None) -> List[CoordinationPolyhedron]: if not central_atoms: return [] polyhedra = [] lattice = central_atoms[0].site.lattice distance_matrix = lattice.get_all_distances( [c.frac_coords for c in central_atoms], [v.frac_coords for v in vertex_atoms]) for dr, c_atom in zip(distance_matrix, central_atoms): indices = np.where(dr <= cutoff)[0] vertices = [vertex_atoms[i] for i in indices] polyhedra.append( CoordinationPolyhedron(central_atom=c_atom, vertices=vertices, label=label)) return polyhedra
def test_neighbours(self): mock_central_atom_i = Mock(spec=Atom) mock_central_atom_i.in_polyhedra = [] mock_central_atom_i.index = 3 mock_central_atom_i.label = 'Li' mock_central_atom_j = Mock(spec=Atom) mock_central_atom_j.in_polyhedra = [] mock_central_atom_j.index = 7 mock_central_atom_j.label = 'Li' mock_vertices = [Mock(spec=Atom) for i in range(10)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.__lt__ = mock_atom_lt v.index = i v.in_polyhedra = [] polyhedron_i = CoordinationPolyhedron(central_atom=mock_central_atom_i, vertices=mock_vertices[:6]) polyhedron_j = CoordinationPolyhedron(central_atom=mock_central_atom_j, vertices=mock_vertices[-6:]) polyhedron_i._edge_graph = Mock() polyhedron_j._edge_graph = Mock() self.assertEqual(polyhedron_i.neighbours(), (polyhedron_j, )) self.assertEqual(polyhedron_j.neighbours(), (polyhedron_i, ))
class TestCoordinationPolyhedron(unittest.TestCase): def setUp(self): mock_central_atom = Mock(spec=Atom) mock_central_atom.in_polyhedra = [] mock_central_atom.index = 0 mock_central_atom.label = 'A' mock_central_atom.__eq__ = mock_atom_eq mock_vertices = [Mock(spec=Atom) for i in range(6)] for i, v in enumerate(mock_vertices, 1): v._neighbours = {} v.index = i v.__lt__ = mock_atom_lt v.__eq__ = mock_atom_eq v.in_polyhedra = [] self.coordination_polyhedron = CoordinationPolyhedron( central_atom=mock_central_atom, vertices=mock_vertices) def test_equal_members(self): other_coordination_polyhedron = copy.deepcopy( self.coordination_polyhedron) other_coordination_polyhedron.vertices[0].neighbours = {0: [1, 2, 3]} other_coordination_polyhedron.vertices[4].neighbours = {4: [1, 3, 5]} self.assertTrue( self.coordination_polyhedron.equal_members( other_coordination_polyhedron)) def test_vertex_vectors(self): vectors = [np.array([1.0, 0.0, 0.0]), np.array([0.0, 1.0, 2.0])] self.coordination_polyhedron._abstract_geometry = Mock( spec=AbstractGeometry) self.coordination_polyhedron.abstract_geometry.points_wocs_ctwocc.return_value = vectors returned_vectors = self.coordination_polyhedron.vertex_vectors for v1, v2 in zip(vectors, returned_vectors): np.testing.assert_equal(v1, v2) def test_angles(self): vertex_vectors = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0]]) with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.vertex_vectors', new_callable=PropertyMock) as mock_vertex_vectors: mock_vertex_vectors.return_value = vertex_vectors angles = self.coordination_polyhedron.angles() np.testing.assert_equal(angles, [90.0, 90.0, 180.0]) def test_vertex_distances(self): mock_vertex_distances = [2.0, 1.0, 1.0, 1.0, 1.0, 1.5] self.coordination_polyhedron.central_atom.distance = \ Mock(side_effect=mock_vertex_distances) vertex_distances = self.coordination_polyhedron.vertex_distances() np.testing.assert_equal(vertex_distances, mock_vertex_distances) def test_vertex_distances_and_labels(self): mock_vertex_distances = (2.0, 1.0, 1.0, 1.0, 1.0, 1.5) mock_labels = ['O', 'O', 'F', 'F', 'F', 'F'] self.coordination_polyhedron.central_atom = Mock(spec=Atom) self.coordination_polyhedron.vertex_distances = \ Mock(return_value=mock_vertex_distances) with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.vertex_labels', new_callable=PropertyMock) as mock_vertex_labels: mock_vertex_labels.__get__ = Mock(return_value=mock_labels) output = self.coordination_polyhedron.vertex_distances_and_labels() np.testing.assert_equal(output, list(zip(mock_vertex_distances, mock_labels))) def test_update_vertex_neighbours(self): with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.edge_graph', new_callable=PropertyMock) as mock_edge_graph: edge_graph = { 1: [2, 3, 4, 5], 2: [1, 3, 5, 6], 3: [1, 2, 4, 6], 4: [1, 3, 5, 6], 5: [1, 2, 4, 6], 6: [2, 3, 4, 5] } mock_edge_graph.return_value = edge_graph self.coordination_polyhedron.update_vertex_neighbours() for v, n in zip(self.coordination_polyhedron.vertices, edge_graph.values()): self.assertEqual( v._neighbours[self.coordination_polyhedron.index], n) def test_vertices_by_indices(self): vertices = self.coordination_polyhedron.vertices_by_indices([2, 4]) self.assertEqual(sorted([v.index for v in vertices]), [2, 4]) def test_vertex_indices(self): self.assertEqual(self.coordination_polyhedron.vertex_indices, [1, 2, 3, 4, 5, 6]) def test_vertex_coords(self): coords = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]) for v, c in zip(self.coordination_polyhedron.vertices, coords): v.coords = c np.testing.assert_array_equal( self.coordination_polyhedron.vertex_coords, coords) def test_vertex_labels(self): labels = ['A', 'B', 'C', 'D', 'E', 'F'] for v, l in zip(self.coordination_polyhedron.vertices, labels): v.label = l self.assertEqual(self.coordination_polyhedron.vertex_labels, labels) def test_coordination_number(self): self.assertEqual(self.coordination_polyhedron.coordination_number, len(self.coordination_polyhedron.vertices)) def test_index(self): self.assertEqual(self.coordination_polyhedron.index, self.coordination_polyhedron.central_atom.index) def test_edge_graph_if_cached(self): edge_graph = { 1: [2, 3, 4, 5], 2: [1, 3, 5, 6], 3: [1, 2, 4, 6], 4: [1, 3, 5, 6], 5: [1, 2, 4, 6], 6: [2, 3, 4, 5] } self.coordination_polyhedron._edge_graph = edge_graph self.assertEqual(self.coordination_polyhedron.edge_graph, edge_graph) def test_edge_graph_if_not_cached(self): edge_graph = { 1: [2, 3, 4, 5], 2: [1, 3, 5, 6], 3: [1, 2, 4, 6], 4: [1, 3, 5, 6], 5: [1, 2, 4, 6], 6: [2, 3, 4, 5] } self.coordination_polyhedron.construct_edge_graph = Mock( return_value=edge_graph) self.coordination_polyhedron.update_vertex_neighbours = Mock() self.assertEqual(self.coordination_polyhedron.edge_graph, edge_graph) self.assertEqual( self.coordination_polyhedron.update_vertex_neighbours.call_count, 1) self.assertEqual( self.coordination_polyhedron.construct_edge_graph.call_count, 1) def test_abstract_geometry_if_cached(self): abstract_geometry = Mock(spec=AbstractGeometry) self.coordination_polyhedron._abstract_geometry = abstract_geometry self.assertEqual(self.coordination_polyhedron.abstract_geometry, abstract_geometry) def test_abstract_geometry_if_not_cached(self): abstract_geometry = Mock(spec=AbstractGeometry) self.coordination_polyhedron.construct_abstract_geometry = Mock( return_value=abstract_geometry) self.assertEqual(self.coordination_polyhedron.abstract_geometry, abstract_geometry) self.assertEqual( self.coordination_polyhedron.construct_abstract_geometry. call_count, 1) def test_construct_abstract_geometry(self): polyhedron = self.coordination_polyhedron polyhedron.central_atom.coords = np.array([1.0, 2.0, 3.0]) polyhedron.minimum_image_vertex_coordinates = Mock(return_value='foo') abstract_geometry = Mock(spec=AbstractGeometry) with patch( f'polyhedral_analysis.coordination_polyhedron.AbstractGeometry' ) as mock_abstract_geometry: mock_abstract_geometry.return_value = abstract_geometry ag = polyhedron.construct_abstract_geometry() self.assertEqual(ag, abstract_geometry) mock_abstract_geometry.assert_called_with( central_site=polyhedron.central_atom.coords, bare_coords='foo', include_central_site_in_centroid=False) def test_faces(self): polyhedron = self.coordination_polyhedron polyhedron.convex_hull = Mock(return_value=Mock(spec=ConvexHull)) returned_vertex_indices = [1, 2, 3, 4, 5, 6] with patch(f'{module_str}.vertex_indices', new_callable=PropertyMock) as mock_vertex_indices: mock_vertex_indices.return_value = returned_vertex_indices simplices = [ np.array([0, 3, 1]), np.array([0, 3, 4]), np.array([5, 3, 1]), np.array([5, 3, 4]), np.array([2, 0, 4]), np.array([2, 0, 1]), np.array([2, 5, 4]), np.array([2, 5, 1]) ] with patch( 'polyhedral_analysis.coordination_polyhedron.merge_coplanar_simplices' ) as mock_merge_coplanar_simplices: mock_merge_coplanar_simplices.return_value = simplices faces = polyhedron.faces() self.assertEqual(faces, ((1, 2, 4), (1, 4, 5), (2, 4, 6), (4, 5, 6), (1, 3, 5), (1, 2, 3), (3, 5, 6), (2, 3, 6))) def test_neighbours(self): mock_central_atom_i = Mock(spec=Atom) mock_central_atom_i.in_polyhedra = [] mock_central_atom_i.index = 3 mock_central_atom_i.label = 'Li' mock_central_atom_j = Mock(spec=Atom) mock_central_atom_j.in_polyhedra = [] mock_central_atom_j.index = 7 mock_central_atom_j.label = 'Li' mock_vertices = [Mock(spec=Atom) for i in range(10)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.__lt__ = mock_atom_lt v.index = i v.in_polyhedra = [] polyhedron_i = CoordinationPolyhedron(central_atom=mock_central_atom_i, vertices=mock_vertices[:6]) polyhedron_j = CoordinationPolyhedron(central_atom=mock_central_atom_j, vertices=mock_vertices[-6:]) polyhedron_i._edge_graph = Mock() polyhedron_j._edge_graph = Mock() self.assertEqual(polyhedron_i.neighbours(), (polyhedron_j, )) self.assertEqual(polyhedron_j.neighbours(), (polyhedron_i, )) def test_interection(self): mock_central_atom_i = Mock(spec=Atom) mock_central_atom_i.in_polyhedra = [] mock_central_atom_i.index = 3 mock_central_atom_i.label = 'Li' mock_central_atom_j = Mock(spec=Atom) mock_central_atom_j.in_polyhedra = [] mock_central_atom_j.index = 7 mock_central_atom_j.label = 'Li' mock_vertices = [Mock(spec=Atom) for i in range(10)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.__lt__ = mock_atom_lt v.index = i v.in_polyhedra = [] polyhedron_i = CoordinationPolyhedron(central_atom=mock_central_atom_i, vertices=mock_vertices[:6]) polyhedron_j = CoordinationPolyhedron(central_atom=mock_central_atom_j, vertices=mock_vertices[-6:]) self.assertEqual(polyhedron_i.intersection(polyhedron_j), (5, 6)) self.assertEqual(polyhedron_j.intersection(polyhedron_i), (5, 6)) def test_neighbours_by_index_and_shared_vertices(self): polyhedron_i = self.coordination_polyhedron polyhedron_j = copy.deepcopy(polyhedron_i) polyhedron_i.neighbours = Mock(return_value=(polyhedron_j, )) polyhedron_j.neighbours = Mock(return_value=(polyhedron_i, )) polyhedron_i.intersection = Mock(return_value=(5, 6)) polyhedron_j.intersection = Mock(return_value=(5, 6)) self.assertEqual( polyhedron_i.neighbours_by_index_and_shared_vertices(), {polyhedron_j.index: (5, 6)}) self.assertEqual( polyhedron_j.neighbours_by_index_and_shared_vertices(), {polyhedron_i.index: (5, 6)})
class TestCoordinationPolyhedron(unittest.TestCase): def setUp(self): mock_central_atom = Mock(spec=Atom) mock_central_atom.in_polyhedra = [] mock_central_atom.index = 0 mock_central_atom.label = 'A' mock_central_atom.__eq__ = mock_atom_eq mock_vertices = [Mock(spec=Atom) for i in range(6)] for i, v in enumerate(mock_vertices, 1): v.neighbours = None v.index = i v.__lt__ = mock_atom_lt v.__eq__ = mock_atom_eq v.in_polyhedra = [] with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.construct_edge_graph' ) as mock_construct_edge_graph: mock_construct_edge_graph.return_value = { 0: [1, 2, 3, 4], 1: [0, 2, 3, 5], 2: [0, 1, 3, 5], 3: [0, 2, 4, 5], 4: [0, 1, 3, 5], 5: [1, 2, 3, 4] } with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.construct_abstract_geometry' ) as mock_construct_abstract_geometry: mock_construct_abstract_geometry.return_value = Mock( spec=AbstractGeometry) self.coordination_polyhedron = CoordinationPolyhedron( central_atom=mock_central_atom, vertices=mock_vertices) def test_equal_members(self): other_coordination_polyhedron = copy.deepcopy( self.coordination_polyhedron) other_coordination_polyhedron.vertices[0].neighbours = {0: [1, 2, 3]} other_coordination_polyhedron.vertices[4].neighbours = {4: [1, 3, 5]} self.assertTrue( self.coordination_polyhedron.equal_members( other_coordination_polyhedron)) def test_vertex_vectors(self): vectors = [np.array([1.0, 0.0, 0.0]), np.array([0.0, 1.0, 2.0])] self.coordination_polyhedron.abstract_geometry.points_wocs_ctwocc.return_value = vectors returned_vectors = self.coordination_polyhedron.vertex_vectors for v1, v2 in zip(vectors, returned_vectors): np.testing.assert_equal(v1, v2) def test_angles(self): vertex_vectors = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0]]) with patch( 'polyhedral_analysis.coordination_polyhedron.CoordinationPolyhedron.vertex_vectors', new_callable=PropertyMock) as mock_vertex_vectors: mock_vertex_vectors.return_value = vertex_vectors angles = self.coordination_polyhedron.angles() np.testing.assert_equal(angles, [90.0, 90.0, 180.0]) def test_vertex_distances(self): mock_vertex_distances = [2.0, 1.0, 1.0, 1.0, 1.0, 1.5] self.coordination_polyhedron.central_atom.site = Mock(spec=Site) self.coordination_polyhedron.central_atom.site.distance = \ Mock( side_effect=mock_vertex_distances ) for v in self.coordination_polyhedron.vertices: v.site = Mock(spec=Site) vertex_distances = self.coordination_polyhedron.vertex_distances() np.testing.assert_equal(vertex_distances, mock_vertex_distances) def test_vertex_distances_with_vertex_labels(self): mock_vertex_distances = [2.0, 1.0, 1.0, 1.0, 1.0, 1.5] mock_labels = ['O', 'O', 'F', 'F', 'F', 'F'] self.coordination_polyhedron.central_atom.site = Mock(spec=Site) self.coordination_polyhedron.central_atom.site.distance = \ Mock( side_effect=mock_vertex_distances ) for v, mock_label in zip(self.coordination_polyhedron.vertices, mock_labels): v.site = Mock(spec=Site) v.label = mock_label vertex_distances = self.coordination_polyhedron.vertex_distances( vertex_labels=True) np.testing.assert_equal(vertex_distances, list(zip(mock_vertex_distances, mock_labels)))