def test_constructor(self): """Tests different valid and invalid constructor values. """ with self.assertRaises(ValueError): EwaldMatrix(n_atoms_max=5, permutation="unknown") with self.assertRaises(ValueError): EwaldMatrix(n_atoms_max=-1)
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_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 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_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)
from functional.streams import ParallelStream as pseq from collections import namedtuple import ase.build.bulk from dscribe.descriptors import CoulombMatrix from dscribe.descriptors import SineMatrix from dscribe.descriptors import EwaldMatrix # Setup the descriptors n_atoms_max = 4 n_proc = 4 coulombmatrix = CoulombMatrix(n_atoms_max=n_atoms_max) sinematrix = SineMatrix(n_atoms_max=n_atoms_max) ewaldmatrix = EwaldMatrix(n_atoms_max=n_atoms_max) # Define a dataset data = { "NaCl": ase.build.bulk("NaCl", "rocksalt", 5.64), "Diamond": ase.build.bulk("C", "diamond", 3.567), "Al": ase.build.bulk("Al", "fcc", 4.046), "GaAs": ase.build.bulk("GaAs", "zincblende", 5.653), } # Setup an iterable that runs through the samples. Result = namedtuple("Result", "cm sm em") Sample = namedtuple("Sample", "key value") samples = [Sample(key, value) for key, value in data.items()]
def test_number_of_features(self): """Tests that the reported number of features is correct. """ desc = EwaldMatrix(n_atoms_max=5, permutation="none", flatten=False) n_features = desc.get_number_of_features() self.assertEqual(n_features, 25)
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 create(system): desc = EwaldMatrix(n_atoms_max=3, permutation="sorted_l2", flatten=True) return desc.create(system)