class Structure(object): """ Structure class :param symbols: atoms symbols to initialize structure :type symbols: list :param numbers: atomic numbers to initialize structure :type numbers: list """ def __init__(self, symbols=None, numbers=None, unitcell=1, ncells=None, positions=None, rotations=False, translations=False, magnetic_moments=False): if positions is not None: numberOfAtoms = len(positions) else: numberOfAtoms = 0 if ncells is not None: ncells = np.asarray(ncells) if ncells is not None and positions is not None and ncells.prod() != numberOfAtoms: raise ValueError("Product of ncells values doesn't equal length of positions") if isinstance(unitcell, UnitCell): self.unitcell = unitcell else: self.unitcell = UnitCell(unitcell) miindex = get_miindex(numberOfAtoms, ncells) self.atoms = DataFrame(index=miindex, columns=['Z', 'symbol', 'x', 'y', 'z']) if numbers is not None or symbols is not None: self.atoms.Z, self.atoms.symbol = get_atomic_number_symbol(Z=numbers, symbol=symbols) positions = np.asarray(positions) self.atoms[['x', 'y', 'z']] = positions if rotations: self.rotations = DataFrame(index=miindex.droplevel(3), columns=['w', 'x', 'y', 'z']) else: self.rotations = None if translations: self.translations = DataFrame(index=miindex.droplevel(3), columns=['x', 'y', 'z']) else: self.translations = None if magnetic_moments: self.magmons = DataFrame(index=miindex, columns=['spinx', 'spiny', 'spinz']) else: self.magmons = None @property def number_of_atoms(self): return len(self.atoms) @property def element(self): return self.atoms.symbol.values @property def xyz(self): return self.atoms[['x', 'y', 'z']].values @property def x(self): return self.atoms.x.values @property def y(self): return self.atoms.y.values @property def z(self): return self.atoms.z.values @property def xyz_cartn(self): return self.unitcell.cartesian(self.atoms[['x', 'y', 'z']].values + np.asarray([self.atoms.index.get_level_values(0).values, self.atoms.index.get_level_values(1).values, self.atoms.index.get_level_values(2).values]).T) def get_atom_symbols(self): return self.atoms.symbol.unique() def get_atom_Zs(self): return self.atoms.Z.unique() def get_atom_count(self): return self.atoms.symbol.value_counts() def get_atomic_numbers(self): return self.atoms.Z.values def get_chemical_symbols(self): return self.atoms.symbol.values def get_scaled_positions(self): return (self.atoms[['x', 'y', 'z']].values + np.asarray([self.atoms.index.get_level_values(0).values, self.atoms.index.get_level_values(1).values, self.atoms.index.get_level_values(2).values]).T) def get_positions(self): return self.xyz_cartn def get_magnetic_moments(self): return self.magmons.values def add_atom(self, i=0, j=0, k=0, site=0, Z=None, symbol=None, position=None): Z, symbol = get_atomic_number_symbol([Z], [symbol]) if position is None: raise ValueError("position not provided") self.atoms.loc[i, j, k, site] = [Z[0], symbol[0], position[0], position[1], position[2]] if self.rotations is not None: self.rotations[i, j, k] = [1, 0, 0, 0] if self.translations is not None: self.translations[i, j, k] = [0, 0, 0] def rattle(self, scale=0.001, seed=None): """Randomly move all atoms by a normal distbution with a standard deviation given by scale. :param scale: standard deviation :type scale: float :param scale: seed for random number generator :type scale: int """ rs = np.random.RandomState(seed) self.atoms[['x', 'y', 'z']] += rs.normal(scale=scale, size=self.xyz.shape) def repeat(self, rep): """Repeat the cells a number of time along each dimension *rep* argument should be either three value like *(1,2,3)* or a single value *r* equivalent to *(r,r,r)*.""" if isinstance(rep, int): rep = np.array((rep, rep, rep, 1)) else: rep = np.append(rep, 1) ncells = np.array(self.atoms.index.max()) + 1 x = np.tile(np.reshape(self.x, ncells), rep).flatten() y = np.tile(np.reshape(self.y, ncells), rep).flatten() z = np.tile(np.reshape(self.z, ncells), rep).flatten() Z = np.tile(np.reshape(self.get_atomic_numbers(), ncells), rep).flatten() miindex = get_miindex(0, ncells * rep) self.atoms = DataFrame(index=miindex, columns=['Z', 'symbol', 'x', 'y', 'z']) self.atoms.Z, self.atoms.symbol = get_atomic_number_symbol(Z=Z) self.atoms.x = x self.atoms.y = y self.atoms.z = z def reindex(self, ncells): self.atoms.set_index(get_miindex(ncells=ncells), inplace=True)
class Structure(object): """ Structure class :param symbols: atoms symbols to initialize structure :type symbols: list :param numbers: atomic numbers to initialize structure :type numbers: list """ def __init__(self, symbols=None, numbers=None, unitcell=1, ncells=None, positions=None, rotations=False, translations=False, magnetic_moments=False): if positions is not None: numberOfAtoms = len(positions) else: numberOfAtoms = 0 if ncells is not None: ncells = np.asarray(ncells) if ncells is not None and positions is not None and ncells.prod( ) != numberOfAtoms: raise ValueError( "Product of ncells values doesn't equal length of positions") if isinstance(unitcell, UnitCell): self.unitcell = unitcell else: self.unitcell = UnitCell(unitcell) miindex = get_miindex(numberOfAtoms, ncells) self.atoms = DataFrame(index=miindex, columns=[ 'Z', 'symbol', 'x', 'y', 'z', 'cartn_x', 'cartn_y', 'cartn_z' ]) if numbers is not None or symbols is not None: self.atoms.Z, self.atoms.symbol = get_atomic_number_symbol( Z=numbers, symbol=symbols) positions = np.asarray(positions) self.atoms[['x', 'y', 'z']] = positions if rotations: self.rotations = DataFrame(index=miindex.droplevel(3), columns=['w', 'x', 'y', 'z']) else: self.rotations = None if translations: self.translations = DataFrame(index=miindex.droplevel(3), columns=['x', 'y', 'z']) else: self.translations = None if magnetic_moments: self.magmons = DataFrame(index=miindex, columns=['spinx', 'spiny', 'spinz']) else: self.magmons = None self._recalculate_cartn() @property def number_of_atoms(self): return len(self.atoms) @property def element(self): return self.atoms.symbol.values @property def xyz(self): return self.atoms[['x', 'y', 'z']].values @property def x(self): return self.atoms.x.values @property def y(self): return self.atoms.y.values @property def z(self): return self.atoms.z.values @property def xyz_cartn(self): return self.atoms[['cartn_x', 'cartn_y', 'cartn_z']].values def get_atom_symbols(self): return self.atoms.symbol.unique() def get_atom_Zs(self): return self.atoms.Z.unique() def get_atom_count(self): return self.atoms.symbol.value_counts() def get_atomic_numbers(self): return self.atoms.Z.values def get_chemical_symbols(self): return self.atoms.symbol.values def get_scaled_positions(self): return (self.atoms[['x', 'y', 'z']].values + np.asarray([ self.atoms.index.get_level_values(0).values, self.atoms.index.get_level_values(1).values, self.atoms.index.get_level_values(2).values ]).T) def get_positions(self): return self.xyz_cartn def get_magnetic_moments(self): return self.magmons.values def add_atom(self, i=0, j=0, k=0, site=0, Z=None, symbol=None, position=None): Z, symbol = get_atomic_number_symbol([Z], [symbol]) if position is None: raise ValueError("position not provided") cartn = self.unitcell.cartesian(position)[0] self.atoms.loc[i, j, k, site] = [ Z[0], symbol[0], position[0], position[1], position[2], cartn[0], cartn[1], cartn[2] ] if self.rotations is not None: self.rotations[i, j, k] = [1, 0, 0, 0] if self.translations is not None: self.translations[i, j, k] = [0, 0, 0] def repeat(self, rep): """Repeat the cells a number of time along each dimension *rep* argument should be either three value like *(1,2,3)* or a single value *r* equivalent to *(r,r,r)*.""" if isinstance(rep, int): rep = np.array((rep, rep, rep, 1)) else: rep = np.append(rep, 1) ncells = np.array(self.atoms.index.max()) + 1 x = np.tile(np.reshape(self.x, ncells), rep).flatten() y = np.tile(np.reshape(self.y, ncells), rep).flatten() z = np.tile(np.reshape(self.z, ncells), rep).flatten() Z = np.tile(np.reshape(self.get_atomic_numbers(), ncells), rep).flatten() miindex = get_miindex(0, ncells * rep) self.atoms = DataFrame(index=miindex, columns=[ 'Z', 'symbol', 'x', 'y', 'z', 'cartn_x', 'cartn_y', 'cartn_z' ]) self.atoms.Z, self.atoms.symbol = get_atomic_number_symbol(Z=Z) self.atoms.x = x self.atoms.y = y self.atoms.z = z self._recalculate_cartn() def reindex(self, ncells): self.atoms.set_index(get_miindex(ncells=ncells), inplace=True) self._recalculate_cartn() def _recalculate_cartn(self): self.atoms[['cartn_x', 'cartn_y', 'cartn_z']] = self.unitcell.cartesian( self.atoms[['x', 'y', 'z']].values + np.asarray([ self.atoms.index.get_level_values(0).values, self.atoms.index.get_level_values(1).values, self.atoms.index.get_level_values(2).values ]).T)
def test_UnitCell_init(): from numpy import pi unitcell = UnitCell() assert unitcell.cell == (1, 1, 1, 90, 90, 90) assert unitcell.reciprocalCell == (1, 1, 1, 90, 90, 90) assert unitcell.d(1, 0, 0) == 1 assert unitcell.dstar(1, 0, 0) == 1 assert unitcell.recAngle(1, 0, 0, 1, 0, 0) == 0 assert unitcell.recAngle(1, 0, 0, 0, 1, 0, degrees=True) == 90 assert_almost_equal(unitcell.recAngle(0, 1, 0, 0, 1, 1), pi/4) assert_almost_equal(unitcell.recAngle(0, 1, 0, 0, 1, 1, degrees=True), 45) assert unitcell.volume == 1 assert unitcell.reciprocalVolume == 1 assert_array_equal(unitcell.G, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert_array_equal(unitcell.B, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert_array_equal(unitcell.Binv, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) unitcell = UnitCell(5) assert unitcell.cell == (5, 5, 5, 90, 90, 90) assert_array_almost_equal(unitcell.reciprocalCell, (0.2, 0.2, 0.2, 90, 90, 90)) assert_almost_equal(unitcell.volume, 125) assert_almost_equal(unitcell.reciprocalVolume, 0.008) assert_array_almost_equal(unitcell.G, [[25, 0, 0], [0, 25, 0], [0, 0, 25]]) assert_array_almost_equal(unitcell.B, [[0.2, 0, 0], [0, 0.2, 0], [0, 0, 0.2]]) assert_array_almost_equal(unitcell.Binv, [[5, 0, 0], [0, 5, 0], [0, 0, 5]]) unitcell = UnitCell(1, 2, 3) assert unitcell.cell == (1, 2, 3, 90, 90, 90) assert_array_almost_equal(unitcell.reciprocalCell, (1, 0.5, 0.333333, 90, 90, 90)) assert unitcell.volume == 6 assert_almost_equal(unitcell.reciprocalVolume, 0.1666667) assert_array_almost_equal(unitcell.G, [[1, 0, 0], [0, 4, 0], [0, 0, 9]]) unitcell = UnitCell(4, 5, 6, 90, 90, 120) assert unitcell.d(1, 0, 0) == 3.4641016151377548 assert unitcell.d(0, 1, 0) == 4.3301270189221936 assert unitcell.d(0, 0, 1) == 6 assert_almost_equal(unitcell.recAngle(1, 0, 0, 1, 0, 0), 0) assert_almost_equal(unitcell.recAngle(1, 0, 0, 1, 0, 0, degrees=True), 0, decimal=5) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 1, 0), pi/3) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 1, 0, degrees=True), 60) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 0, 1), pi/2) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 0, 1, degrees=True), 90) assert_array_almost_equal(unitcell.cell, (4, 5, 6, 90, 90, 120)) assert_array_almost_equal(unitcell.reciprocalCell, (0.288675, 0.2309401, 0.1666667, 90, 90, 60)) assert_almost_equal(unitcell.volume, 103.9230485) assert_almost_equal(unitcell.reciprocalVolume, 0.0096225) assert_array_almost_equal(unitcell.G, [[16, -10, 0], [-10, 25, 0], [0, 0, 36]]) assert_array_almost_equal(unitcell.B, [[0.288675, 0.11547, 0], [0, 0.2, 0], [0, 0, 0.166667]]) assert_array_almost_equal(unitcell.Binv, [[3.464101, -2, 0], [0, 5, 0], [0, 0, 6]]) assert_array_almost_equal(unitcell.cartesian([1, 0, 0]), [[3.464102, -2, 0]]) assert_array_almost_equal(unitcell.cartesian([[1, 0, 0], [0.1, 0.3, 0.5]]), [[3.464102, -2, 0], [0.34641, 1.3, 3]]) assert_array_almost_equal(unitcell.fractional([3.464102, -2, 0]), [[1, 0, 0]]) assert_array_almost_equal(unitcell.fractional([[0, 5, 0], [0, 0, 3]]), [[0, 1, 0], [0, 0, 0.5]]) # test __eq__ assert unitcell != UnitCell() assert unitcell != UnitCell(5) assert unitcell == UnitCell(4, 5, 6, 90, 90, 120) assert unitcell != UnitCell(6, 5, 4, 120, 90, 90) unitcell = UnitCell([5, 6, 7, 89, 92, 121]) assert unitcell.cell == (5, 6, 7, 89, 92, 121) assert_array_almost_equal(unitcell.reciprocalCell, (0.233433, 0.194439, 0.142944, 89.965076, 88.267509, 59.014511)) assert_almost_equal(unitcell.volume, 179.8954455) assert_almost_equal(unitcell.reciprocalVolume, 0.0055587844) assert_array_almost_equal(unitcell.G, [[25, -15.45114225, -1.22148238], [-15.45114225, 36, 0.73300107], [-1.22148238, 0.73300107, 49]]) # Test __str__ assert str(unitcell) == '(5.0, 6.0, 7.0, 89.0, 92.0, 121.0)'
def test_UnitCell_init(): from numpy import pi unitcell = UnitCell() assert unitcell.cell == (1, 1, 1, 90, 90, 90) assert unitcell.reciprocalCell == (1, 1, 1, 90, 90, 90) assert unitcell.d(1, 0, 0) == 1 assert unitcell.dstar(1, 0, 0) == 1 assert unitcell.recAngle(1, 0, 0, 1, 0, 0) == 0 assert unitcell.recAngle(1, 0, 0, 0, 1, 0, degrees=True) == 90 assert_almost_equal(unitcell.recAngle(0, 1, 0, 0, 1, 1), pi / 4) assert_almost_equal(unitcell.recAngle(0, 1, 0, 0, 1, 1, degrees=True), 45) assert unitcell.volume == 1 assert unitcell.reciprocalVolume == 1 assert_array_equal(unitcell.G, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert_array_equal(unitcell.B, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert_array_equal(unitcell.Binv, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) unitcell = UnitCell(5) assert unitcell.cell == (5, 5, 5, 90, 90, 90) assert_array_almost_equal(unitcell.reciprocalCell, (0.2, 0.2, 0.2, 90, 90, 90)) assert_almost_equal(unitcell.volume, 125) assert_almost_equal(unitcell.reciprocalVolume, 0.008) assert_array_almost_equal(unitcell.G, [[25, 0, 0], [0, 25, 0], [0, 0, 25]]) assert_array_almost_equal(unitcell.B, [[0.2, 0, 0], [0, 0.2, 0], [0, 0, 0.2]]) assert_array_almost_equal(unitcell.Binv, [[5, 0, 0], [0, 5, 0], [0, 0, 5]]) unitcell = UnitCell(1, 2, 3) assert unitcell.cell == (1, 2, 3, 90, 90, 90) assert_array_almost_equal(unitcell.reciprocalCell, (1, 0.5, 0.333333, 90, 90, 90)) assert unitcell.volume == 6 assert_almost_equal(unitcell.reciprocalVolume, 0.1666667) assert_array_almost_equal(unitcell.G, [[1, 0, 0], [0, 4, 0], [0, 0, 9]]) unitcell = UnitCell(4, 5, 6, 90, 90, 120) assert unitcell.d(1, 0, 0) == 3.4641016151377548 assert unitcell.d(0, 1, 0) == 4.3301270189221936 assert unitcell.d(0, 0, 1) == 6 assert_almost_equal(unitcell.recAngle(1, 0, 0, 1, 0, 0), 0) assert_almost_equal(unitcell.recAngle(1, 0, 0, 1, 0, 0, degrees=True), 0, decimal=5) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 1, 0), pi / 3) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 1, 0, degrees=True), 60) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 0, 1), pi / 2) assert_almost_equal(unitcell.recAngle(1, 0, 0, 0, 0, 1, degrees=True), 90) assert_array_almost_equal(unitcell.cell, (4, 5, 6, 90, 90, 120)) assert_array_almost_equal(unitcell.reciprocalCell, (0.288675, 0.2309401, 0.1666667, 90, 90, 60)) assert_almost_equal(unitcell.volume, 103.9230485) assert_almost_equal(unitcell.reciprocalVolume, 0.0096225) assert_array_almost_equal(unitcell.G, [[16, -10, 0], [-10, 25, 0], [0, 0, 36]]) assert_array_almost_equal( unitcell.B, [[0.288675, 0.11547, 0], [0, 0.2, 0], [0, 0, 0.166667]]) assert_array_almost_equal(unitcell.Binv, [[3.464101, -2, 0], [0, 5, 0], [0, 0, 6]]) assert_array_almost_equal(unitcell.cartesian([1, 0, 0]), [[3.464102, -2, 0]]) assert_array_almost_equal(unitcell.cartesian([[1, 0, 0], [0.1, 0.3, 0.5]]), [[3.464102, -2, 0], [0.34641, 1.3, 3]]) assert_array_almost_equal(unitcell.fractional([3.464102, -2, 0]), [[1, 0, 0]]) assert_array_almost_equal(unitcell.fractional([[0, 5, 0], [0, 0, 3]]), [[0, 1, 0], [0, 0, 0.5]]) # test __eq__ assert unitcell != UnitCell() assert unitcell != UnitCell(5) assert unitcell == UnitCell(4, 5, 6, 90, 90, 120) assert unitcell != UnitCell(6, 5, 4, 120, 90, 90) unitcell = UnitCell([5, 6, 7, 89, 92, 121]) assert unitcell.cell == (5, 6, 7, 89, 92, 121) assert_array_almost_equal( unitcell.reciprocalCell, (0.233433, 0.194439, 0.142944, 89.965076, 88.267509, 59.014511)) assert_almost_equal(unitcell.volume, 179.8954455) assert_almost_equal(unitcell.reciprocalVolume, 0.0055587844) assert_array_almost_equal( unitcell.G, [[25, -15.45114225, -1.22148238], [-15.45114225, 36, 0.73300107], [-1.22148238, 0.73300107, 49]]) # Test __str__ assert str(unitcell) == '(5.0, 6.0, 7.0, 89.0, 92.0, 121.0)'