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 _get_second_displacements(atom2, disp2, cell, reduced_bond_sym, symprec, is_diagonal): positions = cell.get_scaled_positions() dds_atom2 = {'number': atom2, 'direction': disp2, 'third_atoms': []} reduced_bond_sym2 = get_reduced_site_symmetry(reduced_bond_sym, disp2, symprec) third_atoms = get_least_orbits(atom2, cell, reduced_bond_sym2, symprec) for atom3 in third_atoms: reduced_plane_sym = get_bond_symmetry( reduced_bond_sym2, cell.get_scaled_positions(), atom2, atom3, symprec) dds_atom3 = get_next_displacements(atom2, atom3, reduced_plane_sym, positions, symprec, is_diagonal) dds_atom2['third_atoms'].append(dds_atom3) return dds_atom2
def _get_reduced_bond_sym(self, reduced_site_sym, first_atom_num, second_atom_num, disp2): bond_sym = get_bond_symmetry(reduced_site_sym, self._positions, first_atom_num, second_atom_num, self._symprec) direction = np.dot(np.linalg.inv(self._lattice), disp2) reduced_bond_sym = get_reduced_site_symmetry(bond_sym, direction, self._symprec) return reduced_bond_sym
def get_constrained_fc2( supercell, dataset_second_atoms, atom1, reduced_site_sym, translational_symmetry_type, is_permutation_symmetry, symprec, ): """ dataset_second_atoms: [{'number': 7, 'displacement': [], 'delta_forces': []}, ...] """ num_atom = supercell.get_number_of_atoms() fc2 = np.zeros((num_atom, num_atom, 3, 3), dtype="double") atom_list = np.unique([x["number"] for x in dataset_second_atoms]) for atom2 in atom_list: disps2 = [] sets_of_forces = [] for disps_second in dataset_second_atoms: if atom2 != disps_second["number"]: continue bond_sym = get_bond_symmetry(reduced_site_sym, supercell.get_scaled_positions(), atom1, atom2, symprec) disps2.append(disps_second["displacement"]) sets_of_forces.append(disps_second["delta_forces"]) solve_force_constants(fc2, atom2, disps2, sets_of_forces, supercell, bond_sym, symprec) # Shift positions according to set atom1 is at origin lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() pos_center = positions[atom1].copy() positions -= pos_center distribute_force_constants( fc2, range(num_atom), atom_list, lattice, positions, np.array(reduced_site_sym, dtype="intc", order="C"), np.zeros((len(reduced_site_sym), 3), dtype="double"), symprec, ) if translational_symmetry_type: set_translational_invariance(fc2, translational_symmetry_type=translational_symmetry_type) if is_permutation_symmetry: set_permutation_symmetry(fc2) return fc2
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 _get_reduced_bond_sym(self, reduced_site_sym, first_atom_num, second_atom_num, disp2): bond_sym = get_bond_symmetry( reduced_site_sym, self._positions, first_atom_num, second_atom_num, self._symprec) direction = np.dot(np.linalg.inv(self._lattice), disp2) reduced_bond_sym = get_reduced_site_symmetry( bond_sym, direction, self._symprec) return reduced_bond_sym
def get_constrained_fc2(supercell, dataset_second_atoms, atom1, reduced_site_sym, translational_symmetry_type, is_permutation_symmetry, symprec): """ dataset_second_atoms: [{'number': 7, 'displacement': [], 'delta_forces': []}, ...] """ num_atom = supercell.get_number_of_atoms() fc2 = np.zeros((num_atom, num_atom, 3, 3), dtype='double') atom_list = np.unique([x['number'] for x in dataset_second_atoms]) atom_list_done = [] for atom2 in atom_list: disps2 = [] sets_of_forces = [] for disps_second in dataset_second_atoms: if atom2 != disps_second['number']: continue atom_list_done.append(atom2) bond_sym = get_bond_symmetry(reduced_site_sym, supercell.get_scaled_positions(), atom1, atom2, symprec) disps2.append(disps_second['displacement']) sets_of_forces.append(disps_second['delta_forces']) solve_force_constants(fc2, atom2, disps2, sets_of_forces, supercell, bond_sym, symprec) # Shift positions according to set atom1 is at origin lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() pos_center = positions[atom1].copy() positions -= pos_center distribute_force_constants( fc2, range(num_atom), atom_list_done, lattice, positions, np.array(reduced_site_sym, dtype='intc', order='C'), np.zeros((len(reduced_site_sym), 3), dtype='double'), symprec) if translational_symmetry_type: set_translational_invariance( fc2, translational_symmetry_type=translational_symmetry_type) if is_permutation_symmetry: set_permutation_symmetry(fc2) return fc2
def _get_second_displacements(atom2, disp2, cell, reduced_bond_sym, symprec, is_diagonal): positions = cell.get_scaled_positions() dds_atom2 = {'number': atom2, 'direction': disp2, 'third_atoms': []} reduced_bond_sym2 = get_reduced_site_symmetry(reduced_bond_sym, disp2, symprec) third_atoms = get_least_orbits(atom2, cell, reduced_bond_sym2, symprec) for atom3 in third_atoms: reduced_plane_sym = get_bond_symmetry(reduced_bond_sym2, cell.get_scaled_positions(), atom2, atom3, symprec) dds_atom3 = get_next_displacements(atom2, atom3, reduced_plane_sym, positions, symprec, is_diagonal) dds_atom2['third_atoms'].append(dds_atom3) return dds_atom2
def _get_constrained_fc3(supercell, displacements, reduced_site_sym, translational_symmetry_type, is_permutation_symmetry, symprec, verbose): """ Two displacements and force constants calculation (e.g. DFPT) displacements = {'number': 3, 'displacement': [0.01, 0.00, 0.01] 'second_atoms': [{'number': 7, 'displacement': [], 'delta_fc2': }]} Three displacements and force calculation displacements = {'number': 3, 'displacement': [0.01, 0.00, 0.01] 'second_atoms': [{'number': 7, 'displacement': [], 'third_atoms': ... }]} third_atoms is like: 'third_atoms': [{'number': 56, 'displacement': [], 'delta_forces': ... }]} """ num_atom = supercell.get_number_of_atoms() atom1 = displacements['number'] disp1 = displacements['displacement'] delta_fc3 = np.zeros((num_atom, num_atom, num_atom, 3, 3, 3), dtype='double') if 'delta_forces' in displacements['second_atoms'][0]: fc2_with_one_disp = get_constrained_fc2( supercell, displacements['second_atoms'], atom1, reduced_site_sym, translational_symmetry_type, is_permutation_symmetry, symprec) atom_list = np.unique([x['number'] for x in displacements['second_atoms']]) for atom2 in atom_list: bond_sym = get_bond_symmetry(reduced_site_sym, supercell.get_scaled_positions(), atom1, atom2, symprec) disps2 = [] delta_fc2s = [] for disps_second in displacements['second_atoms']: if atom2 != disps_second['number']: continue disps2.append(disps_second['displacement']) if 'delta_fc2' in disps_second: delta_fc2s.append(disps_second['delta_fc2']) else: direction = np.dot(disps_second['displacement'], np.linalg.inv(supercell.get_cell())) reduced_bond_sym = get_reduced_site_symmetry( bond_sym, direction, symprec) delta_fc2s.append( get_delta_fc2(disps_second['third_atoms'], atom2, fc2_with_one_disp, supercell, reduced_bond_sym, translational_symmetry_type, is_permutation_symmetry, symprec)) if verbose > 1: print("Second displacements for fc4[ %d, %d, x, x ]" % (atom1 + 1, atom2 + 1)) for i, v in enumerate(disps2): print " [%7.4f %7.4f %7.4f]" % tuple(v) solve_fc3(delta_fc3, atom2, supercell, bond_sym, disps2, delta_fc2s, symprec=symprec, verbose=False) # Shift positions according to set atom1 is at origin lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() pos_center = positions[atom1].copy() positions -= pos_center if verbose: print "Expanding delta fc3" distribute_fc3(delta_fc3, atom_list, lattice, positions, np.array(reduced_site_sym, dtype='intc', order='C'), np.zeros((len(reduced_site_sym), 3), dtype='double'), symprec, verbose=verbose) if translational_symmetry_type > 0: set_translational_invariance_fc3_per_index(delta_fc3) if is_permutation_symmetry: set_permutation_symmetry_fc3(delta_fc3) return delta_fc3
def _get_constrained_fc3(supercell, displacements, reduced_site_sym, is_translational_symmetry, is_permutation_symmetry, symprec, verbose): """ Two displacements and force constants calculation (e.g. DFPT) displacements = {'number': 3, 'displacement': [0.01, 0.00, 0.01] 'second_atoms': [{'number': 7, 'displacement': [], 'delta_fc2': }]} Three displacements and force calculation displacements = {'number': 3, 'displacement': [0.01, 0.00, 0.01] 'second_atoms': [{'number': 7, 'displacement': [], 'third_atoms': ... }]} third_atoms is like: 'third_atoms': [{'number': 56, 'displacement': [], 'delta_forces': ... }]} """ num_atom = supercell.get_number_of_atoms() atom1 = displacements['number'] disp1 = displacements['displacement'] fc3 = np.zeros((num_atom, num_atom, num_atom, 3, 3, 3), dtype='double') atom_list_done = [] if 'delta_forces' in displacements['second_atoms'][0]: fc2_with_one_disp = get_constrained_fc2(supercell, displacements['second_atoms'], atom1, reduced_site_sym, is_translational_symmetry, is_permutation_symmetry, symprec) atom_list = np.unique([x['number'] for x in displacements['second_atoms']]) for atom2 in atom_list: disps2 = [] delta_fc2s = [] for disps_second in displacements['second_atoms']: if atom2 != disps_second['number']: continue atom_list_done.append(atom2) bond_sym = get_bond_symmetry( reduced_site_sym, supercell.get_scaled_positions(), atom1, atom2, symprec) disps2.append(disps_second['displacement']) if 'delta_fc2' in disps_second: delta_fc2s.append(disps_second['delta_fc2']) else: direction = np.dot(disps_second['displacement'], np.linalg.inv(supercell.get_cell())) reduced_bond_sym = get_reduced_site_symmetry( bond_sym, direction, symprec) delta_fc2s.append(get_delta_fc2( disps_second['third_atoms'], atom2, fc2_with_one_disp, supercell, reduced_bond_sym, is_translational_symmetry, is_permutation_symmetry, symprec)) if verbose > 1: print ("Second displacements for fc4[ %d, %d, x, x ]" % (atom1 + 1, atom2 + 1)) for i, v in enumerate(disps2): print " [%7.4f %7.4f %7.4f]" % tuple(v) solve_fc3(fc3, atom2, supercell, bond_sym, disps2, delta_fc2s, symprec=symprec) # Shift positions according to set atom1 is at origin lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() pos_center = positions[atom1].copy() positions -= pos_center if verbose: print "(Copying delta fc3...)" distribute_fc3(fc3, atom_list_done, lattice, positions, np.intc(reduced_site_sym).copy(), np.zeros((len(reduced_site_sym), 3), dtype='double'), symprec, verbose) if is_translational_symmetry: set_translational_invariance_fc3_per_index(fc3) if is_permutation_symmetry: set_permutation_symmetry_fc3(fc3) return fc3