def test_intersection(self): h1 = Halfspace.from_hyperplane([[1, 0, 0], [0, 1, 0]], [1, 1, 1], [0.9, 0.9, 0.9], True) h2 = Halfspace.from_hyperplane([[0, 1, 0], [0, 0, 1]], [1, 1, 1], [0.9, 0.9, 0.9], True) h3 = Halfspace.from_hyperplane([[0, 0, 1], [1, 0, 0]], [1, 1, 1], [0.9, 0.9, 0.9], True) h4 = Halfspace.from_hyperplane([[-1, 0, 1], [0, -1, 1]], [1, 1, 0], [0.9, 0.9, 0.9], True) hi = HalfspaceIntersection([h1, h2, h3, h4], [0.9, 0.9, 0.9]) self.assertTrue(np.allclose(np.sum(hi.vertices, axis=0), [3, 3, 3])) h5 = Halfspace.from_hyperplane([[1, 0, 0], [0, 1, 0]], [1, 2, 2], [0.9, 0.9, 0.9], True) hi = HalfspaceIntersection([h5, h2, h3, h4], [0.9, 0.9, 0.9]) self.assertTrue(np.allclose(np.sum(hi.vertices, axis=0), [2, 2, 6])) for h, vs in zip(hi.halfspaces, hi.facets_by_halfspace): for v in vs: self.assertAlmostEqual( np.dot(h.normal, hi.vertices[v]) + h.offset, 0) for v, hss in zip(hi.vertices, hi.facets_by_vertex): for i in hss: hs = hi.halfspaces[i] self.assertAlmostEqual(np.dot(hs.normal, v) + hs.offset, 0)
def test_non_unit_normals(self): h1 = Halfspace([3, 0], -3) h2 = Halfspace([0, 2], 2) h3 = Halfspace([-2, -1], 0) hi = HalfspaceIntersection([h1, h2, h3], [0.9, -1.1]) self.assertTrue( np.any(np.all(hi.vertices == np.array([1, -2]), axis=1))) self.assertTrue( np.any(np.all(hi.vertices == np.array([1, -1]), axis=1)))
def test_infinite_vertex(self): h1 = Halfspace.from_hyperplane([[1, 0, 0], [0, 1, 0]], [1, 1, 1], [0.9, 0.9, 0.9], True) h2 = Halfspace.from_hyperplane([[0, 1, 0], [0, 0, 1]], [1, 1, 1], [0.9, 0.9, 0.9], True) h3 = Halfspace.from_hyperplane([[0, 0, 1], [1, 0, 0]], [1, 1, 1], [0.9, 0.9, 0.9], True) h4 = Halfspace.from_hyperplane([[1, 0, 0], [0, 1, 0]], [2, 2, 2], [0.9, 0.9, 0.9], True) hi = HalfspaceIntersection([h1, h2, h3, h4], [0.9, 0.9, 0.9]) self.assertTrue( np.allclose(np.sum(hi.vertices, axis=0), [np.inf, np.inf, np.inf]))
def vertices(self): """ Calls qhull for determining the vertices of the polytope (it computes the vertices only when called). """ if self._vertices is None: if self.empty: self._vertices = [] return self._vertices halfspaces = [] for i in range(0, self.n_facets): halfspace = Halfspace(self.A[i,:].tolist(), (-self.b[i,0]).tolist()) halfspaces.append(halfspace) polyhedron_qhull = HalfspaceIntersection(halfspaces, self.center.flatten().tolist()) self._vertices = polyhedron_qhull.vertices return self._vertices
def visualize_3d_polytope(p, name, visualizer): p = reorder_coordinates_visualizer(p) halfspaces = [] # change of coordinates because qhull is stupid... b_qhull = p.rhs_min - p.lhs_min.dot(p.center) for i in range(p.lhs_min.shape[0]): halfspace = Halfspace(p.lhs_min[i, :].tolist(), (-b_qhull[i, 0]).tolist()) halfspaces.append(halfspace) p_qhull = HalfspaceIntersection( halfspaces, np.zeros(p.center.shape).flatten().tolist()) vertices = p_qhull.vertices + np.hstack( [p.center] * len(p_qhull.vertices)).T mesh = Mesh(vertices.tolist(), p_qhull.facets_by_halfspace) visualizer[name].setgeometry(mesh) return visualizer
def check(self, x0, x1, y0, y1): points = np.array([[x0, 0], [x1, 0], [0, y0], [0, y1], [self.side, self.side]]) halfspaces = [] for pt in points: halfspaces += self.constraints(pt) hi = HalfspaceIntersection(halfspaces, [0.0, 0.0, self.top[0], self.top[1] / 2.0]) # find best vertex bestNorm = 0 bestV = None for v in hi.vertices: n = la.norm(v[:2]) if n > bestNorm: bestNorm = n bestV = v if bestNorm < 1.0: return False return self.mp.verify(x0, x1, y0, y1, bestV[0], bestV[1], bestV[2], bestV[3])
def vertices(self): """ Calls qhull for determining the vertices of the polytope (it computes the vertices only when called). """ if self._vertices is None: if self.n_variables == 1: self._vertices = [np.array([[self.rhs_min[i,0]/self.lhs_min[i,0]]]) for i in [0,1]] return self._vertices if self.empty: self._vertices = [] return self._vertices halfspaces = [] # change of coordinates because qhull is stupid... b_qhull = self.rhs_min - self.lhs_min.dot(self.center) for i in range(self.lhs_min.shape[0]): halfspace = Halfspace(self.lhs_min[i,:].tolist(), (-b_qhull[i,0]).tolist()) halfspaces.append(halfspace) polyhedron_qhull = HalfspaceIntersection(halfspaces, np.zeros(self.center.shape).flatten().tolist()) self._vertices = polyhedron_qhull.vertices self._vertices += np.repeat(self.center.T, self._vertices.shape[0], axis=0) self._vertices = [np.reshape(vertex, (vertex.shape[0],1)) for vertex in self._vertices] return self._vertices
def test_intersection2d(self): h1 = Halfspace([1, 0], -1) h2 = Halfspace([0, 1], 1) h3 = Halfspace([-2, -1], 0) h_redundant = Halfspace([1, 0], -2) hi = HalfspaceIntersection([h1, h2, h_redundant, h3], [0.9, -1.1]) for h, vs in zip(hi.halfspaces, hi.facets_by_halfspace): for v in vs: self.assertAlmostEqual( np.dot(h.normal, hi.vertices[v]) + h.offset, 0) for v, hss in zip(hi.vertices, hi.facets_by_vertex): for i in hss: hs = hi.halfspaces[i] self.assertAlmostEqual(np.dot(hs.normal, v) + hs.offset, 0) #redundant halfspace should have no vertices self.assertEqual(len(hi.facets_by_halfspace[2]), 0) self.assertTrue( np.any(np.all(hi.vertices == np.array([1, -2]), axis=1))) self.assertTrue( np.any(np.all(hi.vertices == np.array([1, -1]), axis=1)))
def get_chempot_range_map(self, limits=[[-2,16], [-4,4]]): """ Returns a chemical potential range map for each stable entry. Args: elements: Sequence of elements to be considered as independent variables. E.g., if you want to show the stability ranges of all Li-Co-O phases wrt to uLi and uO, you will supply [Element("Li"), Element("O")] Returns: Returns a dict of the form {entry: [simplices]}. The list of simplices are the sides of the N-1 dim polytope bounding the allowable chemical potential range of each entry. """ tol = PourbaixAnalyzer.numerical_tol all_chempots = [] facets = self._pd.facets for facet in facets: chempots = self.get_facet_chempots(facet) chempots["H+"] /= -0.0591 chempots["V"] = -chempots["V"] chempots["1"] = chempots["1"] all_chempots.append([chempots[el] for el in self._keys]) basis_vecs = [] on_plane_points = [] # Create basis vectors for entry in self._pd.stable_entries: ie = self._pd.qhull_entries.index(entry) row = self._pd._qhull_data[ie] on_plane_points.append([0, 0, row[2]]) this_basis_vecs = [] norm_vec = [-0.0591 * row[0], -1 * row[1], 1] if abs(norm_vec[0]) > tol: this_basis_vecs.append([-norm_vec[2]/norm_vec[0], 0, 1]) if abs(norm_vec[1]) > tol: this_basis_vecs.append([0, -norm_vec[2]/norm_vec[1], 1]) if len(this_basis_vecs) == 0: basis_vecs.append([[1, 0, 0], [0, 1, 0]]) elif len(this_basis_vecs) == 1: if abs(this_basis_vecs[0][0]) < tol: this_basis_vecs.append([1, 0, 0]) else: this_basis_vecs.append([0, 1, 0]) basis_vecs.append(this_basis_vecs) else: basis_vecs.append(this_basis_vecs) # Find point in half-space in which optimization is desired ph_max_contrib = -1 * max([abs(0.0591 * row[0]) for row in self._pd._qhull_data]) * limits[0][1] V_max_contrib = -1 * max([abs(row[1]) for row in self._pd._qhull_data]) * limits[1][1] g_max = (-1 * max([abs(pt[2]) for pt in on_plane_points]) + ph_max_contrib + V_max_contrib) - 10 point_in_region = [7, 0, g_max] # Append border hyperplanes along limits for i in range(len(limits)): for j in range(len(limits[i])): basis_vec_1 = [0.0] * 3 basis_vec_2 = [0.0] * 3 point = [0.0] * 3 basis_vec_1[2] = 1.0 basis_vec_2[2] = 0.0 for axis in range(len(limits)): if axis is not i: basis_vec_1[axis] = 0.0 basis_vec_2[axis] = 1.0 basis_vecs.append([basis_vec_1, basis_vec_2]) point[i] = limits[i][j] on_plane_points.append(point) # Hyperplane enclosing the very bottom basis_vecs.append([[1, 0, 0], [0, 1, 0]]) on_plane_points.append([0, 0, 2 * g_max]) hyperplane_list = [Halfspace.from_hyperplane(basis_vecs[i], on_plane_points[i], point_in_region) for i in range(len(basis_vecs))] hs_int = HalfspaceIntersection(hyperplane_list, point_in_region) int_points = hs_int.vertices pourbaix_domains = {} self.pourbaix_domain_vertices = {} for i in range(len(self._pd.stable_entries)): vertices = [[int_points[vert][0], int_points[vert][1]] for vert in hs_int.facets_by_halfspace[i]] if len(vertices) < 1: continue pourbaix_domains[self._pd.stable_entries[i]] = ConvexHull(vertices).simplices # Need to order vertices for highcharts area plot cx = sum([vert[0] for vert in vertices]) / len(vertices) cy = sum([vert[1] for vert in vertices]) / len(vertices) point_comp = lambda x, y: x[0]*y[1] - x[1]*y[0] vert_center = [[v[0] - cx, v[1] - cy] for v in vertices] vert_center.sort(key=cmp_to_key(point_comp)) self.pourbaix_domain_vertices[self._pd.stable_entries[i]] =\ [[v[0] + cx, v[1] + cy] for v in vert_center] self.pourbaix_domains = pourbaix_domains return pourbaix_domains