Beispiel #1
0
class Conductivity(object):
    def __init__(self,
                 interaction,
                 symmetry,
                 grid_points=None,
                 temperatures=None,
                 sigmas=None,
                 sigma_cutoff=None,
                 is_isotope=False,
                 mass_variances=None,
                 mesh_divisors=None,
                 coarse_mesh_shifts=None,
                 boundary_mfp=None,  # in micrometre
                 is_kappa_star=True,
                 gv_delta_q=None,  # finite difference for group veolocity
                 is_full_pp=False,
                 log_level=0):
        if sigmas is None:
            self._sigmas = []
        else:
            self._sigmas = sigmas
        self._sigma_cutoff = sigma_cutoff
        self._pp = interaction
        self._is_full_pp = is_full_pp
        self._collision = None  # has to be set derived class
        if temperatures is None:
            self._temperatures = None
        else:
            self._temperatures = np.array(temperatures, dtype='double')
        self._is_kappa_star = is_kappa_star
        self._gv_delta_q = gv_delta_q
        self._log_level = log_level
        self._primitive = self._pp.get_primitive()
        self._dm = self._pp.get_dynamical_matrix()
        self._frequency_factor_to_THz = self._pp.get_frequency_factor_to_THz()
        self._cutoff_frequency = self._pp.get_cutoff_frequency()
        self._boundary_mfp = boundary_mfp

        self._symmetry = symmetry

        if not self._is_kappa_star:
            self._point_operations = np.array([np.eye(3, dtype='intc')],
                                              dtype='intc')
        else:
            self._point_operations = symmetry.get_reciprocal_operations()
        rec_lat = np.linalg.inv(self._primitive.get_cell())
        self._rotations_cartesian = np.array(
            [similarity_transformation(rec_lat, r)
             for r in self._point_operations], dtype='double')

        self._grid_points = None
        self._grid_weights = None
        self._grid_address = None
        self._ir_grid_points = None
        self._ir_grid_weights = None

        self._read_gamma = False
        self._read_gamma_iso = False

        self._kappa = None
        self._mode_kappa = None

        self._frequencies = None
        self._cv = None
        self._gv = None
        self._gv_sum2 = None
        self._gamma = None
        self._gamma_iso = None
        self._num_sampling_grid_points = 0

        self._mesh = None
        self._mesh_divisors = None
        self._coarse_mesh = None
        self._coarse_mesh_shifts = None
        self._set_mesh_numbers(mesh_divisors=mesh_divisors,
                               coarse_mesh_shifts=coarse_mesh_shifts)
        volume = self._primitive.get_volume()
        self._conversion_factor = unit_to_WmK / volume

        self._isotope = None
        self._mass_variances = None
        self._is_isotope = is_isotope
        if mass_variances is not None:
            self._is_isotope = True
        if self._is_isotope:
            self._set_isotope(mass_variances)

        self._grid_point_count = None
        self._set_grid_properties(grid_points)

        if (self._dm.is_nac() and
            self._dm.get_nac_method() == 'gonze' and
            self._gv_delta_q is None):
            self._gv_delta_q = 1e-5
            if self._log_level:
                msg = "Group velocity calculation:\n"
                text = ("Analytical derivative of dynamical matrix is not "
                        "implemented for NAC by Gonze et al. Instead "
                        "numerical derivative of it is used with dq=1e-5 "
                        "for group velocity calculation.")
                msg += textwrap.fill(text,
                                     initial_indent="  ",
                                     subsequent_indent="  ",
                                     width=70)
                print(msg)
        self._gv_obj = GroupVelocity(
            self._dm,
            q_length=self._gv_delta_q,
            symmetry=self._symmetry,
            frequency_factor_to_THz=self._frequency_factor_to_THz)
        # gv_delta_q may be changed.
        self._gv_delta_q = self._gv_obj.get_q_length()

    def __iter__(self):
        return self

    def __next__(self):
        if self._grid_point_count == len(self._grid_points):
            if self._log_level:
                print("=================== End of collection of collisions "
                      "===================")
            raise StopIteration
        else:
            self._run_at_grid_point()
            self._grid_point_count += 1
            return self._grid_point_count - 1

    def next(self):
        return self.__next__()

    def get_mesh_divisors(self):
        return self._mesh_divisors

    def get_mesh_numbers(self):
        return self._mesh

    def get_mode_heat_capacities(self):
        return self._cv

    def get_group_velocities(self):
        return self._gv

    def get_gv_by_gv(self):
        return self._gv_sum2

    def get_frequencies(self):
        return self._frequencies[self._grid_points]

    def get_qpoints(self):
        return self._qpoints

    def get_grid_points(self):
        return self._grid_points

    def get_grid_weights(self):
        return self._grid_weights

    def get_temperatures(self):
        return self._temperatures

    def set_temperatures(self, temperatures):
        self._temperatures = temperatures
        self._allocate_values()

    def set_gamma(self, gamma):
        self._gamma = gamma
        self._read_gamma = True

    def set_gamma_isotope(self, gamma_iso):
        self._gamma_iso = gamma_iso
        self._read_gamma_iso = True

    def get_gamma(self):
        return self._gamma

    def get_gamma_isotope(self):
        return self._gamma_iso

    def get_kappa(self):
        return self._kappa

    def get_mode_kappa(self):
        return self._mode_kappa

    def get_sigmas(self):
        return self._sigmas

    def get_sigma_cutoff_width(self):
        return self._sigma_cutoff

    def get_grid_point_count(self):
        return self._grid_point_count

    def get_averaged_pp_interaction(self):
        return self._averaged_pp_interaction

    def _run_at_grid_point(self):
        """This has to be implementated in the derived class"""
        pass

    def _allocate_values(self):
        """This has to be implementated in the derived class"""
        pass

    def _set_grid_properties(self, grid_points):
        self._grid_address = self._pp.get_grid_address()
        self._pp.set_nac_q_direction(nac_q_direction=None)

        if grid_points is not None:  # Specify grid points
            self._grid_points = reduce_grid_points(
                self._mesh_divisors,
                self._grid_address,
                grid_points,
                coarse_mesh_shifts=self._coarse_mesh_shifts)
            (self._ir_grid_points,
             self._ir_grid_weights) = self._get_ir_grid_points()
        elif not self._is_kappa_star:  # All grid points
            coarse_grid_address = get_grid_address(self._coarse_mesh)
            coarse_grid_points = np.arange(np.prod(self._coarse_mesh),
                                           dtype='uintp')
            self._grid_points = from_coarse_to_dense_grid_points(
                self._mesh,
                self._mesh_divisors,
                coarse_grid_points,
                coarse_grid_address,
                coarse_mesh_shifts=self._coarse_mesh_shifts)
            self._grid_weights = np.ones(len(self._grid_points), dtype='intc')
            self._ir_grid_points = self._grid_points
            self._ir_grid_weights = self._grid_weights
        else:  # Automatic sampling
            self._grid_points, self._grid_weights = self._get_ir_grid_points()
            self._ir_grid_points = self._grid_points
            self._ir_grid_weights = self._grid_weights

        self._qpoints = np.array(self._grid_address[self._grid_points] /
                                 self._mesh.astype('double'),
                                 dtype='double', order='C')

        self._grid_point_count = 0
        # set_phonons is unnecessary now because all phonons are calculated in
        # self._pp.set_dynamical_matrix, though Gamma-point is an exception,
        # which is treatd at self._pp.set_grid_point.
        # self._pp.set_phonons(self._grid_points)
        self._frequencies, self._eigenvectors, _ = self._pp.get_phonons()

    def _get_gamma_isotope_at_sigmas(self, i):
        gamma_iso = []
        bz_map = self._pp.get_bz_map()
        pp_freqs, pp_eigvecs, pp_phonon_done = self._pp.get_phonons()

        for j, sigma in enumerate(self._sigmas):
            if self._log_level:
                text = "Calculating Gamma of ph-isotope with "
                if sigma is None:
                    text += "tetrahedron method"
                else:
                    text += "sigma=%s" % sigma
                print(text)

            self._isotope.set_sigma(sigma)
            self._isotope.set_phonons(self._grid_address,
                                      bz_map,
                                      pp_freqs,
                                      pp_eigvecs,
                                      pp_phonon_done,
                                      dm=self._dm)
            gp = self._grid_points[i]
            self._isotope.set_grid_point(gp)
            self._isotope.run()
            gamma_iso.append(self._isotope.get_gamma())

        return np.array(gamma_iso, dtype='double', order='C')

    def _set_mesh_numbers(self, mesh_divisors=None, coarse_mesh_shifts=None):
        self._mesh = self._pp.get_mesh_numbers()

        if mesh_divisors is None:
            self._mesh_divisors = np.array([1, 1, 1], dtype='intc')
        else:
            self._mesh_divisors = []
            for i, (m, n) in enumerate(zip(self._mesh, mesh_divisors)):
                if m % n == 0:
                    self._mesh_divisors.append(n)
                else:
                    self._mesh_divisors.append(1)
                    print(("Mesh number %d for the " +
                           ["first", "second", "third"][i] +
                           " axis is not dividable by divisor %d.") % (m, n))
            self._mesh_divisors = np.array(self._mesh_divisors, dtype='intc')
            if coarse_mesh_shifts is None:
                self._coarse_mesh_shifts = [False, False, False]
            else:
                self._coarse_mesh_shifts = coarse_mesh_shifts
            for i in range(3):
                if (self._coarse_mesh_shifts[i] and
                    (self._mesh_divisors[i] % 2 != 0)):
                    print("Coarse grid along " +
                          ["first", "second", "third"][i] +
                          " axis can not be shifted. Set False.")
                    self._coarse_mesh_shifts[i] = False

        self._coarse_mesh = self._mesh // self._mesh_divisors

        if self._log_level:
            print("Lifetime sampling mesh: [ %d %d %d ]" %
                  tuple(self._mesh // self._mesh_divisors))

    def _get_ir_grid_points(self):
        if self._coarse_mesh_shifts is None:
            mesh_shifts = [False, False, False]
        else:
            mesh_shifts = self._coarse_mesh_shifts
        (coarse_grid_points,
         coarse_grid_weights,
         coarse_grid_address, _) = get_ir_grid_points(
             self._coarse_mesh,
             self._symmetry.get_pointgroup_operations(),
             mesh_shifts=mesh_shifts)
        grid_points = from_coarse_to_dense_grid_points(
            self._mesh,
            self._mesh_divisors,
            coarse_grid_points,
            coarse_grid_address,
            coarse_mesh_shifts=self._coarse_mesh_shifts)
        grid_weights = coarse_grid_weights

        assert grid_weights.sum() == np.prod(self._mesh // self._mesh_divisors)

        return grid_points, grid_weights

    def _set_isotope(self, mass_variances):
        if mass_variances is True:
            mv = None
        else:
            mv = mass_variances
        self._isotope = Isotope(
            self._mesh,
            self._primitive,
            mass_variances=mv,
            frequency_factor_to_THz=self._frequency_factor_to_THz,
            symprec=self._symmetry.get_symmetry_tolerance(),
            cutoff_frequency=self._cutoff_frequency,
            lapack_zheev_uplo=self._pp.get_lapack_zheev_uplo())
        self._mass_variances = self._isotope.get_mass_variances()

    def _set_harmonic_properties(self, i_irgp, i_data):
        grid_point = self._grid_points[i_irgp]
        freqs = self._frequencies[grid_point][self._pp.get_band_indices()]
        self._cv[:, i_data, :] = self._get_cv(freqs)
        gv = self._get_gv(self._qpoints[i_irgp])
        self._gv[i_data] = gv[self._pp.get_band_indices(), :]

        # Outer product of group velocities (v x v) [num_k*, num_freqs, 3, 3]
        gv_by_gv_tensor, order_kstar = self._get_gv_by_gv(i_irgp, i_data)
        self._num_sampling_grid_points += order_kstar

        # Sum all vxv at k*
        for j, vxv in enumerate(
            ([0, 0], [1, 1], [2, 2], [1, 2], [0, 2], [0, 1])):
            self._gv_sum2[i_data, :, j] = gv_by_gv_tensor[:, vxv[0], vxv[1]]

    def _get_gv(self, q):
        self._gv_obj.set_q_points([q])
        return self._gv_obj.get_group_velocity()[0]

    def _get_gv_by_gv(self, i_irgp, i_data):
        rotation_map = get_grid_points_by_rotations(
            self._grid_address[self._grid_points[i_irgp]],
            self._point_operations,
            self._mesh)
        gv = self._gv[i_data]
        gv_by_gv = np.zeros((len(gv), 3, 3), dtype='double')

        for r in self._rotations_cartesian:
            gvs_rot = np.dot(gv, r.T)
            gv_by_gv += [np.outer(r_gv, r_gv) for r_gv in gvs_rot]
        gv_by_gv /= len(rotation_map) // len(np.unique(rotation_map))
        order_kstar = len(np.unique(rotation_map))

        if self._grid_weights is not None:
            if order_kstar != self._grid_weights[i_irgp]:
                if self._log_level:
                    print("*" * 33 + "Warning" + "*" * 33)
                    print(" Number of elements in k* is unequal "
                          "to number of equivalent grid-points.")
                    print("*" * 73)

        return gv_by_gv, order_kstar

    def _get_cv(self, freqs):
        cv = np.zeros((len(self._temperatures), len(freqs)), dtype='double')
        # T/freq has to be large enough to avoid divergence.
        # Otherwise just set 0.
        for i, f in enumerate(freqs):
            finite_t = (self._temperatures > f / 100)
            if f > self._cutoff_frequency:
                cv[:, i] = np.where(
                    finite_t, get_mode_cv(
                        np.where(finite_t, self._temperatures, 10000),
                        f * THzToEv), 0)
        return cv

    def _get_main_diagonal(self, i, j, k):
        num_band = self._primitive.get_number_of_atoms() * 3
        main_diagonal = self._gamma[j, k, i].copy()
        if self._gamma_iso is not None:
            main_diagonal += self._gamma_iso[j, i]
        if self._boundary_mfp is not None:
            main_diagonal += self._get_boundary_scattering(i)

        # if self._boundary_mfp is not None:
        #     for l in range(num_band):
        #         # Acoustic modes at Gamma are avoided.
        #         if i == 0 and l < 3:
        #             continue
        #         gv_norm = np.linalg.norm(self._gv[i, l])
        #         mean_free_path = (gv_norm * Angstrom * 1e6 /
        #                           (4 * np.pi * main_diagonal[l]))
        #         if mean_free_path > self._boundary_mfp:
        #             main_diagonal[l] = (
        #                 gv_norm / (4 * np.pi * self._boundary_mfp))

        return main_diagonal

    def _get_boundary_scattering(self, i):
        num_band = self._primitive.get_number_of_atoms() * 3
        g_boundary = np.zeros(num_band, dtype='double')
        for l in range(num_band):
            g_boundary[l] = (np.linalg.norm(self._gv[i, l]) * Angstrom * 1e6 /
                             (4 * np.pi * self._boundary_mfp))
        return g_boundary

    def _show_log_header(self, i):
        if self._log_level:
            gp = self._grid_points[i]
            print("======================= Grid point %d (%d/%d) "
                  "=======================" %
                  (gp, i + 1, len(self._grid_points)))
            print("q-point: (%5.2f %5.2f %5.2f)" % tuple(self._qpoints[i]))
            if self._boundary_mfp is not None:
                if self._boundary_mfp > 1000:
                    print("Boundary mean free path (millimetre): %.3f" %
                          (self._boundary_mfp / 1000.0))
                else:
                    print("Boundary mean free path (micrometre): %.5f" %
                          self._boundary_mfp)
            if self._is_isotope:
                print(("Mass variance parameters: " +
                       "%5.2e " * len(self._mass_variances)) %
                      tuple(self._mass_variances))
Beispiel #2
0
class Phonopy(object):
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 primitive_matrix=None,
                 nac_params=None,
                 distance=None,
                 factor=VaspToTHz,
                 is_auto_displacements=None,
                 dynamical_matrix_decimals=None,
                 force_constants_decimals=None,
                 symprec=1e-5,
                 is_symmetry=True,
                 use_lapack_solver=False,
                 log_level=0):

        if is_auto_displacements is not None:
            print("Warning: \'is_auto_displacements\' argument is obsolete.")
            if is_auto_displacements is False:
                print("Sets of displacements are not created as default.")
            else:
                print("Use \'generate_displacements\' method explicitly to "
                      "create sets of displacements.")

        if distance is not None:
            print("Warning: \'distance\' keyword argument is obsolete at "
                  "Phonopy instantiation.")
            print("Specify \'distance\' keyword argument when calling "
                  "\'generate_displacements\'")
            print("method (See the Phonopy API document).")

        self._symprec = symprec
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._use_lapack_solver = use_lapack_solver
        self._log_level = log_level

        # Create supercell and primitive cell
        self._unitcell = Atoms(atoms=unitcell)
        self._supercell_matrix = supercell_matrix
        self._primitive_matrix = primitive_matrix
        self._supercell = None
        self._primitive = None
        self._build_supercell()
        self._build_primitive_cell()

        # Set supercell and primitive symmetry
        self._symmetry = None
        self._primitive_symmetry = None
        self._search_symmetry()
        self._search_primitive_symmetry()

        # set_displacements (used only in preprocess)
        self._displacement_dataset = None
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None

        # set_force_constants or set_forces
        self._force_constants = None
        self._force_constants_decimals = force_constants_decimals

        # set_dynamical_matrix
        self._dynamical_matrix = None
        self._nac_params = nac_params
        self._dynamical_matrix_decimals = dynamical_matrix_decimals

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_tetrahedron_method
        self._tetrahedron_method = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None

        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None

    def get_version(self):
        return __version__

    def get_primitive(self):
        return self._primitive
    primitive = property(get_primitive)

    def get_unitcell(self):
        return self._unitcell
    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell
    supercell = property(get_supercell)

    def get_symmetry(self):
        """return symmetry of supercell"""
        return self._symmetry
    symmetry = property(get_symmetry)

    def get_primitive_symmetry(self):
        """return symmetry of primitive cell"""
        return self._primitive_symmetry

    def get_supercell_matrix(self):
        return self._supercell_matrix

    def get_primitive_matrix(self):
        return self._primitive_matrix

    def get_unit_conversion_factor(self):
        return self._factor
    unit_conversion_factor = property(get_unit_conversion_factor)

    def get_displacement_dataset(self):
        return self._displacement_dataset

    def get_displacements(self):
        return self._displacements
    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions
    displacement_directions = property(get_displacement_directions)

    def get_supercells_with_displacements(self):
        if self._displacement_dataset is None:
            return None
        else:
            self._build_supercells_with_displacements()
            return self._supercells_with_displacements

    def get_force_constants(self):
        return self._force_constants
    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants,
                                     self._supercell,
                                     self._primitive,
                                     self._symprec)

    def get_nac_params(self):
        return self._nac_params

    def get_dynamical_matrix(self):
        return self._dynamical_matrix
    dynamical_matrix = property(get_dynamical_matrix)

    def set_unitcell(self, unitcell):
        self._unitcell = unitcell
        self._build_supercell()
        self._build_primitive_cell()
        self._search_symmetry()
        self._search_primitive_symmetry()
        self._displacement_dataset = None

    def set_masses(self, masses):
        p_masses = np.array(masses)
        self._primitive.set_masses(p_masses)
        p2p_map = self._primitive.get_primitive_to_primitive_map()
        s_masses = p_masses[[p2p_map[x] for x in
                             self._primitive.get_supercell_to_primitive_map()]]
        self._supercell.set_masses(s_masses)
        u2s_map = self._supercell.get_unitcell_to_supercell_map()
        u_masses = s_masses[u2s_map]
        self._unitcell.set_masses(u_masses)
        self._set_dynamical_matrix()

    def set_nac_params(self, nac_params=None):
        self._nac_params = nac_params
        self._set_dynamical_matrix()

    def set_displacement_dataset(self, displacement_dataset):
        """
        displacement_dataset:
           {'natom': number_of_atoms_in_supercell,
            'first_atoms': [
              {'number': atom index of displaced atom,
               'displacement': displacement in Cartesian coordinates,
               'direction': displacement direction with respect to axes
               'forces': forces on atoms in supercell},
              {...}, ...]}
        """
        self._displacement_dataset = displacement_dataset

        self._displacements = []
        self._displacement_directions = []
        for disp in self._displacement_dataset['first_atoms']:
            x = disp['displacement']
            self._displacements.append([disp['number'], x[0], x[1], x[2]])
            if 'direction' in disp:
                y = disp['direction']
                self._displacement_directions.append(
                    [disp['number'], y[0], y[1], y[2]])
        if not self._displacement_directions:
            self._displacement_directions = None

    def set_forces(self, sets_of_forces):
        """
        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]
        """
        for disp, forces in zip(
                self._displacement_dataset['first_atoms'], sets_of_forces):
            disp['forces'] = forces

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants
        self._set_dynamical_matrix()

    def set_force_constants_zero_with_radius(self, cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)
        self._set_dynamical_matrix()

    def set_dynamical_matrix(self):
        self._set_dynamical_matrix()

    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 produce_force_constants(self,
                                forces=None,
                                calculate_full_force_constants=True,
                                computation_algorithm="svd"):
        if forces is not None:
            self.set_forces(forces)

        # A primitive check if 'forces' key is in displacement_dataset.
        for disp in self._displacement_dataset['first_atoms']:
            if 'forces' not in disp:
                return False

        if calculate_full_force_constants:
            self._run_force_constants_from_forces(
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)
        else:
            p2s_map = self._primitive.get_primitive_to_supercell_map()
            self._run_force_constants_from_forces(
                distributed_atom_list=p2s_map,
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)

        self._set_dynamical_matrix()

        return True

    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)
        self._set_dynamical_matrix()

    def symmetrize_force_constants_by_space_group(self):
        from phonopy.harmonic.force_constants import (set_tensor_symmetry,
                                                      set_tensor_symmetry_PJ)
        set_tensor_symmetry_PJ(self._force_constants,
                               self._supercell.get_cell().T,
                               self._supercell.get_scaled_positions(),
                               self._symmetry)

        self._set_dynamical_matrix()

    #####################
    # Phonon properties #
    #####################

    # Single q-point
    def get_dynamical_matrix_at_q(self, q):
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()


    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q

        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor

    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q

        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors

    # Band structure
    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._band_structure = None
            return False

        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)
        return True

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(),
                band.get_distances(),
                band.get_frequencies(),
                band.get_eigenvectors())

    def plot_band_structure(self, labels=None):
        import matplotlib.pyplot as plt
        if labels:
            from matplotlib import rc
            rc('text', usetex=True)

        fig, ax = plt.subplots()
        ax.xaxis.set_ticks_position('both')
        ax.yaxis.set_ticks_position('both')
        ax.xaxis.set_tick_params(which='both', direction='in')
        ax.yaxis.set_tick_params(which='both', direction='in')

        self._band_structure.plot(plt, labels=labels)
        return plt

    def write_yaml_band_structure(self,
                                  labels=None,
                                  comment=None,
                                  filename="band.yaml"):
        self._band_structure.write_yaml(labels=labels,
                                        comment=comment,
                                        filename=filename)

    # Sampling mesh
    def run_mesh(self):
        if self._mesh is not None:
            self._mesh.run()

    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_mesh_symmetry=True,
                 is_eigenvectors=False,
                 is_gamma_center=False,
                 run_immediately=True):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._mesh = None
            return False

        self._mesh = Mesh(
            self._dynamical_matrix,
            mesh,
            shift=shift,
            is_time_reversal=is_time_reversal,
            is_mesh_symmetry=is_mesh_symmetry,
            is_eigenvectors=is_eigenvectors,
            is_gamma_center=is_gamma_center,
            group_velocity=self._group_velocity,
            rotations=self._primitive_symmetry.get_pointgroup_operations(),
            factor=self._factor,
            use_lapack_solver=self._use_lapack_solver)
        if run_immediately:
            self._mesh.run()
        return True

    def get_mesh(self):
        if self._mesh is None:
            return None
        else:
            return (self._mesh.get_qpoints(),
                    self._mesh.get_weights(),
                    self._mesh.get_frequencies(),
                    self._mesh.get_eigenvectors())
    
    def get_mesh_grid_info(self):
        if self._mesh is None:
            return None
        else:
            return (self._mesh.get_grid_address(),
                    self._mesh.get_ir_grid_points(),
                    self._mesh.get_grid_mapping_table())

    def write_hdf5_mesh(self):
        self._mesh.write_hdf5()

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    # Plot band structure and DOS (PDOS) together
    def plot_band_structure_and_dos(self, pdos_indices=None, labels=None):
        import matplotlib.pyplot as plt
        import matplotlib.gridspec as gridspec
        if labels:
            from matplotlib import rc
            rc('text', usetex=True)


        plt.figure(figsize=(10, 6))
        gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1])
        ax1 = plt.subplot(gs[0, 0])
        ax1.xaxis.set_ticks_position('both')
        ax1.yaxis.set_ticks_position('both')
        ax1.xaxis.set_tick_params(which='both', direction='in')
        ax1.yaxis.set_tick_params(which='both', direction='in')
        self._band_structure.plot(plt, labels=labels)
        ax2 = plt.subplot(gs[0, 1], sharey=ax1)
        ax2.xaxis.set_ticks_position('both')
        ax2.yaxis.set_ticks_position('both')
        ax2.xaxis.set_tick_params(which='both', direction='in')
        ax2.yaxis.set_tick_params(which='both', direction='in')
        plt.subplots_adjust(wspace=0.03)
        plt.setp(ax2.get_yticklabels(), visible=False)

        if pdos_indices is None:
            self._total_dos.plot(plt,
                                 ylabel="",
                                 draw_grid=False,
                                 flip_xy=True)
        else:
            self._pdos.plot(plt,
                            indices=pdos_indices,
                            ylabel="",
                            draw_grid=False,
                            flip_xy=True)

        ax2.set_xlim((0, None))

        return plt

    # DOS
    def set_total_DOS(self,
                      sigma=None,
                      freq_min=None,
                      freq_max=None,
                      freq_pitch=None,
                      tetrahedron_method=False):

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before DOS calculation.")
            self._total_dos = None
            return False

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(freq_min, freq_max, freq_pitch)
        total_dos.run()
        self._total_dos = total_dos
        return True

    def get_total_DOS(self):
        """
        Retern frequencies and total dos.
        The first element is freqs and the second is total dos.

        frequencies: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(),
            freq_max_fit=freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        import matplotlib.pyplot as plt

        fig, ax = plt.subplots()
        ax.xaxis.set_ticks_position('both')
        ax.yaxis.set_ticks_position('both')
        ax.xaxis.set_tick_params(which='both', direction='in')
        ax.yaxis.set_tick_params(which='both', direction='in')

        self._total_dos.plot(plt, draw_grid=False)

        ax.set_ylim((0, None))

        return plt

    def write_total_DOS(self):
        self._total_dos.write()

    # PDOS
    def set_partial_DOS(self,
                        sigma=None,
                        freq_min=None,
                        freq_max=None,
                        freq_pitch=None,
                        tetrahedron_method=False,
                        direction=None,
                        xyz_projection=False):
        self._pdos = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to be called before "
                  "PDOS calculation.")
            return False

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        num_grid = np.prod(self._mesh.get_mesh_numbers())
        if num_grid != len(self._mesh.get_ir_grid_points()):
            print("Warning: \'set_mesh\' has to be called with "
                  "is_mesh_symmetry=False.")
            return False

        if direction is not None:
            direction_cart = np.dot(direction, self._primitive.get_cell())
        else:
            direction_cart = None
        self._pdos = PartialDos(self._mesh,
                                sigma=sigma,
                                tetrahedron_method=tetrahedron_method,
                                direction=direction_cart,
                                xyz_projection=xyz_projection)
        self._pdos.set_draw_area(freq_min, freq_max, freq_pitch)
        self._pdos.run()
        return True

    def get_partial_DOS(self):
        """
        Retern frequencies and partial_dos.
        The first element is freqs and the second is partial_dos.

        frequencies: [freq1, freq2, ...]
        partial_dos:
          [[atom1-freq1, atom1-freq2, ...],
           [atom2-freq1, atom2-freq2, ...],
           ...]
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        import matplotlib.pyplot as plt

        fig, ax = plt.subplots()
        ax.xaxis.set_ticks_position('both')
        ax.yaxis.set_ticks_position('both')
        ax.xaxis.set_tick_params(which='both', direction='in')
        ax.yaxis.set_tick_params(which='both', direction='in')

        self._pdos.plot(plt,
                        indices=pdos_indices,
                        legend=legend,
                        draw_grid=False)

        ax.set_ylim((0, None))

        return plt

    def write_partial_DOS(self):
        self._pdos.write()

    # Thermal property
    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               temperatures=None,
                               is_projection=False,
                               band_indices=None,
                               cutoff_frequency=None,
                               pretend_real=False):
        if self._mesh is None:
            print("Warning: set_mesh has to be done before "
                  "set_thermal_properties")
            return False
        else:
            tp = ThermalProperties(self._mesh.get_frequencies(),
                                   weights=self._mesh.get_weights(),
                                   eigenvectors=self._mesh.get_eigenvectors(),
                                   is_projection=is_projection,
                                   band_indices=band_indices,
                                   cutoff_frequency=cutoff_frequency,
                                   pretend_real=pretend_real)
            if temperatures is None:
                tp.set_temperature_range(t_step=t_step,
                                         t_max=t_max,
                                         t_min=t_min)
            else:
                tp.set_temperatures(temperatures)
            tp.run()
            self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        import matplotlib.pyplot as plt

        fig, ax = plt.subplots()
        ax.xaxis.set_ticks_position('both')
        ax.yaxis.set_ticks_position('both')
        ax.xaxis.set_tick_params(which='both', direction='in')
        ax.yaxis.set_tick_params(which='both', direction='in')

        self._thermal_properties.plot(plt)

        temps, _, _, _ = self._thermal_properties.get_thermal_properties()
        ax.set_xlim((0, temps[-1]))

        return plt

    def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    # Thermal displacement
    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  temperatures=None,
                                  direction=None,
                                  cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        self._thermal_displacements = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before \'set_thermal_displacements\'.")
            return False

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        if np.prod(mesh_nums) != len(eigvecs):
            print("Warning: Sampling mesh must not be symmetrized.")
            return False

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_frequency=cutoff_frequency)
        if temperatures is None:
            td.set_temperature_range(t_min, t_max, t_step)
        else:
            td.set_temperatures(temperatures)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        td.run()

        self._thermal_displacements = td
        return True

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()

    def plot_thermal_displacements(self, is_legend=False):
        import matplotlib.pyplot as plt

        fig, ax = plt.subplots()
        ax.xaxis.set_ticks_position('both')
        ax.yaxis.set_ticks_position('both')
        ax.xaxis.set_tick_params(which='both', direction='in')
        ax.yaxis.set_tick_params(which='both', direction='in')

        self._thermal_displacements.plot(plt, is_legend=is_legend)

        temps, _ = self._thermal_displacements.get_thermal_displacements()
        ax.set_xlim((0, temps[-1]))

        return plt

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    # Thermal displacement matrix
    def set_thermal_displacement_matrices(self,
                                          t_step=10,
                                          t_max=1000,
                                          t_min=0,
                                          cutoff_frequency=None,
                                          t_cif=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        self._thermal_displacement_matrices = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before \'set_thermal_displacement_matrices\'.")
            return False

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        if np.prod(mesh_nums) != len(eigvecs):
            print("Warning: Sampling mesh must not be symmetrized.")
            return False

        tdm = ThermalDisplacementMatrices(frequencies,
                                          eigvecs,
                                          self._primitive.get_masses(),
                                          cutoff_frequency=cutoff_frequency,
                                          lattice=self._primitive.get_cell().T)
        if t_cif is None:
            tdm.set_temperature_range(t_min, t_max, t_step)
        else:
            tdm.set_temperatures([t_cif])
        tdm.run()

        self._thermal_displacement_matrices = tdm
        return True

    def get_thermal_displacement_matrices(self):
        tdm = self._thermal_displacement_matrices
        if tdm is not None:
            return tdm.get_thermal_displacement_matrices()

    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()

    def write_thermal_displacement_matrix_to_cif(self,
                                                 temperature_index):
        self._thermal_displacement_matrices.write_cif(self._primitive,
                                                      temperature_index)

    # Mean square distance between a pair of atoms
    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_frequency=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        td.run(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    # Sampling at q-points
    def set_qpoints_phonon(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._qpoints_phonon = None
            return False

        self._qpoints_phonon = QpointsPhonon(
            np.reshape(q_points, (-1, 3)),
            self._dynamical_matrix,
            nac_q_direction=nac_q_direction,
            is_eigenvectors=is_eigenvectors,
            group_velocity=self._group_velocity,
            write_dynamical_matrices=write_dynamical_matrices,
            factor=self._factor)
        return True

    def get_qpoints_phonon(self):
        return (self._qpoints_phonon.get_frequencies(),
                self._qpoints_phonon.get_eigenvectors())

    def write_hdf5_qpoints_phonon(self):
        self._qpoints_phonon.write_hdf5()

    def write_yaml_qpoints_phonon(self):
        self._qpoints_phonon.write_yaml()

    # Normal mode animation
    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return False

        if q_point is None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  shift=shift)
        else:
            animation = Animation(q_point,
                                  self._dynamical_matrix,
                                  shift=shift)
        if anime_type == 'v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)
        if (anime_type == 'arc' or
            anime_type == 'xyz' or
            anime_type == 'jmol' or
            anime_type == 'poscar'):
            if band_index is None or amplitude is None or num_div is None:
                print("Warning: Parameters are not correctly set for "
                      "animation.")
                return False

            if anime_type == 'arc' or anime_type is None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div)

            if anime_type == 'xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor)

            if anime_type == 'jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)

            if anime_type == 'poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div)

        return True

    # Atomic modulation of normal mode
    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._modulation = None
            return False

        self._modulation = Modulation(self._dynamical_matrix,
                                      dimension,
                                      phonon_modes,
                                      delta_q=delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()
        return True

    def get_modulated_supercells(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulated_supercells()

    def get_modulations_and_supercell(self):
        """Return modulations and supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.

        """
        return self._modulation.get_modulations_and_supercell()

    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()

    def write_yaml_modulations(self):
        self._modulation.write_yaml()

    # Irreducible representation
    def set_irreps(self,
                   q,
                   is_little_cogroup=False,
                   nac_q_direction=None,
                   degeneracy_tolerance=1e-4):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._irreps = None
            return None

        self._irreps = IrReps(
            self._dynamical_matrix,
            q,
            is_little_cogroup=is_little_cogroup,
            nac_q_direction=nac_q_direction,
            factor=self._factor,
            symprec=self._symprec,
            degeneracy_tolerance=degeneracy_tolerance,
            log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps

    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    # Group velocity
    def set_group_velocity(self, q_length=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._group_velocity = None
            return False

        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_length=q_length,
            symmetry=self._primitive_symmetry,
            frequency_factor_to_THz=self._factor)
        return True

    def get_group_velocity(self):
        return self._group_velocity.get_group_velocity()

    def get_group_velocity_at_q(self, q_point):
        if self._group_velocity is None:
            self.set_group_velocity()
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    # Moment
    def set_moment(self,
                   order=1,
                   is_projection=False,
                   freq_min=None,
                   freq_max=None):
        if self._mesh is None:
            print("Warning: set_mesh has to be done before set_moment")
            return False
        else:
            if is_projection:
                if self._mesh.get_eigenvectors() is None:
                    print("Warning: Eigenvectors have to be calculated.")
                    return False
                moment = PhononMoment(
                    self._mesh.get_frequencies(),
                    weights=self._mesh.get_weights(),
                    eigenvectors=self._mesh.get_eigenvectors())
            else:
                moment = PhononMoment(
                    self._mesh.get_frequencies(),
                    weights=self._mesh.get_weights())
            if freq_min is not None or freq_max is not None:
                moment.set_frequency_range(freq_min=freq_min,
                                           freq_max=freq_max)
            moment.run(order=order)
            self._moment = moment.get_moment()
            return True

    def get_moment(self):
        return self._moment

    #################
    # Local methods #
    #################
    def _run_force_constants_from_forces(self,
                                         distributed_atom_list=None,
                                         decimals=None,
                                         computation_algorithm="svd"):
        if self._displacement_dataset is not None:
            self._force_constants = get_fc2(
                self._supercell,
                self._symmetry,
                self._displacement_dataset,
                atom_list=distributed_atom_list,
                decimals=decimals,
                computation_algorithm=computation_algorithm)

    def _set_dynamical_matrix(self):
        self._dynamical_matrix = None

        if (self._supercell is None or self._primitive is None):
            print("Bug: Supercell or primitive is not created.")
            return False
        elif self._force_constants is None:
            print("Warning: Force constants are not prepared.")
            return False
        elif self._primitive.get_masses() is None:
            print("Warning: Atomic masses are not correctly set.")
            return False
        else:
            if self._nac_params is None:
                self._dynamical_matrix = DynamicalMatrix(
                    self._supercell,
                    self._primitive,
                    self._force_constants,
                    decimals=self._dynamical_matrix_decimals,
                    symprec=self._symprec)
            else:
                self._dynamical_matrix = DynamicalMatrixNAC(
                    self._supercell,
                    self._primitive,
                    self._force_constants,
                    nac_params=self._nac_params,
                    decimals=self._dynamical_matrix_decimals,
                    symprec=self._symprec)
            return True

    def _search_symmetry(self):
        self._symmetry = Symmetry(self._supercell,
                                  self._symprec,
                                  self._is_symmetry)

    def _search_primitive_symmetry(self):
        self._primitive_symmetry = Symmetry(self._primitive,
                                            self._symprec,
                                            self._is_symmetry)

        if (len(self._symmetry.get_pointgroup_operations()) !=
            len(self._primitive_symmetry.get_pointgroup_operations())):
            print("Warning: Point group symmetries of supercell and primitive"
                  "cell are different.")

    def _build_supercell(self):
        self._supercell = get_supercell(self._unitcell,
                                        self._supercell_matrix,
                                        self._symprec)

    def _build_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacement_dataset['first_atoms']:
            positions = self._supercell.get_positions()
            positions[disp['number']] += disp['displacement']
            supercells.append(Atoms(
                    numbers=self._supercell.get_atomic_numbers(),
                    masses=self._supercell.get_masses(),
                    magmoms=self._supercell.get_magnetic_moments(),
                    positions=positions,
                    cell=self._supercell.get_cell(),
                    pbc=True))

        self._supercells_with_displacements = supercells

    def _build_primitive_cell(self):
        """
        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T
        """

        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        if self._primitive_matrix is None:
            trans_mat = inv_supercell_matrix
        else:
            trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix)
        self._primitive = get_primitive(
            self._supercell, trans_mat, self._symprec)
        num_satom = self._supercell.get_number_of_atoms()
        num_patom = self._primitive.get_number_of_atoms()
        if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1:
            return True
        else:
            return False
Beispiel #3
0
class Phonopy:
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 primitive_matrix=None,
                 nac_params=None,
                 distance=0.01,
                 factor=VaspToTHz,
                 is_auto_displacements=True,
                 dynamical_matrix_decimals=None,
                 force_constants_decimals=None,
                 symprec=1e-5,
                 is_symmetry=True,
                 log_level=0):
        self._symprec = symprec
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._log_level = log_level

        # Create supercell and primitive cell
        self._unitcell = unitcell
        self._supercell_matrix = supercell_matrix
        self._primitive_matrix = primitive_matrix
        self._supercell = None
        self._primitive = None
        self._build_supercell()
        self._build_primitive_cell()

        # Set supercell and primitive symmetry
        self._symmetry = None
        self._primitive_symmetry = None
        self._search_symmetry()
        self._search_primitive_symmetry()

        # set_displacements (used only in preprocess)
        self._displacement_dataset = None
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None
        if is_auto_displacements:
            self.generate_displacements(distance=distance)

        # set_force_constants or set_forces
        self._force_constants = None
        self._force_constants_decimals = force_constants_decimals
        
        # set_dynamical_matrix
        self._dynamical_matrix = None
        self._nac_params = nac_params
        self._dynamical_matrix_decimals = dynamical_matrix_decimals

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_tetrahedron_method
        self._tetrahedron_method = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None
        
        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None

    def set_post_process(self,
                         primitive_matrix=None,
                         sets_of_forces=None,
                         displacement_dataset=None,
                         force_constants=None,
                         is_nac=None):
        print 
        print ("********************************** Warning"
               "**********************************")
        print "set_post_process will be obsolete."
        print ("  produce_force_constants is used instead of set_post_process"
               " for producing")
        print ("  force constants from forces.")
        if primitive_matrix is not None:
            print ("  primitive_matrix has to be given at Phonopy::__init__"
                   " object creation.")
        print ("******************************************"
               "**********************************")
        print 

        if primitive_matrix is not None:
            self._primitive_matrix = primitive_matrix
            self._build_primitive_cell()
            self._search_primitive_symmetry()
        
        if sets_of_forces is not None:
            self.set_forces(sets_of_forces)
        elif displacement_dataset is not None:
            self._displacement_dataset = displacement_dataset
        elif force_constants is not None:
            self.set_force_constants(force_constants)
            
        if self._displacement_dataset is not None:
            self.produce_force_constants()

    def set_masses(self, masses):
        p_masses = np.array(masses)
        self._primitive.set_masses(p_masses)
        p2p_map = self._primitive.get_primitive_to_primitive_map()
        s_masses = p_masses[[p2p_map[x] for x in
                             self._primitive.get_supercell_to_primitive_map()]]
        self._supercell.set_masses(s_masses)
        u2s_map = self._supercell.get_unitcell_to_supercell_map()
        u_masses = s_masses[u2s_map]
        self._unitcell.set_masses(u_masses)
        
    def get_primitive(self):
        return self._primitive
    primitive = property(get_primitive)

    def get_unitcell(self):
        return self._unitcell
    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell
    supercell = property(get_supercell)

    def set_supercell(self, supercell):
        self._supercell = supercell

    def get_symmetry(self):
        """return symmetry of supercell"""
        return self._symmetry
    symmetry = property(get_symmetry)

    def get_primitive_symmetry(self):
        """return symmetry of primitive cell"""
        return self._primitive_symmetry

    def get_unit_conversion_factor(self):
        return self._factor
    unit_conversion_factor = property(get_unit_conversion_factor)

    def produce_force_constants(self,
                                forces=None,
                                calculate_full_force_constants=True,
                                computation_algorithm="svd"):
        if forces is not None:
            self.set_forces(forces)
        
        if calculate_full_force_constants:
            self._run_force_constants_from_forces(
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)
        else:
            p2s_map = self._primitive.get_primitive_to_supercell_map()
            self._run_force_constants_from_forces(
                distributed_atom_list=p2s_map,
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)

    def set_nac_params(self, nac_params=None, method=None):
        if method is not None:
            print "set_nac_params:"
            print "  Keyword argument of \"method\" is not more supported."
        self._nac_params = nac_params
        
    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 set_displacements(self, displacements):
        print 
        print ("********************************** Warning"
               "**********************************")
        print "set_displacements is obsolete. Do nothing."
        print ("******************************************"
               "**********************************")
        print 

    def get_displacements(self):
        return self._displacements
    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions
    displacement_directions = property(get_displacement_directions)

    def get_displacement_dataset(self):
        return self._displacement_dataset

    def get_supercells_with_displacements(self):
        if self._displacement_dataset is None:
            return None
        else:
            self._build_supercells_with_displacements()
            return self._supercells_with_displacements

    def get_dynamical_matrix(self):
        return self._dynamical_matrix
    dynamical_matrix = property(get_dynamical_matrix)

    def set_forces(self, sets_of_forces):
        """
        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]
        """
        for disp, forces in zip(
                self._displacement_dataset['first_atoms'], sets_of_forces):
            disp['forces'] = forces

    def set_force_constants_zero_with_radius(self, cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants

    def set_force_sets(self, force_sets):
        print 
        print ("********************************** Warning"
               "**********************************")
        print "set_force_sets will be obsolete."
        print ("   The method name is changed to set_displacement_dataset.")
        print ("******************************************"
               "**********************************")
        print
        self.set_displacement_dataset(force_sets)

    def set_displacement_dataset(self, displacement_dataset):
        """
        displacement_dataset:
           {'natom': number_of_atoms_in_supercell,
            'first_atoms': [
              {'number': atom index of displaced atom,
               'displacement': displacement in Cartesian coordinates,
               'direction': displacement direction with respect to axes
               'forces': forces on atoms in supercell},
              {...}, ...]}
        """
        self._displacement_dataset = displacement_dataset

        self._displacements = []
        self._displacement_directions = []
        for disp in self._displacement_dataset['first_atoms']:
            x = disp['displacement']
            self._displacements.append([disp['number'], x[0], x[1], x[2]])
            if 'direction' in disp:
                y = disp['direction']
                self._displacement_directions.append(
                    [disp['number'], y[0], y[1], y[2]])
        if not self._displacement_directions:
            self._displacement_directions = None
        
    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)

    def symmetrize_force_constants_by_space_group(self):
        rotations = self._symmetry.get_symmetry_operations()['rotations']
        translations = self._symmetry.get_symmetry_operations()['translations']
        set_tensor_symmetry(self._force_constants,
                            self._supercell.get_cell().T,
                            self._supercell.get_scaled_positions(),
                            rotations,
                            translations,
                            self._symprec)
        
    def get_force_constants(self):
        return self._force_constants
    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants,
                                     self._supercell,
                                     self._primitive,
                                     self._symprec)

    def set_dynamical_matrix(self):
        self._set_dynamical_matrix()
        
    def get_dynamical_matrix_at_q(self, q):
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()

    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))
            
        return np.array(frequencies) * self._factor

    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors

    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):
        self._set_dynamical_matrix()
        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(),
                band.get_distances(),                
                band.get_frequencies(),
                band.get_eigenvectors())

    def plot_band_structure(self, symbols=None):
        return self._band_structure.plot_band(symbols)

    def write_yaml_band_structure(self):
        self._band_structure.write_yaml()

    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_mesh_symmetry=True,
                 is_eigenvectors=False,
                 is_gamma_center=False):
        self._set_dynamical_matrix()
        self._mesh = Mesh(
            self._dynamical_matrix,
            mesh,
            shift=shift,
            is_time_reversal=is_time_reversal,
            is_mesh_symmetry=is_mesh_symmetry,
            is_eigenvectors=is_eigenvectors,
            is_gamma_center=is_gamma_center,
            group_velocity=self._group_velocity,
            rotations=self._primitive_symmetry.get_pointgroup_operations(),
            factor=self._factor)

    def get_mesh(self):
        return (self._mesh.get_qpoints(),
                self._mesh.get_weights(),
                self._mesh.get_frequencies(),
                self._mesh.get_eigenvectors())

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               is_projection=False,
                               band_indices=None,
                               cutoff_frequency=None):
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            return False
        else:
            tp = ThermalProperties(self._mesh.get_frequencies(),
                                   weights=self._mesh.get_weights(),
                                   eigenvectors=self._mesh.get_eigenvectors(),
                                   is_projection=is_projection,
                                   band_indices=band_indices,
                                   cutoff_frequency=cutoff_frequency)
            tp.set_thermal_properties(t_step=t_step,
                                      t_max=t_max,
                                      t_min=t_min)
            self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        return self._thermal_properties.plot_thermal_properties()

    def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    def set_partial_DOS(self,
                        sigma=None,
                        freq_min=None,
                        freq_max=None,
                        freq_pitch=None,
                        tetrahedron_method=False,
                        direction=None):
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)
        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
        if direction is not None:
            direction_cart = np.dot(direction, self._primitive.get_cell())
        else:
            direction_cart = None
        pdos = PartialDos(self._mesh,
                          sigma=sigma,
                          tetrahedron_method=tetrahedron_method,
                          direction=direction_cart)
        pdos.set_draw_area(freq_min, freq_max, freq_pitch)
        pdos.run()
        self._pdos = pdos

    def get_partial_DOS(self):
        """
        Retern frequencies and partial_dos.
        The first element is freqs and the second is partial_dos.
        
        frequencies: [freq1, freq2, ...]
        partial_dos:
          [[atom1-freq1, atom1-freq2, ...],
           [atom2-freq1, atom2-freq2, ...],
           ...]
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        return self._pdos.plot_pdos(indices=pdos_indices,
                                    legend=legend)

    def write_partial_DOS(self):
        self._pdos.write()

    def set_total_DOS(self,
                      sigma=None,
                      freq_min=None,
                      freq_max=None,
                      freq_pitch=None,
                      tetrahedron_method=False):

        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(freq_min, freq_max, freq_pitch)
        total_dos.run()
        self._total_dos = total_dos

    def get_total_DOS(self):
        """
        Retern frequencies and total dos.
        The first element is freqs and the second is total dos.
        
        frequencies: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(),
            freq_max_fit=freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        return self._total_dos.plot_dos()

    def write_total_DOS(self):
        self._total_dos.write()

    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  direction=None,
                                  cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers() 

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
            
        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        td.run()
        
        self._thermal_displacements = td

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()
        
    def plot_thermal_displacements(self, is_legend=False):
        return self._thermal_displacements.plot(is_legend)

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    def set_thermal_displacement_matrices(self,
                                           t_step=10,
                                           t_max=1000,
                                           t_min=0,
                                           cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers() 

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
            
        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        tdm = ThermalDisplacementMatrices(frequencies,
                                           eigvecs,
                                           self._primitive.get_masses(),
                                           cutoff_frequency=cutoff_frequency)
        tdm.set_temperature_range(t_min, t_max, t_step)
        tdm.run()
        
        self._thermal_displacement_matrices = tdm

    def get_thermal_displacement_matrices(self):
        if self._thermal_displacement_matrices is not None:
            return self._thermal_displacement_matrices.get_thermal_displacement_matrices()
        
    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()
        
    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_frequency=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        td.run(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    def set_qpoints_phonon(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False,
                           factor=VaspToTHz):
        self._set_dynamical_matrix()
        self._qpoints_phonon = QpointsPhonon(
            q_points,
            self._dynamical_matrix,
            nac_q_direction=nac_q_direction,
            is_eigenvectors=is_eigenvectors,
            group_velocity=self._group_velocity,
            write_dynamical_matrices=write_dynamical_matrices,
            factor=self._factor)
        
    def get_qpoints_phonon(self):
        return (self._qpoints_phonon.get_frequencies(),
                self._qpoints_phonon.get_eigenvectors())
    
    def write_yaml_qpoints_phonon(self):
        self._qpoints_phonon.write_yaml()

    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        self._set_dynamical_matrix()
        if q_point is None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  shift=shift)
        else:
            animation = Animation(q_point,
                                  self._dynamical_matrix,
                                  shift=shift)
        if anime_type == 'v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)

            
        if (anime_type == 'arc' or
            anime_type == 'xyz' or
            anime_type == 'jmol' or
            anime_type == 'poscar'):
            if band_index is None or amplitude is None or num_div is None:
                print "Parameters are not correctly set for animation."
                sys.exit(1)

            if anime_type == 'arc' or anime_type is None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div)
    
            if anime_type == 'xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor)
    
            if anime_type == 'jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)
    
            if anime_type == 'poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div)
                    
    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        self._set_dynamical_matrix()
        self._modulation = Modulation(self._dynamical_matrix,
                                      dimension,
                                      phonon_modes,
                                      delta_q=delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()
                    
    def get_modulations(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulations()
                
    def get_delta_modulations(self):
        """Return modulations relative to equilibrium supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.
        
        """
        return self._modulation.get_delta_modulations()
                
    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()
                          
    def write_yaml_modulations(self):
        self._modulation.write_yaml()

    def set_irreps(self,
                   q,
                   is_little_cogroup=False,
                   nac_q_direction=None,
                   degeneracy_tolerance=1e-4):
        self._set_dynamical_matrix()
        self._irreps = IrReps(
            self._dynamical_matrix,
            q,
            is_little_cogroup=is_little_cogroup,
            nac_q_direction=nac_q_direction,
            factor=self._factor,
            symprec=self._symprec,
            degeneracy_tolerance=degeneracy_tolerance,
            log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps
        
    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    def set_group_velocity(self, q_length=None):
        self._set_dynamical_matrix()
        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_length=q_length,
            symmetry=self._primitive_symmetry,
            frequency_factor_to_THz=self._factor)

    def get_group_velocity(self):
        return self._group_velocity.get_group_velocity()
        
    def get_group_velocity_at_q(self, q_point):
        if self._group_velocity is None:
            self.set_group_velocity()
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    def _run_force_constants_from_forces(self,
                                         distributed_atom_list=None,
                                         decimals=None,
                                         computation_algorithm="svd"):
        if self._displacement_dataset is not None:
            self._force_constants = get_fc2(
                self._supercell,
                self._symmetry,
                self._displacement_dataset,
                atom_list=distributed_atom_list,
                decimals=decimals,
                computation_algorithm=computation_algorithm)

    def _set_dynamical_matrix(self):
        if self._nac_params is None:
            self._dynamical_matrix = DynamicalMatrix(
                self._supercell,
                self._primitive,
                self._force_constants,
                decimals=self._dynamical_matrix_decimals,
                symprec=self._symprec)
        else:
            self._dynamical_matrix = DynamicalMatrixNAC(
                self._supercell,
                self._primitive,
                self._force_constants,
                nac_params=self._nac_params,
                decimals=self._dynamical_matrix_decimals,
                symprec=self._symprec)

    def _search_symmetry(self):
        self._symmetry = Symmetry(self._supercell,
                                  self._symprec,
                                  self._is_symmetry)

    def _search_primitive_symmetry(self):
        self._primitive_symmetry = Symmetry(self._primitive,
                                            self._symprec,
                                            self._is_symmetry)
        
        if (len(self._symmetry.get_pointgroup_operations()) !=
            len(self._primitive_symmetry.get_pointgroup_operations())):
            print ("Warning: point group symmetries of supercell and primitive"
                   "cell are different.")

    def _build_supercell(self):
        self._supercell = get_supercell(self._unitcell,
                                        self._supercell_matrix,
                                        self._symprec)

    def _build_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacement_dataset['first_atoms']:
            positions = self._supercell.get_positions()
            positions[disp['number']] += disp['displacement']
            supercells.append(Atoms(
                    numbers=self._supercell.get_atomic_numbers(),
                    masses=self._supercell.get_masses(),
                    magmoms=self._supercell.get_magnetic_moments(),
                    positions=positions,
                    cell=self._supercell.get_cell(),
                    pbc=True))

        self._supercells_with_displacements = supercells

    def _build_primitive_cell(self):
        """
        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T
        """

        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        if self._primitive_matrix is None:
            trans_mat = inv_supercell_matrix
        else:
            trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix)
        self._primitive = get_primitive(
            self._supercell, trans_mat, self._symprec)
        num_satom = self._supercell.get_number_of_atoms()
        num_patom = self._primitive.get_number_of_atoms()
        if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1:
            return True
        else:
            return False
Beispiel #4
0
class Phonopy:
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 distance=0.01,
                 factor=VaspToTHz,
                 is_auto_displacements=True,
                 symprec=1e-5,
                 is_symmetry=True,
                 log_level=0):
        self._symprec = symprec
        self._unitcell = unitcell
        self._supercell_matrix = supercell_matrix
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._log_level = log_level
        self._supercell = None
        self._set_supercell()
        self._symmetry = None
        self._set_symmetry()

        # set_displacements (used only in preprocess)
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None
        if is_auto_displacements:
            self.generate_displacements(distance)

        # set_post_process
        self._primitive = None
        self._dynamical_matrix = None
        self._is_nac = False

        # set_force_constants or set_forces
        self._set_of_forces_objects = None
        self._force_constants = None

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None

        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None

    def get_primitive(self):
        return self._primitive

    primitive = property(get_primitive)

    def set_primitive(self, primitive):
        self._primitive = primitive

    def get_unitcell(self):
        return self._unitcell

    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell

    supercell = property(get_supercell)

    def set_supercell(self, supercell):
        self._supercell = supercell

    def get_symmetry(self):
        return self._symmetry

    symmetry = property(get_symmetry)

    def get_unit_conversion_factor(self):
        return self._factor

    unit_conversion_factor = property(get_unit_conversion_factor)

    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 set_displacements(self, displacements):
        """Set displacements manually

        displacemsts: List of disctionaries
           [[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
           
        """

        self._displacements = displacements
        self._set_supercells_with_displacements()

    def get_displacements(self):
        return self._displacements

    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions

    displacement_directions = property(get_displacement_directions)

    def get_supercells_with_displacements(self):
        return self._supercells_with_displacements

    def set_post_process(self,
                         primitive_matrix=np.eye(3, dtype=float),
                         sets_of_forces=None,
                         set_of_forces_objects=None,
                         force_constants=None,
                         is_nac=False,
                         calculate_full_force_constants=False,
                         force_constants_decimals=None,
                         dynamical_matrix_decimals=None):
        """
        Set forces or force constants to prepare phonon calculations.
        The order of 'sets_of_forces' has to correspond to that of
        'displacements' that should be already stored.

        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T

        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]

        set_of_forces_objects:
           [FORCES_object, FORCES_object, FORCES_object, ...]
        """

        self._is_nac = is_nac

        # Primitive cell
        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        self._primitive = Primitive(
            self._supercell, np.dot(inv_supercell_matrix, primitive_matrix),
            self._symprec)

        # Set set of FORCES objects or force constants
        if sets_of_forces is not None:
            self.set_forces(sets_of_forces)
        elif set_of_forces_objects is not None:
            self.set_force_sets(set_of_forces_objects)
        elif force_constants is not None:
            self.set_force_constants(force_constants)

        # Calculate force cosntants from forces (full or symmetry reduced)
        if self._set_of_forces_objects is not None:
            if calculate_full_force_constants:
                self.set_force_constants_from_forces(
                    distributed_atom_list=None,
                    force_constants_decimals=force_constants_decimals)
            else:
                p2s_map = self._primitive.get_primitive_to_supercell_map()
                self.set_force_constants_from_forces(
                    distributed_atom_list=p2s_map,
                    force_constants_decimals=force_constants_decimals)

        if self._force_constants is None:
            print "In set_post_process, sets_of_forces or force_constants"
            print "has to be set."
            return False

        # Dynamical Matrix
        self.set_dynamical_matrix(decimals=dynamical_matrix_decimals)

    def set_nac_params(self, nac_params, method='wang'):
        if self._is_nac:
            self._dynamical_matrix.set_nac_params(nac_params, method)

    def set_dynamical_matrix(self, decimals=None):
        if self._is_nac:
            self._dynamical_matrix = \
                DynamicalMatrixNAC(self._supercell,
                                   self._primitive,
                                   self._force_constants,
                                   decimals=decimals,
                                   symprec=self._symprec)
        else:
            self._dynamical_matrix = \
                DynamicalMatrix(self._supercell,
                                self._primitive,
                                self._force_constants,
                                decimals=decimals,
                                symprec=self._symprec)

    def get_dynamical_matrix(self):
        return self._dynamical_matrix

    dynamical_matrix = property(get_dynamical_matrix)

    def set_forces(self, sets_of_forces):
        forces = []
        for i, disp in enumerate(self._displacements):
            forces.append(Forces(disp[0], disp[1:4], sets_of_forces[i]))
        self._set_of_forces_objects = forces

    def set_force_constants_from_forces(self,
                                        distributed_atom_list=None,
                                        force_constants_decimals=None):
        self._force_constants = get_force_constants(
            self._set_of_forces_objects,
            self._symmetry,
            self._supercell,
            atom_list=distributed_atom_list,
            decimals=force_constants_decimals)

    def set_force_constants_zero_with_radius(self, cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants

    def set_force_sets(self, sets_of_forces_objects):
        self._set_of_forces_objects = sets_of_forces_objects

    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)

    def get_force_constants(self):
        return self._force_constants

    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants, self._supercell,
                                     self._primitive, self._symprec)

    def get_dynamical_matrix_at_q(self, q):
        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()

    # Frequency at a q-point
    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor

    # Frequency and eigenvector at a q-point
    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors

        ## This expression may not be supported in old python versions.
        # frequencies = np.array(
        #     [np.sqrt(x) if x > 0 else -np.sqrt(-x) for x in eigvals])
        # return frequencies * self._factor, eigenvectors

    # Band structure
    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):

        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            self._primitive,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(), band.get_distances(),
                band.get_frequencies(), band.get_eigenvectors())

    def plot_band_structure(self, symbols=None):
        return self._band_structure.plot_band(symbols)

    def write_yaml_band_structure(self):
        self._band_structure.write_yaml()

    # Mesh sampling
    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_symmetry=True,
                 is_band_connection=False,
                 is_eigenvectors=False,
                 is_gamma_center=False):

        self._mesh = Mesh(self._dynamical_matrix,
                          self._primitive,
                          mesh,
                          shift=shift,
                          is_time_reversal=is_time_reversal,
                          is_mesh_symmetry=is_symmetry,
                          is_eigenvectors=is_eigenvectors,
                          is_band_connection=is_band_connection,
                          is_gamma_center=is_gamma_center,
                          group_velocity=self._group_velocity,
                          factor=self._factor,
                          symprec=self._symprec)

    def get_mesh(self):
        return (self._mesh.get_qpoints(), self._mesh.get_weights(),
                self._mesh.get_frequencies(), self._mesh.get_eigenvectors())

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    def write_hdf5_mesh(self):
        self._mesh.write_hdf5()

    # Thermal property
    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               is_projection=False,
                               cutoff_frequency=None):
        if self._mesh == None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        tp = ThermalProperties(self._mesh.get_frequencies(),
                               weights=self._mesh.get_weights(),
                               eigenvectors=self._mesh.get_eigenvectors(),
                               is_projection=is_projection,
                               cutoff_frequency=cutoff_frequency)
        tp.set_thermal_properties(t_step, t_max, t_min)
        self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        return self._thermal_properties.plot_thermal_properties()

    def write_yaml_thermal_properties(self,
                                      filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    # Partial DOS
    def set_partial_DOS(self,
                        sigma=None,
                        omega_min=None,
                        omega_max=None,
                        omega_pitch=None,
                        tetrahedron_method=False):

        if self._mesh == None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)
        if self._mesh.get_eigenvectors() == None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        pdos = PartialDos(self._mesh,
                          sigma=sigma,
                          tetrahedron_method=tetrahedron_method)
        pdos.set_draw_area(omega_min, omega_max, omega_pitch)
        pdos.calculate()
        self._pdos = pdos

    def get_partial_DOS(self):
        """
        Retern omegas and partial_dos.
        The first element is omegas and the second is partial_dos.
        
        omegas: [freq1, freq2, ...]
        partial_dos:
          [[elem1-freq1, elem1-freq2, ...],
           [elem2-freq1, elem2-freq2, ...],
           ...]

          where
           elem1: atom1-x compornent
           elem2: atom1-y compornent
           elem3: atom1-z compornent
           elem4: atom2-x compornent
           ...
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        return self._pdos.plot_pdos(indices=pdos_indices, legend=legend)

    def write_partial_DOS(self):
        self._pdos.write()

    # Total DOS
    def set_total_DOS(self,
                      sigma=None,
                      omega_min=None,
                      omega_max=None,
                      omega_pitch=None,
                      tetrahedron_method=False):

        if self._mesh == None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(omega_min, omega_max, omega_pitch)
        total_dos.calculate()
        self._total_dos = total_dos

    def get_total_DOS(self):
        """
        Retern omegas and total dos.
        The first element is omegas and the second is total dos.
        
        omegas: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(), freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        return self._total_dos.plot_dos()

    def write_total_DOS(self):
        self._total_dos.write()

    # Thermal displacement
    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  direction=None,
                                  cutoff_eigenvalue=None):
        """
        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh == None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_eigenvalue=cutoff_eigenvalue)
        td.set_temperature_range(t_min, t_max, t_step)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        # td.run()
        td.run_mesh()

        self._thermal_displacements = td

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()

    def plot_thermal_displacements(self, is_legend=False):
        return self._thermal_displacements.plot(is_legend)

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    def write_hdf5_thermal_displacements(self):
        self._thermal_displacements.write_hdf5()

    # Thermal displacement matrices
    def set_thermal_displacement_matrices(self,
                                          t_step=10,
                                          t_max=1000,
                                          t_min=0,
                                          cutoff_eigenvalue=None):
        """
        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh == None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        tdm = ThermalDisplacementMatrices(frequencies,
                                          eigvecs,
                                          self._primitive.get_masses(),
                                          cutoff_eigenvalue=cutoff_eigenvalue)
        tdm.set_temperature_range(t_min, t_max, t_step)
        # tdm.run()
        tdm.run_mesh()

        self._thermal_displacement_matrices = tdm

    def get_thermal_displacement_matrices(self):
        if self._thermal_displacement_matrices is not None:
            return self._thermal_displacement_matrices.get_thermal_displacement_matrices(
            )

    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()

    def write_hdf5_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_hdf5()

    # Thermal displacement
    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_eigenvalue=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              symprec=self._symprec,
                              cutoff_eigenvalue=cutoff_eigenvalue)
        td.set_temperature_range(t_min, t_max, t_step)
        # td.run(atom_pairs)
        td.run_mesh(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    def write_hdf5_thermal_distances(self):
        self._thermal_distances.write_hdf5()

    # Q-points mode
    def write_yaml_qpoints(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False,
                           factor=VaspToTHz):

        write_yaml_qpoints(q_points,
                           self._primitive,
                           self._dynamical_matrix,
                           nac_q_direction=nac_q_direction,
                           is_eigenvectors=is_eigenvectors,
                           group_velocity=self._group_velocity,
                           write_dynamical_matrices=write_dynamical_matrices,
                           factor=self._factor)

    # Animation
    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        if q_point == None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  self._primitive,
                                  shift=shift)
        else:
            animation = Animation(q_point,
                                  self._dynamical_matrix,
                                  self._primitive,
                                  shift=shift)
        if anime_type == 'v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)

        if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol'
                or anime_type == 'poscar'):
            if band_index == None or amplitude == None or num_div == None:
                print "Parameters are not correctly set for animation."
                sys.exit(1)

            if anime_type == 'arc' or anime_type == None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index, amplitude, num_div)

            if anime_type == 'xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index, amplitude, num_div,
                                        self._factor)

            if anime_type == 'jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)

            if anime_type == 'poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index, amplitude, num_div)

    # Modulation
    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        self._modulation = Modulation(self._dynamical_matrix,
                                      self._primitive,
                                      dimension=dimension,
                                      phonon_modes=phonon_modes,
                                      delta_q=delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()

    def get_modulations(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulations()

    def get_delta_modulations(self):
        """Return modulations relative to equilibrium supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.
        
        """
        return self._modulation.get_delta_modulations()

    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()

    def write_yaml_modulations(self):
        self._modulation.write_yaml()

    # Characters of irreducible representations
    def set_irreps(self, q, degeneracy_tolerance=1e-4):
        self._irreps = IrReps(self._dynamical_matrix,
                              q,
                              factor=self._factor,
                              symprec=self._symprec,
                              degeneracy_tolerance=degeneracy_tolerance,
                              log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps

    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    # Group velocity
    def set_group_velocity(self, q_points=None, q_length=1e-4):
        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_points=q_points,
            symmetry=self._symmetry,
            q_length=q_length,
            frequency_factor_to_THz=self._factor)

    def get_group_velocity(self, q_point):
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    def _set_supercell(self):
        self._supercell = get_supercell(self._unitcell, self._supercell_matrix,
                                        self._symprec)

    def _set_symmetry(self):
        self._symmetry = Symmetry(self._supercell, self._symprec,
                                  self._is_symmetry)

    def _set_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacements:
            positions = self._supercell.get_positions()
            positions[disp[0]] += disp[1:4]
            supercells.append(
                Atoms(numbers=self._supercell.get_atomic_numbers(),
                      masses=self._supercell.get_masses(),
                      magmoms=self._supercell.get_magnetic_moments(),
                      positions=positions,
                      cell=self._supercell.get_cell(),
                      pbc=True))

        self._supercells_with_displacements = supercells
Beispiel #5
0
class Phonopy:
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 primitive_matrix=None,
                 nac_params=None,
                 distance=0.01,
                 factor=VaspToTHz,
                 is_auto_displacements=True,
                 dynamical_matrix_decimals=None,
                 force_constants_decimals=None,
                 symprec=1e-5,
                 is_symmetry=True,
                 log_level=0):
        self._symprec = symprec
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._log_level = log_level

        # Create supercell and primitive cell
        self._unitcell = unitcell
        self._supercell_matrix = supercell_matrix
        self._primitive_matrix = primitive_matrix
        self._supercell = None
        self._primitive = None
        self._build_supercell()
        self._build_primitive_cell()

        # Set supercell and primitive symmetry
        self._symmetry = None
        self._primitive_symmetry = None
        self._search_symmetry()
        self._search_primitive_symmetry()

        # set_displacements (used only in preprocess)
        self._displacement_dataset = None
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None
        if is_auto_displacements:
            self.generate_displacements(distance=distance)

        # set_force_constants or set_forces
        self._force_constants = None
        self._force_constants_decimals = force_constants_decimals

        # set_dynamical_matrix
        self._dynamical_matrix = None
        self._nac_params = nac_params
        self._dynamical_matrix_decimals = dynamical_matrix_decimals

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_tetrahedron_method
        self._tetrahedron_method = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None

        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None

    def set_post_process(self,
                         primitive_matrix=None,
                         sets_of_forces=None,
                         displacement_dataset=None,
                         force_constants=None,
                         is_nac=None):
        print
        print(
            "********************************** Warning"
            "**********************************")
        print "set_post_process will be obsolete."
        print(
            "  produce_force_constants is used instead of set_post_process"
            " for producing")
        print("  force constants from forces.")
        if primitive_matrix is not None:
            print(
                "  primitive_matrix has to be given at Phonopy::__init__"
                " object creation.")
        print(
            "******************************************"
            "**********************************")
        print

        if primitive_matrix is not None:
            self._primitive_matrix = primitive_matrix
            self._build_primitive_cell()
            self._search_primitive_symmetry()

        if sets_of_forces is not None:
            self.set_forces(sets_of_forces)
        elif displacement_dataset is not None:
            self._displacement_dataset = displacement_dataset
        elif force_constants is not None:
            self.set_force_constants(force_constants)

        if self._displacement_dataset is not None:
            self.produce_force_constants()

    def set_masses(self, masses):
        p_masses = np.array(masses)
        self._primitive.set_masses(p_masses)
        p2p_map = self._primitive.get_primitive_to_primitive_map()
        s_masses = p_masses[[
            p2p_map[x]
            for x in self._primitive.get_supercell_to_primitive_map()
        ]]
        self._supercell.set_masses(s_masses)
        u2s_map = self._supercell.get_unitcell_to_supercell_map()
        u_masses = s_masses[u2s_map]
        self._unitcell.set_masses(u_masses)

    def get_primitive(self):
        return self._primitive

    primitive = property(get_primitive)

    def get_unitcell(self):
        return self._unitcell

    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell

    supercell = property(get_supercell)

    def set_supercell(self, supercell):
        self._supercell = supercell

    def get_symmetry(self):
        """return symmetry of supercell"""
        return self._symmetry

    symmetry = property(get_symmetry)

    def get_primitive_symmetry(self):
        """return symmetry of primitive cell"""
        return self._primitive_symmetry

    def get_unit_conversion_factor(self):
        return self._factor

    unit_conversion_factor = property(get_unit_conversion_factor)

    def produce_force_constants(self,
                                forces=None,
                                calculate_full_force_constants=True,
                                computation_algorithm="svd"):
        if forces is not None:
            self.set_forces(forces)

        if calculate_full_force_constants:
            self._run_force_constants_from_forces(
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)
        else:
            p2s_map = self._primitive.get_primitive_to_supercell_map()
            self._run_force_constants_from_forces(
                distributed_atom_list=p2s_map,
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)

    def set_nac_params(self, nac_params=None, method=None):
        if method is not None:
            print "set_nac_params:"
            print "  Keyword argument of \"method\" is not more supported."
        self._nac_params = nac_params

    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 set_displacements(self, displacements):
        print
        print(
            "********************************** Warning"
            "**********************************")
        print "set_displacements is obsolete. Do nothing."
        print(
            "******************************************"
            "**********************************")
        print

    def get_displacements(self):
        return self._displacements

    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions

    displacement_directions = property(get_displacement_directions)

    def get_displacement_dataset(self):
        return self._displacement_dataset

    def get_supercells_with_displacements(self):
        if self._displacement_dataset is None:
            return None
        else:
            self._build_supercells_with_displacements()
            return self._supercells_with_displacements

    def get_dynamical_matrix(self):
        return self._dynamical_matrix

    dynamical_matrix = property(get_dynamical_matrix)

    def set_forces(self, sets_of_forces):
        """
        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]
        """
        for disp, forces in zip(self._displacement_dataset['first_atoms'],
                                sets_of_forces):
            disp['forces'] = forces

    def set_force_constants_zero_with_radius(self, cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants

    def set_force_sets(self, force_sets):
        print
        print(
            "********************************** Warning"
            "**********************************")
        print "set_force_sets will be obsolete."
        print("   The method name is changed to set_displacement_dataset.")
        print(
            "******************************************"
            "**********************************")
        print
        self.set_displacement_dataset(force_sets)

    def set_displacement_dataset(self, displacement_dataset):
        """
        displacement_dataset:
           {'natom': number_of_atoms_in_supercell,
            'first_atoms': [
              {'number': atom index of displaced atom,
               'displacement': displacement in Cartesian coordinates,
               'direction': displacement direction with respect to axes
               'forces': forces on atoms in supercell},
              {...}, ...]}
        """
        self._displacement_dataset = displacement_dataset

        self._displacements = []
        self._displacement_directions = []
        for disp in self._displacement_dataset['first_atoms']:
            x = disp['displacement']
            self._displacements.append([disp['number'], x[0], x[1], x[2]])
            if 'direction' in disp:
                y = disp['direction']
                self._displacement_directions.append(
                    [disp['number'], y[0], y[1], y[2]])
        if not self._displacement_directions:
            self._displacement_directions = None

    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)

    def symmetrize_force_constants_by_space_group(self):
        rotations = self._symmetry.get_symmetry_operations()['rotations']
        translations = self._symmetry.get_symmetry_operations()['translations']
        set_tensor_symmetry(self._force_constants,
                            self._supercell.get_cell().T,
                            self._supercell.get_scaled_positions(), rotations,
                            translations, self._symprec)

    def get_force_constants(self):
        return self._force_constants

    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants, self._supercell,
                                     self._primitive, self._symprec)

    def set_dynamical_matrix(self):
        self._set_dynamical_matrix()

    def get_dynamical_matrix_at_q(self, q):
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()

    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor

    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors

    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):
        self._set_dynamical_matrix()
        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(), band.get_distances(),
                band.get_frequencies(), band.get_eigenvectors())

    def plot_band_structure(self, symbols=None):
        return self._band_structure.plot_band(symbols)

    def write_yaml_band_structure(self):
        self._band_structure.write_yaml()

    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_mesh_symmetry=True,
                 is_eigenvectors=False,
                 is_gamma_center=False):
        self._set_dynamical_matrix()
        self._mesh = Mesh(
            self._dynamical_matrix,
            mesh,
            shift=shift,
            is_time_reversal=is_time_reversal,
            is_mesh_symmetry=is_mesh_symmetry,
            is_eigenvectors=is_eigenvectors,
            is_gamma_center=is_gamma_center,
            group_velocity=self._group_velocity,
            rotations=self._primitive_symmetry.get_pointgroup_operations(),
            factor=self._factor)

    def get_mesh(self):
        return (self._mesh.get_qpoints(), self._mesh.get_weights(),
                self._mesh.get_frequencies(), self._mesh.get_eigenvectors())

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               is_projection=False,
                               band_indices=None,
                               cutoff_frequency=None):
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            return False
        else:
            tp = ThermalProperties(self._mesh.get_frequencies(),
                                   weights=self._mesh.get_weights(),
                                   eigenvectors=self._mesh.get_eigenvectors(),
                                   is_projection=is_projection,
                                   band_indices=band_indices,
                                   cutoff_frequency=cutoff_frequency)
            tp.set_thermal_properties(t_step=t_step, t_max=t_max, t_min=t_min)
            self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        return self._thermal_properties.plot_thermal_properties()

    def write_yaml_thermal_properties(self,
                                      filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    def set_partial_DOS(self,
                        sigma=None,
                        freq_min=None,
                        freq_max=None,
                        freq_pitch=None,
                        tetrahedron_method=False,
                        direction=None):
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)
        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
        if direction is not None:
            direction_cart = np.dot(direction, self._primitive.get_cell())
        else:
            direction_cart = None
        pdos = PartialDos(self._mesh,
                          sigma=sigma,
                          tetrahedron_method=tetrahedron_method,
                          direction=direction_cart)
        pdos.set_draw_area(freq_min, freq_max, freq_pitch)
        pdos.run()
        self._pdos = pdos

    def get_partial_DOS(self):
        """
        Retern frequencies and partial_dos.
        The first element is freqs and the second is partial_dos.
        
        frequencies: [freq1, freq2, ...]
        partial_dos:
          [[atom1-freq1, atom1-freq2, ...],
           [atom2-freq1, atom2-freq2, ...],
           ...]
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        return self._pdos.plot_pdos(indices=pdos_indices, legend=legend)

    def write_partial_DOS(self):
        self._pdos.write()

    def set_total_DOS(self,
                      sigma=None,
                      freq_min=None,
                      freq_max=None,
                      freq_pitch=None,
                      tetrahedron_method=False):

        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(freq_min, freq_max, freq_pitch)
        total_dos.run()
        self._total_dos = total_dos

    def get_total_DOS(self):
        """
        Retern frequencies and total dos.
        The first element is freqs and the second is total dos.
        
        frequencies: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        return self._total_dos.plot_dos()

    def write_total_DOS(self):
        self._total_dos.write()

    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  direction=None,
                                  cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        td.run()

        self._thermal_displacements = td

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()

    def plot_thermal_displacements(self, is_legend=False):
        return self._thermal_displacements.plot(is_legend)

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    def set_thermal_displacement_matrices(self,
                                          t_step=10,
                                          t_max=1000,
                                          t_min=0,
                                          cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh is None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        tdm = ThermalDisplacementMatrices(frequencies,
                                          eigvecs,
                                          self._primitive.get_masses(),
                                          cutoff_frequency=cutoff_frequency)
        tdm.set_temperature_range(t_min, t_max, t_step)
        tdm.run()

        self._thermal_displacement_matrices = tdm

    def get_thermal_displacement_matrices(self):
        if self._thermal_displacement_matrices is not None:
            return self._thermal_displacement_matrices.get_thermal_displacement_matrices(
            )

    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()

    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_frequency=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        td.run(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    def set_qpoints_phonon(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False,
                           factor=VaspToTHz):
        self._set_dynamical_matrix()
        self._qpoints_phonon = QpointsPhonon(
            q_points,
            self._dynamical_matrix,
            nac_q_direction=nac_q_direction,
            is_eigenvectors=is_eigenvectors,
            group_velocity=self._group_velocity,
            write_dynamical_matrices=write_dynamical_matrices,
            factor=self._factor)

    def get_qpoints_phonon(self):
        return (self._qpoints_phonon.get_frequencies(),
                self._qpoints_phonon.get_eigenvectors())

    def write_yaml_qpoints_phonon(self):
        self._qpoints_phonon.write_yaml()

    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        self._set_dynamical_matrix()
        if q_point is None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  shift=shift)
        else:
            animation = Animation(q_point, self._dynamical_matrix, shift=shift)
        if anime_type == 'v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)

        if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol'
                or anime_type == 'poscar'):
            if band_index is None or amplitude is None or num_div is None:
                print "Parameters are not correctly set for animation."
                sys.exit(1)

            if anime_type == 'arc' or anime_type is None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index, amplitude, num_div)

            if anime_type == 'xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index, amplitude, num_div,
                                        self._factor)

            if anime_type == 'jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)

            if anime_type == 'poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index, amplitude, num_div)

    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        self._set_dynamical_matrix()
        self._modulation = Modulation(self._dynamical_matrix,
                                      dimension,
                                      phonon_modes,
                                      delta_q=delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()

    def get_modulations(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulations()

    def get_delta_modulations(self):
        """Return modulations relative to equilibrium supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.
        
        """
        return self._modulation.get_delta_modulations()

    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()

    def write_yaml_modulations(self):
        self._modulation.write_yaml()

    def set_irreps(self,
                   q,
                   is_little_cogroup=False,
                   nac_q_direction=None,
                   degeneracy_tolerance=1e-4):
        self._set_dynamical_matrix()
        self._irreps = IrReps(self._dynamical_matrix,
                              q,
                              is_little_cogroup=is_little_cogroup,
                              nac_q_direction=nac_q_direction,
                              factor=self._factor,
                              symprec=self._symprec,
                              degeneracy_tolerance=degeneracy_tolerance,
                              log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps

    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    def set_group_velocity(self, q_length=None):
        self._set_dynamical_matrix()
        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_length=q_length,
            symmetry=self._primitive_symmetry,
            frequency_factor_to_THz=self._factor)

    def get_group_velocity(self):
        return self._group_velocity.get_group_velocity()

    def get_group_velocity_at_q(self, q_point):
        if self._group_velocity is None:
            self.set_group_velocity()
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    def _run_force_constants_from_forces(self,
                                         distributed_atom_list=None,
                                         decimals=None,
                                         computation_algorithm="svd"):
        if self._displacement_dataset is not None:
            self._force_constants = get_fc2(
                self._supercell,
                self._symmetry,
                self._displacement_dataset,
                atom_list=distributed_atom_list,
                decimals=decimals,
                computation_algorithm=computation_algorithm)

    def _set_dynamical_matrix(self):
        if self._nac_params is None:
            self._dynamical_matrix = DynamicalMatrix(
                self._supercell,
                self._primitive,
                self._force_constants,
                decimals=self._dynamical_matrix_decimals,
                symprec=self._symprec)
        else:
            self._dynamical_matrix = DynamicalMatrixNAC(
                self._supercell,
                self._primitive,
                self._force_constants,
                nac_params=self._nac_params,
                decimals=self._dynamical_matrix_decimals,
                symprec=self._symprec)

    def _search_symmetry(self):
        self._symmetry = Symmetry(self._supercell, self._symprec,
                                  self._is_symmetry)

    def _search_primitive_symmetry(self):
        self._primitive_symmetry = Symmetry(self._primitive, self._symprec,
                                            self._is_symmetry)

        if (len(self._symmetry.get_pointgroup_operations()) != len(
                self._primitive_symmetry.get_pointgroup_operations())):
            print(
                "Warning: point group symmetries of supercell and primitive"
                "cell are different.")

    def _build_supercell(self):
        self._supercell = get_supercell(self._unitcell, self._supercell_matrix,
                                        self._symprec)

    def _build_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacement_dataset['first_atoms']:
            positions = self._supercell.get_positions()
            positions[disp['number']] += disp['displacement']
            supercells.append(
                Atoms(numbers=self._supercell.get_atomic_numbers(),
                      masses=self._supercell.get_masses(),
                      magmoms=self._supercell.get_magnetic_moments(),
                      positions=positions,
                      cell=self._supercell.get_cell(),
                      pbc=True))

        self._supercells_with_displacements = supercells

    def _build_primitive_cell(self):
        """
        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T
        """

        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        if self._primitive_matrix is None:
            trans_mat = inv_supercell_matrix
        else:
            trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix)
        self._primitive = get_primitive(self._supercell, trans_mat,
                                        self._symprec)
        num_satom = self._supercell.get_number_of_atoms()
        num_patom = self._primitive.get_number_of_atoms()
        if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1:
            return True
        else:
            return False
Beispiel #6
0
class Collision():
    def __init__(
            self,
            interaction,
            sigmas=np.array([0.1]),
            temperatures=None,
            lang="C",
            is_tetrahedron_method=False,
            mass_variances=None,
            length=None,
            gv_delta_q=None,
            is_adaptive_sigma=False,
            is_group_velocity=False,  # Use gv to determine smearing factor
            write=False,
            read=False,
            cutoff_frequency=1e-4):  #unit: THz
        self._pp = interaction
        self._sigmas = sigmas
        self._temperatures = temperatures
        self._mesh = self._pp.get_mesh_numbers()
        self._is_nosym = self._pp._is_nosym
        if interaction._is_nosym:
            self._point_operations = np.array(
                [[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype="intc")
        else:
            self._point_operations = self._pp.get_point_group_operations()
        self._kpoint_operations = self._pp._kpoint_operations
        self._is_dispersed = self._pp.get_is_dispersed()
        self._cutoff_frequency = cutoff_frequency
        self._grid_point = None
        self._grid_point_triplets = None
        self._triplet_weights = None
        self._is_thm = is_tetrahedron_method
        self._lang = lang
        self._frequencies_all = None
        self._eigenvectors_all = None
        self._band_indices = None
        self._fc3_normal_squared = None
        self._occupations_all = None
        self._sigma = None
        self._temperature = None
        self._isigma = None
        self._itemp = None
        self._is_group_velocity = is_group_velocity
        self._write_col = write
        self._read_col = read
        self._is_adaptive_sigma = is_adaptive_sigma
        self._is_on_iteration = False
        self._asigma = None
        self._gamma_all = None
        self._irr_mapping_indices = None
        self._collision_in = None
        self._collision_out = None
        self._collision_in_all = None
        self._is_read_error = None
        self._unit_conversion = np.pi / 4 * (
            (Hbar * EV)**3  # Hbar * EV : hbar
            * EV**2 / Angstrom**6 / (2 * np.pi * THz)**3 / AMU**3 /
            (Hbar * EV)**2 / (2 * np.pi * THz)**2 / np.prod(self._mesh))
        self._init_grids_properties()
        self._length = length
        self._gv_delta_q = gv_delta_q
        if is_group_velocity or self._length is not None:
            self._init_group_velocity()
        if mass_variances is not None:
            self._collision_iso = \
                CollisionIso(self._mesh,
                             self._pp._primitive,
                             mass_variances,
                             self._band_indices,
                             self._sigma)
            self._collision_iso.set_dynamical_matrix(
                dynamical_matrix=self._pp.get_dynamical_matrix())
        else:
            self._collision_iso = None

    def _init_group_velocity(self):
        self._group_velocity = GroupVelocity(
            self._pp.get_dynamical_matrix(),
            symmetry=self._pp._symmetry,
            q_length=self._gv_delta_q,
            frequency_factor_to_THz=self._pp.get_frequency_factor_to_THz())
        num_grids_all, num_band = self._pp._frequencies.shape
        self._gv_all = np.zeros((num_grids_all, num_band, 3), dtype="double")
        self._gv_done_all = np.zeros(num_grids_all, dtype="bool")

    def _init_grids_properties(self):
        num_grids_all, num_band = self._pp._frequencies.shape
        num_temp = len(self._temperatures)
        num_sigma = len(self._sigmas)
        self._occupations_all = np.zeros((num_grids_all, num_temp, num_band),
                                         dtype="double")
        self._collision_out_all = np.zeros(
            (num_sigma, num_grids_all, num_temp, num_band), dtype="double")
        self._gamma_all = np.zeros_like(self._collision_out_all)
        self._n_done = np.zeros((num_grids_all, num_temp), dtype="bool")

    def set_grid(self, grid_point):
        if grid_point is None:
            self._grid_point = None
        else:
            self._pp.set_grid_point(grid_point)
            (self._grid_point_triplets,
             self._triplet_weights) = self._pp.get_triplets_at_q()

            self._grid_point = self._grid_point_triplets[0, 0]
            self._grid_address = self._pp.get_grid_address()
            self._qpoint = self._grid_address[self._grid_point] / np.double(
                self._mesh)
            kpg_index = get_kgp_index_at_grid(self._grid_address[grid_point],
                                              self._mesh,
                                              self._kpoint_operations)
            self._inv_rot_sum = self._kpoint_operations[kpg_index].sum(axis=0)
            self._kpg_at_q_index = kpg_index

            self._fc3_normal_squared = None
            if self._collision_iso is not None:
                grid_points2 = self.get_grid_point_triplets()[:, 1].astype(
                    "intc").copy()
                weights2 = self.get_triplets_weights()
                self._collision_iso.set_grid_point(grid_point,
                                                   grid_points2=grid_points2,
                                                   weights2=weights2)

    def set_pp_grid_points_all(self, grid_points):
        self._pp.set_grid_points(grid_points)

    def set_grids(self, grid_points):
        self._pp._grid_points = grid_points
        self._collision_done = None
        if self._pp._unique_triplets == None:
            self._pp.set_grid_points(grid_points)
        if not self._is_dispersed:
            bi = self._pp._band_indices
            nband = 3 * self._pp._primitive.get_number_of_atoms()
            luniq = len(self._pp._unique_triplets)
            try:
                self._collision_in_all = np.zeros((luniq, 3, len(bi), nband),
                                                  dtype="double")
            except MemoryError:
                print "A memory error occurs in allocating the whole collision array"
                print "--disperse is recommended as a possible solution"
                sys.exit(1)
            self._collision_done = np.zeros(luniq, dtype="bool")

    def get_occupation(self):
        return self._occupations_all

    def get_grid_point_triplets(self):
        return self._grid_point_triplets

    def get_triplets_weights(self):
        return self._triplet_weights

    def get_collision_out(self):
        if self._cutoff_frequency is None:
            return self._collision_out
        else:  # Averaging imag-self-energies by degenerate bands
            deg_sets = self._degeneracy_all[self._grid_point]
            cout = np.zeros_like(self._collision_out)
            for i, dset in enumerate(deg_sets):
                cout[i] = np.average(
                    self._collision_out[np.where(deg_sets == dset)])
            return cout

    def get_collision_in(self):
        return self._collision_in

    def read_collision_all(self, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        is_error = read_collision_all_from_hdf5(
            self._collision_in_all,
            self._mesh,
            self._sigma,
            self._temperature,
            is_adaptive_sigma=is_adaptive_sigma,
            log_level=log_level,
            is_nosym=self._pp._is_nosym)

        if is_error:
            self.set_write_collision(True)
        else:
            if self._is_adaptive_sigma and (not self._is_on_iteration):
                self._collision_done[:] = False
            else:
                self._collision_done[:] = True

    def read_collision_at_grid(self,
                               grid,
                               log_level=0,
                               is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        is_error = read_collision_at_grid_from_hdf5(
            self._collision_in,
            self._mesh,
            grid,
            self._sigma,
            self._temperature,
            is_adaptive_sigma=is_adaptive_sigma,
            log_level=log_level,
            is_nosym=self._pp._is_nosym)
        self._is_read_error = is_error
        if self._is_read_error:
            self.set_read_collision(False)
            self.set_write_collision(True)

    def write_collision_at_grid(self,
                                grid,
                                log_level=0,
                                is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        write_collision_to_hdf5_at_grid(self._collision_in,
                                        self._mesh,
                                        grid,
                                        self._sigma,
                                        self._temperature,
                                        is_adaptive_sigma=is_adaptive_sigma,
                                        log_level=log_level,
                                        is_nosym=self._pp._is_nosym)
        if self._is_read_error:  # Condition: read argument is invoked
            self.set_read_collision(True)
            self.set_write_collision(False)

    def write_collision_all(self, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        if self._write_col:
            write_collision_to_hdf5_all(self._collision_in_all,
                                        self._mesh,
                                        self._sigma,
                                        self._temperature,
                                        is_adaptive_sigma=is_adaptive_sigma,
                                        log_level=log_level,
                                        is_nosym=self._pp._is_nosym)

    def set_temperature(self, temperature):
        for i, t in enumerate(self._temperatures):
            if temperature == t:
                self._itemp = i
                break
        self._temperature = temperature

    def set_is_adaptive_sigma(self, is_adaptive_sigma=False):
        self._is_adaptive_sigma = is_adaptive_sigma

    def set_is_on_iteration(self, is_on_iteration=True):
        self._is_on_iteration = is_on_iteration

    def set_sigma(self, sigma):
        for i, s in enumerate(self._sigmas):
            if s == sigma:
                self._isigma = i
                break
        self._sigma = sigma
        if self._sigma is None:
            self._is_thm = True

    @total_time.timeit
    def set_asigma(self, gamma_prev=None, gamma_pprev=None):
        if self._is_dispersed:
            if not self._read_col:
                triplets = self._grid_point_triplets
            elif self._is_adaptive_sigma or self._is_group_velocity:
                return
        else:
            triplets = self._triplets_reduced

        if self._is_adaptive_sigma:
            if gamma_prev is not None:
                if gamma_pprev is not None:
                    gamma_all = (self._gamma_all[self._isigma, :, self._itemp]
                                 + gamma_prev + gamma_pprev) / 3
                else:
                    gamma_all = (self._gamma_all[self._isigma, :, self._itemp]
                                 + gamma_prev) / 2
            else:
                gamma_all = self._gamma_all[self._isigma, :, self._itemp]
            triplet_gammas = gamma_all[triplets]
            gt = (triplet_gammas * 2 * np.pi)**2 / (
                2 * np.log(2))  # 2pi comes from the definition of gamma
            self._asigma = np.sqrt(gt[:, 0, :, np.newaxis, np.newaxis] +
                                   gt[:, 1, np.newaxis, :, np.newaxis] +
                                   gt[:, 2, np.newaxis, np.newaxis, :])
        elif self._is_group_velocity:
            self._set_group_velocity(grid_points=np.unique(triplets))
            triplet1, triplet2 = triplets[:, 1], triplets[:, 2]
            gv1, gv2 = self._gv_all[triplet1], self._gv_all[triplet2]
            reciprocal_lattice = np.linalg.inv(self._pp._primitive.get_cell())
            num_band = len(self._band_indices)
            gvs = gv1[:, :, np.newaxis] - gv2[:, np.newaxis, :]
            normalized_gvs = np.dot(gvs, reciprocal_lattice) / np.array(
                self._mesh)
            sigmas = np.sqrt(np.sum(normalized_gvs**2, axis=-1) / 12)
            self._asigma = np.repeat(sigmas[:, np.newaxis],
                                     repeats=num_band,
                                     axis=1)
        else:
            if self._read_col:
                nband = self._pp._primitive.get_number_of_atoms() * 3
                self._asigma = np.zeros((0, nband, nband, nband),
                                        dtype="double")

    def _set_group_velocity(self, grid_points):
        undone_grid_points = np.extract(self._gv_done_all[grid_points] == 0,
                                        grid_points)
        qpoints = [
            self._grid_address[gp] / np.double(self._mesh)
            for gp in undone_grid_points
        ]
        if len(qpoints) != 0:
            self._group_velocity.set_q_points(q_points=qpoints)
            self._gv_all[
                undone_grid_points] = self._group_velocity.get_group_velocity(
                )
            self._gv_done_all[undone_grid_points] = True

    def set_write_collision(self, is_write_col):
        self._write_col = is_write_col

    def set_read_collision(self, is_read_col):
        self._read_col = is_read_col

    def get_write_collision(self):
        return self._write_col

    def get_read_collision(self):
        return self._read_col

    def get_is_dispersed(self):
        return self._is_dispersed

    def get_collision_out_all(self):
        return self._collision_out_all

    @total_time.timeit
    def run_interaction_at_grid_point(self, g_skip=None):
        self.set_phonons_triplets()
        if self._read_col and ((not self._is_adaptive_sigma)
                               or self._is_on_iteration):
            nband = self._pp._primitive.get_number_of_atoms() * 3
            self._fc3_normal_squared = np.zeros((0, nband, nband, nband),
                                                dtype="double")
            self._fc3_normal_squared_reduced = np.zeros(
                (0, nband, nband, nband), dtype="double")
        else:
            self._pp.run(g_skip=g_skip, lang=self._lang)
            self._fc3_normal_squared = self._pp.get_interaction_strength()
            self._fc3_normal_squared_reduced = self._fc3_normal_squared[
                self._undone_triplet_index]

        if self._collision_iso:
            self._collision_iso.set_frequencies(
                frequencies=self._frequencies_all,
                eigenvectors=self._eigenvectors_all,
                phonons_done=self._pp._phonon_done,
                degeneracies=self._degeneracy_all)

    def set_phonons_triplets(self):
        self._pp.set_phonons(lang=self._lang)
        (self._frequencies_all,
         self._eigenvectors_all) = self._pp.get_phonons()[:2]
        self._degeneracy_all = self._pp.get_degeneracy()
        self._band_indices = self._pp.get_band_indices()

    def reduce_triplets(self):
        num_triplets = len(self._pp.get_triplets_at_q()[0])

        num_band0 = len(self._band_indices)
        num_band = self._pp._primitive.get_number_of_atoms() * 3
        self._collision_out = None
        self._collision_in = np.zeros((num_triplets, num_band0, num_band),
                                      dtype="double")
        self._triplets_mapping = self._pp.get_triplets_mapping_at_grid()
        self.extract_undone_triplets()
        self._collision_in_reduced = np.zeros(
            (len(self._undone_uniq_index), 3, num_band0, num_band),
            dtype="double")
        self._triplets_reduced = self._pp.get_triplets_at_q()[0][
            self._undone_triplet_index]
        assert (self._pp._phonon_done[self._triplets_reduced] == True).all()
        self._frequencies_reduced = self._frequencies_all[
            self._triplets_reduced]
        # self.set_grid_points_occupation()
        self._occu_reduced = self._occupations_all[self._triplets_reduced,
                                                   self._itemp]

    def set_grid_points_occupation(self, grid_points=None):
        if grid_points is None:
            grid_points = self._grid_point_triplets
        uniq_grids = np.unique(grid_points)
        for g in uniq_grids:
            if not self._n_done[g, self._itemp]:
                n = occupation(self._frequencies_all[g], self._temperature)
                n[np.where(
                    self._frequencies_all[g] < self._cutoff_frequency)] = 0
                self._occupations_all[g, self._itemp] = n
                self._n_done[g, self._itemp] = True
        if self._collision_iso is not None:
            self._collision_iso.set_occupation(
                occupations=self._occupations_all[:, self._itemp],
                occupations_done=self._n_done[:, self._itemp])

    def set_grid_points_group_velocity(self, grid_points=None):
        if grid_points is None:
            grid_points = self._grid_point_triplets
        uniq_grids = np.unique(grid_points)

    def extract_undone_triplets(self):
        unique_map, index = np.unique(self._triplets_mapping,
                                      return_index=True)
        if self._is_dispersed:
            self._undone_uniq_index = unique_map
            self._undone_triplet_index = index
        else:
            self._undone_uniq_index = np.extract(
                self._collision_done[unique_map] == False, unique_map)
            self._undone_triplet_index = np.extract(
                self._collision_done[unique_map] == False, index)

    def broadcast_collision_out(self):
        grid_map = self._pp.get_grid_mapping()
        bz_to_pp_map = self._pp.get_bz_to_pp_map()
        bz_to_irred_map = grid_map[bz_to_pp_map]
        equiv_pos = np.where(bz_to_irred_map == self._grid_point)
        cm_out = self.get_collision_out()
        self._collision_out_all[self._isigma, equiv_pos, self._itemp] = cm_out
        n = self.get_occupation()[self._grid_point, self._itemp]
        nn1 = n * (n + 1)
        is_pass = self._frequencies_all[
            self._grid_point] < self._cutoff_frequency
        self._gamma_all[self._isigma, equiv_pos, self._itemp] = \
            cm_out * np.where(is_pass, 0, 1 / nn1) / 2.

    def run(self):
        if self._is_dispersed and self._read_col:
            self.read_collision_at_grid(self._grid_point)
        if self._lang == "C":
            self.run_c()
        else:
            self.run_py()
        if self._is_dispersed and self._write_col:
            self.write_collision_at_grid(self._grid_point)
        # self._collision_in *= self._unit_conversion # unit in THz
        summation = np.sum(self._collision_in, axis=-1)
        self._collision_out = np.dot(self._triplet_weights, summation) / 2.0

        # Set the diagonal part in A_in as 0
        # bz_to_pp = self._pp._bz_to_pp_map
        # triplets = bz_to_pp[self.get_grid_point_triplets()]
        # diag_pos = np.where(triplets[:, 1] == triplets[0,0])[0][0]
        # self._collision_out += self._collision_in[diag_pos].diagonal()
        # np.fill_diagonal(self._collision_in[diag_pos], 0)
        # Set the diagonal part in A_in as 0

        if self._collision_iso is not None:
            self._collision_iso.run()
            self._collision_out += self._collision_iso._collision_out
            self._collision_in += self._collision_iso._collision_in
        if self._length is not None:
            self._collision_out += self.get_boundary_scattering_strength()
        self.broadcast_collision_out()

    @total_time.timeit
    def get_boundary_scattering_strength(self):
        self._set_group_velocity(grid_points=[self._grid_point])
        bnd_collision_unit = 100. / 1e-6 / THz / (
            2 * np.pi)  # unit in THz, unit of length is micron
        gv = self._gv_all[self._grid_point]
        dm = self._pp.get_dynamical_matrix()
        gv1 = get_group_velocity(
            self._qpoint,
            dm,
            symmetry=self._pp._symmetry,
            q_length=self._gv_delta_q,
            frequency_factor_to_THz=self._pp.get_frequency_factor_to_THz())

        gv_average = np.sqrt(np.sum(gv**2, axis=-1))
        n = self._occupations_all[self._grid_point, self._itemp]
        cout_bnd = gv_average / self._length * n * (n + 1)
        return cout_bnd * bnd_collision_unit

    def run_py(self):
        for i, triplet in enumerate(self._grid_point_triplets):
            occu = occupation(self._frequencies_all[triplet],
                              self._temperature)
            freq = self._frequencies_all[triplet]
            for (j, k, l) in np.ndindex(self._fc3_normal_squared.shape[1:]):
                if self._asigma:
                    sigma = self._asigma[i, j, k, l]
                else:
                    sigma = self._sigma
                f0 = freq[0, j]
                f1 = freq[1, k]
                f2 = freq[2, l]
                if (f0 < self._cutoff_frequency or f1 < self._cutoff_frequency
                        or f2 < self._cutoff_frequency):
                    continue
                n0 = occu[0, j]
                n1 = occu[1, k]
                n2 = occu[2, l]
                delta1 = gaussian(f0 + f1 - f2, sigma)
                delta2 = gaussian(f0 + f2 - f1, sigma)
                delta3 = gaussian(f0 - f1 - f2, sigma)
                self._collision_in[
                    i, j, k] += self._fc3_normal_squared[i, j, k, l] * (
                        (n0 + 1) * n1 * n2 * delta3 +
                        (n1 + 1) * n0 * n2 * delta2 +
                        (n2 + 1) * n0 * n1 * delta1)
        self._collision_in *= self._unit_conversion

    def set_integration_weights(self, cutoff_g=1e-8):
        f_points = self._frequencies_all[self._grid_point][self._band_indices]

        is_read_g = False
        is_write_g = False
        if not self._is_adaptive_sigma and len(self._temperatures) > 1:
            if self._pp.get_is_read_amplitude(
            ) or self._pp.get_is_write_amplitude():
                if self._itemp == 0:
                    is_write_g = True
                    is_read_g = False
                else:
                    is_write_g = False
                    is_read_g = True
        if self._asigma is not None:
            sigma_object = self._asigma
            cutoff_g = np.where(self._asigma > 0, cutoff_g / self._asigma, 0)
            if self._is_dispersed and not self._read_col:
                cutoff_g = cutoff_g[self._undone_triplet_index]
        else:
            sigma_object = self._sigma
            if sigma_object is not None:
                cutoff_g = cutoff_g / self._sigma
        if self._is_dispersed:
            if is_read_g:
                self._g = read_integration_weight_from_hdf5_at_grid(
                    self._mesh, self._sigma, self._grid_point)
            elif not self._read_col:
                self._g = get_triplets_integration_weights(
                    self._pp,
                    np.array(f_points, dtype='double'),
                    sigma_object,
                    band_indices=self._band_indices,
                    triplets=self._grid_point_triplets,
                    is_triplet_symmetry=self._pp._symmetrize_fc3_q)
                # when all the integration weights are smaller than the cutoff value
                # the interaction strength will not be calculated
                if is_write_g:
                    write_integration_weight_to_hdf5_at_grid(
                        self._mesh, self._sigma, self._grid_point, self._g)
                self._g_skip = np.array(
                    self._g[:,
                            self._undone_triplet_index].sum(axis=0) < cutoff_g,
                    dtype="bool")
            else:
                self._g_skip = None

        else:
            if is_read_g:
                self._g = read_integration_weight_from_hdf5_at_grid(
                    self._mesh, self._sigma, self._grid_point)
            else:
                self._g = get_triplets_integration_weights(
                    self._pp,
                    np.array(f_points, dtype='double'),
                    sigma_object,
                    band_indices=self._band_indices,
                    triplets=self._triplets_reduced,
                    is_triplet_symmetry=self._pp._symmetrize_fc3_q)
                if is_write_g:
                    write_integration_weight_to_hdf5_at_grid(
                        self._mesh, self._sigma, self._grid_point, self._g)
                if self._pp.get_triplets_done().all(
                ):  #when the interaction strengths are all done
                    self._g_skip = None
                else:
                    self._g_skip = np.array(
                        np.abs(self._g).sum(axis=0) < cutoff_g, dtype="bool")

    def get_integration_weights(self):
        return self._g

    def get_interaction_skip(self):
        return self._g_skip

    @total_time.timeit
    def run_c(self):
        import anharmonic._phono3py as phono3c
        freq = self._frequencies_all[self._grid_point_triplets]

        if self._is_dispersed:
            if not self._read_col:
                phono3c.collision(self._collision_in,
                                  self._fc3_normal_squared.copy(), freq.copy(),
                                  self._g.copy(), self._temperature,
                                  self._cutoff_frequency)
                phono3c.collision_degeneracy(
                    self._collision_in, self._degeneracy_all.astype('intc'),
                    self._grid_point_triplets.astype("intc"), False)
                self._collision_in *= self._unit_conversion  # unit in THz
        else:
            phono3c.collision_all_permute(
                self._collision_in_reduced,
                self._fc3_normal_squared_reduced.copy(),
                self._occu_reduced.copy(), self._frequencies_reduced.copy(),
                self._g.copy(), self._cutoff_frequency)
            self._collision_in_reduced *= self._unit_conversion  # unit in THz
            phono3c.collision_degeneracy(
                self._collision_in_reduced,
                self._degeneracy_all.astype('intc'),
                self._triplets_reduced.astype('intc').copy(), True)
            self._collision_in_all[
                self._undone_uniq_index] = self._collision_in_reduced[:]
            self._collision_done[self._undone_uniq_index] = True
            phono3c.collision_from_reduced(
                self._collision_in, self._collision_in_all,
                self._pp.get_triplets_mapping_at_grid().astype("intc"),
                self._pp.get_triplets_sequence_at_grid().astype("byte"))

            # #############debugging############################
            # f_points = self._frequencies_all[self._grid_point][self._band_indices]
            # if self._asigma is not None:
            #     sigma_object = self._asigma
            # else:
            #     sigma_object = self._sigma
            # self._g = get_triplets_integration_weights(
            #     self._pp,
            #     np.array(f_points, dtype='double'),
            #     sigma_object,
            #     triplets = self._grid_point_triplets)
            # collision_in = np.zeros_like(self._collision_in)
            #
            # phono3c.collision(collision_in,
            #                   self._fc3_normal_squared.copy(),
            #                   freq.copy(),
            #                   self._g.copy(),
            #                   self._temperature,
            #                   self._cutoff_frequency)
            # grid_points2 = self._grid_point_triplets[:, 1]
            # phono3c.collision_degeneracy_grid(collision_in,
            #                                   self._degeneracy_all.astype('intc'),
            #                                   grid_points2.astype('intc').copy(),
            #                                   self._grid_point)
            # collision_in *= self._unit_conversion # unit in THz
            # diff = np.abs(collision_in - self._collision_in)
            # if len(diff)>0:
            #     print diff.max()
            # tt, bb0, bb1 = np.unravel_index(diff.argmax(), diff.shape)
            # grid0, grid1, grid2 = self._grid_point_triplets[tt]
            # n0 = self._occupations_all[grid0, self._itemp, bb0]
            # n1 = self._occupations_all[grid1, self._itemp, bb1]
            # n2 = self._occupations_all[grid2, self._itemp]
            # g0 = self._g[0, tt, bb0, bb1]
            # g1 = self._g[1, tt, bb0, bb1]
            # g2 = self._g[2, tt, bb0, bb1]
            # ng = n0 * n1 * (n2 + 1) * g2 + n0 * (n1 + 1) * n2 * g1 + (n0 + 1) * n1 * n2 * g0
            # nga = ng * self._fc3_normal_squared[tt, bb0, bb1] * self._unit_conversion
            # print

    @total_time.timeit
    def calculate_collision(self, grid_point, sigma=None, temperature=None):
        "On behalf of the limited memory, each time only one temperature and one sigma is calculated"
        if sigma is not None:
            self.set_sigma(sigma)
        if temperature is not None:
            self.set_temperature(temperature)

        self.set_grid(grid_point)
        self.set_phonons_triplets()
        self.set_grid_points_occupation()
        self.reduce_triplets()
        self.set_asigma()
        self.set_integration_weights()
        self.run_interaction_at_grid_point(g_skip=self._g_skip)
        self.run()
Beispiel #7
0
class Phonopy:
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 distance=0.01,
                 factor=VaspToTHz,
                 is_auto_displacements=True,
                 symprec=1e-5,
                 is_symmetry=True,
                 log_level=0):
        self._symprec = symprec
        self._unitcell = unitcell
        self._supercell_matrix = supercell_matrix
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._log_level = log_level
        self._supercell = None
        self._set_supercell()
        self._symmetry = None
        self._set_symmetry()

        # set_displacements (used only in preprocess)
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None
        if is_auto_displacements:
            self.generate_displacements(distance)

        # set_post_process
        self._primitive = None
        self._dynamical_matrix = None
        self._is_nac = False

        # set_force_constants or set_forces
        self._set_of_forces_objects = None
        self._force_constants = None

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None
        
        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None
        
    def get_primitive(self):
        return self._primitive
    primitive = property(get_primitive)

    def set_primitive(self, primitive):
        self._primitive = primitive

    def get_unitcell(self):
        return self._unitcell
    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell
    supercell = property(get_supercell)

    def set_supercell(self, supercell):
        self._supercell = supercell

    def get_symmetry(self):
        return self._symmetry
    symmetry = property(get_symmetry)

    def get_unit_conversion_factor(self):
        return self._factor
    unit_conversion_factor = property(get_unit_conversion_factor)
    
    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 set_displacements(self, displacements):
        """Set displacements manually

        displacemsts: List of disctionaries
           [[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
           
        """

        self._displacements = displacements
        self._set_supercells_with_displacements()

    def get_displacements(self):
        return self._displacements
    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions
    displacement_directions = property(get_displacement_directions)

    def get_supercells_with_displacements(self):
        return self._supercells_with_displacements

    def set_post_process(self,
                         primitive_matrix=np.eye(3, dtype=float),
                         sets_of_forces=None,
                         set_of_forces_objects=None,
                         force_constants=None,
                         is_nac=False,
                         calculate_full_force_constants=False,
                         force_constants_decimals=None,
                         dynamical_matrix_decimals=None):
        """
        Set forces or force constants to prepare phonon calculations.
        The order of 'sets_of_forces' has to correspond to that of
        'displacements' that should be already stored.

        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T

        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]

        set_of_forces_objects:
           [FORCES_object, FORCES_object, FORCES_object, ...]
        """

        self._is_nac = is_nac

        # Primitive cell
        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        self._primitive = Primitive(
            self._supercell,
            np.dot(inv_supercell_matrix, primitive_matrix),
            self._symprec)

        # Set set of FORCES objects or force constants
        if sets_of_forces is not None:
            self.set_forces(sets_of_forces)
        elif set_of_forces_objects is not None:
            self.set_force_sets(set_of_forces_objects)
        elif force_constants is not None:
            self.set_force_constants(force_constants)

        # Calculate force cosntants from forces (full or symmetry reduced)
        if self._set_of_forces_objects is not None:
            if calculate_full_force_constants:
                self.set_force_constants_from_forces(
                    distributed_atom_list=None,
                    force_constants_decimals=force_constants_decimals)
            else:
                p2s_map = self._primitive.get_primitive_to_supercell_map()
                self.set_force_constants_from_forces(
                    distributed_atom_list=p2s_map,
                    force_constants_decimals=force_constants_decimals)

        if self._force_constants is None:
            print "In set_post_process, sets_of_forces or force_constants"
            print "has to be set."
            return False
            
        # Dynamical Matrix
        self.set_dynamical_matrix(decimals=dynamical_matrix_decimals)
        
    def set_nac_params(self, nac_params, method='wang'):
        if self._is_nac:
            self._dynamical_matrix.set_nac_params(nac_params, method)

    def set_dynamical_matrix(self, decimals=None):
        if self._is_nac:
            self._dynamical_matrix = \
                DynamicalMatrixNAC(self._supercell,
                                   self._primitive,
                                   self._force_constants,
                                   decimals=decimals,
                                   symprec=self._symprec)
        else:
            self._dynamical_matrix = \
                DynamicalMatrix(self._supercell,
                                self._primitive,
                                self._force_constants,
                                decimals=decimals,
                                symprec=self._symprec)

    def get_dynamical_matrix(self):
        return self._dynamical_matrix
    dynamical_matrix = property(get_dynamical_matrix)

    def set_forces(self, sets_of_forces):
        forces = []
        for i, disp in enumerate(self._displacements):
            forces.append(Forces(disp[0],
                                 disp[1:4],
                                 sets_of_forces[i]))
        self._set_of_forces_objects = forces

    def set_force_constants_from_forces(self,
                                        distributed_atom_list=None,
                                        force_constants_decimals=None):
        self._force_constants = get_force_constants(
            self._set_of_forces_objects,
            self._symmetry,
            self._supercell,
            atom_list=distributed_atom_list,
            decimals=force_constants_decimals)

    def set_force_constants_zero_with_radius(self,
                                             cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants

    def set_force_sets(self, sets_of_forces_objects):
        self._set_of_forces_objects = sets_of_forces_objects
        
    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)
        
    def get_force_constants(self):
        return self._force_constants
    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants,
                                     self._supercell,
                                     self._primitive,
                                     self._symprec)

    def get_dynamical_matrix_at_q(self, q):
        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()

    # Frequency at a q-point
    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))
            
        return np.array(frequencies) * self._factor

    # Frequency and eigenvector at a q-point
    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q
        
        q: q-vector in reduced coordinates of primitive cell
        """
        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors
                
        ## This expression may not be supported in old python versions.
        # frequencies = np.array(
        #     [np.sqrt(x) if x > 0 else -np.sqrt(-x) for x in eigvals])
        # return frequencies * self._factor, eigenvectors

    # Band structure
    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):

        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            self._primitive,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(),
                band.get_distances(),                
                band.get_frequencies(),
                band.get_eigenvectors())

    def plot_band_structure(self, symbols=None):
        return self._band_structure.plot_band(symbols)

    def write_yaml_band_structure(self):
        self._band_structure.write_yaml()

    # Mesh sampling
    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_symmetry=True,
                 is_band_connection=False,
                 is_eigenvectors=False,
                 is_gamma_center=False):

        self._mesh = Mesh(self._dynamical_matrix,
                          self._primitive,
                          mesh,
                          shift=shift,
                          is_time_reversal=is_time_reversal,
                          is_mesh_symmetry=is_symmetry,
                          is_eigenvectors=is_eigenvectors,
                          is_band_connection=is_band_connection,
                          is_gamma_center=is_gamma_center,
                          group_velocity=self._group_velocity,
                          factor=self._factor,
                          symprec=self._symprec)

    def get_mesh(self):
        return (self._mesh.get_qpoints(),
                self._mesh.get_weights(),
                self._mesh.get_frequencies(),
                self._mesh.get_eigenvectors())

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    def write_hdf5_mesh(self):
        self._mesh.write_hdf5()
    # Thermal property
    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               is_projection=False,
                               cutoff_frequency=None):
        if self._mesh==None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        tp = ThermalProperties(self._mesh.get_frequencies(),
                               weights=self._mesh.get_weights(),
                               eigenvectors=self._mesh.get_eigenvectors(),
                               is_projection=is_projection,
                               cutoff_frequency=cutoff_frequency)
        tp.set_thermal_properties(t_step, t_max, t_min)
        self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        return self._thermal_properties.plot_thermal_properties()

    def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    # Partial DOS
    def set_partial_DOS(self,
                        sigma=None,
                        omega_min=None,
                        omega_max=None,
                        omega_pitch=None,
                        tetrahedron_method=False):

        if self._mesh==None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)
        if self._mesh.get_eigenvectors() == None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)

        pdos = PartialDos(self._mesh,
                          sigma=sigma,
                          tetrahedron_method=tetrahedron_method)
        pdos.set_draw_area(omega_min,
                           omega_max,
                           omega_pitch)
        pdos.calculate()
        self._pdos = pdos

    def get_partial_DOS(self):
        """
        Retern omegas and partial_dos.
        The first element is omegas and the second is partial_dos.
        
        omegas: [freq1, freq2, ...]
        partial_dos:
          [[elem1-freq1, elem1-freq2, ...],
           [elem2-freq1, elem2-freq2, ...],
           ...]

          where
           elem1: atom1-x compornent
           elem2: atom1-y compornent
           elem3: atom1-z compornent
           elem4: atom2-x compornent
           ...
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        return self._pdos.plot_pdos(indices=pdos_indices,
                                    legend=legend)

    def write_partial_DOS(self):
        self._pdos.write()

    # Total DOS
    def set_total_DOS(self,
                      sigma=None,
                      omega_min=None,
                      omega_max=None,
                      omega_pitch=None,
                      tetrahedron_method=False):

        if self._mesh==None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(omega_min,
                                omega_max,
                                omega_pitch)
        total_dos.calculate()
        self._total_dos = total_dos

    def get_total_DOS(self):
        """
        Retern omegas and total dos.
        The first element is omegas and the second is total dos.
        
        omegas: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(), freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        return self._total_dos.plot_dos()

    def write_total_DOS(self):
        self._total_dos.write()

    # Thermal displacement
    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  direction=None,
                                  cutoff_eigenvalue=None):
        """
        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh==None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers() 

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
            
        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_eigenvalue=cutoff_eigenvalue)
        td.set_temperature_range(t_min, t_max, t_step)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        # td.run()
        td.run_mesh()

        self._thermal_displacements = td

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()
        
    def plot_thermal_displacements(self, is_legend=False):
        return self._thermal_displacements.plot(is_legend)

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    def write_hdf5_thermal_displacements(self):
        self._thermal_displacements.write_hdf5()

    # Thermal displacement matrices
    def set_thermal_displacement_matrices(self,
                                           t_step=10,
                                           t_max=1000,
                                           t_min=0,
                                           cutoff_eigenvalue=None):
        """
        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)

        direction:
          Projection direction in reduced coordinates
        """
        if self._mesh==None:
            print "set_mesh has to be done before set_thermal_properties"
            sys.exit(1)

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers() 

        if self._mesh.get_eigenvectors() is None:
            print "Eigenvectors have to be calculated."
            sys.exit(1)
            
        if np.prod(mesh_nums) != len(eigvecs):
            print "Sampling mesh must not be symmetrized."
            sys.exit(1)

        tdm = ThermalDisplacementMatrices(frequencies,
                                           eigvecs,
                                           self._primitive.get_masses(),
                                           cutoff_eigenvalue=cutoff_eigenvalue)
        tdm.set_temperature_range(t_min, t_max, t_step)
        # tdm.run()
        tdm.run_mesh()

        self._thermal_displacement_matrices = tdm

    def get_thermal_displacement_matrices(self):
        if self._thermal_displacement_matrices is not None:
            return self._thermal_displacement_matrices.get_thermal_displacement_matrices()
        
    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()

    def write_hdf5_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_hdf5()

    # Thermal displacement
    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_eigenvalue=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_eigenvalue:
          phonon modes that have frequencies below cutoff_eigenvalue
          are ignored.
          e.g. 0.1 (THz^2)
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              symprec=self._symprec,
                              cutoff_eigenvalue=cutoff_eigenvalue)
        td.set_temperature_range(t_min, t_max, t_step)
        # td.run(atom_pairs)
        td.run_mesh(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    def write_hdf5_thermal_distances(self):
        self._thermal_distances.write_hdf5()

    # Q-points mode
    def write_yaml_qpoints(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False,
                           factor=VaspToTHz):
        
        write_yaml_qpoints(q_points,
                           self._primitive,
                           self._dynamical_matrix,
                           nac_q_direction=nac_q_direction,
                           is_eigenvectors=is_eigenvectors,
                           group_velocity=self._group_velocity,
                           write_dynamical_matrices=write_dynamical_matrices,
                           factor=self._factor)

    # Animation
    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        if q_point==None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  self._primitive,
                                  shift=shift)
        else:
            animation = Animation(q_point,
                                  self._dynamical_matrix,
                                  self._primitive,
                                  shift=shift)
        if anime_type=='v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)

            
        if (anime_type=='arc' or
            anime_type=='xyz' or
            anime_type=='jmol' or
            anime_type=='poscar'):
            if band_index==None or amplitude==None or num_div==None:
                print "Parameters are not correctly set for animation."
                sys.exit(1)

            if anime_type=='arc' or anime_type==None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div)
    
            if anime_type=='xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor)
    
            if anime_type=='jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)
    
            if anime_type=='poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div)
                    

    # Modulation
    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        self._modulation = Modulation(self._dynamical_matrix,
                                      self._primitive,
                                      dimension=dimension,
                                      phonon_modes=phonon_modes,
                                      delta_q= delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()
                    
    def get_modulations(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulations()
                
    def get_delta_modulations(self):
        """Return modulations relative to equilibrium supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.
        
        """
        return self._modulation.get_delta_modulations()
                
    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()
                          
    def write_yaml_modulations(self):
        self._modulation.write_yaml()


    # Characters of irreducible representations
    def set_irreps(self, q, degeneracy_tolerance=1e-4):
        self._irreps = IrReps(
            self._dynamical_matrix,
            q,
            factor=self._factor,
            symprec=self._symprec,
            degeneracy_tolerance=degeneracy_tolerance,
            log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps
        
    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    # Group velocity
    def set_group_velocity(self,
                           q_points=None,
                           q_length=1e-4):
        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_points=q_points,
            symmetry=self._symmetry,
            q_length=q_length,
            frequency_factor_to_THz=self._factor)

    def get_group_velocity(self, q_point):
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    def _set_supercell(self):
        self._supercell = get_supercell(self._unitcell,
                                        self._supercell_matrix,
                                        self._symprec)

    def _set_symmetry(self):
        self._symmetry = Symmetry(self._supercell,
                                  self._symprec,
                                  self._is_symmetry)

    def _set_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacements:
            positions = self._supercell.get_positions()
            positions[disp[0]] += disp[1:4]
            supercells.append(Atoms(
                    numbers=self._supercell.get_atomic_numbers(),
                    masses=self._supercell.get_masses(),
                    magmoms=self._supercell.get_magnetic_moments(),
                    positions=positions,
                    cell=self._supercell.get_cell(),
                    pbc=True))

        self._supercells_with_displacements = supercells
Beispiel #8
0
class Phonopy(object):
    def __init__(self,
                 unitcell,
                 supercell_matrix,
                 primitive_matrix=None,
                 nac_params=None,
                 distance=None,
                 factor=VaspToTHz,
                 is_auto_displacements=None,
                 dynamical_matrix_decimals=None,
                 force_constants_decimals=None,
                 symprec=1e-5,
                 is_symmetry=True,
                 use_lapack_solver=False,
                 log_level=0):

        if is_auto_displacements is not None:
            print("Warning: \'is_auto_displacements\' argument is obsolete.")
            if is_auto_displacements is False:
                print("Sets of displacements are not created as default.")
            else:
                print("Use \'generate_displacements\' method explicitly to "
                      "create sets of displacements.")

        if distance is not None:
            print("Warning: \'distance\' keyword argument is obsolete at "
                  "Phonopy instantiation.")
            print("Specify \'distance\' keyword argument when calling "
                  "\'generate_displacements\'")
            print("method (See the Phonopy API document).")

        self._symprec = symprec
        self._factor = factor
        self._is_symmetry = is_symmetry
        self._use_lapack_solver = use_lapack_solver
        self._log_level = log_level

        # Create supercell and primitive cell
        self._unitcell = Atoms(atoms=unitcell)
        self._supercell_matrix = supercell_matrix
        self._primitive_matrix = primitive_matrix
        self._supercell = None
        self._primitive = None
        self._build_supercell()
        self._build_primitive_cell()

        # Set supercell and primitive symmetry
        self._symmetry = None
        self._primitive_symmetry = None
        self._search_symmetry()
        self._search_primitive_symmetry()

        # set_displacements (used only in preprocess)
        self._displacement_dataset = None
        self._displacements = None
        self._displacement_directions = None
        self._supercells_with_displacements = None

        # set_force_constants or set_forces
        self._force_constants = None
        self._force_constants_decimals = force_constants_decimals

        # set_dynamical_matrix
        self._dynamical_matrix = None
        self._nac_params = nac_params
        self._dynamical_matrix_decimals = dynamical_matrix_decimals

        # set_band_structure
        self._band_structure = None

        # set_mesh
        self._mesh = None

        # set_tetrahedron_method
        self._tetrahedron_method = None

        # set_thermal_properties
        self._thermal_properties = None

        # set_thermal_displacements
        self._thermal_displacements = None

        # set_thermal_displacement_matrices
        self._thermal_displacement_matrices = None

        # set_partial_DOS
        self._pdos = None

        # set_total_DOS
        self._total_dos = None

        # set_modulation
        self._modulation = None

        # set_character_table
        self._irreps = None

        # set_group_velocity
        self._group_velocity = None

    def get_version(self):
        return __version__

    def get_primitive(self):
        return self._primitive
    primitive = property(get_primitive)

    def get_unitcell(self):
        return self._unitcell
    unitcell = property(get_unitcell)

    def get_supercell(self):
        return self._supercell
    supercell = property(get_supercell)

    def get_symmetry(self):
        """return symmetry of supercell"""
        return self._symmetry
    symmetry = property(get_symmetry)

    def get_primitive_symmetry(self):
        """return symmetry of primitive cell"""
        return self._primitive_symmetry

    def get_supercell_matrix(self):
        return self._supercell_matrix

    def get_primitive_matrix(self):
        return self._primitive_matrix

    def get_unit_conversion_factor(self):
        return self._factor
    unit_conversion_factor = property(get_unit_conversion_factor)

    def get_displacement_dataset(self):
        return self._displacement_dataset

    def get_displacements(self):
        return self._displacements
    displacements = property(get_displacements)

    def get_displacement_directions(self):
        return self._displacement_directions
    displacement_directions = property(get_displacement_directions)

    def get_supercells_with_displacements(self):
        if self._displacement_dataset is None:
            return None
        else:
            self._build_supercells_with_displacements()
            return self._supercells_with_displacements

    def get_force_constants(self):
        return self._force_constants
    force_constants = property(get_force_constants)

    def get_rotational_condition_of_fc(self):
        return rotational_invariance(self._force_constants,
                                     self._supercell,
                                     self._primitive,
                                     self._symprec)

    def get_nac_params(self):
        return self._nac_params

    def get_dynamical_matrix(self):
        return self._dynamical_matrix
    dynamical_matrix = property(get_dynamical_matrix)

    def set_unitcell(self, unitcell):
        self._unitcell = unitcell
        self._build_supercell()
        self._build_primitive_cell()
        self._search_symmetry()
        self._search_primitive_symmetry()
        self._displacement_dataset = None

    def set_masses(self, masses):
        p_masses = np.array(masses)
        self._primitive.set_masses(p_masses)
        p2p_map = self._primitive.get_primitive_to_primitive_map()
        s_masses = p_masses[[p2p_map[x] for x in
                             self._primitive.get_supercell_to_primitive_map()]]
        self._supercell.set_masses(s_masses)
        u2s_map = self._supercell.get_unitcell_to_supercell_map()
        u_masses = s_masses[u2s_map]
        self._unitcell.set_masses(u_masses)
        self._set_dynamical_matrix()

    def set_nac_params(self, nac_params=None):
        self._nac_params = nac_params
        self._set_dynamical_matrix()

    def set_displacement_dataset(self, displacement_dataset):
        """
        displacement_dataset:
           {'natom': number_of_atoms_in_supercell,
            'first_atoms': [
              {'number': atom index of displaced atom,
               'displacement': displacement in Cartesian coordinates,
               'direction': displacement direction with respect to axes
               'forces': forces on atoms in supercell},
              {...}, ...]}
        """
        self._displacement_dataset = displacement_dataset

        self._displacements = []
        self._displacement_directions = []
        for disp in self._displacement_dataset['first_atoms']:
            x = disp['displacement']
            self._displacements.append([disp['number'], x[0], x[1], x[2]])
            if 'direction' in disp:
                y = disp['direction']
                self._displacement_directions.append(
                    [disp['number'], y[0], y[1], y[2]])
        if not self._displacement_directions:
            self._displacement_directions = None

    def set_forces(self, sets_of_forces):
        """
        sets_of_forces:
           [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell
             [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell
             ...                                                  ]
        """
        for disp, forces in zip(
                self._displacement_dataset['first_atoms'], sets_of_forces):
            disp['forces'] = forces

    def set_force_constants(self, force_constants):
        self._force_constants = force_constants
        self._set_dynamical_matrix()

    def set_force_constants_zero_with_radius(self, cutoff_radius):
        cutoff_force_constants(self._force_constants,
                               self._supercell,
                               cutoff_radius,
                               symprec=self._symprec)
        self._set_dynamical_matrix()

    def set_dynamical_matrix(self):
        self._set_dynamical_matrix()

    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 produce_force_constants(self,
                                forces=None,
                                calculate_full_force_constants=True,
                                computation_algorithm="svd"):
        if forces is not None:
            self.set_forces(forces)

        # A primitive check if 'forces' key is in displacement_dataset.
        for disp in self._displacement_dataset['first_atoms']:
            if 'forces' not in disp:
                return False

        if calculate_full_force_constants:
            self._run_force_constants_from_forces(
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)
        else:
            p2s_map = self._primitive.get_primitive_to_supercell_map()
            self._run_force_constants_from_forces(
                distributed_atom_list=p2s_map,
                decimals=self._force_constants_decimals,
                computation_algorithm=computation_algorithm)

        self._set_dynamical_matrix()

        return True

    def symmetrize_force_constants(self, iteration=3):
        symmetrize_force_constants(self._force_constants, iteration)
        self._set_dynamical_matrix()

    def symmetrize_force_constants_by_space_group(self):
        from phonopy.harmonic.force_constants import (set_tensor_symmetry,
                                                      set_tensor_symmetry_PJ)
        set_tensor_symmetry_PJ(self._force_constants,
                               self._supercell.get_cell().T,
                               self._supercell.get_scaled_positions(),
                               self._symmetry)

        self._set_dynamical_matrix()

    #####################
    # Phonon properties #
    #####################

    # Single q-point
    def get_dynamical_matrix_at_q(self, q):
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        return self._dynamical_matrix.get_dynamical_matrix()


    def get_frequencies(self, q):
        """
        Calculate phonon frequencies at q

        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        for eig in np.linalg.eigvalsh(dm).real:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor

    def get_frequencies_with_eigenvectors(self, q):
        """
        Calculate phonon frequencies and eigenvectors at q

        q: q-vector in reduced coordinates of primitive cell
        """
        self._set_dynamical_matrix()
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return None

        self._dynamical_matrix.set_dynamical_matrix(q)
        dm = self._dynamical_matrix.get_dynamical_matrix()
        frequencies = []
        eigvals, eigenvectors = np.linalg.eigh(dm)
        frequencies = []
        for eig in eigvals:
            if eig < 0:
                frequencies.append(-np.sqrt(-eig))
            else:
                frequencies.append(np.sqrt(eig))

        return np.array(frequencies) * self._factor, eigenvectors

    # Band structure
    def set_band_structure(self,
                           bands,
                           is_eigenvectors=False,
                           is_band_connection=False):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._band_structure = None
            return False

        self._band_structure = BandStructure(
            bands,
            self._dynamical_matrix,
            is_eigenvectors=is_eigenvectors,
            is_band_connection=is_band_connection,
            group_velocity=self._group_velocity,
            factor=self._factor)
        return True

    def get_band_structure(self):
        band = self._band_structure
        return (band.get_qpoints(),
                band.get_distances(),
                band.get_frequencies(),
                band.get_eigenvectors())

    def plot_band_structure(self, labels=None):
        import matplotlib.pyplot as plt
        if labels:
            from matplotlib import rc
            rc('text', usetex=True)

        self._band_structure.plot(plt, labels=labels)
        return plt

    def write_yaml_band_structure(self,
                                  labels=None,
                                  comment=None,
                                  filename="band.yaml"):
        self._band_structure.write_yaml(labels=labels,
                                        comment=comment,
                                        filename=filename)

    # Sampling mesh
    def set_mesh(self,
                 mesh,
                 shift=None,
                 is_time_reversal=True,
                 is_mesh_symmetry=True,
                 is_eigenvectors=False,
                 is_gamma_center=False):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._mesh = None
            return False

        self._mesh = Mesh(
            self._dynamical_matrix,
            mesh,
            shift=shift,
            is_time_reversal=is_time_reversal,
            is_mesh_symmetry=is_mesh_symmetry,
            is_eigenvectors=is_eigenvectors,
            is_gamma_center=is_gamma_center,
            group_velocity=self._group_velocity,
            rotations=self._primitive_symmetry.get_pointgroup_operations(),
            factor=self._factor,
            use_lapack_solver=self._use_lapack_solver)
        return True

    def get_mesh(self):
        if self._mesh is None:
            return None
        else:
            return (self._mesh.get_qpoints(),
                    self._mesh.get_weights(),
                    self._mesh.get_frequencies(),
                    self._mesh.get_eigenvectors())
    
    def get_mesh_grid_info(self):
        if self._mesh is None:
            return None
        else:
            return (self._mesh.get_grid_address(),
                    self._mesh.get_ir_grid_points(),
                    self._mesh.get_grid_mapping_table())

    def write_hdf5_mesh(self):
        self._mesh.write_hdf5()

    def write_yaml_mesh(self):
        self._mesh.write_yaml()

    # Plot band structure and DOS (PDOS) together
    def plot_band_structure_and_dos(self, pdos_indices=None, labels=None):
        import matplotlib.pyplot as plt
        import matplotlib.gridspec as gridspec
        if labels:
            from matplotlib import rc
            rc('text', usetex=True)

        plt.figure(figsize=(10, 6))
        gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1])
        ax1 = plt.subplot(gs[0, 0])
        self._band_structure.plot(plt, labels=labels)
        ax2 = plt.subplot(gs[0, 1], sharey=ax1)
        plt.subplots_adjust(wspace=0.03)
        plt.setp(ax2.get_yticklabels(), visible=False)

        if pdos_indices is None:
            self._total_dos.plot(plt,
                                 ylabel="",
                                 draw_grid=False,
                                 flip_xy=True)
        else:
            self._pdos.plot(plt,
                            indices=pdos_indices,
                            ylabel="",
                            draw_grid=False,
                            flip_xy=True)

        return plt

    # DOS
    def set_total_DOS(self,
                      sigma=None,
                      freq_min=None,
                      freq_max=None,
                      freq_pitch=None,
                      tetrahedron_method=False):

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before DOS calculation.")
            self._total_dos = None
            return False

        total_dos = TotalDos(self._mesh,
                             sigma=sigma,
                             tetrahedron_method=tetrahedron_method)
        total_dos.set_draw_area(freq_min, freq_max, freq_pitch)
        total_dos.run()
        self._total_dos = total_dos
        return True

    def get_total_DOS(self):
        """
        Retern frequencies and total dos.
        The first element is freqs and the second is total dos.

        frequencies: [freq1, freq2, ...]
        total_dos: [dos1, dos2, ...]
        """
        return self._total_dos.get_dos()

    def set_Debye_frequency(self, freq_max_fit=None):
        self._total_dos.set_Debye_frequency(
            self._primitive.get_number_of_atoms(),
            freq_max_fit=freq_max_fit)

    def get_Debye_frequency(self):
        return self._total_dos.get_Debye_frequency()

    def plot_total_DOS(self):
        import matplotlib.pyplot as plt
        self._total_dos.plot(plt)
        return plt

    def write_total_DOS(self):
        self._total_dos.write()

    # PDOS
    def set_partial_DOS(self,
                        sigma=None,
                        freq_min=None,
                        freq_max=None,
                        freq_pitch=None,
                        tetrahedron_method=False,
                        direction=None,
                        xyz_projection=False):
        self._pdos = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to be called before "
                  "PDOS calculation.")
            return False

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        num_grid = np.prod(self._mesh.get_mesh_numbers())
        if num_grid != len(self._mesh.get_ir_grid_points()):
            print("Warning: \'set_mesh\' has to be called with "
                  "is_mesh_symmetry=False.")
            return False

        if direction is not None:
            direction_cart = np.dot(direction, self._primitive.get_cell())
        else:
            direction_cart = None
        self._pdos = PartialDos(self._mesh,
                                sigma=sigma,
                                tetrahedron_method=tetrahedron_method,
                                direction=direction_cart,
                                xyz_projection=xyz_projection)
        self._pdos.set_draw_area(freq_min, freq_max, freq_pitch)
        self._pdos.run()
        return True

    def get_partial_DOS(self):
        """
        Retern frequencies and partial_dos.
        The first element is freqs and the second is partial_dos.

        frequencies: [freq1, freq2, ...]
        partial_dos:
          [[atom1-freq1, atom1-freq2, ...],
           [atom2-freq1, atom2-freq2, ...],
           ...]
        """
        return self._pdos.get_partial_dos()

    def plot_partial_DOS(self, pdos_indices=None, legend=None):
        import matplotlib.pyplot as plt
        self._pdos.plot(plt,
                        indices=pdos_indices,
                        legend=legend)
        return plt

    def write_partial_DOS(self):
        self._pdos.write()

    # Thermal property
    def set_thermal_properties(self,
                               t_step=10,
                               t_max=1000,
                               t_min=0,
                               temperatures=None,
                               is_projection=False,
                               band_indices=None,
                               cutoff_frequency=None):
        if self._mesh is None:
            print("Warning: set_mesh has to be done before "
                  "set_thermal_properties")
            return False
        else:
            tp = ThermalProperties(self._mesh.get_frequencies(),
                                   weights=self._mesh.get_weights(),
                                   eigenvectors=self._mesh.get_eigenvectors(),
                                   is_projection=is_projection,
                                   band_indices=band_indices,
                                   cutoff_frequency=cutoff_frequency)
            if temperatures is None:
                tp.set_temperature_range(t_step=t_step,
                                         t_max=t_max,
                                         t_min=t_min)
            else:
                tp.set_temperatures(temperatures)
            tp.run()
            self._thermal_properties = tp

    def get_thermal_properties(self):
        temps, fe, entropy, cv = \
            self._thermal_properties.get_thermal_properties()
        return temps, fe, entropy, cv

    def plot_thermal_properties(self):
        import matplotlib.pyplot as plt
        self._thermal_properties.plot(plt)
        return plt

    def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'):
        self._thermal_properties.write_yaml(filename=filename)

    # Thermal displacement
    def set_thermal_displacements(self,
                                  t_step=10,
                                  t_max=1000,
                                  t_min=0,
                                  temperatures=None,
                                  direction=None,
                                  cutoff_frequency=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        self._thermal_displacements = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before \'set_thermal_displacements\'.")
            return False

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        if np.prod(mesh_nums) != len(eigvecs):
            print("Warning: Sampling mesh must not be symmetrized.")
            return False

        td = ThermalDisplacements(frequencies,
                                  eigvecs,
                                  self._primitive.get_masses(),
                                  cutoff_frequency=cutoff_frequency)
        if temperatures is None:
            td.set_temperature_range(t_min, t_max, t_step)
        else:
            td.set_temperatures(temperatures)
        if direction is not None:
            td.project_eigenvectors(direction, self._primitive.get_cell())
        td.run()

        self._thermal_displacements = td
        return True

    def get_thermal_displacements(self):
        if self._thermal_displacements is not None:
            return self._thermal_displacements.get_thermal_displacements()

    def plot_thermal_displacements(self, is_legend=False):
        import matplotlib.pyplot as plt
        self._thermal_displacements.plot(plt, is_legend=is_legend)
        return plt

    def write_yaml_thermal_displacements(self):
        self._thermal_displacements.write_yaml()

    # Thermal displacement matrix
    def set_thermal_displacement_matrices(self,
                                          t_step=10,
                                          t_max=1000,
                                          t_min=0,
                                          cutoff_frequency=None,
                                          t_cif=None):
        """
        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.

        direction:
          Projection direction in reduced coordinates
        """
        self._thermal_displacement_matrices = None

        if self._mesh is None:
            print("Warning: \'set_mesh\' has to finish correctly "
                  "before \'set_thermal_displacement_matrices\'.")
            return False

        eigvecs = self._mesh.get_eigenvectors()
        frequencies = self._mesh.get_frequencies()
        mesh_nums = self._mesh.get_mesh_numbers()

        if self._mesh.get_eigenvectors() is None:
            print("Warning: Eigenvectors have to be calculated.")
            return False

        if np.prod(mesh_nums) != len(eigvecs):
            print("Warning: Sampling mesh must not be symmetrized.")
            return False

        tdm = ThermalDisplacementMatrices(frequencies,
                                          eigvecs,
                                          self._primitive.get_masses(),
                                          cutoff_frequency=cutoff_frequency,
                                          lattice=self._primitive.get_cell().T)
        if t_cif is None:
            tdm.set_temperature_range(t_min, t_max, t_step)
        else:
            tdm.set_temperatures([t_cif])
        tdm.run()

        self._thermal_displacement_matrices = tdm
        return True

    def get_thermal_displacement_matrices(self):
        tdm = self._thermal_displacement_matrices
        if tdm is not None:
            return tdm.get_thermal_displacement_matrices()

    def write_yaml_thermal_displacement_matrices(self):
        self._thermal_displacement_matrices.write_yaml()

    def write_thermal_displacement_matrix_to_cif(self,
                                                 temperature_index):
        self._thermal_displacement_matrices.write_cif(self._primitive,
                                                      temperature_index)

    # Mean square distance between a pair of atoms
    def set_thermal_distances(self,
                              atom_pairs,
                              t_step=10,
                              t_max=1000,
                              t_min=0,
                              cutoff_frequency=None):
        """
        atom_pairs: List of list
          Mean square distances are calculated for the atom_pairs
          e.g. [[1, 2], [1, 4]]

        cutoff_frequency:
          phonon modes that have frequencies below cutoff_frequency
          are ignored.
        """

        td = ThermalDistances(self._mesh.get_frequencies(),
                              self._mesh.get_eigenvectors(),
                              self._supercell,
                              self._primitive,
                              self._mesh.get_qpoints(),
                              cutoff_frequency=cutoff_frequency)
        td.set_temperature_range(t_min, t_max, t_step)
        td.run(atom_pairs)

        self._thermal_distances = td

    def write_yaml_thermal_distances(self):
        self._thermal_distances.write_yaml()

    # Sampling at q-points
    def set_qpoints_phonon(self,
                           q_points,
                           nac_q_direction=None,
                           is_eigenvectors=False,
                           write_dynamical_matrices=False):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._qpoints_phonon = None
            return False

        self._qpoints_phonon = QpointsPhonon(
            np.reshape(q_points, (-1, 3)),
            self._dynamical_matrix,
            nac_q_direction=nac_q_direction,
            is_eigenvectors=is_eigenvectors,
            group_velocity=self._group_velocity,
            write_dynamical_matrices=write_dynamical_matrices,
            factor=self._factor)
        return True

    def get_qpoints_phonon(self):
        return (self._qpoints_phonon.get_frequencies(),
                self._qpoints_phonon.get_eigenvectors())

    def write_hdf5_qpoints_phonon(self):
        self._qpoints_phonon.write_hdf5()

    def write_yaml_qpoints_phonon(self):
        self._qpoints_phonon.write_yaml()

    # Normal mode animation
    def write_animation(self,
                        q_point=None,
                        anime_type='v_sim',
                        band_index=None,
                        amplitude=None,
                        num_div=None,
                        shift=None,
                        filename=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            return False

        if q_point is None:
            animation = Animation([0, 0, 0],
                                  self._dynamical_matrix,
                                  shift=shift)
        else:
            animation = Animation(q_point,
                                  self._dynamical_matrix,
                                  shift=shift)
        if anime_type == 'v_sim':
            if amplitude:
                amplitude_ = amplitude
            else:
                amplitude_ = 1.0

            if filename:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor,
                                      filename=filename)
            else:
                animation.write_v_sim(amplitude=amplitude_,
                                      factor=self._factor)
        if (anime_type == 'arc' or
            anime_type == 'xyz' or
            anime_type == 'jmol' or
            anime_type == 'poscar'):
            if band_index is None or amplitude is None or num_div is None:
                print("Warning: Parameters are not correctly set for "
                      "animation.")
                return False

            if anime_type == 'arc' or anime_type is None:
                if filename:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div,
                                        filename=filename)
                else:
                    animation.write_arc(band_index,
                                        amplitude,
                                        num_div)

            if anime_type == 'xyz':
                if filename:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor,
                                        filename=filename)
                else:
                    animation.write_xyz(band_index,
                                        amplitude,
                                        num_div,
                                        self._factor)

            if anime_type == 'jmol':
                if filename:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor,
                                             filename=filename)
                else:
                    animation.write_xyz_jmol(amplitude=amplitude,
                                             factor=self._factor)

            if anime_type == 'poscar':
                if filename:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div,
                                           filename=filename)
                else:
                    animation.write_POSCAR(band_index,
                                           amplitude,
                                           num_div)

        return True

    # Atomic modulation of normal mode
    def set_modulations(self,
                        dimension,
                        phonon_modes,
                        delta_q=None,
                        derivative_order=None,
                        nac_q_direction=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._modulation = None
            return False

        self._modulation = Modulation(self._dynamical_matrix,
                                      dimension,
                                      phonon_modes,
                                      delta_q=delta_q,
                                      derivative_order=derivative_order,
                                      nac_q_direction=nac_q_direction,
                                      factor=self._factor)
        self._modulation.run()
        return True

    def get_modulated_supercells(self):
        """Returns cells with modulations as Atoms objects"""
        return self._modulation.get_modulated_supercells()

    def get_modulations_and_supercell(self):
        """Return modulations and supercell

        (modulations, supercell)

        modulations: Atomic modulations of supercell in Cartesian coordinates
        supercell: Supercell as an Atoms object.

        """
        return self._modulation.get_modulations_and_supercell()

    def write_modulations(self):
        """Create MPOSCAR's"""
        self._modulation.write()

    def write_yaml_modulations(self):
        self._modulation.write_yaml()

    # Irreducible representation
    def set_irreps(self,
                   q,
                   is_little_cogroup=False,
                   nac_q_direction=None,
                   degeneracy_tolerance=1e-4):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._irreps = None
            return None

        self._irreps = IrReps(
            self._dynamical_matrix,
            q,
            is_little_cogroup=is_little_cogroup,
            nac_q_direction=nac_q_direction,
            factor=self._factor,
            symprec=self._symprec,
            degeneracy_tolerance=degeneracy_tolerance,
            log_level=self._log_level)

        return self._irreps.run()

    def get_irreps(self):
        return self._irreps

    def show_irreps(self, show_irreps=False):
        self._irreps.show(show_irreps=show_irreps)

    def write_yaml_irreps(self, show_irreps=False):
        self._irreps.write_yaml(show_irreps=show_irreps)

    # Group velocity
    def set_group_velocity(self, q_length=None):
        if self._dynamical_matrix is None:
            print("Warning: Dynamical matrix has not yet built.")
            self._group_velocity = None
            return False

        self._group_velocity = GroupVelocity(
            self._dynamical_matrix,
            q_length=q_length,
            symmetry=self._primitive_symmetry,
            frequency_factor_to_THz=self._factor)
        return True

    def get_group_velocity(self):
        return self._group_velocity.get_group_velocity()

    def get_group_velocity_at_q(self, q_point):
        if self._group_velocity is None:
            self.set_group_velocity()
        self._group_velocity.set_q_points([q_point])
        return self._group_velocity.get_group_velocity()[0]

    # Moment
    def set_moment(self,
                   order=1,
                   is_projection=False,
                   freq_min=None,
                   freq_max=None):
        if self._mesh is None:
            print("Warning: set_mesh has to be done before set_moment")
            return False
        else:
            if is_projection:
                if self._mesh.get_eigenvectors() is None:
                    print("Warning: Eigenvectors have to be calculated.")
                    return False
                moment = PhononMoment(
                    self._mesh.get_frequencies(),
                    weights=self._mesh.get_weights(),
                    eigenvectors=self._mesh.get_eigenvectors())
            else:
                moment = PhononMoment(
                    self._mesh.get_frequencies(),
                    weights=self._mesh.get_weights())
            if freq_min is not None or freq_max is not None:
                moment.set_frequency_range(freq_min=freq_min,
                                           freq_max=freq_max)
            moment.run(order=order)
            self._moment = moment.get_moment()
            return True

    def get_moment(self):
        return self._moment

    #################
    # Local methods #
    #################
    def _run_force_constants_from_forces(self,
                                         distributed_atom_list=None,
                                         decimals=None,
                                         computation_algorithm="svd"):
        if self._displacement_dataset is not None:
            self._force_constants = get_fc2(
                self._supercell,
                self._symmetry,
                self._displacement_dataset,
                atom_list=distributed_atom_list,
                decimals=decimals,
                computation_algorithm=computation_algorithm)

    def _set_dynamical_matrix(self):
        self._dynamical_matrix = None

        if (self._supercell is None or self._primitive is None):
            print("Bug: Supercell or primitive is not created.")
            return False
        elif self._force_constants is None:
            print("Warning: Force constants are not prepared.")
            return False
        elif self._primitive.get_masses() is None:
            print("Warning: Atomic masses are not correctly set.")
            return False
        else:
            if self._nac_params is None:
                self._dynamical_matrix = DynamicalMatrix(
                    self._supercell,
                    self._primitive,
                    self._force_constants,
                    decimals=self._dynamical_matrix_decimals,
                    symprec=self._symprec)
            else:
                self._dynamical_matrix = DynamicalMatrixNAC(
                    self._supercell,
                    self._primitive,
                    self._force_constants,
                    nac_params=self._nac_params,
                    decimals=self._dynamical_matrix_decimals,
                    symprec=self._symprec)
            return True

    def _search_symmetry(self):
        self._symmetry = Symmetry(self._supercell,
                                  self._symprec,
                                  self._is_symmetry)

    def _search_primitive_symmetry(self):
        self._primitive_symmetry = Symmetry(self._primitive,
                                            self._symprec,
                                            self._is_symmetry)

        if (len(self._symmetry.get_pointgroup_operations()) !=
            len(self._primitive_symmetry.get_pointgroup_operations())):
            print("Warning: Point group symmetries of supercell and primitive"
                  "cell are different.")

    def _build_supercell(self):
        self._supercell = get_supercell(self._unitcell,
                                        self._supercell_matrix,
                                        self._symprec)

    def _build_supercells_with_displacements(self):
        supercells = []
        for disp in self._displacement_dataset['first_atoms']:
            positions = self._supercell.get_positions()
            positions[disp['number']] += disp['displacement']
            supercells.append(Atoms(
                    numbers=self._supercell.get_atomic_numbers(),
                    masses=self._supercell.get_masses(),
                    magmoms=self._supercell.get_magnetic_moments(),
                    positions=positions,
                    cell=self._supercell.get_cell(),
                    pbc=True))

        self._supercells_with_displacements = supercells

    def _build_primitive_cell(self):
        """
        primitive_matrix:
          Relative axes of primitive cell to the input unit cell.
          Relative axes to the supercell is calculated by:
             supercell_matrix^-1 * primitive_matrix
          Therefore primitive cell lattice is finally calculated by:
             (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T
        """

        inv_supercell_matrix = np.linalg.inv(self._supercell_matrix)
        if self._primitive_matrix is None:
            trans_mat = inv_supercell_matrix
        else:
            trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix)
        self._primitive = get_primitive(
            self._supercell, trans_mat, self._symprec)
        num_satom = self._supercell.get_number_of_atoms()
        num_patom = self._primitive.get_number_of_atoms()
        if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1:
            return True
        else:
            return False
Beispiel #9
0
class Collision():
    def __init__(self,
                 interaction,
                 sigmas=np.array([0.1]),
                 temperatures=None,
                 lang="C",
                 is_tetrahedron_method=False,
                 mass_variances=None,
                 length=None,
                 gv_delta_q = None,
                 is_adaptive_sigma=False,
                 is_group_velocity=False, # Use gv to determine smearing factor
                 write=False,
                 read=False,
                 cutoff_frequency = 1e-4): #unit: THz
        self._pp = interaction
        self._sigmas = sigmas
        self._temperatures = temperatures
        self._mesh = self._pp.get_mesh_numbers()
        self._is_nosym = self._pp._is_nosym
        if interaction._is_nosym:
            self._point_operations = np.array([[1,0,0],[0,1,0],[0,0,1]], dtype="intc")
        else:
            self._point_operations = self._pp.get_point_group_operations()
        self._kpoint_operations = self._pp._kpoint_operations
        self._is_dispersed=self._pp.get_is_dispersed()
        self._cutoff_frequency = cutoff_frequency
        self._grid_point = None
        self._grid_point_triplets = None
        self._triplet_weights = None
        self._is_thm = is_tetrahedron_method
        self._lang = lang
        self._frequencies_all = None
        self._eigenvectors_all = None
        self._band_indices = None
        self._fc3_normal_squared = None
        self._occupations_all = None
        self._sigma =None
        self._temperature = None
        self._isigma = None
        self._itemp = None
        self._is_group_velocity = is_group_velocity
        self._write_col=write
        self._read_col=read
        self._is_adaptive_sigma =is_adaptive_sigma
        self._is_on_iteration = False
        self._asigma = None
        self._gamma_all=None
        self._irr_mapping_indices = None
        self._collision_in = None
        self._collision_out = None
        self._collision_in_all = None
        self._is_read_error = None
        self._unit_conversion =  np.pi / 4 * ((Hbar * EV) ** 3 # Hbar * EV : hbar
                                 * EV ** 2 / Angstrom ** 6
                                 / (2 * np.pi * THz) ** 3
                                 / AMU ** 3
                                 / (Hbar * EV) ** 2
                                 / (2 * np.pi * THz) ** 2
                                 / np.prod(self._mesh))
        self._init_grids_properties()
        self._length = length
        self._gv_delta_q = gv_delta_q
        if is_group_velocity or self._length is not None:
            self._init_group_velocity()
        if mass_variances is not None:
            self._collision_iso = \
                CollisionIso(self._mesh,
                             self._pp._primitive,
                             mass_variances,
                             self._band_indices,
                             self._sigma)
            self._collision_iso.set_dynamical_matrix(dynamical_matrix=self._pp.get_dynamical_matrix())
        else:
            self._collision_iso = None

    def _init_group_velocity(self):
        self._group_velocity = GroupVelocity(
            self._pp.get_dynamical_matrix(),
            symmetry=self._pp._symmetry,
            q_length=self._gv_delta_q,
            frequency_factor_to_THz=self._pp.get_frequency_factor_to_THz())
        num_grids_all, num_band = self._pp._frequencies.shape
        self._gv_all = np.zeros((num_grids_all, num_band, 3), dtype="double")
        self._gv_done_all = np.zeros(num_grids_all, dtype="bool")

    def _init_grids_properties(self):
        num_grids_all, num_band = self._pp._frequencies.shape
        num_temp = len(self._temperatures)
        num_sigma = len(self._sigmas)
        self._occupations_all = np.zeros((num_grids_all, num_temp, num_band), dtype="double")
        self._collision_out_all = np.zeros((num_sigma, num_grids_all, num_temp, num_band), dtype="double")
        self._gamma_all = np.zeros_like(self._collision_out_all)
        self._n_done = np.zeros((num_grids_all, num_temp), dtype="bool")

    def set_grid(self, grid_point):
        if grid_point is None:
            self._grid_point = None
        else:
            self._pp.set_grid_point(grid_point)
            (self._grid_point_triplets,
             self._triplet_weights) = self._pp.get_triplets_at_q()

            self._grid_point = self._grid_point_triplets[0, 0]
            self._grid_address = self._pp.get_grid_address()
            self._qpoint = self._grid_address[self._grid_point] / np.double(self._mesh)
            kpg_index = get_kgp_index_at_grid(self._grid_address[grid_point], self._mesh, self._kpoint_operations)
            self._inv_rot_sum = self._kpoint_operations[kpg_index].sum(axis=0)
            self._kpg_at_q_index = kpg_index

            self._fc3_normal_squared = None
            if self._collision_iso is not None:
                grid_points2 = self.get_grid_point_triplets()[:,1].astype("intc").copy()
                weights2 = self.get_triplets_weights()
                self._collision_iso.set_grid_point(grid_point, grid_points2=grid_points2, weights2=weights2)


    def set_pp_grid_points_all(self, grid_points):
        self._pp.set_grid_points(grid_points)

    def set_grids(self, grid_points):
        self._pp._grid_points = grid_points
        self._collision_done = None
        if self._pp._unique_triplets == None:
            self._pp.set_grid_points(grid_points)
        if not self._is_dispersed:
            bi = self._pp._band_indices
            nband = 3 * self._pp._primitive.get_number_of_atoms()
            luniq = len(self._pp._unique_triplets)
            try:
                self._collision_in_all = np.zeros((luniq,3, len(bi), nband), dtype="double")
            except MemoryError:
                print "A memory error occurs in allocating the whole collision array"
                print "--disperse is recommended as a possible solution"
                sys.exit(1)
            self._collision_done = np.zeros(luniq, dtype="bool")



    def get_occupation(self):
        return self._occupations_all

    def get_grid_point_triplets(self):
        return self._grid_point_triplets

    def get_triplets_weights(self):
        return self._triplet_weights

    def get_collision_out(self):
        if self._cutoff_frequency is None:
            return self._collision_out
        else: # Averaging imag-self-energies by degenerate bands
            deg_sets = self._degeneracy_all[self._grid_point]
            cout = np.zeros_like(self._collision_out)
            for i, dset in enumerate(deg_sets):
                cout[i] = np.average(self._collision_out[np.where(deg_sets == dset)])
            return cout

    def get_collision_in(self):
        return self._collision_in

    def read_collision_all(self, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        is_error = read_collision_all_from_hdf5(self._collision_in_all,
                                                      self._mesh,
                                                      self._sigma,
                                                      self._temperature,
                                                      is_adaptive_sigma=is_adaptive_sigma,
                                                      log_level=log_level,
                                                      is_nosym=self._pp._is_nosym)

        if is_error:
            self.set_write_collision(True)
        else:
            if self._is_adaptive_sigma and (not self._is_on_iteration):
                self._collision_done[:] = False
            else:
                self._collision_done[:] = True

    def read_collision_at_grid(self, grid, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        is_error = read_collision_at_grid_from_hdf5(self._collision_in,
                                                    self._mesh,
                                                    grid,
                                                    self._sigma,
                                                    self._temperature,
                                                    is_adaptive_sigma=is_adaptive_sigma,
                                                    log_level=log_level,
                                                    is_nosym=self._pp._is_nosym)
        self._is_read_error = is_error
        if self._is_read_error:
            self.set_read_collision(False)
            self.set_write_collision(True)

    def write_collision_at_grid(self, grid, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        write_collision_to_hdf5_at_grid(self._collision_in,
                                         self._mesh,
                                         grid,
                                         self._sigma,
                                         self._temperature,
                                         is_adaptive_sigma=is_adaptive_sigma,
                                         log_level=log_level,
                                         is_nosym=self._pp._is_nosym)
        if self._is_read_error: # Condition: read argument is invoked
            self.set_read_collision(True)
            self.set_write_collision(False)

    def write_collision_all(self, log_level=0, is_adaptive_sigma=None):
        if is_adaptive_sigma == None:
            is_adaptive_sigma = self._is_adaptive_sigma
        if self._write_col:
            write_collision_to_hdf5_all(self._collision_in_all,
                                        self._mesh,
                                        self._sigma,
                                        self._temperature,
                                        is_adaptive_sigma = is_adaptive_sigma,
                                        log_level=log_level,
                                        is_nosym=self._pp._is_nosym)

    def set_temperature(self, temperature):
        for i, t in enumerate(self._temperatures):
            if temperature == t:
                self._itemp = i
                break
        self._temperature=temperature

    def set_is_adaptive_sigma(self, is_adaptive_sigma=False):
        self._is_adaptive_sigma = is_adaptive_sigma

    def set_is_on_iteration(self, is_on_iteration=True):
        self._is_on_iteration = is_on_iteration


    def set_sigma(self, sigma):
        for i, s in enumerate(self._sigmas):
            if s == sigma:
                self._isigma = i
                break
        self._sigma = sigma
        if self._sigma is None:
            self._is_thm = True

    @total_time.timeit
    def set_asigma(self, gamma_prev = None, gamma_pprev=None):
        if self._is_dispersed:
            if not self._read_col:
                triplets = self._grid_point_triplets
            elif self._is_adaptive_sigma or self._is_group_velocity:
                return
        else:
            triplets = self._triplets_reduced

        if self._is_adaptive_sigma:
            if gamma_prev is not None:
                if gamma_pprev is not None:
                    gamma_all = (self._gamma_all[self._isigma, :, self._itemp] + gamma_prev + gamma_pprev) / 3
                else:
                    gamma_all = (self._gamma_all[self._isigma, :, self._itemp] + gamma_prev) / 2
            else:
                gamma_all = self._gamma_all[self._isigma, :, self._itemp]
            triplet_gammas = gamma_all[triplets]
            gt = (triplet_gammas * 2 * np.pi) ** 2 / (2 * np.log(2)) # 2pi comes from the definition of gamma
            self._asigma = np.sqrt(gt[:,0, :, np.newaxis, np.newaxis] +
                                      gt[:,1, np.newaxis, :, np.newaxis] +
                                      gt[:,2, np.newaxis, np.newaxis, :])
        elif self._is_group_velocity:
            self._set_group_velocity(grid_points=np.unique(triplets))
            triplet1, triplet2 = triplets[:, 1], triplets[:,2]
            gv1, gv2 = self._gv_all[triplet1], self._gv_all[triplet2]
            reciprocal_lattice = np.linalg.inv(self._pp._primitive.get_cell())
            num_band = len(self._band_indices)
            gvs = gv1[:, :, np.newaxis] - gv2[:, np.newaxis, :]
            normalized_gvs = np.dot(gvs, reciprocal_lattice) / np.array(self._mesh)
            sigmas=  np.sqrt(np.sum(normalized_gvs ** 2, axis=-1) / 12)
            self._asigma = np.repeat(sigmas[:, np.newaxis], repeats=num_band, axis=1)
        else:
            if self._read_col:
                nband = self._pp._primitive.get_number_of_atoms() * 3
                self._asigma = np.zeros((0,nband, nband, nband), dtype="double")

    def _set_group_velocity(self, grid_points):
        undone_grid_points = np.extract(self._gv_done_all[grid_points] == 0, grid_points)
        qpoints = [self._grid_address[gp] / np.double(self._mesh) for gp in undone_grid_points]
        if len(qpoints) != 0:
            self._group_velocity.set_q_points(q_points=qpoints)
            self._gv_all[undone_grid_points] = self._group_velocity.get_group_velocity()
            self._gv_done_all[undone_grid_points] = True

    def set_write_collision(self, is_write_col):
        self._write_col = is_write_col

    def set_read_collision(self, is_read_col):
        self._read_col = is_read_col

    def get_write_collision(self):
        return self._write_col

    def get_read_collision(self):
        return self._read_col

    def get_is_dispersed(self):
        return self._is_dispersed

    def get_collision_out_all(self):
        return self._collision_out_all

    @total_time.timeit
    def run_interaction_at_grid_point(self, g_skip = None):
        self.set_phonons_triplets()
        if self._read_col and ((not self._is_adaptive_sigma) or self._is_on_iteration):
            nband = self._pp._primitive.get_number_of_atoms() * 3
            self._fc3_normal_squared = np.zeros((0,nband, nband, nband), dtype="double")
            self._fc3_normal_squared_reduced = np.zeros((0,nband, nband, nband), dtype="double")
        else:
            self._pp.run(g_skip = g_skip, lang=self._lang)
            self._fc3_normal_squared = self._pp.get_interaction_strength()
            self._fc3_normal_squared_reduced= self._fc3_normal_squared[self._undone_triplet_index]

        if self._collision_iso:
            self._collision_iso.set_frequencies(frequencies=self._frequencies_all,
                                                eigenvectors=self._eigenvectors_all,
                                                phonons_done=self._pp._phonon_done,
                                                degeneracies=self._degeneracy_all)

    def set_phonons_triplets(self):
        self._pp.set_phonons(lang=self._lang)
        (self._frequencies_all,
        self._eigenvectors_all) = self._pp.get_phonons()[:2]
        self._degeneracy_all = self._pp.get_degeneracy()
        self._band_indices = self._pp.get_band_indices()

    def reduce_triplets(self):
        num_triplets = len(self._pp.get_triplets_at_q()[0])

        num_band0 = len(self._band_indices)
        num_band = self._pp._primitive.get_number_of_atoms() * 3
        self._collision_out = None
        self._collision_in = np.zeros((num_triplets, num_band0, num_band), dtype="double")
        self._triplets_mapping = self._pp.get_triplets_mapping_at_grid()
        self.extract_undone_triplets()
        self._collision_in_reduced = np.zeros((len(self._undone_uniq_index),
                                           3,
                                           num_band0,
                                           num_band), dtype="double")
        self._triplets_reduced = self._pp.get_triplets_at_q()[0][self._undone_triplet_index]
        assert (self._pp._phonon_done[self._triplets_reduced] == True).all()
        self._frequencies_reduced = self._frequencies_all[self._triplets_reduced]
        # self.set_grid_points_occupation()
        self._occu_reduced = self._occupations_all[self._triplets_reduced, self._itemp]

    def set_grid_points_occupation(self, grid_points=None):
        if grid_points is None:
            grid_points = self._grid_point_triplets
        uniq_grids = np.unique(grid_points)
        for g in uniq_grids:
            if not self._n_done[g, self._itemp]:
                n = occupation(self._frequencies_all[g], self._temperature)
                n[np.where(self._frequencies_all[g]<self._cutoff_frequency)] = 0
                self._occupations_all[g, self._itemp] = n
                self._n_done[g, self._itemp] = True
        if self._collision_iso is not None:
            self._collision_iso.set_occupation(occupations=self._occupations_all[:,self._itemp],
                                               occupations_done=self._n_done[:,self._itemp])

    def set_grid_points_group_velocity(self, grid_points=None):
        if grid_points is None:
            grid_points = self._grid_point_triplets
        uniq_grids = np.unique(grid_points)


    def extract_undone_triplets(self):
        unique_map, index = np.unique(self._triplets_mapping, return_index=True)
        if self._is_dispersed:
            self._undone_uniq_index = unique_map
            self._undone_triplet_index = index
        else:
            self._undone_uniq_index = np.extract(self._collision_done[unique_map]==False, unique_map)
            self._undone_triplet_index = np.extract(self._collision_done[unique_map]==False, index)


    def broadcast_collision_out(self):
        grid_map = self._pp.get_grid_mapping()
        bz_to_pp_map = self._pp.get_bz_to_pp_map()
        bz_to_irred_map = grid_map[bz_to_pp_map]
        equiv_pos = np.where(bz_to_irred_map == self._grid_point)
        cm_out = self.get_collision_out()
        self._collision_out_all[self._isigma, equiv_pos, self._itemp] = cm_out
        n = self.get_occupation()[self._grid_point, self._itemp]
        nn1 = n * (n + 1)
        is_pass = self._frequencies_all[self._grid_point] < self._cutoff_frequency
        self._gamma_all[self._isigma, equiv_pos, self._itemp] = \
            cm_out * np.where(is_pass, 0, 1 / nn1) / 2.

    def run(self):
        if self._is_dispersed and self._read_col:
            self.read_collision_at_grid(self._grid_point)
        if self._lang=="C":
            self.run_c()
        else:
            self.run_py()
        if self._is_dispersed and self._write_col:
            self.write_collision_at_grid(self._grid_point)
        # self._collision_in *= self._unit_conversion # unit in THz
        summation = np.sum(self._collision_in, axis=-1)
        self._collision_out = np.dot(self._triplet_weights, summation) / 2.0

        # Set the diagonal part in A_in as 0
        # bz_to_pp = self._pp._bz_to_pp_map
        # triplets = bz_to_pp[self.get_grid_point_triplets()]
        # diag_pos = np.where(triplets[:, 1] == triplets[0,0])[0][0]
        # self._collision_out += self._collision_in[diag_pos].diagonal()
        # np.fill_diagonal(self._collision_in[diag_pos], 0)
        # Set the diagonal part in A_in as 0

        if self._collision_iso is not None:
            self._collision_iso.run()
            self._collision_out += self._collision_iso._collision_out
            self._collision_in += self._collision_iso._collision_in
        if self._length is not None:
            self._collision_out += self.get_boundary_scattering_strength()
        self.broadcast_collision_out()

    @total_time.timeit
    def get_boundary_scattering_strength(self):
        self._set_group_velocity(grid_points=[self._grid_point])
        bnd_collision_unit = 100. / 1e-6 / THz / (2 * np.pi) # unit in THz, unit of length is micron
        gv = self._gv_all[self._grid_point]
        dm = self._pp.get_dynamical_matrix()
        gv1 = get_group_velocity(self._qpoint,
                                dm,
                                symmetry=self._pp._symmetry,
                                q_length=self._gv_delta_q,
                                frequency_factor_to_THz=self._pp.get_frequency_factor_to_THz())

        gv_average = np.sqrt(np.sum(gv ** 2, axis=-1))
        n = self._occupations_all[self._grid_point, self._itemp]
        cout_bnd = gv_average / self._length * n * (n + 1)
        return cout_bnd * bnd_collision_unit

    def run_py(self):
        for i, triplet in enumerate(self._grid_point_triplets):
            occu = occupation(self._frequencies_all[triplet], self._temperature)
            freq = self._frequencies_all[triplet]
            for (j,k,l) in np.ndindex(self._fc3_normal_squared.shape[1:]):
                if self._asigma:
                    sigma=self._asigma[i,j,k,l]
                else:
                    sigma = self._sigma
                f0 = freq[0, j]; f1 = freq[1, k]; f2 = freq[2, l]
                if (f0<self._cutoff_frequency or f1 < self._cutoff_frequency or f2 < self._cutoff_frequency):
                    continue
                n0 = occu[0, j]; n1 = occu[1, k];  n2 = occu[2, l]
                delta1 = gaussian(f0+f1-f2, sigma)
                delta2 = gaussian(f0+f2-f1, sigma)
                delta3 = gaussian(f0-f1-f2, sigma)
                self._collision_in[i,j,k] += self._fc3_normal_squared[i,j,k,l] * ((n0 + 1) * n1 * n2 * delta3 +
                                                                                      (n1 + 1) * n0 * n2 * delta2 +
                                                                                      (n2 + 1) * n0 * n1 * delta1)
        self._collision_in *= self._unit_conversion

    def set_integration_weights(self, cutoff_g = 1e-8):
        f_points = self._frequencies_all[self._grid_point][self._band_indices]

        is_read_g = False; is_write_g = False
        if not self._is_adaptive_sigma and len(self._temperatures) > 1:
            if self._pp.get_is_read_amplitude() or self._pp.get_is_write_amplitude():
                if self._itemp == 0:
                    is_write_g = True; is_read_g = False
                else:
                    is_write_g = False; is_read_g = True
        if self._asigma is not None:
            sigma_object = self._asigma
            cutoff_g = np.where(self._asigma > 0, cutoff_g / self._asigma, 0)
            if self._is_dispersed and not self._read_col:
                cutoff_g = cutoff_g[self._undone_triplet_index]
        else:
            sigma_object = self._sigma
            if sigma_object is not None:
                cutoff_g = cutoff_g / self._sigma
        if self._is_dispersed:
            if is_read_g:
                self._g = read_integration_weight_from_hdf5_at_grid(self._mesh, self._sigma, self._grid_point)
            elif not self._read_col:
                self._g = get_triplets_integration_weights(
                    self._pp,
                    np.array(f_points, dtype='double'),
                    sigma_object,
                    band_indices=self._band_indices,
                    triplets = self._grid_point_triplets,
                    is_triplet_symmetry=self._pp._symmetrize_fc3_q)
                # when all the integration weights are smaller than the cutoff value
                # the interaction strength will not be calculated
                if is_write_g:
                    write_integration_weight_to_hdf5_at_grid(self._mesh, self._sigma, self._grid_point, self._g)
                self._g_skip = np.array(self._g[:, self._undone_triplet_index].sum(axis=0) < cutoff_g, dtype="bool")
            else:
                self._g_skip = None

        else:
            if is_read_g:
                self._g = read_integration_weight_from_hdf5_at_grid(self._mesh, self._sigma, self._grid_point)
            else:
                self._g = get_triplets_integration_weights(
                        self._pp,
                        np.array(f_points, dtype='double'),
                        sigma_object,
                        band_indices=self._band_indices,
                        triplets = self._triplets_reduced,
                        is_triplet_symmetry=self._pp._symmetrize_fc3_q)
                if is_write_g:
                    write_integration_weight_to_hdf5_at_grid(self._mesh, self._sigma, self._grid_point, self._g)
                if self._pp.get_triplets_done().all(): #when the interaction strengths are all done
                    self._g_skip = None
                else:
                    self._g_skip = np.array(np.abs(self._g).sum(axis=0) < cutoff_g, dtype="bool")



    def get_integration_weights(self):
        return self._g

    def get_interaction_skip(self):
        return self._g_skip

    @total_time.timeit
    def run_c(self):
        import anharmonic._phono3py as phono3c
        freq = self._frequencies_all[self._grid_point_triplets]

        if self._is_dispersed:
            if not self._read_col:
                phono3c.collision(self._collision_in,
                                  self._fc3_normal_squared.copy(),
                                  freq.copy(),
                                  self._g.copy(),
                                  self._temperature,
                                  self._cutoff_frequency)
                phono3c.collision_degeneracy(self._collision_in,
                                             self._degeneracy_all.astype('intc'),
                                             self._grid_point_triplets.astype("intc"),
                                             False)
                self._collision_in *= self._unit_conversion # unit in THz
        else:
            phono3c.collision_all_permute(self._collision_in_reduced,
                                          self._fc3_normal_squared_reduced.copy(),
                                          self._occu_reduced.copy(),
                                          self._frequencies_reduced.copy(),
                                          self._g.copy(),
                                          self._cutoff_frequency)
            self._collision_in_reduced *= self._unit_conversion # unit in THz
            phono3c.collision_degeneracy(self._collision_in_reduced,
                                         self._degeneracy_all.astype('intc'),
                                         self._triplets_reduced.astype('intc').copy(),
                                         True)
            self._collision_in_all[self._undone_uniq_index] = self._collision_in_reduced[:]
            self._collision_done[self._undone_uniq_index] = True
            phono3c.collision_from_reduced(self._collision_in,
                                           self._collision_in_all,
                                           self._pp.get_triplets_mapping_at_grid().astype("intc"),
                                           self._pp.get_triplets_sequence_at_grid().astype("byte"))


            # #############debugging############################
            # f_points = self._frequencies_all[self._grid_point][self._band_indices]
            # if self._asigma is not None:
            #     sigma_object = self._asigma
            # else:
            #     sigma_object = self._sigma
            # self._g = get_triplets_integration_weights(
            #     self._pp,
            #     np.array(f_points, dtype='double'),
            #     sigma_object,
            #     triplets = self._grid_point_triplets)
            # collision_in = np.zeros_like(self._collision_in)
            #
            # phono3c.collision(collision_in,
            #                   self._fc3_normal_squared.copy(),
            #                   freq.copy(),
            #                   self._g.copy(),
            #                   self._temperature,
            #                   self._cutoff_frequency)
            # grid_points2 = self._grid_point_triplets[:, 1]
            # phono3c.collision_degeneracy_grid(collision_in,
            #                                   self._degeneracy_all.astype('intc'),
            #                                   grid_points2.astype('intc').copy(),
            #                                   self._grid_point)
            # collision_in *= self._unit_conversion # unit in THz
            # diff = np.abs(collision_in - self._collision_in)
            # if len(diff)>0:
            #     print diff.max()
            # tt, bb0, bb1 = np.unravel_index(diff.argmax(), diff.shape)
            # grid0, grid1, grid2 = self._grid_point_triplets[tt]
            # n0 = self._occupations_all[grid0, self._itemp, bb0]
            # n1 = self._occupations_all[grid1, self._itemp, bb1]
            # n2 = self._occupations_all[grid2, self._itemp]
            # g0 = self._g[0, tt, bb0, bb1]
            # g1 = self._g[1, tt, bb0, bb1]
            # g2 = self._g[2, tt, bb0, bb1]
            # ng = n0 * n1 * (n2 + 1) * g2 + n0 * (n1 + 1) * n2 * g1 + (n0 + 1) * n1 * n2 * g0
            # nga = ng * self._fc3_normal_squared[tt, bb0, bb1] * self._unit_conversion
            # print

    @total_time.timeit
    def calculate_collision(self,grid_point, sigma=None, temperature=None):
        "On behalf of the limited memory, each time only one temperature and one sigma is calculated"
        if sigma is not None:
            self.set_sigma(sigma)
        if temperature is not None:
            self.set_temperature(temperature)

        self.set_grid(grid_point)
        self.set_phonons_triplets()
        self.set_grid_points_occupation()
        self.reduce_triplets()
        self.set_asigma()
        self.set_integration_weights()
        self.run_interaction_at_grid_point(g_skip = self._g_skip)
        self.run()