def test_random(it): rng = np.random.RandomState(seed=it) B = rng.uniform(-1, 1, (3, 3)) R, H = minkowski_reduce(B) assert_allclose(H @ B, R, atol=TOL) assert np.sign(np.linalg.det(B)) == np.sign(np.linalg.det(R)) norms = np.linalg.norm(R, axis=1) assert (np.argsort(norms) == range(3)).all() # Test idempotency _, _H = minkowski_reduce(R) assert (_H == np.eye(3).astype(int)).all() rcell, _ = Cell(B).minkowski_reduce() assert_allclose(rcell, R, atol=TOL)
def test_pbc(self, pbc): lcell = self.lcell rcell, op = minkowski_reduce(lcell, pbc=pbc) assert_almost_equal(np.linalg.det(rcell), 1) rdet = np.linalg.det(rcell) ldet = np.linalg.det(lcell) assert np.sign(ldet) == np.sign(rdet)
def test_1d(self, axis): lcell = self.lcell rcell, op = minkowski_reduce(lcell, pbc=np.roll([1, 0, 0], axis)) assert (rcell == lcell).all() # 1D reduction does nothing zcell = np.zeros((3, 3)) zcell[0] = lcell[0] rcell, _ = Cell(zcell).minkowski_reduce() assert_allclose(rcell, zcell, atol=TOL)
def test_2d(self, axis): lcell = self.lcell pbc = np.roll([0, 1, 1], axis) rcell, op = minkowski_reduce(lcell.astype(float), pbc=pbc) assert (rcell[axis] == lcell[axis]).all() zcell = np.copy(lcell) zcell[axis] = 0 rzcell, _ = Cell(zcell).minkowski_reduce() rcell[axis] = 0 assert_allclose(rzcell, rcell, atol=TOL)
def _get_neighbors(self, dx: np.ndarray) -> Iterator[np.ndarray]: pbc = self.atoms.pbc if self.cell is None or not np.all(self.cell == self.atoms.cell): self.cell = self.atoms.cell.array.copy() rcell, self.op = minkowski_reduce(complete_cell(self.cell), pbc=pbc) self.rcell = Cell(rcell) dx_sc = dx @ self.rcell.reciprocal().T offset = np.zeros(3, dtype=np.int32) for _ in range(2): offset += pbc * ((dx_sc - offset) // 1.).astype(np.int32) for ts in product(*[np.arange(-1 * p, p + 1) for p in pbc]): yield (np.array(ts) - offset) @ self.op
import numpy as np from ase.geometry import minkowski_reduce from ase.cell import Cell tol = 1E-14 rng = np.random.RandomState(0) np.seterr(all='raise') cell = Cell([[8.972058879514716, 0.0009788104586639142, 0.0005932485724084841], [4.485181755775297, 7.770520334862034, 0.00043663339838788054], [4.484671994095723, 2.5902066679984634, 16.25695615743613]]) cell.minkowski_reduce() for i in range(40): B = rng.uniform(-1, 1, (3, 3)) R, H = minkowski_reduce(B) assert np.allclose(H @ B, R, atol=tol) assert np.sign(np.linalg.det(B)) == np.sign(np.linalg.det(R)) norms = np.linalg.norm(R, axis=1) assert (np.argsort(norms) == range(3)).all() # Test idempotency _, _H = minkowski_reduce(R) assert (_H == np.eye(3).astype(np.int)).all() rcell, _ = Cell(B).minkowski_reduce() assert np.allclose(rcell, R, atol=tol) cell = np.array([[1, 1, 2], [0, 1, 4], [0, 0, 1]]) unimodular = np.array([[1, 2, 2], [0, 1, 2], [0, 0, 1]])
def build(self, pbc, cell, coordinates): """Build the list. Coordinates are taken to be scaled or not according to self.use_scaled_positions. """ self.pbc = pbc = np.array(pbc, copy=True) self.cell = cell = Cell(cell) self.coordinates = coordinates = np.array(coordinates, copy=True) if len(self.cutoffs) != len(coordinates): raise ValueError('Wrong number of cutoff radii: {0} != {1}'.format( len(self.cutoffs), len(coordinates))) if len(self.cutoffs) > 0: rcmax = self.cutoffs.max() else: rcmax = 0.0 if self.use_scaled_positions: positions0 = cell.cartesian_positions(coordinates) else: positions0 = coordinates rcell, op = minkowski_reduce(cell, pbc) positions = wrap_positions(positions0, rcell, pbc=pbc, eps=0) natoms = len(positions) self.nneighbors = 0 self.npbcneighbors = 0 self.neighbors = [np.empty(0, int) for a in range(natoms)] self.displacements = [np.empty((0, 3), int) for a in range(natoms)] self.nupdates += 1 if natoms == 0: return N = [] ircell = np.linalg.pinv(rcell) for i in range(3): if self.pbc[i]: v = ircell[:, i] h = 1 / np.linalg.norm(v) n = int(2 * rcmax / h) + 1 else: n = 0 N.append(n) tree = cKDTree(positions, copy_data=True) offsets = cell.scaled_positions(positions - positions0) offsets = offsets.round().astype(np.int) for n1, n2, n3 in itertools.product(range(0, N[0] + 1), range(-N[1], N[1] + 1), range(-N[2], N[2] + 1)): if n1 == 0 and (n2 < 0 or n2 == 0 and n3 < 0): continue displacement = (n1, n2, n3) @ rcell for a in range(natoms): indices = tree.query_ball_point(positions[a] - displacement, r=self.cutoffs[a] + rcmax) if not len(indices): continue indices = np.array(indices) delta = positions[indices] + displacement - positions[a] cutoffs = self.cutoffs[indices] + self.cutoffs[a] i = indices[np.linalg.norm(delta, axis=1) < cutoffs] if n1 == 0 and n2 == 0 and n3 == 0: if self.self_interaction: i = i[i >= a] else: i = i[i > a] self.nneighbors += len(i) self.neighbors[a] = np.concatenate((self.neighbors[a], i)) disp = (n1, n2, n3) @ op + offsets[i] - offsets[a] self.npbcneighbors += disp.any(1).sum() self.displacements[a] = np.concatenate( (self.displacements[a], disp)) if self.bothways: neighbors2 = [[] for a in range(natoms)] displacements2 = [[] for a in range(natoms)] for a in range(natoms): for b, disp in zip(self.neighbors[a], self.displacements[a]): neighbors2[b].append(a) displacements2[b].append(-disp) for a in range(natoms): nbs = np.concatenate((self.neighbors[a], neighbors2[a])) disp = np.array( list(self.displacements[a]) + displacements2[a]) # Force correct type and shape for case of no neighbors: self.neighbors[a] = nbs.astype(int) self.displacements[a] = disp.astype(int).reshape((-1, 3)) if self.sorted: for a, i in enumerate(self.neighbors): mask = (i < a) if mask.any(): j = i[mask] offsets = self.displacements[a][mask] for b, offset in zip(j, offsets): self.neighbors[b] = np.concatenate( (self.neighbors[b], [a])) self.displacements[b] = np.concatenate( (self.displacements[b], [-offset])) mask = np.logical_not(mask) self.neighbors[a] = self.neighbors[a][mask] self.displacements[a] = self.displacements[a][mask]
def run(): tol = 1E-14 rng = np.random.RandomState(0) cell = Cell( [[8.972058879514716, 0.0009788104586639142, 0.0005932485724084841], [4.485181755775297, 7.770520334862034, 0.00043663339838788054], [4.484671994095723, 2.5902066679984634, 16.25695615743613]]) cell.minkowski_reduce() for i in range(40): B = rng.uniform(-1, 1, (3, 3)) R, H = minkowski_reduce(B) assert np.allclose(H @ B, R, atol=tol) assert np.sign(np.linalg.det(B)) == np.sign(np.linalg.det(R)) norms = np.linalg.norm(R, axis=1) assert (np.argsort(norms) == range(3)).all() # Test idempotency _, _H = minkowski_reduce(R) assert (_H == np.eye(3).astype(np.int)).all() rcell, _ = Cell(B).minkowski_reduce() assert np.allclose(rcell, R, atol=tol) cell = np.array([[1, 1, 2], [0, 1, 4], [0, 0, 1]]) unimodular = np.array([[1, 2, 2], [0, 1, 2], [0, 0, 1]]) assert np.linalg.det(unimodular) == 1 lcell = unimodular.T @ cell # test 3D rcell, op = minkowski_reduce(lcell) assert np.linalg.det(rcell) == 1 for pbc in [1, True, (1, 1, 1)]: rcell, op = minkowski_reduce(lcell, pbc=pbc) assert np.linalg.det(rcell) == 1 assert np.sign(np.linalg.det(rcell)) == np.sign(np.linalg.det(lcell)) # test 0D rcell, op = minkowski_reduce(lcell, pbc=[0, 0, 0]) assert (rcell == lcell).all() # 0D reduction does nothing # test 1D for i in range(3): rcell, op = minkowski_reduce(lcell, pbc=np.roll([1, 0, 0], i)) assert (rcell == lcell).all() # 1D reduction does nothing zcell = np.zeros((3, 3)) zcell[0] = lcell[0] rcell, _ = Cell(zcell).minkowski_reduce() assert np.allclose(rcell, zcell, atol=tol) # test 2D for i in range(3): pbc = np.roll([0, 1, 1], i) rcell, op = minkowski_reduce(lcell.astype(np.float), pbc=pbc) assert (rcell[i] == lcell[i]).all() zcell = np.copy(lcell.astype(np.float)) zcell[i] = 0 rzcell, _ = Cell(zcell).minkowski_reduce() rcell[i] = 0 assert np.allclose(rzcell, rcell, atol=tol)
def test_3d(self): lcell = self.lcell rcell, op = minkowski_reduce(lcell) assert_almost_equal(np.linalg.det(rcell), 1)
def test_0d(self): lcell = self.lcell rcell, op = minkowski_reduce(lcell, pbc=[0, 0, 0]) assert (rcell == lcell).all() # 0D reduction does nothing
def test_cycle(): # Without cycle-checking in the MR code, this cell causes failure a, b, c = 4.374006080444519, 2.0714140579127145, 3.671070851026261 cell = np.array([[-a, b, c], [a, c, b], [a, b, c]]) minkowski_reduce(cell)
import numpy as np from ase.geometry import minkowski_reduce tol = 1E-14 rng = np.random.RandomState(0) for i in range(40): B = rng.uniform(-1, 1, (3, 3)) R, H = minkowski_reduce(B) assert np.allclose(H @ B, R, atol=tol) norms = np.linalg.norm(R, axis=1) assert (np.argsort(norms) == range(3)).all() # Test idempotency _, _H = minkowski_reduce(R) assert (_H == np.eye(3).astype(np.int)).all()