def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell)
def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset)
def get_fourth_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. Fourth order force constant # between Atoms 1, 2, 3, and 4 is calculated. # Atom 2: The second displaced atom. Third order force constant # between Atoms 2, 3, and 4 is calculated. # Atom 3: The third displaced atom. Second order force constant # between Atoms 3 and 4 is calculated. # Atom 4: Force is mesuared on this atom. # Least displacements for third order force constants # # Data structure # [{'number': atom1, # 'displacement': [0.00000, 0.007071, 0.007071], # 'second_atoms': [ {'number': atom2, # 'displacement': [0.007071, 0.000000, 0.007071], # 'third_atoms': [ {'number': atom3, # 'displacements': # [[-0.007071, 0.000000, -0.007071], # ,...]}, {...}, ... ]}, # 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_sym = get_reduced_site_symmetry(site_sym, disp1, symprec) second_atoms = get_least_orbits(atom1, cell, reduced_site_sym, symprec) for atom2 in second_atoms: reduced_bond_sym = get_bond_symmetry(reduced_site_sym, cell.get_scaled_positions(), atom1, atom2, symprec) for disp2 in _get_displacements_second(reduced_bond_sym, symprec, is_diagonal): dds_atom2 = _get_second_displacements(atom2, disp2, cell, reduced_bond_sym, symprec, is_diagonal) dds_atom1['second_atoms'].append(dds_atom2) dds.append(dds_atom1) write_supercells_with_three_displacements(cell, dds) return dds
def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: # 'is_diagonal=False' below is made intentionally. For # third-order force constants, we need better accuracy, # and I expect this choice is better for it, but not very # sure. # In phono3py, two atoms are displaced for each # configuration and the displacements are chosen, first # displacement from the perfect supercell, then second # displacement, considering symmetry. If I choose # is_diagonal=False for the first displacement, the # symmetry is less broken and the number of second # displacements can be smaller than in the case of # is_diagonal=True for the first displacement. This is # done in the call get_least_displacements() in # phonon3.displacement_fc3.get_third_order_displacements(). # # The call get_least_displacements() is only for the # second order force constants, but 'is_diagonal=False' to # be consistent with the above function call, and also for # the accuracy when calculating ph-ph interaction # strength because displacement directions are better to be # close to perpendicular each other to fit force constants. # # On the discussion of the accuracy, these are just my # expectation when I designed phono3py in the early time, # and in fact now I guess not very different. If these are # little different, then I should not surprise users to # change the default behaviour. At this moment, this is # open question and we will have more advance and should # have better specificy external software on this. phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = directions_to_displacement_dataset( phonon_displacement_directions, distance, self._phonon_supercell)
def get_fourth_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. Fourth order force constant # between Atoms 1, 2, 3, and 4 is calculated. # Atom 2: The second displaced atom. Third order force constant # between Atoms 2, 3, and 4 is calculated. # Atom 3: The third displaced atom. Second order force constant # between Atoms 3 and 4 is calculated. # Atom 4: Force is mesuared on this atom. # Least displacements for third order force constants # # Data structure # [{'number': atom1, # 'displacement': [0.00000, 0.007071, 0.007071], # 'second_atoms': [ {'number': atom2, # 'displacement': [0.007071, 0.000000, 0.007071], # 'third_atoms': [ {'number': atom3, # 'displacements': # [[-0.007071, 0.000000, -0.007071], # ,...]}, {...}, ... ]}, # 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_sym = get_reduced_site_symmetry(site_sym, disp1, symprec) second_atoms = get_least_orbits(atom1, cell, reduced_site_sym, symprec) for atom2 in second_atoms: reduced_bond_sym = get_bond_symmetry(reduced_site_sym, cell.get_scaled_positions(), atom1, atom2, symprec) for disp2 in _get_displacements_second(reduced_bond_sym, symprec, is_diagonal): dds_atom2 = _get_second_displacements(atom2, disp2, cell, reduced_bond_sym, symprec, is_diagonal) dds_atom1["second_atoms"].append(dds_atom2) dds.append(dds_atom1) write_supercells_with_three_displacements(cell, dds) return dds
def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacements: List of displacements in Cartesian coordinates. See 'set_displacements' displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ lattice = self._supercell.get_cell() self._displacements = [] self._displacement_directions = \ get_least_displacements(self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) for disp in self._displacement_directions: atom_num = disp[0] disp_cartesian = np.dot(disp[1:], lattice) disp_cartesian *= distance / np.linalg.norm(disp_cartesian) self._displacements.append([ atom_num, disp_cartesian[0], disp_cartesian[1], disp_cartesian[2] ]) self._set_supercells_with_displacements()
def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacements: List of displacements in Cartesian coordinates. See 'set_displacements' displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ lattice = self._supercell.get_cell() self._displacements = [] self._displacement_directions = \ get_least_displacements(self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) for disp in self._displacement_directions: atom_num = disp[0] disp_cartesian = np.dot(disp[1:], lattice) disp_cartesian *= distance / np.linalg.norm(disp_cartesian) self._displacements.append([atom_num, disp_cartesian[0], disp_cartesian[1], disp_cartesian[2]]) self._set_supercells_with_displacements()
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_third_order_displacements(cell, symmetry, is_plusminus='auto', is_diagonal=False): """Create dispalcement dataset Note ---- 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. Parameters ---------- cell : PhonopyAtoms Supercell symmetry : Symmetry Symmetry of supercell is_plusminus : str or bool, optional Type of displacements, plus only (False), always plus and minus (True), and plus and minus depending on site symmetry ('auto'). is_diagonal : bool, optional Whether allow diagonal displacements of Atom 2 or not Returns ------- dict Data structure is like: {'natom': 64, 'cutoff_distance': 4.000000, 'first_atoms': [{'number': atom1, 'displacement': [0.03, 0., 0.], 'second_atoms': [ {'number': atom2, 'displacement': [0., -0.03, 0.], 'distance': 2.353}, {'number': ... }, ... ] }, {'number': atom1, ... } ]} """ positions = cell.get_scaled_positions() lattice = cell.get_cell().T # Least displacements of first atoms (Atom 1) are searched by # using respective site symmetries of the original crystal. # 'is_diagonal=False' below is made intentionally to expect # better accuracy. 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, lattice, positions, symprec, is_diagonal) min_vec = get_equivalent_smallest_vectors(atom1, atom2, cell, symprec)[0] min_distance = np.linalg.norm(np.dot(lattice, min_vec)) dds_atom2['distance'] = min_distance dds_atom1['second_atoms'].append(dds_atom2) dds.append(dds_atom1) return dds
def get_third_order_displacements(cell: PhonopyAtoms, symmetry: Symmetry, is_plusminus="auto", is_diagonal=False): """Create dispalcement dataset. Note ---- 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. Parameters ---------- cell : PhonopyAtoms Supercell symmetry : Symmetry Symmetry of supercell is_plusminus : str or bool, optional Type of displacements, plus only (False), always plus and minus (True), and plus and minus depending on site symmetry ('auto'). is_diagonal : bool, optional Whether allow diagonal displacements of Atom 2 or not Returns ------- [{'number': atom1, 'direction': [1, 0, 0], # int 'second_atoms': [ {'number': atom2, 'directions': [ [1, 0, 0], [-1, 0, 0], ... ] 'distance': distance-between-atom1-and-atom2}, {'number': ... }, ... ] }, {'number': atom1, ... } ] """ positions = cell.scaled_positions lattice = cell.cell.T # Least displacements of first atoms (Atom 1) are searched by # using respective site symmetries of the original crystal. # 'is_diagonal=False' below is made intentionally to expect # better accuracy. disps_first = get_least_displacements(symmetry, is_plusminus=is_plusminus, is_diagonal=False) symprec = 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, lattice, positions, symprec, is_diagonal) min_vec = get_smallest_vector_of_atom_pair(atom1, atom2, cell, symprec) min_distance = np.linalg.norm(np.dot(lattice, min_vec)) dds_atom2["distance"] = min_distance dds_atom1["second_atoms"].append(dds_atom2) dds.append(dds_atom1) return dds