def _expand_fc2(fc2_alm, supercell, pure_trans, rotations, symprec=1e-5, verbose=True): natom = supercell.get_number_of_atoms() fc2 = np.zeros((natom, natom, 3, 3), dtype='double', order='C') (fc_values, elem_indices) = fc2_alm first_atoms = np.unique(elem_indices[:, 0] // 3) for (fc, indices) in zip(fc_values, elem_indices): v1 = indices[0] // 3 c1 = indices[0] % 3 v2 = indices[1] // 3 c2 = indices[1] % 3 fc2[v1, v2, c1, c2] = fc lattice = np.array(supercell.get_cell().T, dtype='double', order='C') positions = supercell.get_scaled_positions() permutations = compute_all_sg_permutations(positions, rotations, pure_trans, lattice, symprec) distribute_force_constants(fc2, first_atoms, lattice, rotations, permutations) return fc2
def _distribute(self): rotations = self._symmetry.get_symmetry_operations()['rotations'] trans = self._symmetry.get_symmetry_operations()['translations'] distribute_force_constants(self._fc2, range(self._num_atom), self._unique_first_atom_nums, self._lattice, self._positions, rotations, trans, self._symprec)
def _calculate(self): unique_first_atom_nums = np.unique( [x['number'] for x in self._dataset['first_atoms']]) for first_atom_num in unique_first_atom_nums: disp_triplets = [] sets_of_forces = [] for dataset_1st in self._dataset['first_atoms']: if first_atom_num != dataset_1st['number']: continue d1 = dataset_1st['displacement'] d3, f = self._collect_forces_and_disps(dataset_1st) disp_triplets.append(d3) sets_of_forces.append(f) self._fit(first_atom_num, disp_triplets, sets_of_forces) rotations = self._symmetry.get_symmetry_operations()['rotations'] translations = self._symmetry.get_symmetry_operations()['translations'] print "ditributing fc4..." distribute_fc4(self._fc4, unique_first_atom_nums, self._lattice, self._positions, rotations, translations, self._symprec, self._verbose) print "ditributing fc3..." distribute_fc3(self._fc3, unique_first_atom_nums, self._lattice, self._positions, rotations, translations, self._symprec, self._verbose) print "ditributing fc2..." distribute_force_constants(self._fc2, range(self._num_atom), unique_first_atom_nums, self._lattice, self._positions, rotations, translations, self._symprec)
def get_constrained_fc2(supercell, dataset_second_atoms, atom1, forces1, reduced_site_sym, symprec): """ dataset_second_atoms: [{'number': 7, 'displacement': [], 'forces': []}, ...] """ lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() 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, lattice, positions, atom1, atom2, symprec) disps2.append(disps_second['displacement']) sets_of_forces.append(disps_second['forces'] - forces1) solve_force_constants(fc2, atom2, disps2, sets_of_forces, supercell, bond_sym, symprec) # Shift positions according to set atom1 is at origin pos_center = positions[atom1].copy() positions -= pos_center rotations = np.array(reduced_site_sym, dtype='intc', order='C') translations = np.zeros((len(reduced_site_sym), 3), dtype='double', order='C') permutations = compute_all_sg_permutations(positions, rotations, translations, lattice, symprec) distribute_force_constants(fc2, atom_list, lattice, rotations, permutations) return fc2
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 distribute_force_constants_by_translations(fc, primitive, supercell): s2p = primitive.get_supercell_to_primitive_map() p2s = primitive.get_primitive_to_supercell_map() positions = supercell.get_scaled_positions() lattice = supercell.get_cell().T diff = positions - positions[p2s[0]] trans = np.array(diff[np.where(s2p == p2s[0])[0]], dtype='double', order='C') rotations = np.array([np.eye(3, dtype='intc')] * len(trans), dtype='intc', order='C') permutations = primitive.get_atomic_permutations() distribute_force_constants(fc, p2s, lattice, rotations, permutations)
def _distribute_force_constants(self): s2p = self._primitive.get_supercell_to_primitive_map() p2s = self._primitive.get_primitive_to_supercell_map() positions = self._supercell.get_scaled_positions() lattice = self._supercell.get_cell().T diff = positions - positions[p2s[0]] trans = np.array(diff[np.where(s2p == p2s[0])[0]], dtype='double', order='C') rotations = np.array([np.eye(3, dtype='intc')] * len(trans), dtype='intc', order='C') distribute_force_constants( self._force_constants, range(self._supercell.get_number_of_atoms()), p2s, lattice, positions, rotations, trans, 1e-5)
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 _distribute_force_constants(self): s2p = self._primitive.get_supercell_to_primitive_map() p2s = self._primitive.get_primitive_to_supercell_map() positions = self._supercell.get_scaled_positions() lattice = self._supercell.get_cell().T diff = positions - positions[p2s[0]] trans = np.array(diff[np.where(s2p == p2s[0])[0]], dtype='double', order='C') rotations = np.array([np.eye(3, dtype='intc')] * len(trans), dtype='intc', order='C') distribute_force_constants(self._force_constants, range(self._supercell.get_number_of_atoms()), p2s, lattice, positions, rotations, trans, 1e-5)
def _distribute_fc2(self, fc, s_indices, scell, natom): dim = self.dimension t = np.zeros((np.prod(dim), 3), dtype='double', order='C') r = np.zeros((np.prod(dim), 3, 3), dtype='intc', order='C') for i, (d3, d2, d1) in enumerate(np.ndindex(dim[::-1])): r[i] = np.identity(3, dtype='intc') t[i] = np.array([d1, d2, d3], dtype='double') / dim natom_s = scell.get_number_of_atoms() lattice = np.array(scell.get_cell().T, dtype='double', order='C') distribute_force_constants(fc, np.arange(natom, dtype='intc'), s_indices, lattice, scell.get_scaled_positions(), r, t, self._symprec)
def _get_constrained_fc2(supercell, dataset_second_atoms, atom1, forces1, reduced_site_sym, symprec): """Return fc2 under reduced (broken) site symmetry by first displacement. dataset_second_atoms: [{'number': 7, 'displacement': [], 'forces': []}, ...] """ lattice = supercell.cell.T positions = supercell.scaled_positions num_atom = len(supercell) 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, lattice, positions, atom1, atom2, symprec) disps2.append(disps_second["displacement"]) sets_of_forces.append(disps_second["forces"] - forces1) solve_force_constants(fc2, atom2, disps2, sets_of_forces, supercell, bond_sym, symprec) # Shift positions according to set atom1 is at origin pos_center = positions[atom1].copy() positions -= pos_center rotations = np.array(reduced_site_sym, dtype="intc", order="C") translations = np.zeros((len(reduced_site_sym), 3), dtype="double", order="C") permutations = compute_all_sg_permutations(positions, rotations, translations, lattice, symprec) distribute_force_constants(fc2, atom_list, lattice, rotations, permutations) return fc2
def get_fc2(supercell, primitive, disp_dataset, atom_list=None, log_level=0): lattice = supercell.get_cell().T positions = supercell.get_scaled_positions() numbers = supercell.get_atomic_numbers() natom = len(numbers) disps, forces = _collect_disps_and_forces(disp_dataset) p2s_map = primitive.p2s_map p2p_map = primitive.p2p_map if log_level: print("-------------------------------" " ALM FC2 start " "------------------------------") print("ALM by T. Tadano, https://github.com/ttadano/ALM") if log_level == 1: print("Use -v option to watch detailed ALM log.") try: from alm import ALM except ImportError: raise ModuleNotFoundError("ALM python module was not found.") sys.stdout.flush() with ALM(lattice, positions, numbers) as alm: if log_level > 0: log_level_alm = log_level - 1 else: log_level_alm = 0 alm.set_verbosity(log_level_alm) alm.define(1) alm.set_displacement_and_force(disps, forces) alm.optimize() if (atom_list == p2s_map).all(): fc2 = np.zeros((len(p2s_map), natom, 3, 3), dtype='double', order='C') for fc, indices in zip(*alm.get_fc(1, mode='origin')): v1, v2 = indices // 3 c1, c2 = indices % 3 fc2[p2p_map[v1], v2, c1, c2] = fc elif atom_list is None or (atom_list == np.range(natom)): fc2 = np.zeros((natom, natom, 3, 3), dtype='double', order='C') for fc, indices in zip(*alm.get_fc(1, mode='all')): v1, v2 = indices // 3 c1, c2 = indices % 3 fc2[v1, v2, c1, c2] = fc else: # This case would not happen. fc2 = np.zeros((natom, natom, 3, 3), dtype='double', order='C') for fc, indices in zip(*alm.get_fc(1, mode='origin')): v1, v2 = indices // 3 c1, c2 = indices % 3 fc2[v1, v2, c1, c2] = fc N = natom // primitive.get_number_of_atoms() rotations = np.array([ np.eye(3, dtype='intc'), ] * N, dtype='intc', order='C') distribute_force_constants(fc2, p2s_map, lattice, rotations, primitive.atomic_permutations, atom_list=atom_list) fc2 = np.array(fc2[atom_list], dtype='double', order='C') if log_level: print("--------------------------------" " ALM FC2 end " "-------------------------------") return fc2