def __init__(self, **kwargs): self.forceconstants = kwargs.pop('forceconstants') self.is_classic = bool(kwargs.pop('is_classic', False)) if 'temperature' in kwargs: self.temperature = float(kwargs['temperature']) self.folder = kwargs.pop('folder', FOLDER_NAME) self.kpts = kwargs.pop('kpts', (1, 1, 1)) self._grid_type = kwargs.pop('grid_type', 'C') self._reciprocal_grid = Grid(self.kpts, order=self._grid_type) self.is_unfolding = kwargs.pop('is_unfolding', False) if self.is_unfolding: logging.info('Using unfolding.') self.kpts = np.array(self.kpts) self.min_frequency = kwargs.pop('min_frequency', 0) self.max_frequency = kwargs.pop('max_frequency', None) self.broadening_shape = kwargs.pop('broadening_shape', 'gauss') self.is_nw = kwargs.pop('is_nw', False) self.third_bandwidth = kwargs.pop('third_bandwidth', None) self.storage = kwargs.pop('storage', 'formatted') self.is_symmetrizing_frequency = kwargs.pop( 'is_symmetrizing_frequency', False) self.is_antisymmetrizing_velocity = kwargs.pop( 'is_antisymmetrizing_velocity', False) self.is_balanced = kwargs.pop('is_balanced', False) self.atoms = self.forceconstants.atoms self.supercell = np.array(self.forceconstants.supercell) self.n_k_points = int(np.prod(self.kpts)) self.n_atoms = self.forceconstants.n_atoms self.n_modes = self.forceconstants.n_modes self.n_phonons = self.n_k_points * self.n_modes self.is_able_to_calculate = True self.hbar = units._hbar if self.is_classic: self.hbar = self.hbar * 1e-6
def from_supercell(cls, atoms, supercell, grid_type, value=None, folder='kALDo'): _direct_grid = Grid(supercell, grid_type) replicated_positions = _direct_grid.grid(is_wrapping=False).dot(atoms.cell)[:, np.newaxis, :] + atoms.positions[ np.newaxis, :, :] inst = cls(atoms=atoms, replicated_positions=replicated_positions, supercell=supercell, value=value, folder=folder) inst._direct_grid = _direct_grid return inst
def read_third_order_matrix(third_file, atoms, supercell, order='C'): n_unit_atoms = atoms.positions.shape[0] n_replicas = np.prod(supercell) third_order = np.zeros((n_unit_atoms, 3, n_replicas, n_unit_atoms, 3, n_replicas, n_unit_atoms, 3)) second_cell_list = [] third_cell_list = [] current_grid = Grid(supercell, order=order).grid(is_wrapping=True) list_of_index = current_grid list_of_replicas = list_of_index.dot(atoms.cell) with open(third_file, 'r') as file: line = file.readline() n_third = int(line) for i in range(n_third): file.readline() file.readline() second_cell_position = np.fromstring(file.readline(), dtype=np.float, sep=' ') second_cell_index = second_cell_position.dot( np.linalg.inv(atoms.cell)).round(0).astype(int) second_cell_list.append(second_cell_index) # create mask to find the index second_cell_id = (list_of_index[:] == second_cell_index).prod( axis=1) second_cell_id = np.argwhere(second_cell_id).flatten() third_cell_position = np.fromstring(file.readline(), dtype=np.float, sep=' ') third_cell_index = third_cell_position.dot( np.linalg.inv(atoms.cell)).round(0).astype(int) third_cell_list.append(third_cell_index) # create mask to find the index third_cell_id = (list_of_index[:] == third_cell_index).prod(axis=1) third_cell_id = np.argwhere(third_cell_id).flatten() atom_i, atom_j, atom_k = np.fromstring( file.readline(), dtype=np.int, sep=' ') - 1 for _ in range(27): values = np.fromstring(file.readline(), dtype=np.float, sep=' ') alpha, beta, gamma = values[:3].round(0).astype(int) - 1 third_order[atom_i, alpha, second_cell_id, atom_j, beta, third_cell_id, atom_k, gamma] = values[3] third_order = third_order.reshape( (n_unit_atoms * 3, n_replicas * n_unit_atoms * 3, n_replicas * n_unit_atoms * 3)) return third_order
def __init__(self, *kargs, **kwargs): Observable.__init__(self, *kargs, **kwargs) self.atoms = kwargs['atoms'] replicated_positions = kwargs['replicated_positions'] self.supercell = kwargs['supercell'] try: self.value = kwargs['value'] except KeyError: self.value = None self._replicated_atoms = None self.replicated_positions = replicated_positions.reshape( (-1, self.atoms.positions.shape[0], self.atoms.positions.shape[1])) self.n_replicas = np.prod(self.supercell) self._cell_inv = None self._replicated_cell_inv = None self._list_of_replicas = None n_replicas, n_unit_atoms, _ = self.replicated_positions.shape atoms_positions = self.atoms.positions detected_grid = np.round( (replicated_positions.reshape((n_replicas, n_unit_atoms, 3)) - atoms_positions[np.newaxis, :, :]).dot( np.linalg.inv(self.atoms.cell))[:, 0, :], 0).astype(np.int) grid_c = Grid(grid_shape=self.supercell, order='C') grid_fortran = Grid(grid_shape=self.supercell, order='F') if (grid_c.grid(is_wrapping=False) == detected_grid).all(): grid_type = 'C' logging.debug("Using C-style position grid") elif (grid_fortran.grid(is_wrapping=False) == detected_grid).all(): grid_type = 'F' logging.debug("Using fortran-style position grid") else: err_msg = "Unable to detect grid type" logging.error(err_msg) raise TypeError(err_msg) self._direct_grid = Grid(self.supercell, grid_type)
class Phonons: """The Phonons object exposes all the phononic properties of a system. It's can be fed into a Conductivity object and must be built with a ForceConstant object. Parameters ---------- forceconstants : ForceConstants contains all the information about the system and the derivatives of the potential. is_classic : bool specifies if the system is classic, `True` or quantum, `False` Default is `False` kpts : (3) tuple, optional defines the number of k points to use to create the k mesh Default is (1, 1, 1) temperature : float defines the temperature of the simulation. Units: K. min_frequency : float, optional ignores all phonons with frequency below `min_frequency` THz, Default is `None` max_frequency : float, optional ignores all phonons with frequency above `max_frequency` THz Default is `None` third_bandwidth : float, optional Defines the width of the energy conservation smearing in the phonons scattering calculation. If `None` the width is calculated dynamically. Otherwise the input value corresponds to the width. Units: THz. broadening_shape : string, optional Defines the algorithm to use for the broadening of the conservation of the energy for third irder interactions. Available broadenings are `gauss`, `lorentz` and `triangle`. Default is `gauss`. folder : string, optional Specifies where to store the data files. Default is `output`. storage : `default`, `formatted`, `numpy`, `memory`, `hdf5`, optional Defines the storing strategy used to store the observables. The `default` strategy stores formatted output and numpy arrays. `memory` storage doesn't generate any output. grid_type : 'F' or 'C, optional Specify if to use 'C" style atoms replica grid of fortran style 'F', Default 'C' is_balanced : Enforce detailed balance when calculating anharmonic properties, Default: False Returns ------- Phonons Object """ def __init__(self, **kwargs): self.forceconstants = kwargs.pop('forceconstants') self.is_classic = bool(kwargs.pop('is_classic', False)) if 'temperature' in kwargs: self.temperature = float(kwargs['temperature']) self.folder = kwargs.pop('folder', FOLDER_NAME) self.kpts = kwargs.pop('kpts', (1, 1, 1)) self._grid_type = kwargs.pop('grid_type', 'C') self._reciprocal_grid = Grid(self.kpts, order=self._grid_type) self.is_unfolding = kwargs.pop('is_unfolding', False) if self.is_unfolding: logging.info('Using unfolding.') self.kpts = np.array(self.kpts) self.min_frequency = kwargs.pop('min_frequency', 0) self.max_frequency = kwargs.pop('max_frequency', None) self.broadening_shape = kwargs.pop('broadening_shape', 'gauss') self.is_nw = kwargs.pop('is_nw', False) self.third_bandwidth = kwargs.pop('third_bandwidth', None) self.storage = kwargs.pop('storage', 'formatted') self.is_symmetrizing_frequency = kwargs.pop( 'is_symmetrizing_frequency', False) self.is_antisymmetrizing_velocity = kwargs.pop( 'is_antisymmetrizing_velocity', False) self.is_balanced = kwargs.pop('is_balanced', False) self.atoms = self.forceconstants.atoms self.supercell = np.array(self.forceconstants.supercell) self.n_k_points = int(np.prod(self.kpts)) self.n_atoms = self.forceconstants.n_atoms self.n_modes = self.forceconstants.n_modes self.n_phonons = self.n_k_points * self.n_modes self.is_able_to_calculate = True self.hbar = units._hbar if self.is_classic: self.hbar = self.hbar * 1e-6 @lazy_property(label='') def physical_mode(self): """Calculate physical modes. Non physical modes are the first 3 modes of q=(0, 0, 0) and, if defined, all the modes outside the frequency range min_frequency and max_frequency. Returns ------- physical_mode : np array (n_k_points, n_modes) bool """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) physical_mode = np.zeros((self.n_k_points, self.n_modes), dtype=np.bool) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQ( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, is_nw=self.is_nw, is_unfolding=self.is_unfolding) physical_mode[ik] = phonon.physical_mode if self.min_frequency is not None: physical_mode[self.frequency < self.min_frequency] = False if self.max_frequency is not None: physical_mode[self.frequency > self.max_frequency] = False return physical_mode @lazy_property(label='') def frequency(self): """Calculate phonons frequency Returns ------- frequency : np array (n_k_points, n_modes) frequency in THz """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) frequency = np.zeros((self.n_k_points, self.n_modes)) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQ( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, is_nw=self.is_nw, is_unfolding=self.is_unfolding) frequency[ik] = phonon.frequency return frequency @lazy_property(label='') def participation_ratio(self): """Calculate phonons participation ratio. A value of 1 corresponds to translation. Returns ------- participation_ratio : np array (n_k_points, n_modes) atomic participation """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) participation_ratio = np.zeros((self.n_k_points, self.n_modes)) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQ( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, is_nw=self.is_nw, is_unfolding=self.is_unfolding) participation_ratio[ik] = phonon.participation_ratio return participation_ratio @lazy_property(label='') def velocity(self): """Calculates the velocity using Hellmann-Feynman theorem. Returns ------- velocity : np array (n_k_points, n_unit_cell * 3, 3) velocity in 100m/s or A/ps """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) velocity = np.zeros((self.n_k_points, self.n_modes, 3)) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQ( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, is_nw=self.is_nw, is_unfolding=self.is_unfolding) velocity[ik] = phonon.velocity return velocity @lazy_property(label='') def _eigensystem(self): """Calculate the eigensystems, for each k point in k_points. Returns ------- _eigensystem : np.array(n_k_points, n_unit_cell * 3, n_unit_cell * 3 + 1) eigensystem is calculated for each k point, the three dimensional array records the eigenvalues in the last column of the last dimension. If the system is not amorphous, these values are stored as complex numbers. """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) shape = (self.n_k_points, self.n_modes + 1, self.n_modes) log_size(shape, name='eigensystem', type=np.complex) eigensystem = np.zeros(shape, dtype=np.complex) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQ( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, is_nw=self.is_nw, is_unfolding=self.is_unfolding) eigensystem[ik] = phonon._eigensystem return eigensystem @lazy_property(label='<temperature>/<statistics>') def heat_capacity(self): """Calculate the heat capacity for each k point in k_points and each mode. If classical, it returns the Boltzmann constant in W/m/K. If quantum it returns the derivative of the Bose-Einstein weighted by each phonons energy. .. math:: c_\\mu = k_B \\frac{\\nu_\\mu^2}{ \\tilde T^2} n_\\mu (n_\\mu + 1) where the frequency :math:`\\nu` and the temperature :math:`\\tilde T` are in THz. Returns ------- c_v : np.array(n_k_points, n_modes) heat capacity in W/m/K for each k point and each mode """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) c_v = np.zeros((self.n_k_points, self.n_modes)) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQTemp( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, temperature=self.temperature, is_classic=self.is_classic, is_nw=self.is_nw, is_unfolding=self.is_unfolding) c_v[ik] = phonon.heat_capacity return c_v @lazy_property(label='<temperature>/<statistics>') def heat_capacity_2d(self): """Calculate the generalized 2d heat capacity for each k point in k_points and each mode. If classical, it returns the Boltzmann constant in W/m/K. Returns ------- heat_capacity_2d : np.array(n_k_points, n_modes, n_modes) heat capacity in W/m/K for each k point and each modes couple. """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) shape = (self.n_k_points, self.n_modes, self.n_modes) log_size(shape, name='heat_capacity_2d', type=np.float) heat_capacity_2d = np.zeros(shape) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQTemp( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, temperature=self.temperature, is_classic=self.is_classic, is_nw=self.is_nw, is_unfolding=self.is_unfolding) heat_capacity_2d[ik] = phonon.heat_capacity_2d return heat_capacity_2d @lazy_property(label='<temperature>/<statistics>') def population(self): """Calculate the phonons population for each k point in k_points and each mode. If classical, it returns the temperature divided by each frequency, using equipartition theorem. If quantum it returns the Bose-Einstein distribution Returns ------- population : np.array(n_k_points, n_modes) population for each k point and each mode """ q_points = self._reciprocal_grid.unitary_grid(is_wrapping=False) population = np.zeros((self.n_k_points, self.n_modes)) for ik in range(len(q_points)): q_point = q_points[ik] phonon = HarmonicWithQTemp( q_point=q_point, second=self.forceconstants.second, distance_threshold=self.forceconstants.distance_threshold, folder=self.folder, storage=self.storage, temperature=self.temperature, is_classic=self.is_classic, is_nw=self.is_nw, is_unfolding=self.is_unfolding) population[ik] = phonon.population return population @lazy_property(label='<temperature>/<statistics>/<third_bandwidth>') def bandwidth(self): """Calculate the phonons bandwidth, the inverse of the lifetime, for each k point in k_points and each mode. Returns ------- bandwidth : np.array(n_k_points, n_modes) bandwidth for each k point and each mode """ gamma = self._ps_and_gamma[:, 1].reshape(self.n_k_points, self.n_modes) return gamma @lazy_property(label='<temperature>/<statistics>/<third_bandwidth>') def phase_space(self): """Calculate the 3-phonons-processes phase_space, for each k point in k_points and each mode. Returns ------- phase_space : np.array(n_k_points, n_modes) phase_space for each k point and each mode """ ps = self._ps_and_gamma[:, 0].reshape(self.n_k_points, self.n_modes) return ps @lazy_property(label='') def eigenvalues(self): """Calculates the eigenvalues of the dynamical matrix in Thz^2. Returns ------- eigenvalues : np array (n_phonons) Eigenvalues of the dynamical matrix """ eigenvalues = self._eigensystem[:, 0, :] return eigenvalues @property def eigenvectors(self): """Calculates the eigenvectors of the dynamical matrix. Returns ------- eigenvectors : np array (n_phonons, n_phonons) Eigenvectors of the dynamical matrix """ eigenvectors = self._eigensystem[:, 1:, :] return eigenvectors @lazy_property(label='<temperature>/<statistics>/<third_bandwidth>') def _ps_and_gamma(self): store_format = DEFAULT_STORE_FORMATS['_ps_gamma_and_gamma_tensor'] \ if self.storage == 'formatted' else self.storage if is_calculated('_ps_gamma_and_gamma_tensor', self, '<temperature>/<statistics>/<third_bandwidth>', \ format=store_format): ps_and_gamma = self._ps_gamma_and_gamma_tensor[:, :2] else: ps_and_gamma = self._select_algorithm_for_phase_space_and_gamma( is_gamma_tensor_enabled=False) return ps_and_gamma @lazy_property(label='<temperature>/<statistics>/<third_bandwidth>') def _ps_gamma_and_gamma_tensor(self): ps_gamma_and_gamma_tensor = self._select_algorithm_for_phase_space_and_gamma( is_gamma_tensor_enabled=True) return ps_gamma_and_gamma_tensor # Helpers properties @property def omega(self): """Calculates the angular frequencies from the diagonalized dynamical matrix. Returns ------- frequency : np array (n_k_points, n_modes) frequency in rad """ return self.frequency * 2 * np.pi @property def _rescaled_eigenvectors(self): n_atoms = self.n_atoms n_modes = self.n_modes masses = self.atoms.get_masses() rescaled_eigenvectors = self.eigenvectors[:, :, :].reshape( (self.n_k_points, n_atoms, 3, n_modes)) / np.sqrt( masses[np.newaxis, :, np.newaxis, np.newaxis]) rescaled_eigenvectors = rescaled_eigenvectors.reshape( (self.n_k_points, n_modes, n_modes)) return rescaled_eigenvectors @property def _is_amorphous(self): is_amorphous = (self.kpts == (1, 1, 1)).all() return is_amorphous def _allowed_third_phonons_index(self, index_q, is_plus): q_vec = self._reciprocal_grid.id_to_unitary_grid_index(index_q) qp_vec = self._reciprocal_grid.unitary_grid(is_wrapping=False) qpp_vec = q_vec[np.newaxis, :] + (int(is_plus) * 2 - 1) * qp_vec[:, :] rescaled_qpp = np.round((qpp_vec * self._reciprocal_grid.grid_shape), 0).astype(np.int) rescaled_qpp = np.mod(rescaled_qpp, self._reciprocal_grid.grid_shape) index_qpp_full = np.ravel_multi_index(rescaled_qpp.T, self._reciprocal_grid.grid_shape, mode='raise', order=self._grid_type) return index_qpp_full def _select_algorithm_for_phase_space_and_gamma( self, is_gamma_tensor_enabled=True): self.n_k_points = np.prod(self.kpts) self.n_phonons = self.n_k_points * self.n_modes self.is_gamma_tensor_enabled = is_gamma_tensor_enabled if self._is_amorphous: ps_and_gamma = aha.project_amorphous(self) else: ps_and_gamma = aha.project_crystal(self) return ps_and_gamma
def read_third_order_matrix_2(third_file, atoms, third_supercell, order='C'): supercell = third_supercell n_unit_atoms = atoms.positions.shape[0] n_replicas = np.prod(supercell) current_grid = Grid(third_supercell, order=order).grid(is_wrapping=True) list_of_index = current_grid list_of_replicas = list_of_index.dot(atoms.cell) replicated_cell = atoms.cell * supercell # replicated_cell_inv = np.linalg.inv(replicated_cell) coords = [] data = [] second_cell_positions = [] third_cell_positions = [] atoms_coords = [] sparse_data = [] with open(third_file, 'r') as file: line = file.readline() n_third = int(line) for i in range(n_third): file.readline() file.readline() second_cell_position = np.fromstring(file.readline(), dtype=np.float, sep=' ') second_cell_positions.append(second_cell_position) d_1 = list_of_replicas[:, :] - second_cell_position[np.newaxis, :] # d_1 = wrap_coordinates(d_1, replicated_cell, replicated_cell_inv) mask_second = np.linalg.norm(d_1, axis=1) < 1e-5 second_cell_id = np.argwhere(mask_second).flatten() third_cell_position = np.fromstring(file.readline(), dtype=np.float, sep=' ') third_cell_positions.append(third_cell_position) d_2 = list_of_replicas[:, :] - third_cell_position[np.newaxis, :] # d_2 = wrap_coordinates(d_2, replicated_cell, replicated_cell_inv) mask_third = np.linalg.norm(d_2, axis=1) < 1e-5 third_cell_id = np.argwhere(mask_third).flatten() atom_i, atom_j, atom_k = np.fromstring( file.readline(), dtype=np.int, sep=' ') - 1 atoms_coords.append([atom_i, atom_j, atom_k]) small_data = [] for _ in range(27): values = np.fromstring(file.readline(), dtype=np.float, sep=' ') alpha, beta, gamma = values[:3].round(0).astype(int) - 1 coords.append([ atom_i, alpha, second_cell_id, atom_j, beta, third_cell_id, atom_k, gamma ]) data.append(values[3]) small_data.append(values[3]) sparse_data.append(small_data) third_order = COO(np.array(coords).T, np.array(data), shape=(n_unit_atoms, 3, n_replicas, n_unit_atoms, 3, n_replicas, n_unit_atoms, 3)) third_order = third_order.reshape( (n_unit_atoms * 3, n_replicas * n_unit_atoms * 3, n_replicas * n_unit_atoms * 3)) return third_order, np.array(sparse_data), np.array( second_cell_positions), np.array(third_cell_positions), np.array( atoms_coords)
class ForceConstant(Observable): def __init__(self, *kargs, **kwargs): Observable.__init__(self, *kargs, **kwargs) self.atoms = kwargs['atoms'] replicated_positions = kwargs['replicated_positions'] self.supercell = kwargs['supercell'] try: self.value = kwargs['value'] except KeyError: self.value = None self._replicated_atoms = None self.replicated_positions = replicated_positions.reshape( (-1, self.atoms.positions.shape[0], self.atoms.positions.shape[1])) self.n_replicas = np.prod(self.supercell) self._cell_inv = None self._replicated_cell_inv = None self._list_of_replicas = None n_replicas, n_unit_atoms, _ = self.replicated_positions.shape atoms_positions = self.atoms.positions detected_grid = np.round( (replicated_positions.reshape((n_replicas, n_unit_atoms, 3)) - atoms_positions[np.newaxis, :, :]).dot( np.linalg.inv(self.atoms.cell))[:, 0, :], 0).astype(np.int) grid_c = Grid(grid_shape=self.supercell, order='C') grid_fortran = Grid(grid_shape=self.supercell, order='F') if (grid_c.grid(is_wrapping=False) == detected_grid).all(): grid_type = 'C' logging.debug("Using C-style position grid") elif (grid_fortran.grid(is_wrapping=False) == detected_grid).all(): grid_type = 'F' logging.debug("Using fortran-style position grid") else: err_msg = "Unable to detect grid type" logging.error(err_msg) raise TypeError(err_msg) self._direct_grid = Grid(self.supercell, grid_type) @classmethod def from_supercell(cls, atoms, supercell, grid_type, value=None, folder='kALDo'): _direct_grid = Grid(supercell, grid_type) replicated_positions = _direct_grid.grid(is_wrapping=False).dot(atoms.cell)[:, np.newaxis, :] + atoms.positions[ np.newaxis, :, :] inst = cls(atoms=atoms, replicated_positions=replicated_positions, supercell=supercell, value=value, folder=folder) inst._direct_grid = _direct_grid return inst @property def positions(self): return self.atoms.positions @property def cell_inv(self): if self._cell_inv is None: self._cell_inv = np.linalg.inv(self.atoms.cell) return self._cell_inv @property def replicated_atoms(self): # TODO: remove this method if self._replicated_atoms is None: supercell = self.supercell atoms = self.atoms replicated_atoms = atoms.copy() * supercell replicated_positions = self._direct_grid.grid(is_wrapping=False).dot(atoms.cell)[:, np.newaxis, :] + atoms.positions[ np.newaxis, :, :] replicated_atoms.set_positions(replicated_positions.reshape(-1, 3)) self._replicated_atoms = replicated_atoms return self._replicated_atoms @property def replicated_cell_inv(self): if self._replicated_cell_inv is None: self._replicated_cell_inv = np.linalg.inv(self.replicated_atoms.cell) return self._replicated_cell_inv @property def list_of_replicas(self): if self._list_of_replicas is None: list_of_index = self._direct_grid.grid(is_wrapping=True) self._list_of_replicas = list_of_index.dot(self.atoms.cell) return self._list_of_replicas def _chi_k(self, k_points): n_k_points = np.shape(k_points)[0] ch = np.zeros((n_k_points, self.n_replicas), dtype=np.complex) for index_q in range(n_k_points): k_point = k_points[index_q] list_of_replicas = self.list_of_replicas cell_inv = self.cell_inv ch[index_q] = chi(k_point, list_of_replicas, cell_inv) return ch