def test_sparse(self): """Tests the sparse matrix creation. """ # Dense desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=True, sparse=False) vec = desc.create(H2O) self.assertTrue(type(vec) == np.ndarray) # Sparse desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=True, sparse=True) vec = desc.create(H2O) self.assertTrue(type(vec) == scipy.sparse.coo_matrix)
def test_flatten(self): """Tests the flattening. """ # Unflattened desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=False) matrix = desc.create(H2O) self.assertEqual(matrix.shape, (5, 5)) # Flattened desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=True) matrix = desc.create(H2O) self.assertEqual(matrix.shape, (25,))
def test_electrostatics(self): """Tests that the results are consistent with the electrostatic interpretation. Each matrix [i, j] element should correspond to the Coulomb energy of a system consisting of the pair of atoms i, j. """ system = H2O n_atoms = len(system) a = 0.5 desc = EwaldMatrix(n_atoms_max=3, permutation="none", flatten=False) # The Ewald matrix contains the electrostatic interaction between atoms # i and j. Here we construct the total electrostatic energy for a # system consisting of atoms i and j. matrix = desc.create(system, a=a, rcut=rcut, gcut=gcut) energy_matrix = np.zeros(matrix.shape) for i in range(n_atoms): for j in range(n_atoms): if i == j: energy_matrix[i, j] = matrix[i, j] else: energy_matrix[i, j] = matrix[i, j] + matrix[i, i] + matrix[j, j] # Converts unit of q*q/r into eV conversion = 1e10 * scipy.constants.e / (4 * math.pi * scipy.constants.epsilon_0) energy_matrix *= conversion # The value in each matrix element should correspond to the Coulomb # energy of a system with with only those atoms. Here the energies from # the Ewald matrix are compared against the Ewald energy calculated # with pymatgen. positions = system.get_positions() atomic_num = system.get_atomic_numbers() for i in range(n_atoms): for j in range(n_atoms): if i == j: pos = [positions[i]] sym = [atomic_num[i]] else: pos = [positions[i], positions[j]] sym = [atomic_num[i], atomic_num[j]] i_sys = Atoms( cell=system.get_cell(), positions=pos, symbols=sym, pbc=True, ) structure = Structure( lattice=i_sys.get_cell(), species=i_sys.get_atomic_numbers(), coords=i_sys.get_scaled_positions(), ) structure.add_oxidation_state_by_site(i_sys.get_atomic_numbers()) ewald = EwaldSummation(structure, eta=a, real_space_cut=rcut, recip_space_cut=gcut) energy = ewald.total_energy # Check that the energy given by the pymatgen implementation is # the same as given by the descriptor self.assertTrue(np.allclose(energy_matrix[i, j], energy, atol=0.00001, rtol=0))
def test_unit_cells(self): """Tests if arbitrary unit cells are accepted """ desc = EwaldMatrix(n_atoms_max=3, permutation="none", flatten=False) molecule = H2O.copy() # A system without cell should produce an error molecule.set_cell([ [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0] ]) with self.assertRaises(ValueError): nocell = desc.create(molecule, a=0.5, rcut=rcut, gcut=gcut) # Large cell molecule.set_pbc(True) molecule.set_cell([ [20.0, 0.0, 0.0], [0.0, 30.0, 0.0], [0.0, 0.0, 40.0] ]) largecell = desc.create(molecule, a=0.5, rcut=rcut, gcut=gcut) # Cubic cell molecule.set_cell([ [2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0] ]) cubic_cell = desc.create(molecule, a=0.5, rcut=rcut, gcut=gcut) # Triclinic cell molecule.set_cell([ [0.0, 2.0, 2.0], [2.0, 0.0, 2.0], [2.0, 2.0, 0.0] ]) triclinic_smallcell = desc.create(molecule, a=0.5, rcut=rcut, gcut=gcut)
def test_create(self): """Tests different valid and invalid create values. """ with self.assertRaises(ValueError): desc = EwaldMatrix(n_atoms_max=5) desc.create(H2O, rcut=10) with self.assertRaises(ValueError): desc = EwaldMatrix(n_atoms_max=5) desc.create(H2O, gcut=10) # Providing a only is valid desc = EwaldMatrix(n_atoms_max=5) desc.create(H2O, a=0.5) # Providing no parameters is valid desc = EwaldMatrix(n_atoms_max=5) desc.create(H2O)
def test_a_independence(self): """Tests that the matrix elements are independent of the screening parameter 'a' used in the Ewald summation. Notice that the real space cutoff and reciprocal space cutoff have to be sufficiently large for this to be true, as 'a' controls the width of the Gaussian charge distribution. """ rcut = 40 gcut = 30 prev_array = None for i, a in enumerate([0.1, 0.5, 1, 2, 3]): desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=False) matrix = desc.create(H2O, a=a, rcut=rcut, gcut=gcut) if i > 0: self.assertTrue(np.allclose(prev_array, matrix, atol=0.001, rtol=0)) prev_array = matrix
def create(system): desc = EwaldMatrix(n_atoms_max=3, permutation="sorted_l2", flatten=True) return desc.create(system)