def get_tetrahedra_cross_section_weights(reciprocal_lattice, kpoints, tetrahedra, e21, e31, e41): # weight (b) defined by equation 3.4 in https://doi.org/10.1002/pssb.2220540211 # this weight is not the Bloechl integrand but a scaling needed to obtain the # DOS directly from the tetrahedron cross section cross_section_weights = {} # volume is 6 * the volume of one tetrahedron volume = np.linalg.det(reciprocal_lattice) / len(kpoints) for spin, s_tetrahedra in tetrahedra.items(): tetrahedra_kpoints = kpoints[s_tetrahedra] k1 = pbc_diff(tetrahedra_kpoints[:, :, 1], tetrahedra_kpoints[:, :, 0]) k2 = pbc_diff(tetrahedra_kpoints[:, :, 2], tetrahedra_kpoints[:, :, 0]) k3 = pbc_diff(tetrahedra_kpoints[:, :, 3], tetrahedra_kpoints[:, :, 0]) k1_cart = np.dot(k1, reciprocal_lattice) k2_cart = np.dot(k2, reciprocal_lattice) k3_cart = np.dot(k3, reciprocal_lattice) contragradient = np.stack( [ np.cross(k2_cart, k3_cart) / volume, np.cross(k3_cart, k1_cart) / volume, np.cross(k1_cart, k2_cart) / volume, ], axis=2, ) energies = np.stack([e21[spin], e31[spin], e41[spin]], axis=2) b_vector = np.sum(contragradient * energies[..., None], axis=2) cross_section_weights[spin] = 1 / np.linalg.norm(b_vector, axis=2) return cross_section_weights
def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b): bands, kpoints, single_overlap = group_bands_and_kpoints( band_a, kpoint_a, band_b, kpoint_b) p = self.get_coefficients(spin, bands, kpoints) centers = self.band_centers[spin][band_a] center = centers[np.argmin( np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))] shift_a = pbc_diff(kpoint_a, center) shift_b = pbc_diff(kpoint_b, center) angles = cosine(shift_a, shift_b) angle_weights = np.abs(pbc_diff(kpoint_a, kpoint_b)) angle_weights /= np.max(angle_weights, axis=1)[:, None] angle_weights[np.isnan(angle_weights)] = 0 # weight the angle masks by the contribution of the transition in that direction # use the mask to get the angle scaling factor which gives how much the angle # is applied to each projection. I.e., if the scaling factor is 1 for a # particular projection, then the projection is scaled by 1 * the angle. If the # scaling factor is 0, the projection is scaled by 0 * the angle. # this allows us to make s orbitals immune to the cosine weight scaling_factor = self.rotation_mask[None] * angle_weights[..., None] prod = p[0] * p[1:] overlap = prod * ( 1 - scaling_factor) + prod * scaling_factor * angles[:, None] overlap = overlap.sum(axis=1)**2 if single_overlap: return overlap[0] else: return overlap
def test_pbc_diff(self): self.assertArrayAlmostEqual( coord.pbc_diff([0.1, 0.1, 0.1], [0.3, 0.5, 0.9]), [-0.2, -0.4, 0.2]) self.assertArrayAlmostEqual( coord.pbc_diff([0.9, 0.1, 1.01], [0.3, 0.5, 0.9]), [-0.4, -0.4, 0.11]) self.assertArrayAlmostEqual( coord.pbc_diff([0.1, 0.6, 1.01], [0.6, 0.1, 0.9]), [-0.5, 0.5, 0.11]) self.assertArrayAlmostEqual( coord.pbc_diff([100.1, 0.2, 0.3], [0123123.4, 0.5, 502312.6]), [-0.3, -0.3, -0.3])
def is_image(self, poly, tol): frac_diff = pbc_diff(poly.frac_coords, self.frac_coords) if not np.allclose(frac_diff, [0, 0, 0], atol=tol): return False to_frac = self.lattice.get_fractional_coords for c1 in self.polyhedron_coords: found = False for c2 in poly.polyhedron_coords: d = pbc_diff(to_frac(c1), to_frac(c2)) if not np.allclose(d, [0, 0, 0], atol=tol): found = True break if not found: return False return True
def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol=1e-2): """ Returns a list of unique symmetrically equivalent k-points. Args: kpoint (1x3 array): coordinate of the k-point cartesian (bool): kpoint is in cartesian or fractional coordinates tol (float): tolerance below which coordinates are considered equal Returns: ([1x3 array] or None): if structure is not available returns None """ if not self.structure: return None sg = SpacegroupAnalyzer(self.structure) symmops = sg.get_point_group_operations(cartesian=cartesian) points = np.dot(kpoint, [m.rotation_matrix for m in symmops]) rm_list = [] # identify and remove duplicates from the list of equivalent k-points: for i in range(len(points) - 1): for j in range(i + 1, len(points)): if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol): rm_list.append(i) break return np.delete(points, rm_list, axis=0)
def rmsd_pbc(file_path_1, file_path_2, original_def=True): """ Calculate absolute root-mean-square diffence between two structures. No rotation nor recentering will be considered. Periodic boundary condition will be considered. """ try: a = Structure.from_file(filename=file_path_1) b = Structure.from_file(filename=file_path_2) except Exception: sys.exit("File import failed.") # check if two structures are valid for compare natoms = check_validity(a, b) # get fractional coords of each structure # a_frac = [a[i].frac_coords for i in range(natoms)] # b_frac = [b[i].frac_coords for i in range(natoms)] a_frac = a.frac_coords b_frac = b.frac_coords # get frac_diff considering pbc (abs(diff) <= 0.5) frac_diff = pbc_diff(a_frac, b_frac) # convert to cartesian coords difference cart_diff = a.lattice.get_cartesian_coords(frac_diff) if original_def: # original definition of RMSD return np.sqrt(np.sum(cart_diff**2)) else: # revised definition. The top 5 deviation is considered # aiming to better compare the difference of two similar structures return np.sum(np.sort(np.abs(cart_diff))[:5]) / 5
def should_stop(self, old_centroids, centroids, iterations): if iterations > self.max_iterations: warnings.warn("Max iterations %d reached!" % self.max_iterations) return True if old_centroids is None: return False for c1, c2 in zip(old_centroids, centroids): if not np.allclose(pbc_diff(c1, c2), [0, 0, 0]): return False return True
def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b): bands, kpoints, single_overlap = group_bands_and_kpoints( band_a, kpoint_a, band_b, kpoint_b) p = self.get_coefficients(spin, bands, kpoints) centers = self.band_centers[spin][band_a] center = centers[np.argmin( np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))] shift_a = pbc_diff(kpoint_a, center) shift_b = pbc_diff(kpoint_b, center) angles = cosine(shift_a, shift_b) prod = p[0] * p[1:] overlap = (prod * (1 - self.rotation_mask) + prod * self.rotation_mask * angles[:, None]) overlap = overlap.sum(axis=1)**2 if single_overlap: return overlap[0] else: return overlap
def get_structures(vaspruns): for i, vr in enumerate(vaspruns): if i == 0: step_skip = vr.ionic_step_skip or 1 final_structure = vr.initial_structure temperature = vr.parameters["TEEND"] time_step = vr.parameters["POTIM"] yield step_skip, temperature, time_step # check that the runs are continuous fdist = pbc_diff(vr.initial_structure.frac_coords, final_structure.frac_coords) if np.any(fdist > 0.001): raise ValueError("initial and final structures do not match.") final_structure = vr.final_structure assert (vr.ionic_step_skip or 1) == step_skip for s in vr.ionic_steps: yield s["structure"]
def should_stop(self, old_centroids, centroids, iterations): """ Check for stopping conditions. Args: old_centroids: List of old centroids centroids: List of centroids iterations: Number of iterations thus far. """ if iterations > self.max_iterations: warnings.warn("Max iterations %d reached!" % self.max_iterations) return True if old_centroids is None: return False for c1, c2 in zip(old_centroids, centroids): if not np.allclose(pbc_diff(c1, c2), [0, 0, 0]): return False return True
def get_equivalent_qpoint(qk, symops, qp, tol=1e-2): """Finds the closest phono3py qpoint to a phonopy qpoint. Arguments --------- qk : array-like all qpoints from the phono3py kappa file. symmops symmetry operations (e.g. from Pymatgen) qp : array-like single qpoint from the phonon dispersion. tol : float, optional tolerance. Default: 1e-2. Returns ------- int nearest qpoint index. """ from pymatgen.util.coord import pbc_diff # Equivalent qpoints points = np.dot(qp, [m.rotation_matrix for m in symops]) # Remove duplicates rm_list = [] for i in range(len(points) - 1): for j in range(i + 1, len(points)): if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol): rm_list.append(i) break seq = np.delete(points, rm_list, axis=0) # Find nearest dists = [np.min(np.sum(np.power(k - seq, 2), axis=1)) for k in qk] return dists.index(np.min(dists))
def get_structures(vaspruns): for i, vr in enumerate(vaspruns): if i == 0: step_skip = vr.ionic_step_skip or 1 final_structure = vr.initial_structure temperature = vr.parameters['TEEND'] time_step = vr.parameters['POTIM'] yield step_skip, temperature, time_step # check that the runs are continuous fdist = pbc_diff(vr.initial_structure.frac_coords, final_structure.frac_coords) if np.any(fdist > 0.001): raise ValueError('initial and final structures do not ' 'match.') final_structure = vr.final_structure assert (vr.ionic_step_skip or 1) == step_skip for s in vr.ionic_steps: yield s['structure']
def test_cluster(self): lattice = Lattice.cubic(4) pts = [] initial = [[0, 0, 0], [0.5, 0.5, 0.5], [0.25, 0.25, 0.25], [0.5, 0, 0]] for c in initial: for i in range(100): pts.append(np.array(c) + np.random.randn(3) * 0.01 + np.random.randint(3)) pts = np.array(pts) k = KmeansPBC(lattice) centroids, labels, ss = k.cluster(pts, 4) for c1 in centroids: found = False for c2 in centroids: if np.allclose(pbc_diff(c1, c2), [0, 0, 0], atol=0.1): found = True break self.assertTrue(found)
def is_periodic_image(self, other, tolerance=1e-8, check_lattice=True): """ Returns True if sites are periodic images of each other. Args: other (PeriodicSite): Other site tolerance (float): Tolerance to compare fractional coordinates check_lattice (bool): Whether to check if the two sites have the same lattice. Returns: bool: True if sites are periodic images of each other. """ if check_lattice and self.lattice != other.lattice: return False if self.species != other.species: return False frac_diff = pbc_diff(self.frac_coords, other.frac_coords) return np.allclose(frac_diff, [0, 0, 0], atol=tolerance)
def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None): rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix ir_kpoints_idx = self.amset_data.ir_kpoints_idx energy = self.amset_data.energies[spin][b_idx, ir_kpoints_idx][k_idx] if energy_diff: energy += energy_diff tbs = self.amset_data.tetrahedral_band_structure tet_dos, tet_mask, cs_weights, tet_contributions = tbs.get_tetrahedra_density_of_states( spin, energy, return_contributions=True, symmetry_reduce=False, # band_idx=b_idx, ) if len(tet_dos) == 0: return 0 # next, get k-point indices and band_indices property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks( spin, tet_mask) k = self.amset_data.ir_kpoints[k_idx] k_primes = self.amset_data.kpoints[kpoint_mask] overlap = self.amset_data.overlap_calculator.get_overlap( spin, b_idx, k, band_mask, k_primes) # put overlap back in array with shape (nbands, nkpoints) all_overlap = np.zeros(self.amset_data.energies[spin].shape) all_overlap[band_kpoint_mask] = overlap # now select the properties at the tetrahedron vertices vert_overlap = all_overlap[property_mask] # get interpolated overlap and k-point at centre of tetrahedra cross sections tet_overlap = get_cross_section_values(vert_overlap, *tet_contributions) tetrahedra = tbs.tetrahedra[spin][tet_mask] # have to deal with the case where the tetrahedron cross section crosses the # zone boundary. This is a slight inaccuracy but we just treat the # cross section as if it is on one side of the boundary tet_kpoints = self.amset_data.kpoints[tetrahedra] base_kpoints = tet_kpoints[:, 0][:, None, :] k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff( base_kpoints, k) k_diff = np.dot(k_diff, rlat) intersections = get_cross_section_values(k_diff, *tet_contributions, average=False) projected_intersections = get_projected_intersections(intersections) if energy_diff: f = np.zeros(self.amset_data.fermi_levels.shape) for n, t in np.ndindex(self.amset_data.fermi_levels.shape): f[n, t] = FD( energy, self.amset_data.fermi_levels[n, t], self.amset_data.temperatures[t] * units.BOLTZMANN, ) functions = np.array([ m.factor(energy_diff <= 0, f) for m in self.inelastic_scatterers ]) else: functions = np.array([m.factor() for m in self.elastic_scatterers]) rates = np.array([ integrate_function_over_cross_section( f, projected_intersections, *tet_contributions[0:3], return_shape=self.amset_data.fermi_levels.shape, cross_section_weights=cs_weights) for f in functions ]) # sometimes the projected intersections can be nan when the density of states # contribution is infinitesimally small; this catches those errors rates[np.isnan(rates)] = 0 rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume rates *= tet_overlap return np.sum(rates, axis=-1)
def displacement_energies(self, structure, potential, displacement_energies_schema, supercell=(1, 1, 1), tollerance=0.1, max_displacement_energy=75, resolution=1, num_steps=1000, site_radius=0.5, timestep=0.001): """ Calculate displacement energy for each atom. Uses bisection method to determine displacement energy. """ def ev2Aps(Z, energy): # sqrt((2 * energy[eV] [J/eV]) / (amu [g/mole] [kg/g])) * [m/s] [A/ps] return math.sqrt((2 * energy * 1.6021766208e-19) / (Z / (6.02214085e23 * 1e3))) * 1e-2 if self.calculator_type == 'lammps': logger.warning('"lammps" calculator is depriciated use "lammps_cython" cannot promise working') relax_lammps_script = load_lammps_set('nve') relax_lammps_script['thermo'] = [] relax_lammps_script relax_lammps_script['timestep'] = timestep # fs relax_lammps_script['run'] = num_steps kwargs = {'lammps_set': relax_lammps_script} elif self.calculator_type == 'lammps_cython': kwargs = {'lammps_additional_commands': [ 'timestep %f' % timestep, 'velocity all zero linear', 'fix 1 all nve', 'run %d' % num_steps ]} energies = {} displacement_energies_schema = displacement_energies_schema.copy() for displacement_energy_name, d in displacement_energies_schema.items(): base_structure = structure.copy() v = base_structure.lattice.get_cartesian_coords(d['direction']) cart_coords = base_structure.lattice.get_cartesian_coords(d['position']) base_structure = base_structure * supercell site = base_structure.get_sites_in_sphere(cart_coords, tollerance)[0][0] original_positions = base_structure.cart_coords original_frac_positions = base_structure.lattice.get_fractional_coords(original_positions) index = base_structure.index(site) min_energy, max_energy = 0.0, max_displacement_energy guess_energy = None while abs(max_energy - min_energy) > resolution: guess_energy = (max_energy - min_energy) / 2 + min_energy velocity = (v / np.linalg.norm(v)) * ev2Aps(Element(d['element']).atomic_mass, guess_energy) velocities = np.zeros((len(base_structure), 3)) velocities[index] = velocity base_structure.add_site_property('velocities', velocities) async def calculate(): future = await self.calculator.submit( base_structure, potential, properties={'positions', 'initial_positions'}, **kwargs) await future return future.result() print('starting calculation (displacement energy): %s ion %s velocity: %f [eV] %f [A/ps]' % (displacement_energy_name, d['element'], guess_energy, ev2Aps(Element(d['element']).atomic_mass, guess_energy))) result = self._run_async_func(calculate()) initial_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['initial_positions']) final_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['positions']) displacements = np.linalg.norm( base_structure.lattice.get_cartesian_coords( pbc_diff(final_frac_positions, initial_frac_positions)), axis=1) is_original_state = np.all(displacements < site_radius) print('finished calculation (displacement energy): %s resulted in ground_state (%s) max displacment %f [A] median %f [A] min %f [A]' % (displacement_energy_name, is_original_state, np.max(displacements), np.median(displacements), np.min(displacements))) if is_original_state: min_energy = guess_energy else: max_energy = guess_energy energies[displacement_energy_name] = guess_energy return energies
def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None): rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix ir_kpoints_idx = self.amset_data.ir_kpoints_idx energy = self.amset_data.energies[spin][b_idx, ir_kpoints_idx][k_idx] if energy_diff: energy += energy_diff tbs = self.amset_data.tetrahedral_band_structure ( tet_dos, tet_mask, cs_weights, tet_contributions, ) = tbs.get_tetrahedra_density_of_states( spin, energy, return_contributions=True, symmetry_reduce=False, # band_idx=b_idx, # turn this on to disable interband scattering ) if len(tet_dos) == 0: return 0 # next, get k-point indices and band_indices # property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks( # spin, tet_mask # ) k = self.amset_data.ir_kpoints[k_idx] # k_primes = self.amset_data.kpoints[kpoint_mask] # overlap = self.amset_data.overlap_calculator.get_overlap( # spin, b_idx, k, band_mask, k_primes # ) # # # put overlap back in array with shape (nbands, nkpoints) # all_overlap = np.zeros(self.amset_data.energies[spin].shape) # all_overlap[band_kpoint_mask] = overlap # # # now select the properties at the tetrahedron vertices # vert_overlap = all_overlap[property_mask] # # # get interpolated overlap at centre of tetrahedra cross sections # tet_overlap = get_cross_section_values(vert_overlap, *tet_contributions) tetrahedra = tbs.tetrahedra[spin][tet_mask] # have to deal with the case where the tetrahedron cross section crosses the # zone boundary. This is a slight inaccuracy but we just treat the # cross section as if it is on one side of the boundary tet_kpoints = self.amset_data.kpoints[tetrahedra] base_kpoints = tet_kpoints[:, 0][:, None, :] k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff( base_kpoints, k) k_diff = np.dot(k_diff, rlat) intersections = get_cross_section_values(k_diff, *tet_contributions, average=False) projected_intersections, basis = get_projected_intersections( intersections) k_spacing = np.linalg.norm( np.dot(rlat, 1 / self.amset_data.kpoint_mesh)) qpoints, weights, mapping = get_fine_mesh_qpoints( projected_intersections, basis, *tet_contributions[0:3], high_tol=k_spacing * 0.5, med_tol=k_spacing * 2, cross_section_weights=cs_weights) qpoint_norm_sq = np.sum(qpoints**2, axis=-1) k_primes = np.dot(qpoints, np.linalg.inv(rlat)) + k k_primes = kpoints_to_first_bz(k_primes) if energy_diff: fd = _get_fd(energy, self.amset_data) emission = energy_diff <= 0 rates = [ s.factor(qpoint_norm_sq, emission, fd) for s in self.inelastic_scatterers ] mrta_factor = 1 else: mrta_factor = self.amset_data.mrta_calculator.get_mrta_factor( spin, b_idx, k, tet_mask[0][mapping], k_primes) rates = [s.factor(qpoint_norm_sq) for s in self.elastic_scatterers] rates = np.array(rates) # sometimes the projected intersections can be nan when the density of states # contribution is infinitesimally small; this catches those errors rates[np.isnan(rates)] = 0 overlap = self.amset_data.overlap_calculator.get_overlap( spin, b_idx, k, tet_mask[0][mapping], k_primes) rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume rates *= overlap * weights * mrta_factor return np.sum(rates, axis=-1)
def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None): rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix energy = self.amset_data.energies[spin][b_idx, k_idx] velocity = self.amset_data.velocities[spin][b_idx, k_idx] if energy_diff: energy += energy_diff tbs = self.amset_data.tetrahedral_band_structure ( tet_dos, tet_mask, cs_weights, tet_contributions, ) = tbs.get_tetrahedra_density_of_states( spin, energy, return_contributions=True, symmetry_reduce=False, # band_idx=b_idx, # turn this on to disable interband scattering ) if len(tet_dos) == 0: return 0 # next, get k-point indices and band_indices property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks( spin, tet_mask) k = self.amset_data.kpoints[k_idx] k_primes = self.amset_data.kpoints[kpoint_mask] if self.cache_wavefunction: # use cached coefficients to calculate the overlap on the fine mesh # tetrahedron vertices p1 = self._coeffs[spin][self._coeffs_mapping[spin][b_idx, k_idx]] p2 = self._coeffs[spin][self._coeffs_mapping[spin][band_mask, kpoint_mask]] overlap = get_overlap(p1, p2) else: overlap = self.amset_data.overlap_calculator.get_overlap( spin, b_idx, k, band_mask, k_primes) # put overlap back in array with shape (nbands, nkpoints) all_overlap = np.zeros(self.amset_data.energies[spin].shape) all_overlap[band_kpoint_mask] = overlap # now select the properties at the tetrahedron vertices vert_overlap = all_overlap[property_mask] # get interpolated overlap at centre of tetrahedra cross sections tet_overlap = get_cross_section_values(vert_overlap, *tet_contributions) tetrahedra = tbs.tetrahedra[spin][tet_mask] # have to deal with the case where the tetrahedron cross section crosses the # zone boundary. This is a slight inaccuracy but we just treat the # cross section as if it is on one side of the boundary tet_kpoints = self.amset_data.kpoints[tetrahedra] base_kpoints = tet_kpoints[:, 0][:, None, :] k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff( base_kpoints, k) # project the tetrahedron cross sections onto 2D surfaces in either a triangle # or quadrilateral k_diff = np.dot(k_diff, rlat) intersections = get_cross_section_values(k_diff, *tet_contributions, average=False) projected_intersections, basis = get_projected_intersections( intersections) k_spacing = np.linalg.norm( np.dot(rlat, 1 / self.amset_data.kpoint_mesh)) qpoints, weights, mapping = get_fine_mesh_qpoints( projected_intersections, basis, *tet_contributions[0:3], high_tol=k_spacing * 0.5, med_tol=k_spacing * 2, cross_section_weights=cs_weights, ) qpoint_norm_sq = np.sum(qpoints**2, axis=-1) k_primes = np.dot(qpoints, np.linalg.inv(rlat)) + k k_primes = kpoints_to_first_bz(k_primes) # unit q in reciprocal cartesian coordinates unit_q = qpoints / np.sqrt(qpoint_norm_sq)[:, None] if energy_diff: e_fd = _get_fd(energy, self.amset_data) emission = energy_diff <= 0 rates = [ s.factor(unit_q, qpoint_norm_sq, emission, e_fd) for s in self.inelastic_scatterers ] mrta_factor = 1 else: mrta_factor = self.amset_data.mrta_calculator.get_mrta_factor( spin, b_idx, k, tet_mask[0][mapping], k_primes) rates = [ s.factor(unit_q, qpoint_norm_sq, spin, b_idx, k, velocity) for s in self.elastic_scatterers ] rates = np.array(rates) rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume rates *= tet_overlap[mapping] * weights * mrta_factor # this is too expensive vs tetrahedron integration and doesn't add much more # accuracy; could offer this as an option # overlap = self.amset_data.overlap_calculator.get_overlap( # spin, b_idx, k, tet_mask[0][mapping], k_primes # ) # rates *= overlap * weights * mrta_factor # sometimes the projected intersections can be nan when the density of states # contribution is infinitesimally small; this catches those errors rates[np.isnan(rates)] = 0 return np.sum(rates, axis=-1)
def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b): # k-points should be in fractional kpoint_a = np.asarray(kpoint_a) kpoint_b = np.asarray(kpoint_b) v1 = np.array([[band_a] + kpoint_a.tolist()]) single_overlap = False if isinstance(band_b, numeric_types): # only one band index given if len(kpoint_b.shape) > 1: # multiple k-point indices given band_b = np.array([band_b] * len(kpoint_b)) else: band_b = np.array([band_b]) kpoint_b = [kpoint_b] single_overlap = True else: band_b = np.asarray(band_b) centers = self.band_centers[spin][band_a] center = centers[np.argmin( np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))] shift_a = pbc_diff(kpoint_a, center) shift_b = pbc_diff(kpoint_b, center) angles = cosine(shift_a, shift_b) angle_weights = np.abs(pbc_diff(kpoint_a, kpoint_b)) angle_weights /= np.max(angle_weights, axis=1)[:, None] angle_weights[np.isnan(angle_weights)] = 0 # v2 now has shape of (nkpoints_b, 4) v2 = np.concatenate([band_b[:, None], kpoint_b], axis=1) # get a big array of all the k-points to interpolate all_v = np.vstack([v1, v2]) # get the interpolate projections for the k-points; p1 is the projections for # kpoint_a, p2 is a list of projections for the kpoint_b p1, *p2 = self.interpolators[spin](all_v) # weight the angle masks by the contribution of the transition in that direction weighted_mask = self.rotation_masks[None, ...] * angle_weights[..., None] # use the mask to get the angle scaling factor which gives how much the angle # is applied to each projection. I.e., if the scaling factor is 1 for a # particular projection, then the projection is scaled by 1 * the angle. If the # scaling factor is 0.5, the projection is scaled by 0.5 * the angle. # this allows us to only weight specific orbitals scaling_factor = np.max(weighted_mask, axis=1) p_product = p1 * p2 overlap = np.sum( p_product * (1 - scaling_factor) + p_product * scaling_factor * angles[:, None], axis=1, ) # overlap = np.ones_like(overlap) if single_overlap: return overlap[0]**2 else: return overlap**2
def get_ir_band_rates(spin, b_idx, scatterers, ediff, gauss_width, s, k_idx, k_p_idx, kpoints, kpoint_norms, kpoint_weights, a_factor, c_factor, reciprocal_lattice_matrix, ir_kpoints_idx, grouped_ir_to_full, g=None, emission=False, calculate_out_rate=True, calculate_in_rate=True): from numpy.core.umath_tests import inner1d # k_idx and k_p_idx are currently their reduced form. E.g., span 0 to # n_ir_kpoints-1; find actual columns of k_p_idx in the full Brillouin zone # by lookup full_k_p_idx_grouped = grouped_ir_to_full[k_p_idx] # get the reduced k_idx including duplicate k_idx for the full k_prime repeated_k_idx = np.repeat(k_idx, [len(g) for g in full_k_p_idx_grouped]) expand_k_idx = np.repeat(np.arange( len(k_idx)), [len(g) for g in full_k_p_idx_grouped]) # flatten the list of mapped columns full_k_p_idx = np.concatenate(full_k_p_idx_grouped) # get the indices of the k_idx in the full Brillouin zone full_k_idx = ir_kpoints_idx[repeated_k_idx] mask = full_k_idx != full_k_p_idx full_k_idx = full_k_idx[mask] full_k_p_idx = full_k_p_idx[mask] expand_k_idx = expand_k_idx[mask] k_dot = inner1d(kpoints[full_k_idx], kpoints[full_k_p_idx]) k_angles = k_dot / (kpoint_norms[full_k_idx] * kpoint_norms[full_k_p_idx]) k_angles[np.isnan(k_angles)] = 1. a_vals = a_factor[k_idx] * a_factor[k_p_idx] c_vals = c_factor[k_idx] * c_factor[k_p_idx] overlap = (a_vals[expand_k_idx] + c_vals[expand_k_idx] * k_angles) ** 2 weighted_overlap = (w0gauss(ediff / gauss_width)[expand_k_idx] * overlap * kpoint_weights[full_k_p_idx]) # norm of k difference squared in 1/nm k_diff_sq = np.linalg.norm(np.dot( pbc_diff(kpoints[full_k_idx], kpoints[full_k_p_idx]), reciprocal_lattice_matrix) / 0.1, axis=1) ** 2 if isinstance(scatterers[0], AbstractElasticScattering): # factors has shape: (n_scatterers, n_doping, n_temperatures, n_kpts) factors = np.array([m.factor(k_diff_sq) for m in scatterers]) else: # factors has shape: (n_scatterers, n_doping, n_temperatures, n_kpts) # first calculate scattering in rate to_stack = [] if calculate_in_rate: in_factors = np.array([ m.factor(spin, b_idx, full_k_idx, k_diff_sq, not emission) for m in scatterers]) in_factors *= g[None, :, :, full_k_p_idx] * k_angles[None, None, None, :] to_stack.append(in_factors) if calculate_out_rate: out_factors = np.array([ m.factor(spin, b_idx, full_k_p_idx, k_diff_sq, emission) for m in scatterers]) to_stack.append(out_factors) factors = np.vstack(to_stack) rates = weighted_overlap * factors repeated_k_idx = repeated_k_idx[mask] - s.start unique_rows = np.unique(repeated_k_idx) # band rates has shape (n_scatterers, n_doping, n_temperatures, n_kpoints) band_rates = np.zeros((list(factors.shape[:-1]) + [s.stop - s.start])) # could vectorize this using numpy apply along axis if need be for s, n, t in np.ndindex(band_rates.shape[:-1]): band_rates[s, n, t, unique_rows] = np.bincount( repeated_k_idx, weights=rates[s, n, t])[unique_rows] return band_rates