def show_rotational_invariance(force_constants, supercell, symprec=1e-5, log_level=1): """ *** Under development *** Just show how force constant is close to the condition of rotational invariance, """ print "Check rotational invariance ..." fc = force_constants unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) volume_unitcell = supercell.get_volume() * len(unit_atoms) / supercell.get_number_of_atoms() abc = "xyz" eijk = np.zeros((3,3,3), dtype="intc") eijk[0,1,2] = eijk[1,2,0] = eijk[2,0,1] = 1 eijk[0,2,1] = eijk[2,1,0] = eijk[1,0,2] = -1 # epsilon matrix, which is an antisymmetric 3 * 3 * 3 tensor stress = np.zeros((3, 3), dtype='double') for pi, p in enumerate(unit_atoms): for i in range(3): mat = np.zeros((3, 3), dtype='double') for s in range(supercell.get_number_of_atoms()): vecs = np.array(get_equivalent_smallest_vectors( s, p, supercell, supercell.get_cell(), symprec)) m = len(vecs) v = np.dot(vecs[:,:].sum(axis=0) / m, supercell.get_cell()) for j in range(3): for k in range(3): mat[j, k] += (fc[p, s, i, j] * v[k] - fc[p, s, i, k] * v[j]) stress += np.abs(mat) if log_level == 2: print "Atom %d %s" % (p+1, abc[i]) for vec in mat: print "%10.5f %10.5f %10.5f" % tuple(vec) if log_level == 1: print "System stress residue enduced by rigid-body rotations(eV/A)" for vec in stress: print "%10.5f %10.5f %10.5f" % tuple(vec) ElasticConstants = np.zeros((3,3,3,3), dtype='double') for s1 in unit_atoms: for s2 in range(supercell.get_number_of_atoms()): vec12s = np.array(get_equivalent_smallest_vectors( s2, s1, supercell, supercell.get_cell(), symprec)) vec12s = np.dot(vec12s, supercell.get_cell()) for v12 in vec12s: ElasticConstants += -np.einsum('ij, k, l->ijkl', fc[s1, s2], v12, v12) / len(vec12s) / 2. ElasticConstants = ElasticConstants.reshape(9,9) non_sym_tensor = ElasticConstants - ElasticConstants.T if log_level == 2: print 'Born-Huang rotational invariance condition (eV)' for i in range(9): print "%10.5f " * 9 %tuple(non_sym_tensor[i]) elif log_level == 1: M_sum = np.abs(non_sym_tensor).sum() print 'Born-Huang rotational invariance condition (eV): %10.5f' %M_sum
def run(self, atom_pairs): s2p = self._primitive.get_supercell_to_primitive_map() p2p = self._primitive.get_primitive_to_primitive_map() dists = np.zeros((len(self._temperatures), len(atom_pairs)), dtype=float) for i, (atom1, atom2) in enumerate(atom_pairs): patom1 = p2p[s2p[atom1]] patom2 = p2p[s2p[atom2]] delta_r = get_equivalent_smallest_vectors( atom2, atom1, self._supercell, self._primitive.get_cell(), self._symprec)[0] self._project_eigenvectors(delta_r, self._primitive.get_cell()) for freqs, vecs, q in zip(self._frequencies, self._p_eigenvectors, self._qpoints): c_cross = 1.0 / np.sqrt( self._masses[patom1] * self._masses[patom2]) c1 = 1.0 / self._masses[patom1] c2 = 1.0 / self._masses[patom2] for f, v in zip(freqs, vecs.T): cross_term = self._get_cross(v, delta_r, q, patom1, patom2) v2 = abs(v)**2 if f > self._cutoff_frequency: for j, t in enumerate(self._temperatures): dists[j, i] += self.get_Q2(f, t) * ( v2[patom1] * c1 + cross_term * c_cross + v2[patom2] * c2) self._atom_pairs = atom_pairs self._distances = dists / len(self._frequencies)
def rotational_invariance(force_constants, supercell, primitive, symprec=1e-5): """ *** Under development *** Just show how force constant is close to the condition of rotational invariance, """ print("Check rotational invariance ...") fc = force_constants p2s = primitive.get_primitive_to_supercell_map() abc = "xyz" for pi, p in enumerate(p2s): for i in range(3): mat = np.zeros((3, 3), dtype='double') for s in range(supercell.get_number_of_atoms()): vecs = np.array(get_equivalent_smallest_vectors( s, p, supercell, primitive.get_cell(), symprec)) m = len(vecs) v = np.dot(vecs[:,:].sum(axis=0) / m, primitive.get_cell()) for j in range(3): for k in range(3): mat[j, k] += (fc[p, s, i, j] * v[k] - fc[p, s, i, k] * v[j]) print("Atom %d %s" % (p + 1, abc[i])) for vec in mat: print("%10.5f %10.5f %10.5f" % tuple(vec))
def get_trim_fc2(supercell, trans, coeff, ifc_map, symprec, precision=1e-5, pairs_included=None, is_trim_boundary=False): unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred = trans.shape[-1] ti_transforms =[np.zeros(num_irred)] for i, atom1 in enumerate(unit_atoms): for atom2 in np.arange(natom): is_trim = False if pairs_included is not None and not pairs_included[ifc_map[atom1, atom2]]: is_trim = True if is_trim_boundary: dist = get_equivalent_smallest_vectors(atom2, atom1, supercell, supercell.get_cell(), symprec=symprec) if len(dist) > 1: is_trim = True if is_trim: irred_doublet = ifc_map[atom1, atom2] ti_transform = np.dot(coeff[atom1, atom2], trans[irred_doublet]) for k in range(9): if not (np.abs(ti_transform[k])< precision).all(): argmax = np.argmax(np.abs(ti_transform[k])) ti_transform[k] /= ti_transform[k, argmax] is_exist = np.all(np.abs(ti_transform[k] - np.array(ti_transforms)) < precision, axis=1) if (is_exist == False).all(): ti_transforms.append(ti_transform[k] / ti_transform[k, argmax]) print "Number of constraints of fc2 from a cutoff of interaction distance:%d"%len(ti_transforms) CC, transform, independent = gaussian(np.array(ti_transforms), precision) return independent, transform
def set_thermal_distances( self, atom_pairs ): s2p = self.primitive.get_supercell_to_primitive_map() p2p = self.primitive.get_primitive_to_primitive_map() dists = np.zeros( ( len( self.temperatures), len( atom_pairs ) ), dtype=float ) for i, ( atom1, atom2 ) in enumerate( atom_pairs ): patom1 = p2p[s2p[atom1]] patom2 = p2p[s2p[atom2]] delta_r = ( get_equivalent_smallest_vectors( atom2, atom1, self.supercell, self.primitive.get_cell(), self.symprec ) )[0] self.project_eigenvectors( delta_r, self.primitive.get_cell() ) for eigs, vecs, q, w in zip( self.eigenvalues, self.p_eigenvectors, self.qpoints, self.weights ): c_cross = w / ( np.sqrt( self.masses[patom1] * self.masses[patom2] ) * AMU ) c1 = w / ( self.masses[patom1] * AMU ) c2 = w / ( self.masses[patom2] * AMU ) for e, v in zip( eigs, vecs.T ): cross_term = self.__get_cross( v, delta_r, q, patom1, patom2 ) v2 = abs(v)**2 if e > self.cutoff_eigenvalue: omega = np.sqrt( e ) * self.factor # To THz for j, t in enumerate( self.temperatures ): dists[j,i] += self.get_Q2( omega, t ) * ( v2[patom1] * c1 + cross_term * c_cross + v2[patom2] * c2 ) self.atom_pairs = atom_pairs self.distances = dists / self.weights.sum()
def test(self): natoms = self._atoms.get_number_of_atoms() symprec = 1e-6 dt_old = 0.0 dt_new = 0.0 for i in range(natoms): for j in range(natoms): t0 = time.time() tmp0 = get_equivalent_smallest_vectors(i, j, self._atoms, self._primitive_matrix, symprec) t1 = time.time() dt_old += t1 - t0 t0 = time.time() tmp1 = get_equivalent_smallest_vectors_np( i, j, self._atoms, self._primitive_matrix, symprec) t1 = time.time() dt_new += t1 - t0 print(tmp0) print(tmp1) self.assertTrue(np.array_equal(tmp0, tmp1)) print(dt_old) print(dt_new)
def run(self, atom_pairs): s2p = self._primitive.get_supercell_to_primitive_map() p2p = self._primitive.get_primitive_to_primitive_map() dists = np.zeros((len(self._temperatures), len(atom_pairs)), dtype=float) for i, (atom1, atom2) in enumerate(atom_pairs): patom1 = p2p[s2p[atom1]] patom2 = p2p[s2p[atom2]] delta_r = get_equivalent_smallest_vectors(atom2, atom1, self._supercell, self._primitive.get_cell(), self._symprec)[0] self._project_eigenvectors(delta_r, self._primitive.get_cell()) for freqs, vecs, q in zip(self._frequencies, self._p_eigenvectors, self._qpoints): c_cross = 1.0 / np.sqrt(self._masses[patom1] * self._masses[patom2]) c1 = 1.0 / self._masses[patom1] c2 = 1.0 / self._masses[patom2] for f, v in zip(freqs, vecs.T): cross_term = self._get_cross(v, delta_r, q, patom1, patom2) v2 = abs(v)**2 if f > self._cutoff_frequency: for j, t in enumerate(self._temperatures): dists[j, i] += self.get_Q2(f, t) * ( v2[patom1] * c1 + cross_term * c_cross + v2[patom2] * c2) self._atom_pairs = atom_pairs self._distances = dists / len(self._frequencies)
def rotational_invariance(force_constants, supercell, primitive, symprec=1e-5): """ *** Under development *** Just show how force constant is close to the condition of rotational invariance, """ print("Check rotational invariance ...") fc = force_constants p2s = primitive.get_primitive_to_supercell_map() abc = "xyz" for pi, p in enumerate(p2s): for i in range(3): mat = np.zeros((3, 3), dtype='double') for s in range(supercell.get_number_of_atoms()): vecs = np.array(get_equivalent_smallest_vectors( s, p, supercell, primitive.get_cell(), symprec)) m = len(vecs) v = np.dot(vecs[:,:].sum(axis=0) / m, primitive.get_cell()) for j in range(3): for k in range(3): mat[j, k] += (fc[p, s, i, j] * v[k] - fc[p, s, i, k] * v[j]) print("Atom %d %s" % (p + 1, abc[i])) for vec in mat: print("%10.5f %10.5f %10.5f" % tuple(vec))
def rotational_invariance(force_constants, supercell, primitive, symprec=1e-5): """ *** Under development *** Just show how force constant is close to the condition of rotational invariance, """ print "Check rotational invariance ..." fc = force_constants p2s = primitive.get_primitive_to_supercell_map() abc = "xyz" eijk = np.zeros((3,3,3), dtype="intc") eijk[0,1,2] = eijk[1,2,0] = eijk[2,0,1] = 1 eijk[0,2,1] = eijk[2,1,0] = eijk[1,0,2] = -1 # epsilon matrix, which is an antisymmetric 3 * 3 * 3 tensor for pi, p in enumerate(p2s): for i in range(3): mat = np.zeros((3, 3), dtype='double') for s in range(supercell.get_number_of_atoms()): vecs = np.array(get_equivalent_smallest_vectors( s, p, supercell, primitive.get_cell(), symprec)) m = len(vecs) v = np.dot(vecs[:,:].sum(axis=0) / m, primitive.get_cell()) for j in range(3): for k in range(3): mat[j, k] += (fc[p, s, i, j] * v[k] - fc[p, s, i, k] * v[j]) print "Atom %d %s" % (p+1, abc[i]) for vec in mat: print "%10.5f %10.5f %10.5f" % tuple(vec) Momentum = np.zeros((3,3,3,3), dtype='double') for s1 in p2s: for s2 in range(supercell.get_number_of_atoms()): vec12s = np.array(get_equivalent_smallest_vectors( s2, s1, supercell, supercell.get_cell(), symprec)) vec12s = np.dot(vec12s, supercell.get_cell()) for v12 in vec12s: Momentum += -np.einsum('ij, k, l->ijkl', fc[s1, s2], v12, v12) / len(vec12s) / 4. Momentum = Momentum.reshape(9,9) Momentum = Momentum - Momentum.T print 'Huang rotational invariance condition (eV)' for i in range(9): print "%10.5f " * 9 %tuple(Momentum[i])
def get_trim_fc3(supercell, trans, triplets_reduced, symprec, precision=1e-5, triplets_included=None, is_trim_boundary=False): num_irred = trans[0].shape[-1] unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) zero_fc3s =[np.zeros(num_irred, dtype='double')] for i, (atom1, atom2, atom3) in enumerate(triplets_reduced): first = np.where(atom1 == unit_atoms)[0][0] is_trim = False if triplets_included is not None and not triplets_included[i]: is_trim = True if is_trim_boundary: dist12 = get_equivalent_smallest_vectors(atom2, atom1, supercell, supercell.get_cell(), symprec=symprec) dist23 = get_equivalent_smallest_vectors(atom3, atom2, supercell, supercell.get_cell(), symprec=symprec) dist13 = get_equivalent_smallest_vectors(atom3, atom1, supercell, supercell.get_cell(), symprec=symprec) if len(dist12) > 1 or len(dist23) > 1 or len(dist13) > 1: is_trim = True if is_trim: import scipy.sparse if scipy.sparse.issparse(trans[i]): zero_fc3 = trans[i].toarray() else: zero_fc3 = trans[i] for k in range(27): if not (np.abs(zero_fc3[k])< precision).all(): argmax = np.argmax(np.abs(zero_fc3[k])) zero_fc3[k] /= zero_fc3[k, argmax] is_exist = np.all(np.abs(zero_fc3[k] - np.array(zero_fc3s)) < precision, axis=1) if (is_exist == False).all(): zero_fc3s.append(zero_fc3[k] / zero_fc3[k, argmax]) print "Number of constraints of fc3 from a cutoff of interaction distance:%d"% (len(zero_fc3s) - 1) try: import _mdfc transform = np.zeros((num_irred, num_irred), dtype='double') independent = np.zeros(num_irred, dtype='intc') num_independent = _mdfc.gaussian(transform, np.array(zero_fc3s, dtype='double'), independent, precision) transform = transform[:, :num_independent] independent = independent[:num_independent] except ImportError: CC, transform, independent = gaussian_py(np.array(zero_fc3s, dtype='double'), prec=precision) return independent, transform
def get_triplet_inclusion(self, triplets=None): lattice = self._cell.get_cell() include_triplet = np.ones(len(triplets), dtype=bool) for i, (a1, a2, a3) in enumerate(triplets): d12 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a2, a1, self._cell, lattice, self._symprec)[0], lattice)) d23 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a2, self._cell, lattice, self._symprec)[0], lattice)) d13 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a1, self._cell, lattice, self._symprec)[0], lattice)) if d12 > self._cut_radius[a1] + self._cut_radius[a2] and\ d23 > self._cut_radius[a2] + self._cut_radius[a3] and\ d13 > self._cut_radius[a1] + self._cut_radius[a3]: include_triplet[i] = False return include_triplet
def get_triplet_inclusion(self, triplets=None): lattice = self._cell.get_cell() include_triplet = np.ones(len(triplets), dtype=bool) for i, (a1, a2, a3) in enumerate(triplets): d12 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a2, a1, self._cell, lattice, self._symprec)[0], lattice)) d23 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a2, self._cell, lattice, self._symprec)[0], lattice)) d13 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a1, self._cell, lattice, self._symprec)[0], lattice)) if d12 > self._cut_radius[a1] + self._cut_radius[a2] and\ d23 > self._cut_radius[a2] + self._cut_radius[a3] and\ d13 > self._cut_radius[a1] + self._cut_radius[a3]: include_triplet[i] = False return include_triplet
def set_pair_distances(self): num_atom = self._cell.get_number_of_atoms() lattice = self._cell.get_cell() min_distances = np.zeros((num_atom, num_atom), dtype='double') for i in range(num_atom): # run in cell for j in range(i): # run in primitive min_distances[i, j] = np.linalg.norm(np.dot( get_equivalent_smallest_vectors( i, j, self._cell, lattice, self._symprec)[0], lattice)) self._pair_distances = (min_distances + min_distances.T) / 2.
def get_fc3_rotational_invariance(fc2, supercell, trans, coeff, ifc_map, symprec=1e-5, precision=1e-6): "Returning the constraint equations to be used in Lagrangian Multipliers" precision *= 1e2 unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred = trans[0].shape[-1] lattice = supercell.get_cell() eijk = np.zeros((3, 3, 3), dtype="intc") eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1 eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[ 1, 0, 2] = -1 # epsilon matrix, which is an antisymmetric 3 * 3 * 3 tensor torques = [np.zeros(num_irred + 1)] # considering the constant column for i, atom1 in enumerate(unit_atoms): for atom2 in range(natom): torque = np.zeros((27, num_irred), dtype=np.float) t2_1 = np.einsum("cb, cav -> abv", fc2[atom1, atom2], eijk).flatten() t2_2 = np.einsum("ac, cbv -> abv", fc2[atom1, atom2], eijk).flatten() torque_fc2 = t2_1 + t2_2 for atom3 in range(natom): fc_temp = mat_dot_product(coeff[i, atom2, atom3], trans[ifc_map[i, atom2, atom3]], is_sparse=True).reshape(3, 3, 3, -1) vectors = get_equivalent_smallest_vectors( atom3, atom1, supercell, lattice, symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(r_frac, lattice) disp = np.einsum("j, ijk -> ik", r, eijk) t3 = np.einsum("abcN, cv -> abvN", fc_temp, disp).reshape(27, num_irred) torque += t3 torque = np.hstack( (torque, -torque_fc2[:, np.newaxis])) #negative sign means Bx = d for k in range(27): if not (np.abs(torque[k]) < precision).all(): argmax = np.argmax(np.abs(torque[k])) torque[k] /= torque[k, argmax] is_exist = np.all( np.abs(torque[k] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(torque[k] / torque[k, argmax]) print "Number of constraints of IFC3 from rotational invariance:%d" % ( len(torques) - 1) torques = np.array(torques)[1:] return torques[:, :-1], torques[:, -1] # return B and d
def get_pair_inclusion(self, pairs=None): lattice = self._cell.get_cell() include_pair = np.ones(len(pairs), dtype=bool) if self._cut_radius_species is not None: for i, (a1, a2) in enumerate(pairs): distance = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a2, a1, self._cell, lattice, self._symprec)[0], lattice)) if distance > self._cut_radius[a1] + self._cut_radius[a2]: include_pair[i] = False return include_pair
def set_pair_distances(self): num_atom = self._cell.get_number_of_atoms() lattice = self._cell.get_cell() self._pair_distances = np.zeros((num_atom, num_atom), dtype='double') for i in range(num_atom): for j in range(i): dist = np.linalg.norm(np.dot( get_equivalent_smallest_vectors( i, j, self._cell, lattice, self._symprec)[0], lattice)) self._pair_distances[i, j] = dist self._pair_distances[j, i] = dist
def get_pair_inclusion(self, pairs=None): lattice = self._cell.get_cell() include_pair = np.ones(len(pairs), dtype=bool) if self._cut_radius_species is not None: for i, (a1, a2) in enumerate(pairs): distance = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a2, a1, self._cell, lattice, self._symprec)[0], lattice)) if distance > self._cut_radius[a1] + self._cut_radius[a2]: include_pair[i] = False return include_pair
def set_pair_distances(self): num_atom = self._cell.get_number_of_atoms() lattice = self._cell.get_cell() min_distances = np.zeros((num_atom, num_atom), dtype='double') for i in range(num_atom): # run in cell for j in range(i): # run in primitive min_distances[i, j] = np.linalg.norm( np.dot( get_equivalent_smallest_vectors( i, j, self._cell, lattice, self._symprec)[0], lattice)) self._pair_distances = (min_distances + min_distances.T) / 2.
def cutoff_fc3_by_zero(fc3, supercell, cutoff_distance, symprec=1e-5): num_atom = supercell.get_number_of_atoms() lattice = supercell.get_cell() min_distances = np.zeros((num_atom, num_atom), dtype='double') for i in range(num_atom): # run in supercell for j in range(num_atom): # run in primitive min_distances[i, j] = np.linalg.norm(np.dot( get_equivalent_smallest_vectors( i, j, supercell, lattice, symprec)[0], lattice)) for i, j, k in np.ndindex(num_atom, num_atom, num_atom): for pair in ((i, j), (j, k), (k, i)): if min_distances[pair] > cutoff_distance: fc3[i, j, k] = 0 break
def cutoff_fc3_by_zero(fc3, supercell, cutoff_distance, symprec=1e-5): num_atom = supercell.get_number_of_atoms() lattice = supercell.get_cell() min_distances = np.zeros((num_atom, num_atom), dtype='double') for i in range(num_atom): # run in supercell for j in range(num_atom): # run in primitive min_distances[i, j] = np.linalg.norm(np.dot( get_equivalent_smallest_vectors( i, j, supercell, lattice, symprec)[0], lattice)) for i, j, k in np.ndindex(num_atom, num_atom, num_atom): for pair in ((i, j), (j, k), (k, i)): if min_distances[pair] > cutoff_distance: fc3[i, j, k] = 0 break
def get_trim_fc2(supercell, trans, coeff, ifc_map, symprec, precision=1e-5, pairs_included=None, is_trim_boundary=False): unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred = trans.shape[-1] ti_transforms = [np.zeros(num_irred)] for i, atom1 in enumerate(unit_atoms): for atom2 in np.arange(natom): is_trim = False if pairs_included is not None and not pairs_included[ifc_map[ atom1, atom2]]: is_trim = True if is_trim_boundary: dist = get_equivalent_smallest_vectors(atom2, atom1, supercell, supercell.get_cell(), symprec=symprec) if len(dist) > 1: is_trim = True if is_trim: irred_doublet = ifc_map[atom1, atom2] ti_transform = np.dot(coeff[atom1, atom2], trans[irred_doublet]) for k in range(9): if not (np.abs(ti_transform[k]) < precision).all(): argmax = np.argmax(np.abs(ti_transform[k])) ti_transform[k] /= ti_transform[k, argmax] is_exist = np.all( np.abs(ti_transform[k] - np.array(ti_transforms)) < precision, axis=1) if (is_exist == False).all(): ti_transforms.append(ti_transform[k] / ti_transform[k, argmax]) print "Number of constraints of fc2 from a cutoff of interaction distance:%d" % len( ti_transforms) CC, transform, independent = gaussian(np.array(ti_transforms), precision) return independent, transform
def _get_rotational_invariance_matrix(self, patom_num): rimat = np.zeros((9, 9 * self._num_atom + 3), dtype='double') rimat[:9, :3] = [[0, 0, 0], [-1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0], [0, -1, 0], [0, 0, -1], [0, 0, 1], [0, 0, 0]] for i in range(self._num_atom): vectors = get_equivalent_smallest_vectors(i, patom_num, self._scell, self._lattice.T, self._symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(self._lattice, r_frac) rimat_each = np.kron( np.eye(3), [[0, r[2], -r[1]], [-r[2], 0, r[0]], [r[1], -r[0], 0]]) rimat[:, (i * 9 + 3):((i + 1) * 9 + 3)] = rimat_each return rimat
def _get_rotational_invariance_matrix(self, patom_num): rimat = np.zeros((9, 9 * self._num_atom + 3), dtype="double") rimat[:9, :3] = [ [0, 0, 0], [-1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 0], [0, -1, 0], [0, 0, -1], [0, 0, 1], [0, 0, 0], ] for i in range(self._num_atom): vectors = get_equivalent_smallest_vectors(i, patom_num, self._scell, self._lattice.T, self._symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(self._lattice, r_frac) rimat_each = np.kron(np.eye(3), [[0, r[2], -r[1]], [-r[2], 0, r[0]], [r[1], -r[0], 0]]) rimat[:, (i * 9 + 3) : ((i + 1) * 9 + 3)] = rimat_each return rimat
def get_fc3_rotational_invariance(fc2, supercell, trans, coeff, ifc_map, symprec=1e-5, precision=1e-6): "Returning the constraint equations to be used in Lagrangian Multipliers" precision *= 1e2 unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred = trans[0].shape[-1] lattice = supercell.get_cell() eijk = np.zeros((3,3,3), dtype="intc") eijk[0,1,2] = eijk[1,2,0] = eijk[2,0,1] = 1 eijk[0,2,1] = eijk[2,1,0] = eijk[1,0,2] = -1 # epsilon matrix, which is an antisymmetric 3 * 3 * 3 tensor torques =[np.zeros(num_irred + 1)] # considering the constant column for i, atom1 in enumerate(unit_atoms): for atom2 in range(natom): torque = np.zeros((27, num_irred), dtype=np.float) t2_1 = np.einsum("cb, cav -> abv", fc2[atom1, atom2], eijk).flatten() t2_2 = np.einsum("ac, cbv -> abv", fc2[atom1, atom2], eijk).flatten() torque_fc2 = t2_1 + t2_2 for atom3 in range(natom): fc_temp = mat_dot_product(coeff[i, atom2, atom3], trans[ifc_map[i, atom2, atom3]], is_sparse=True).reshape(3, 3, 3, -1) vectors = get_equivalent_smallest_vectors(atom3, atom1, supercell, lattice, symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(r_frac, lattice) disp = np.einsum("j, ijk -> ik", r, eijk) t3 = np.einsum("abcN, cv -> abvN", fc_temp, disp).reshape(27, num_irred) torque += t3 torque = np.hstack((torque, -torque_fc2[:, np.newaxis])) #negative sign means Bx = d for k in range(27): if not (np.abs(torque[k])< precision).all(): argmax = np.argmax(np.abs(torque[k])) torque[k] /= torque[k, argmax] is_exist = np.all(np.abs(torque[k] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(torque[k] / torque[k, argmax]) print "Number of constraints of IFC3 from rotational invariance:%d"%(len(torques) - 1) torques = np.array(torques)[1:] return torques[:, :-1], torques[:, -1] # return B and d
def get_triplet_inclusion(self, triplets=None): if self._include_triplet is not None: return self._include_triplet else: lattice = self._cell.get_cell() if triplets is not None: include_triplet = np.ones(len(triplets), dtype=bool) for i, (a1, a2, a3) in enumerate(triplets): d12 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a2, a1, self._cell, lattice, self._symprec)[0], lattice)) d23 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a2, self._cell, lattice, self._symprec)[0], lattice)) d13 = \ np.linalg.norm(np.dot(get_equivalent_smallest_vectors( a3, a1, self._cell, lattice, self._symprec)[0], lattice)) if d12 > self._cut_radius[a1] + self._cut_radius[a2] and\ d23 > self._cut_radius[a2] + self._cut_radius[a3] and\ d13 > self._cut_radius[a1] + self._cut_radius[a3]: include_triplet[i] = False else: num_atom = self._cell.get_number_of_atoms() include_triplet= np.ones((num_atom, num_atom, num_atom), dtype=bool) if self._pair_distances is None: self.set_pair_distances() for i in range(num_atom): for j in range(i): for k in range(j): if (self._pair_distances[i,j] > self._cut_radius[i] + self._cut_radius[j] and self._pair_distances[i,k] > self._cut_radius[i] + self._cut_radius[k] and self._pair_distances[j,k] > self._cut_radius[j] + self._cut_radius[k]): include_triplet[i, j, k] = False include_triplet[i, k, j] = False include_triplet[j, i, k] = False include_triplet[j, k, i] = False include_triplet[k, i, j] = False include_triplet[k, j, i] = False self._include_triplet = include_triplet return include_triplet # # class Cutoff(): # def __init__(self, species, cut_radius, cut_pair, cut_triplets): # self._cell = None # self._pair_distances = None # n = len(species) # if cut_radius is not None: # if len(cut_radius) == 1: # self._cut_radius = [cut_radius[0] for i in range(n)] # elif len(cut_radius) == n: # self._cut_radius = cut_radius # else: # print_error_message("Cutoff radius number %d not equal the number of species %d!" %(len(cut_radius), n)) # else: # self._cut_radius = None # # # cutoff pair # cp = np.ones((n, n), dtype="float") * 10000 # if cut_pair is not None: # if len(cut_pair) == (n +1) * n / 2: # for i in range(n): # for j in range(i,n): # cp[i,j] = cp[j,i] = cut_pair.pop(0) # self._cut_pair = cp # else: # print_error_message("Cutoff pairs %d not equal to the number needed %d!" %(len(cut_pair), (n +1) * n / 2)) # elif self._cut_radius is not None: # for i, j in np.ndindex((n,n)): # cp[i,j] = self._cut_radius[i]+ self._cut_radius[j] # self._cut_pair = cp # else: # self._cut_pair = None # # # cutoff triplet # ct = np.ones((n,n,n), dtype="float") * 10000 # if cut_triplets is not None: # if len(cut_triplets) == n * (n + 1) * (n + 2) / 6: # for i in range(n): # for j in range(i,n): # for k in range(j,n): # ct[i,j,k] = ct[i,k,j] = ct[j,i,k] = ct[j,k,i] =\ # ct[k,i,j] = ct[k,j,i] = cut_triplets.pop(0) # self._cut_triplet = ct # else: # print_error_message("Cutoff triplets %d not equal to the number needed %d!"\ # %(len(cut_triplets), n * (n + 1) * (n + 2) / 6)) # elif self._cut_pair is not None: # for i,j,k in np.ndindex((n,n,n)): # ct[i,j,k] = min(self._cut_pair[i,j], self._cut_pair[i,k], self._cut_pair[j,k]) # self._cut_triplet = ct # else: # self._cut_triplet = None # # def set_cell(self, cell, symprec = 1e-5): # self._cell = cell # self._symprec = symprec # self._pair_distances = None # # def get_cutoff_pair(self): # return self._cut_pair # # def get_cutoff_radius(self): # return self._cut_radius # # def get_cutoff_triplet(self): # return self._cut_triplet # # def expand_pair(self): # unique_atoms, index_unique = np.unique(self._cell.get_atomic_numbers(), return_index=True) # unique_atoms = unique_atoms[np.argsort(index_unique)] # in order to keep the specie sequence unchanged # if self.get_cutoff_pair() is not None: # cutpair_expand = np.zeros((self._cell.get_number_of_atoms(), self._cell.get_number_of_atoms()), dtype="double") # for i in range(self._cell.get_number_of_atoms()): # index_specie_i = np.where(unique_atoms == self._cell.get_atomic_numbers()[i])[0] # for j in range(i, self._cell.get_number_of_atoms()): # index_specie_j = np.where(unique_atoms == self._cell.get_atomic_numbers()[j])[0] # cutpair_expand[i,j] = cutpair_expand[j,i] = self._cut_pair[index_specie_i, index_specie_j] # else: # cutpair_expand = None # return cutpair_expand # # def expand_triplet(self): # natom = self._cell.get_number_of_atoms() # unique_atoms, index_unique = np.unique(self._cell.get_atomic_numbers(), return_index=True) # unique_atoms = unique_atoms[np.argsort(index_unique)] # in order to keep the specie sequence unchanged # if self.get_cutoff_triplet() is not None: # cut_triplet_expand = np.zeros((natom, natom, natom), dtype="double") # for i in range(natom): # index_specie_i = np.where(unique_atoms == self._cell.get_atomic_numbers()[i])[0] # for j in range(i, natom): # index_specie_j = np.where(unique_atoms == self._cell.get_atomic_numbers()[j])[0] # for k in range(j, natom): # index_specie_k = np.where(unique_atoms == self._cell.get_atomic_numbers()[k])[0] # cut_temp = self._cut_triplet[index_specie_i, index_specie_j, index_specie_k] # cut_triplet_expand[i,j,k] = cut_temp # cut_triplet_expand[j,i,k] = cut_temp # cut_triplet_expand[i,k,j] = cut_temp # cut_triplet_expand[j,k,i] = cut_temp # cut_triplet_expand[k,i,j] = cut_temp # cut_triplet_expand[k,j,i] = cut_temp # # cut_triplet_expand[i,j, k] = self._cut_triplet[index_specie_i, index_specie_j, index_specie_k] # else: # cut_triplet_expand = None # return cut_triplet_expand # # def set_pair_distances(self): # num_atom = self._cell.get_number_of_atoms() # lattice = self._cell.get_cell() # # try: # from scipy.spatial.distance import cdist # positions = self._cell.get_scaled_positions() # positions_cart = self._cell.get_positions() # distances = [] # for i in (-1, 0, 1): # for j in (-1, 0, 1): # for k in (-1, 0, 1): # positions_trans = positions + np.array([i,j,k]) # positions_trans_cart = np.dot(positions_trans, lattice) # distances.append(cdist(positions_cart, positions_trans_cart)) # min_distances = np.min(np.array(distances), axis=0) # except ImportError: # min_distances = np.zeros((num_atom, num_atom), dtype='double') # for i in range(num_atom): # run in cell # for j in range(num_atom): # run in primitive # min_distances[i, j] = np.linalg.norm(np.dot( # get_equivalent_smallest_vectors( # i, j, self._cell, lattice, self._symprec)[0], lattice)) # self._pair_distances = min_distances # # def get_pair_inclusion(self): # num_atom = self._cell.get_number_of_atoms() # cut_pair = self.expand_pair() # include_pair= np.ones((num_atom, num_atom), dtype=bool) # if self._pair_distances == None: # self.set_pair_distances() # for i, j in np.ndindex(num_atom, num_atom): # if cut_pair is not None: # max_dist = max(self._pair_distances[i,j], self._pair_distances[j,i]) # if max_dist > cut_pair[i,j]: # include_pair[i,j] = False # return include_pair # # def get_triplet_inclusion(self): # num_atom = self._cell.get_number_of_atoms() # cut_triplet = self.expand_triplet() # include_triplet= np.ones((num_atom, num_atom, num_atom), dtype=bool) # if self._pair_distances == None: # self.set_pair_distances() # if cut_triplet is not None: # for i, j, k in np.ndindex(num_atom, num_atom, num_atom): # max_dist = max(self._pair_distances[i,j], self._pair_distances[j,k],self._pair_distances[i,k]) # if max_dist > cut_triplet[i, j, k]: # include_triplet[i,j, k] = False # return include_triplet
def get_third_order_displacements(cell, symmetry, is_plusminus='auto', is_diagonal=False): # Atoms 1, 2, and 3 are defined as follows: # # Atom 1: The first displaced atom. Third order force constant # between Atoms 1, 2, and 3 is calculated. # Atom 2: The second displaced atom. Second order force constant # between Atoms 2 and 3 is calculated. # Atom 3: Force is mesuared on this atom. positions = cell.get_scaled_positions() lattice = cell.get_cell() # Least displacements for third order force constants in yaml file # # Data structure # [{'number': atom1, # 'displacement': [0.00000, 0.007071, 0.007071], # 'second_atoms': [ {'number': atom2, # 'displacements': [[0.007071, 0.000000, 0.007071], # [-0.007071, 0.000000, -0.007071] # ,...]}, # {'number': ... } ] }, # {'number': atom1, ... } ] # Least displacements of first atoms (Atom 1) are searched by # using respective site symmetries of the original crystal. disps_first = get_least_displacements(symmetry, is_plusminus=is_plusminus, is_diagonal=False) symprec = symmetry.get_symmetry_tolerance() dds = [] for disp in disps_first: atom1 = disp[0] disp1 = disp[1:4] site_sym = symmetry.get_site_symmetry(atom1) dds_atom1 = {'number': atom1, 'direction': disp1, 'second_atoms': []} # Reduced site symmetry at the first atom with respect to # the displacement of the first atoms. reduced_site_sym = get_reduced_site_symmetry(site_sym, disp1, symprec) # Searching orbits (second atoms) with respect to # the first atom and its reduced site symmetry. second_atoms = get_least_orbits(atom1, cell, reduced_site_sym, symprec) for atom2 in second_atoms: dds_atom2 = get_next_displacements(atom1, atom2, reduced_site_sym, positions, symprec, is_diagonal) min_distance = np.linalg.norm( np.dot(get_equivalent_smallest_vectors( atom1, atom2, cell, lattice, symprec)[0], lattice)) dds_atom2['distance'] = min_distance dds_atom1['second_atoms'].append(dds_atom2) dds.append(dds_atom1) return dds
def get_fc2_rotational_invariance(supercell, trans, coeff, ifc_map, symprec, precision=1e-8, is_Huang=True): #Gazis-Wallis invariance positions = supercell.get_scaled_positions() unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred2 = trans.shape[-1] lattice = supercell.get_cell() eijk = np.zeros((3,3,3), dtype="intc") eijk[0,1,2] = eijk[1,2,0] = eijk[2,0,1] = 1 eijk[0,2,1] = eijk[2,1,0] = eijk[1,0,2] = -1 # epsion matrix, which is an antisymmetric 3 * 3 * 3 tensor torques =[np.zeros(num_irred2)] for i, patom_num in enumerate(unit_atoms): force = np.zeros((27, num_irred2), dtype='double') for j in range(natom): fc_temp = np.dot(coeff[patom_num, j], trans[ifc_map[patom_num, j]]).reshape(3,3,-1) vectors = get_equivalent_smallest_vectors(j, patom_num, supercell, lattice, symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(r_frac, lattice) force_tmp = np.einsum('abN, c->abcN', fc_temp, r) - np.einsum('acN, b->abcN', fc_temp, r) force += force_tmp.reshape(27, -1) for k in range(27): if not (np.abs(force[k])< precision).all(): argmax = np.argmax(np.abs(force[k])) force[k] /= force[k, argmax] is_exist = np.all(np.abs(force[k] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(force[k] / force[k, argmax]) print "Number of constraints of fc2 from rotational invariance:%d"%(len(torques)-1) CC, transform, independent = gaussian(np.array(torques), precision) if is_Huang: precision *= 1e2 print "The Born-Huang invariance condition is also included in rotational symmetry" trans2 = mat_dot_product(trans, transform, is_sparse=True) num_irred2 = trans2.shape[-1] torques =[np.zeros(num_irred2)] unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) torque_tmp = np.zeros((9, 9, num_irred2), dtype='double') for patom_num in unit_atoms: for j in range(natom): fc_temp = np.dot(coeff[patom_num, j], trans2[ifc_map[patom_num, j]]) vectors12 = get_equivalent_smallest_vectors(j, patom_num, supercell, lattice, symprec) for r12 in vectors12: v12 = np.dot(r12, lattice) v12_outer = np.kron(v12, v12) torque_tmp += np.einsum('iN, j->ijN', fc_temp, v12_outer) / len(vectors12) torque_tmp = torque_tmp - np.swapaxes(torque_tmp, 0, 1) torque_tmp = torque_tmp.reshape(-1, num_irred2) for i in range(81): if not (np.abs(torque_tmp[i])< precision).all(): argmax = np.argmax(np.abs(torque_tmp[i])) torque_tmp[i] /= torque_tmp[i, argmax] is_exist = np.all(np.abs(torque_tmp[i] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(torque_tmp[i] / torque_tmp[i, argmax]) print "Number of constraints of fc2 from Born-Huang invariance condition:%d"%(len(torques)-1) CC, transform_gw, independent_gw = gaussian(np.array(torques), precision) independent = np.array([independent[i] for i in independent_gw]) transform = np.dot(transform,transform_gw) return independent, transform
def get_fc2_rotational_invariance(supercell, trans, coeff, ifc_map, symprec, precision=1e-8, is_Huang=True): #Gazis-Wallis invariance positions = supercell.get_scaled_positions() unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) natom = supercell.get_number_of_atoms() num_irred2 = trans.shape[-1] lattice = supercell.get_cell() eijk = np.zeros((3, 3, 3), dtype="intc") eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1 eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[ 1, 0, 2] = -1 # epsion matrix, which is an antisymmetric 3 * 3 * 3 tensor torques = [np.zeros(num_irred2)] for i, patom_num in enumerate(unit_atoms): force = np.zeros((27, num_irred2), dtype='double') for j in range(natom): fc_temp = np.dot(coeff[patom_num, j], trans[ifc_map[patom_num, j]]).reshape(3, 3, -1) vectors = get_equivalent_smallest_vectors(j, patom_num, supercell, lattice, symprec) r_frac = np.array(vectors).sum(axis=0) / len(vectors) r = np.dot(r_frac, lattice) force_tmp = np.einsum('abN, c->abcN', fc_temp, r) - np.einsum( 'acN, b->abcN', fc_temp, r) force += force_tmp.reshape(27, -1) for k in range(27): if not (np.abs(force[k]) < precision).all(): argmax = np.argmax(np.abs(force[k])) force[k] /= force[k, argmax] is_exist = np.all( np.abs(force[k] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(force[k] / force[k, argmax]) print "Number of constraints of fc2 from rotational invariance:%d" % ( len(torques) - 1) CC, transform, independent = gaussian(np.array(torques), precision) if is_Huang: precision *= 1e2 print "The Born-Huang invariance condition is also included in rotational symmetry" trans2 = mat_dot_product(trans, transform, is_sparse=True) num_irred2 = trans2.shape[-1] torques = [np.zeros(num_irred2)] unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) torque_tmp = np.zeros((9, 9, num_irred2), dtype='double') for patom_num in unit_atoms: for j in range(natom): fc_temp = np.dot(coeff[patom_num, j], trans2[ifc_map[patom_num, j]]) vectors12 = get_equivalent_smallest_vectors( j, patom_num, supercell, lattice, symprec) for r12 in vectors12: v12 = np.dot(r12, lattice) v12_outer = np.kron(v12, v12) torque_tmp += np.einsum('iN, j->ijN', fc_temp, v12_outer) / len(vectors12) torque_tmp = torque_tmp - np.swapaxes(torque_tmp, 0, 1) torque_tmp = torque_tmp.reshape(-1, num_irred2) for i in range(81): if not (np.abs(torque_tmp[i]) < precision).all(): argmax = np.argmax(np.abs(torque_tmp[i])) torque_tmp[i] /= torque_tmp[i, argmax] is_exist = np.all( np.abs(torque_tmp[i] - np.array(torques)) < precision, axis=1) if (is_exist == False).all(): torques.append(torque_tmp[i] / torque_tmp[i, argmax]) print "Number of constraints of fc2 from Born-Huang invariance condition:%d" % ( len(torques) - 1) CC, transform_gw, independent_gw = gaussian(np.array(torques), precision) independent = np.array([independent[i] for i in independent_gw]) transform = np.dot(transform, transform_gw) return independent, transform
def show_rotational_invariance(force_constants, supercell, symprec=1e-5, log_level=1): """ *** Under development *** Just show how force constant is close to the condition of rotational invariance, """ print "Check rotational invariance ..." fc = force_constants unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) volume_unitcell = supercell.get_volume() * len( unit_atoms) / supercell.get_number_of_atoms() abc = "xyz" eijk = np.zeros((3, 3, 3), dtype="intc") eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1 eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[ 1, 0, 2] = -1 # epsilon matrix, which is an antisymmetric 3 * 3 * 3 tensor stress = np.zeros((3, 3), dtype='double') for pi, p in enumerate(unit_atoms): for i in range(3): mat = np.zeros((3, 3), dtype='double') for s in range(supercell.get_number_of_atoms()): vecs = np.array( get_equivalent_smallest_vectors(s, p, supercell, supercell.get_cell(), symprec)) m = len(vecs) v = np.dot(vecs[:, :].sum(axis=0) / m, supercell.get_cell()) for j in range(3): for k in range(3): mat[j, k] += (fc[p, s, i, j] * v[k] - fc[p, s, i, k] * v[j]) stress += np.abs(mat) if log_level == 2: print "Atom %d %s" % (p + 1, abc[i]) for vec in mat: print "%10.5f %10.5f %10.5f" % tuple(vec) if log_level == 1: print "System stress residue enduced by rigid-body rotations(eV/A)" for vec in stress: print "%10.5f %10.5f %10.5f" % tuple(vec) ElasticConstants = np.zeros((3, 3, 3, 3), dtype='double') for s1 in unit_atoms: for s2 in range(supercell.get_number_of_atoms()): vec12s = np.array( get_equivalent_smallest_vectors(s2, s1, supercell, supercell.get_cell(), symprec)) vec12s = np.dot(vec12s, supercell.get_cell()) for v12 in vec12s: ElasticConstants += -np.einsum('ij, k, l->ijkl', fc[s1, s2], v12, v12) / len(vec12s) / 2. ElasticConstants = ElasticConstants.reshape(9, 9) non_sym_tensor = ElasticConstants - ElasticConstants.T if log_level == 2: print 'Born-Huang rotational invariance condition (eV)' for i in range(9): print "%10.5f " * 9 % tuple(non_sym_tensor[i]) elif log_level == 1: M_sum = np.abs(non_sym_tensor).sum() print 'Born-Huang rotational invariance condition (eV): %10.5f' % M_sum
def get_trim_fc3(supercell, trans, triplets_reduced, symprec, precision=1e-5, triplets_included=None, is_trim_boundary=False): num_irred = trans[0].shape[-1] unit_atoms = np.unique(supercell.get_supercell_to_unitcell_map()) zero_fc3s = [np.zeros(num_irred, dtype='double')] for i, (atom1, atom2, atom3) in enumerate(triplets_reduced): first = np.where(atom1 == unit_atoms)[0][0] is_trim = False if triplets_included is not None and not triplets_included[i]: is_trim = True if is_trim_boundary: dist12 = get_equivalent_smallest_vectors(atom2, atom1, supercell, supercell.get_cell(), symprec=symprec) dist23 = get_equivalent_smallest_vectors(atom3, atom2, supercell, supercell.get_cell(), symprec=symprec) dist13 = get_equivalent_smallest_vectors(atom3, atom1, supercell, supercell.get_cell(), symprec=symprec) if len(dist12) > 1 or len(dist23) > 1 or len(dist13) > 1: is_trim = True if is_trim: import scipy.sparse if scipy.sparse.issparse(trans[i]): zero_fc3 = trans[i].toarray() else: zero_fc3 = trans[i] for k in range(27): if not (np.abs(zero_fc3[k]) < precision).all(): argmax = np.argmax(np.abs(zero_fc3[k])) zero_fc3[k] /= zero_fc3[k, argmax] is_exist = np.all( np.abs(zero_fc3[k] - np.array(zero_fc3s)) < precision, axis=1) if (is_exist == False).all(): zero_fc3s.append(zero_fc3[k] / zero_fc3[k, argmax]) print "Number of constraints of fc3 from a cutoff of interaction distance:%d" % ( len(zero_fc3s) - 1) try: import _mdfc transform = np.zeros((num_irred, num_irred), dtype='double') independent = np.zeros(num_irred, dtype='intc') num_independent = _mdfc.gaussian(transform, np.array(zero_fc3s, dtype='double'), independent, precision) transform = transform[:, :num_independent] independent = independent[:num_independent] except ImportError: CC, transform, independent = gaussian_py(np.array(zero_fc3s, dtype='double'), prec=precision) return independent, transform
def get_third_order_displacements(cell, symmetry, is_plusminus='auto', is_diagonal=False): # Atoms 1, 2, and 3 are defined as follows: # # Atom 1: The first displaced atom. Third order force constant # between Atoms 1, 2, and 3 is calculated. # Atom 2: The second displaced atom. Second order force constant # between Atoms 2 and 3 is calculated. # Atom 3: Force is mesuared on this atom. positions = cell.get_scaled_positions() lattice = cell.get_cell() # Least displacements for third order force constants in yaml file # # Data structure # [{'number': atom1, # 'displacement': [0.00000, 0.007071, 0.007071], # 'second_atoms': [ {'number': atom2, # 'displacements': [[0.007071, 0.000000, 0.007071], # [-0.007071, 0.000000, -0.007071] # ,...]}, # {'number': ... } ] }, # {'number': atom1, ... } ] # Least displacements of first atoms (Atom 1) are searched by # using respective site symmetries of the original crystal. disps_first = get_least_displacements(symmetry, is_plusminus=is_plusminus, is_diagonal=False) symprec = symmetry.get_symmetry_tolerance() dds = [] for disp in disps_first: atom1 = disp[0] disp1 = disp[1:4] site_sym = symmetry.get_site_symmetry(atom1) dds_atom1 = {'number': atom1, 'direction': disp1, 'second_atoms': []} # Reduced site symmetry at the first atom with respect to # the displacement of the first atoms. reduced_site_sym = get_reduced_site_symmetry(site_sym, disp1, symprec) # Searching orbits (second atoms) with respect to # the first atom and its reduced site symmetry. second_atoms = get_least_orbits(atom1, cell, reduced_site_sym, symprec) for atom2 in second_atoms: dds_atom2 = get_next_displacements(atom1, atom2, reduced_site_sym, positions, symprec, is_diagonal) min_distance = np.linalg.norm( np.dot(get_equivalent_smallest_vectors( atom1, atom2, cell, lattice, symprec)[0], lattice)) dds_atom2['distance'] = min_distance dds_atom1['second_atoms'].append(dds_atom2) dds.append(dds_atom1) return dds