Exemplo n.º 1
0
    def __init__(self,
                 fc2,
                 fc3,
                 supercell,
                 primitive,
                 supercell_extra=None,
                 nac_params=None,
                 nac_q_direction=None,
                 ion_clamped=False,
                 factor=VaspToTHz,
                 symprec=1e-5):
        self._fc2 = fc2
        self._fc3 = fc3
        self._scell = supercell
        if supercell_extra is not None:
            self._scell_extra = supercell_extra
        else:
            self._scell_extra = supercell
        self._pcell = primitive
        self._ion_clamped = ion_clamped
        self._factor = factor
        self._symprec = symprec
        if nac_params is None:
            self._dm = DynamicalMatrix(self._scell_extra,
                                       self._pcell,
                                       self._fc2,
                                       symprec=self._symprec)
        else:
            self._dm = DynamicalMatrixNAC(self._scell_extra,
                                          self._pcell,
                                          self._fc2,
                                          symprec=self._symprec)
            self._dm.set_nac_params(nac_params)
        self._nac_q_direction = nac_q_direction
        self._shortest_vectors, self._multiplicity = get_smallest_vectors(
            self._scell, self._pcell, self._symprec)
        self._shortest_vectors_extra, self._multiplicity_extra = get_smallest_vectors(
            self._scell_extra, self._pcell, self._symprec)

        if self._ion_clamped:
            num_atom_prim = self._pcell.get_number_of_atoms()
            self._X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float)
        else:
            self._X = self._get_X()

        self._dPhidu = self._get_dPhidu()

        self._gruneisen_parameters = None
        self._frequencies = None
        self._qpoints = None
        self._mesh = None
        self._band_paths = None
        self._band_distances = None
        self._run_mode = None
        self._weights = None
Exemplo n.º 2
0
 def _set_dynamical_matrix(self):
     if self._nac_params==None:
         self._dm = DynamicalMatrix(self._supercell,
                                    self._primitive,
                                    self._fc2,
                                    frequency_scale_factor=self._frequency_scale_factor,
                                    symprec=self._symprec)
     else:
         self._dm = DynamicalMatrixNAC(self._supercell,
                                       self._primitive,
                                       self._fc2,
                                       frequency_scale_factor=self._frequency_scale_factor,
                                       symprec=self._symprec)
         self._dm.set_nac_params(self._nac_params)
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
def get_dynamical_matrix(fc2,
                         supercell,
                         primitive,
                         nac_params=None,
                         frequency_scale_factor=None,
                         decimals=None,
                         symprec=1e-5):
    if nac_params is None:
        dm = DynamicalMatrix(
            supercell,
            primitive,
            fc2,
            frequency_scale_factor=frequency_scale_factor,
            decimals=decimals,
            symprec=symprec)
    else:
        dm = DynamicalMatrixNAC(
            supercell,
            primitive,
            fc2,
            frequency_scale_factor=frequency_scale_factor,
            decimals=decimals,
            symprec=symprec)
        dm.set_nac_params(nac_params)
    return dm
Exemplo n.º 5
0
 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)
Exemplo n.º 6
0
 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)
Exemplo n.º 7
0
class Gruneisen:
    def __init__(self,
                 fc2,
                 fc3,
                 supercell,
                 primitive,
                 nac_params=None,
                 nac_q_direction=None,
                 ion_clamped=False,
                 factor=VaspToTHz,
                 symprec=1e-5):
        self._fc2 = fc2
        self._fc3 = fc3
        self._scell = supercell
        self._pcell = primitive
        self._ion_clamped = ion_clamped
        self._factor = factor
        self._symprec = symprec
        if nac_params is None:
            self._dm = DynamicalMatrix(self._scell,
                                       self._pcell,
                                       self._fc2,
                                       symprec=self._symprec)
        else:
            self._dm = DynamicalMatrixNAC(self._scell,
                                          self._pcell,
                                          self._fc2,
                                          symprec=self._symprec)
            self._dm.set_nac_params(nac_params)
        self._nac_q_direction = nac_q_direction
        self._shortest_vectors, self._multiplicity = get_smallest_vectors(
            self._scell, self._pcell, self._symprec)

        if self._ion_clamped:
            num_atom_prim = self._pcell.get_number_of_atoms()
            self._X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float)
        else:
            self._X = self._get_X()
        self._dPhidu = self._get_dPhidu()

        self._gruneisen_parameters = None
        self._frequencies = None
        self._qpoints = None
        self._mesh = None
        self._band_paths = None
        self._band_distances = None
        self._run_mode = None
        self._weights = None

    def run(self):
        if self._run_mode == 'band':
            (self._gruneisen_parameters,
             self._frequencies) = self._calculate_band_paths()
        elif self._run_mode == 'qpoints' or self._run_mode == 'mesh':
            (self._gruneisen_parameters,
             self._frequencies) = self._calculate_at_qpoints(self._qpoints)
        else:
            sys.stderr.write('Q-points are not specified.\n')

    def get_gruneisen_parameters(self):
        return self._gruneisen_parameters

    def set_qpoints(self, qpoints):
        self._run_mode = 'qpoints'
        self._qpoints = qpoints

    def set_sampling_mesh(self, mesh, shift=None, is_gamma_center=False):
        self._run_mode = 'mesh'
        self._mesh = mesh
        self._qpoints, self._weights = get_qpoints(
            self._mesh,
            np.linalg.inv(self._pcell.get_cell()),
            q_mesh_shift=shift,
            is_gamma_center=is_gamma_center)

    def set_band_structure(self, paths):
        self._run_mode = 'band'
        self._band_paths = paths
        rec_lattice = np.linalg.inv(self._pcell.get_cell())
        self._band_distances = []
        for path in paths:
            distances_at_path = [0.]
            for i in range(len(path) - 1):
                distances_at_path.append(
                    np.linalg.norm(np.dot(rec_lattice, path[i + 1] -
                                          path[i])) + distances_at_path[-1])
            self._band_distances.append(distances_at_path)

    def write_yaml(self, filename="gruneisen3.yaml"):
        if self._gruneisen_parameters is not None:
            f = open(filename, 'w')
            if self._run_mode == 'band':
                self._write_band_yaml(f)
            elif self._run_mode == 'qpoints' or self._run_mode == 'mesh':
                self._write_yaml(f)
            f.close()

    def _write_yaml(self, f):
        if self._run_mode == 'mesh':
            f.write("mesh: [ %5d, %5d, %5d ]\n" % tuple(self._mesh))
        f.write("nqpoint: %d\n" % len(self._qpoints))
        f.write("phonon:\n")
        for i, (q, g_at_q, freqs_at_q) in enumerate(
                zip(self._qpoints, self._gruneisen_parameters,
                    self._frequencies)):
            f.write("- q-position: [ %10.7f, %10.7f, %10.7f ]\n" % tuple(q))
            if self._weights is not None:
                f.write("  multiplicity: %d\n" % self._weights[i])
            f.write("  band:\n")
            for j, (g, freq) in enumerate(zip(g_at_q, freqs_at_q)):
                f.write("  - # %d\n" % (j + 1))
                f.write("    frequency: %15.10f\n" % freq)
                f.write("    gruneisen: %15.10f\n" % (g.trace() / 3))
                f.write("    gruneisen_tensor:\n")
                for g_xyz in g:
                    f.write("    - [ %10.7f, %10.7f, %10.7f ]\n" %
                            tuple(g_xyz))

    def _write_band_yaml(self, f):
        f.write("path:\n\n")
        for path, distances, gs, fs in zip(self._band_paths,
                                           self._band_distances,
                                           self._gruneisen_parameters,
                                           self._frequencies):
            f.write("- nqpoint: %d\n" % len(path))
            f.write("  phonon:\n")
            for i, (q, d, g_at_q,
                    freqs_at_q) in enumerate(zip(path, distances, gs, fs)):
                f.write("  - q-position: [ %10.7f, %10.7f, %10.7f ]\n" %
                        tuple(q))
                f.write("    distance: %10.7f\n" % d)
                f.write("    band:\n")
                for j, (g, freq) in enumerate(zip(g_at_q, freqs_at_q)):
                    f.write("    - # %d\n" % (j + 1))
                    f.write("      frequency: %15.10f\n" % freq)
                    f.write("      gruneisen: %15.10f\n" % (g.trace() / 3))
                    f.write("      gruneisen_tensor:\n")
                    for g_xyz in g:
                        f.write("      - [ %10.7f, %10.7f, %10.7f ]\n" %
                                tuple(g_xyz))
                f.write("\n")

    def _calculate_at_qpoints(self, qpoints):
        gruneisen_parameters = []
        frequencies = []
        for i, q in enumerate(qpoints):
            if self._dm.is_nac():
                if (np.abs(q) < 1e-5).all():  # If q is almost at Gamma
                    if self._run_mode == 'band':
                        # Direction estimated from neighboring point
                        if i > 0:
                            q_dir = qpoints[i] - qpoints[i - 1]
                        elif i == 0 and len(qpoints) > 1:
                            q_dir = qpoints[i + 1] - qpoints[i]
                        else:
                            q_dir = None
                        g, omega2 = self._get_gruneisen_tensor(
                            q, nac_q_direction=q_dir)
                    else:  # Specified q-vector
                        g, omega2 = self._get_gruneisen_tensor(
                            q, nac_q_direction=self._nac_q_direction)
                else:  # If q is away from Gamma-point, then q-vector
                    g, omega2 = self._get_gruneisen_tensor(q,
                                                           nac_q_direction=q)
            else:  # Without NAC
                g, omega2 = self._get_gruneisen_tensor(q)
            gruneisen_parameters.append(g)
            frequencies.append(
                np.sqrt(abs(omega2)) * np.sign(omega2) * self._factor)

        return gruneisen_parameters, frequencies

    def _calculate_band_paths(self):
        gruneisen_parameters = []
        frequencies = []
        for path in self._band_paths:
            (gruneisen_at_path,
             frequencies_at_path) = self._calculate_at_qpoints(path)
            gruneisen_parameters.append(gruneisen_at_path)
            frequencies.append(frequencies_at_path)

        return gruneisen_parameters, frequencies

    def _get_gruneisen_tensor(self, q, nac_q_direction=None):
        if nac_q_direction is None:
            self._dm.set_dynamical_matrix(q)
        else:
            self._dm.set_dynamical_matrix(q, nac_q_direction)
        omega2, w = np.linalg.eigh(self._dm.get_dynamical_matrix())
        g = np.zeros((len(omega2), 3, 3), dtype=float)
        num_atom_prim = self._pcell.get_number_of_atoms()
        dDdu = self._get_dDdu(q)

        for s in range(len(omega2)):
            if (np.abs(q) < 1e-5).all() and s < 3:
                continue
            for i in range(3):
                for j in range(3):
                    for nu in range(num_atom_prim):
                        for pi in range(num_atom_prim):
                            g[s] += (w[nu * 3 + i, s].conjugate() *
                                     dDdu[nu, pi, i, j] *
                                     w[pi * 3 + j, s]).real

            g[s] *= -1.0 / 2 / omega2[s]

        return g, omega2

    def _get_dDdu(self, q):
        num_atom_prim = self._pcell.get_number_of_atoms()
        num_atom_super = self._scell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        s2p = self._pcell.get_supercell_to_primitive_map()
        vecs = self._shortest_vectors
        multi = self._multiplicity
        m = self._pcell.get_masses()
        dPhidu = self._dPhidu
        dDdu = np.zeros((num_atom_prim, num_atom_prim, 3, 3, 3, 3),
                        dtype=complex)

        for nu in range(num_atom_prim):
            for pi, p in enumerate(p2s):
                for Ppi, s in enumerate(s2p):
                    if not s == p:
                        continue
                    phase = np.exp(
                        2j * np.pi * np.dot(vecs[Ppi, nu, :multi[Ppi, nu], :],
                                            q)).sum() / multi[Ppi, nu]
                    dDdu[nu, pi] += phase * dPhidu[nu, Ppi]
                dDdu[nu, pi] /= np.sqrt(m[nu] * m[pi])

        return dDdu

    def _get_dPhidu(self):
        fc3 = self._fc3
        num_atom_prim = self._pcell.get_number_of_atoms()
        num_atom_super = self._scell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        dPhidu = np.zeros((num_atom_prim, num_atom_super, 3, 3, 3, 3),
                          dtype=float)

        for nu in range(num_atom_prim):
            Y = self._get_Y(nu)
            for pi in range(num_atom_super):
                for i in range(3):
                    for j in range(3):
                        for k in range(3):
                            for l in range(3):
                                for m in range(3):
                                    dPhidu[nu, pi, i, j, k,
                                           l] = (fc3[p2s[nu], pi, :, i, j, :] *
                                                 Y[:, :, k, l]).sum()
                                    # (Y[:,:,k,l] + Y[:,:,l,k]) / 2).sum() # Symmetrization?

        return dPhidu

    def _get_Y(self, nu):
        P = self._fc2
        X = self._X
        vecs = self._shortest_vectors
        multi = self._multiplicity
        lat = self._pcell.get_cell()
        num_atom_super = self._scell.get_number_of_atoms()
        R = np.array([
            np.dot(
                vecs[Npi, nu, :multi[Npi, nu], :].sum(axis=0) / multi[Npi, nu],
                lat) for Npi in range(num_atom_super)
        ])

        p2s = self._pcell.get_primitive_to_supercell_map()
        s2p = self._pcell.get_supercell_to_primitive_map()
        p2p = self._pcell.get_primitive_to_primitive_map()

        Y = np.zeros((num_atom_super, 3, 3, 3), dtype=float)

        for Mmu in range(num_atom_super):
            for i in range(3):
                Y[Mmu, i, i, :] = R[Mmu, :]
            Y[Mmu] += X[p2p[s2p[Mmu]]]

        return Y

    def _get_X(self):
        num_atom_super = self._scell.get_number_of_atoms()
        num_atom_prim = self._pcell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        lat = self._pcell.get_cell()
        vecs = self._shortest_vectors
        multi = self._multiplicity
        X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float)
        G = self._get_Gamma()
        P = self._fc2

        for mu in range(num_atom_prim):
            for nu in range(num_atom_prim):
                R = np.array([
                    np.dot(
                        vecs[Npi, nu, :multi[Npi, nu], :].sum(axis=0) /
                        multi[Npi, nu], lat) for Npi in range(num_atom_super)
                ])
                for i in range(3):
                    for j in range(3):
                        for k in range(3):
                            for l in range(3):
                                X[mu, i, j, k] -= G[mu, nu, i, l] * \
                                    np.dot(P[p2s[nu], :, l, j], R[:, k])

        return X

    def _get_Gamma(self):
        num_atom_prim = self._pcell.get_number_of_atoms()
        m = self._pcell.get_masses()
        self._dm.set_dynamical_matrix([0, 0, 0])
        vals, vecs = np.linalg.eigh(self._dm.get_dynamical_matrix().real)
        G = np.zeros((num_atom_prim, num_atom_prim, 3, 3), dtype=float)

        for pi in range(num_atom_prim):
            for mu in range(num_atom_prim):
                for k in range(3):
                    for i in range(3):
                        # Eigenvectors are real.
                        # 3: means optical modes
                        G[pi, mu, k, i] = 1.0 / np.sqrt(m[pi] * m[mu]) * \
                            (vecs[pi * 3 + k, 3:] * vecs[mu * 3 + i, 3:] /
                             vals[3:]).sum()
        return G
Exemplo n.º 8
0
class JointDos:
    def __init__(self,
                 mesh,
                 primitive,
                 supercell,
                 fc2,
                 nac_params=None,
                 nac_q_direction=None,
                 sigma=None,
                 cutoff_frequency=None,
                 frequency_step=None,
                 num_frequency_points=None,
                 temperatures=None,
                 frequency_factor_to_THz=VaspToTHz,
                 frequency_scale_factor=1.0,
                 is_nosym=False,
                 symprec=1e-5,
                 filename=None,
                 log_level=False,
                 lapack_zheev_uplo='L'):

        self._grid_point = None
        self._mesh = np.array(mesh, dtype='intc')
        self._primitive = primitive
        self._supercell = supercell
        self._fc2 = fc2
        self._nac_params = nac_params
        self._nac_q_direction = None
        self.set_nac_q_direction(nac_q_direction)
        self._sigma = None
        self.set_sigma(sigma)

        if cutoff_frequency is None:
            self._cutoff_frequency = 0
        else:
            self._cutoff_frequency = cutoff_frequency
        self._frequency_step = frequency_step
        self._num_frequency_points = num_frequency_points
        self._temperatures = temperatures
        self._frequency_factor_to_THz = frequency_factor_to_THz
        self._frequency_scale_factor = frequency_scale_factor
        self._is_nosym = is_nosym
        self._symprec = symprec
        self._filename = filename
        self._log_level = log_level
        self._lapack_zheev_uplo = lapack_zheev_uplo

        self._num_band = self._primitive.get_number_of_atoms() * 3
        self._reciprocal_lattice = np.linalg.inv(self._primitive.get_cell())
        self._set_dynamical_matrix()
        self._symmetry = Symmetry(primitive, symprec)

        self._tetrahedron_method = None
        self._phonon_done = None
        self._frequencies = None
        self._eigenvectors = None

        self._joint_dos = None
        self._frequency_points = None

    def set_grid_points(self):
        if self._is_nosym: # All grid points
            self._grid_points = np.arange(np.prod(self._mesh),
                                           dtype='intc')
            self._grid_weights = np.ones(len(self.grid_points), dtype='intc')

        else: # Automatic sampling
            (grid_points,
             grid_weights,
             grid_address) = get_ir_grid_points(
                self._mesh,
                self._primitive,
                mesh_shifts=[False, False, False])
            self._grid_points = grid_points
            self._grid_weights = grid_weights

    def run(self, is_band_freq=False):
        try:
            import anharmonic._phono3py as phono3c
            self._run_c(is_band_freq)
        except ImportError:
            print "Joint density of states in python is not implemented."
            return None, None

    def get_joint_dos(self):
        return self._joint_dos

    def get_frequency_points(self):
        return self._frequency_points

    def get_phonons(self):
        return self._frequencies, self._eigenvectors, self._phonon_done

    def get_primitive(self):
        return self._primitive

    def get_mesh_numbers(self):
        return self._mesh

    def set_nac_q_direction(self, nac_q_direction=None):
        if nac_q_direction is not None:
            self._nac_q_direction = np.array(nac_q_direction, dtype='double')

    def set_sigma(self, sigma):
        if sigma is None:
            self._sigma = None
        else:
            self._sigma = float(sigma)

    def set_grid_point(self, grid_point):
        self._grid_point = grid_point
        self._set_triplets()
        num_grid = np.prod(len(self._grid_address))
        num_band = self._num_band
        if self._phonon_done is None:
            self._phonon_done = np.zeros(num_grid, dtype='byte')
            self._frequencies = np.zeros((num_grid, num_band), dtype='double')
            self._eigenvectors = np.zeros((num_grid, num_band, num_band),
                                          dtype='complex128')

        self._joint_dos = None
        self._frequency_points = None
        self.set_phonons(np.array([grid_point], dtype='intc'))

    def get_triplets_at_q(self):
        return self._triplets_at_q, self._weights_at_q

    def get_grid_address(self):
        return self._grid_address

    def get_bz_map(self):
        return self._bz_map

    def _run_c(self, is_band_freq = False, lang='C'):
        if self._sigma is None:
            if lang == 'C':
                self._run_c_with_g(is_band_freq)
            else:
                if self._temperatures is not None:
                    print "JDOS with phonon occupation numbers doesn't work",
                    print "in this option."
                self._run_py_tetrahedron_method(is_band_freq)
        else:
            self._run_c_with_g(is_band_freq)

    def _run_c_with_g(self, is_band_freq=False):
        self.set_phonons(self._triplets_at_q.ravel())
        if is_band_freq == True:
            self._frequency_points = self._frequencies[self._grid_point]
        else:
            if self._sigma is None:
                f_max = np.max(self._frequencies) * 2
            else:
                f_max = np.max(self._frequencies) * 2 + self._sigma * 4
            f_max *= 1.005
            f_min = 0
            self._set_frequency_points(f_min, f_max)

        num_freq_points = len(self._frequency_points)
        num_mesh = np.prod(self._mesh)

        if self._temperatures is None:
            jdos = np.zeros((num_freq_points, 2), dtype='double')
        else:
            num_temps = len(self._temperatures)
            jdos = np.zeros((num_temps, num_freq_points, 2), dtype='double')
            occ_phonons = []
            for t in self._temperatures:
                freqs = self._frequencies[self._triplets_at_q[:, 1:]]
                occ_phonons.append(np.where(freqs > self._cutoff_frequency,
                                            occupation(freqs, t), 0))

        for i, freq_point in enumerate(self._frequency_points):
            g = get_triplets_integration_weights(
                self,
                np.array([freq_point], dtype='double'),
                self._sigma,
                neighboring_phonons=(i == 0),
                is_triplet_symmetry=False)


            if self._temperatures is None:
                jdos[i, 1] = np.sum(
                    np.tensordot(g[0, :, 0], self._weights_at_q, axes=(0, 0)))
                # gx = g[2] - g[0]
                gx = g[1] + g[2]
                jdos[i, 0] = - np.sum(
                    np.tensordot(gx[:, 0], self._weights_at_q, axes=(0, 0)))# the negative sign is for a clearer plot

            else:
                # g1 = g[1]
                g1 = g[2] - g[1]
                for j, n in enumerate(occ_phonons): # loop over temperature
                    for k, l in list(np.ndindex(g.shape[3:])): # double loop over other two phonon branches
                        jdos[j, i, 1] += np.dot(
                            (n[:, 0, k] + n[:, 1, l] + 1) *
                            g[0, :, 0, k, l], self._weights_at_q)
                        jdos[j, i, 0] += - np.dot((n[:, 0, k] - n[:, 1, l]) *
                                                g1[:, 0, k, l],
                                                self._weights_at_q) # the negative sign is for a clearer plot

        self._joint_dos = jdos / num_mesh

    def _run_py_tetrahedron_method(self, is_band_freq=False):
        thm = TetrahedronMethod(self._reciprocal_lattice, mesh=self._mesh)
        self._vertices = get_tetrahedra_vertices(
            thm.get_tetrahedra(),
            self._mesh,
            self._triplets_at_q,
            self._grid_address)
        self.set_phonons(self._vertices.ravel())
        if is_band_freq:
            self._frequency_points = self._frequencies[self._grid_point]
        else:
            f_max = np.max(self._frequencies) * 2
            f_max *= 1.005
            f_min = 0
            self._set_frequency_points(f_min, f_max)
        num_freq_points = len(self._frequency_points)
        jdos = np.zeros((num_freq_points, 2), dtype='double')
        for vertices, w in zip(self._vertices, self._weights_at_q):
            for i, j in list(np.ndindex(self._num_band, self._num_band)):
                f1 = self._frequencies[vertices[0], i]
                f2 = self._frequencies[vertices[1], j]
                thm.set_tetrahedra_omegas(f1 + f2)
                thm.run(self._frequency_points)
                iw = thm.get_integration_weight()
                jdos[:, 1] += iw * w

                thm.set_tetrahedra_omegas(f1 - f2)
                thm.run(self._frequency_points)
                iw = thm.get_integration_weight()
                jdos[:, 0] += iw * w

                thm.set_tetrahedra_omegas(-f1 + f2)
                thm.run(self._frequency_points)
                iw = thm.get_integration_weight()
                jdos[:, 0] += iw * w

        self._joint_dos = jdos / np.prod(self._mesh)

    def _set_dynamical_matrix(self):
        if self._nac_params==None:
            self._dm = DynamicalMatrix(self._supercell,
                                       self._primitive,
                                       self._fc2,
                                       frequency_scale_factor=self._frequency_scale_factor,
                                       symprec=self._symprec)
        else:
            self._dm = DynamicalMatrixNAC(self._supercell,
                                          self._primitive,
                                          self._fc2,
                                          frequency_scale_factor=self._frequency_scale_factor,
                                          symprec=self._symprec)
            self._dm.set_nac_params(self._nac_params)


    def _set_triplets(self):
        primitive_lattice  = self._primitive.get_cell()
        if self._is_nosym:
            if self._log_level:
                print "Triplets at q without considering symmetry"
                sys.stdout.flush()

            (self._triplets_at_q,
             self._weights_at_q,
             self._grid_address,
             self._bz_map,
             map_triplets,
             map_q) = get_nosym_triplets_at_q(
                 self._grid_point,
                 self._mesh,
                 primitive_lattice)
        else:
            (self._triplets_at_q,
             self._weights_at_q,
             self._grid_address,
             self._bz_map,
             map_q) = get_triplets_at_q(
                 self._grid_point,
                 self._mesh,
                 self._symmetry.get_pointgroup_operations(),
                 primitive_lattice)

    def set_phonons(self, grid_points):
        set_phonon_c(self._dm,
                     self._frequencies,
                     self._eigenvectors,
                     None,
                     self._phonon_done,
                     grid_points,
                     self._grid_address,
                     self._mesh,
                     self._frequency_factor_to_THz,
                     self._nac_q_direction,
                     self._lapack_zheev_uplo)

    def _set_frequency_points(self, f_min, f_max):
        if self._num_frequency_points is None:
            if self._frequency_step is not None:
                self._frequency_points = np.arange(
                    f_min, f_max, self._frequency_step, dtype='double')
            else:
                self._frequency_points = np.array(np.linspace(
                    f_min, f_max, 201), dtype='double')
        else:
            self._frequency_points = np.array(np.linspace(
                f_min, f_max, self._num_frequency_points), dtype='double')
Exemplo n.º 9
0
class Gruneisen:
    def __init__(self,
                 fc2,
                 fc3,
                 supercell,
                 primitive,
                 supercell_extra=None,
                 nac_params=None,
                 nac_q_direction=None,
                 ion_clamped=False,
                 factor=VaspToTHz,
                 symprec=1e-5):
        self._fc2 = fc2
        self._fc3 = fc3
        self._scell = supercell
        if supercell_extra is not None:
            self._scell_extra = supercell_extra
        else:
            self._scell_extra = supercell
        self._pcell = primitive
        self._ion_clamped = ion_clamped
        self._factor = factor
        self._symprec = symprec
        if nac_params is None:
            self._dm = DynamicalMatrix(self._scell_extra,
                                       self._pcell,
                                       self._fc2,
                                       symprec=self._symprec)
        else:
            self._dm = DynamicalMatrixNAC(self._scell_extra,
                                          self._pcell,
                                          self._fc2,
                                          symprec=self._symprec)
            self._dm.set_nac_params(nac_params)
        self._nac_q_direction = nac_q_direction
        self._shortest_vectors, self._multiplicity = get_smallest_vectors(
            self._scell, self._pcell, self._symprec)
        self._shortest_vectors_extra, self._multiplicity_extra = get_smallest_vectors(
            self._scell_extra, self._pcell, self._symprec)

        if self._ion_clamped:
            num_atom_prim = self._pcell.get_number_of_atoms()
            self._X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float)
        else:
            self._X = self._get_X()

        self._dPhidu = self._get_dPhidu()

        self._gruneisen_parameters = None
        self._frequencies = None
        self._qpoints = None
        self._mesh = None
        self._band_paths = None
        self._band_distances = None
        self._run_mode = None
        self._weights = None

    def run(self):
        if self._run_mode == 'band':
            (self._gruneisen_parameters,
             self._frequencies) = self._calculate_band_paths()
        elif self._run_mode == 'qpoints' or self._run_mode == 'mesh':
            (self._gruneisen_parameters,
             self._frequencies) = self._calculate_at_qpoints(self._qpoints)
        else:
            sys.stderr.write('Q-points are not specified.\n')

    def get_gruneisen_parameters(self):
        return self._gruneisen_parameters

    def set_qpoints(self, qpoints):
        self._run_mode = 'qpoints'
        self._qpoints = qpoints

    def set_sampling_mesh(self,
                          mesh,
                          grid_shift=None,
                          is_gamma_center=False):
        self._run_mode = 'mesh'
        self._mesh = mesh
        self._qpoints, self._weights = get_qpoints(self._mesh,
                                                   self._pcell,
                                                   grid_shift,
                                                   is_gamma_center)

    def set_band_structure(self, paths):
        self._run_mode = 'band'
        self._band_paths = paths
        rec_lattice = np.linalg.inv(self._pcell.get_cell())
        self._band_distances = []
        for path in paths:
            distances_at_path = [0.]
            for i in range(len(path) - 1):
                distances_at_path.append(np.linalg.norm(
                        np.dot(rec_lattice, path[i + 1] - path[i])) +
                                         distances_at_path[-1])
            self._band_distances.append(distances_at_path)

    def write_yaml(self, filename="gruneisen3.yaml"):
        if self._gruneisen_parameters is not None:
            f = open(filename, 'w')
            if self._run_mode == 'band':
                self._write_band_yaml(f)
            elif self._run_mode == 'qpoints' or self._run_mode == 'mesh':
                self._write_yaml(f)
            f.close()

    def _write_yaml(self, f):
        if self._run_mode == 'mesh':
            f.write("mesh: [ %5d, %5d, %5d ]\n" % tuple(self._mesh))
        f.write("nqpoint: %d\n" % len(self._qpoints))
        f.write("phonon:\n")
        for i, (q, g_at_q, freqs_at_q) in enumerate(
            zip(self._qpoints,
                self._gruneisen_parameters,
                self._frequencies)):
            f.write("- q-position: [ %10.7f, %10.7f, %10.7f ]\n" % tuple(q))
            if self._weights is not None:
                f.write("  multiplicity: %d\n" % self._weights[i])
            f.write("  band:\n")
            for j, (g, freq) in enumerate(zip(g_at_q, freqs_at_q)):
                f.write("  - # %d\n" % (j + 1))
                f.write("    frequency: %15.10f\n" % freq)
                f.write("    gruneisen: %15.10f\n" % (g.trace() / 3))
                f.write("    gruneisen_tensor:\n")
                for g_xyz in g:
                    f.write("    - [ %10.7f, %10.7f, %10.7f ]\n" %
                            tuple(g_xyz))
        
    def _write_band_yaml(self, f):
        f.write("path:\n\n")
        for path, distances, gs, fs in zip(self._band_paths,
                                           self._band_distances,
                                           self._gruneisen_parameters,
                                           self._frequencies):
            f.write("- nqpoint: %d\n" % len(path))
            f.write("  phonon:\n")
            for i, (q, d, g_at_q, freqs_at_q) in enumerate(
                zip(path, distances, gs, fs)):
                f.write("  - q-position: [ %10.7f, %10.7f, %10.7f ]\n"
                        % tuple(q))
                f.write("    distance: %10.7f\n" % d)
                f.write("    band:\n")
                for j, (g, freq) in enumerate(zip(g_at_q, freqs_at_q)):
                    f.write("    - # %d\n" % (j + 1))
                    f.write("      frequency: %15.10f\n" % freq)
                    f.write("      gruneisen: %15.10f\n" % (g.trace() / 3))
                    f.write("      gruneisen_tensor:\n")
                    for g_xyz in g:
                        f.write("      - [ %10.7f, %10.7f, %10.7f ]\n" %
                                tuple(g_xyz))
                f.write("\n")
                        
        
    def _calculate_at_qpoints(self, qpoints):
        gruneisen_parameters = []
        frequencies = []
        for i, q in enumerate(qpoints):
            if self._dm.is_nac():
                if (np.abs(q) < 1e-5).all(): # If q is almost at Gamma
                    if self._run_mode == 'band': 
                        # Direction estimated from neighboring point
                        if i > 0:
                            q_dir = qpoints[i] - qpoints[i - 1]
                        elif i == 0 and len(qpoints) > 1:
                            q_dir = qpoints[i + 1] - qpoints[i]
                        else:
                            q_dir = None
                        g, omega2 = self._get_gruneisen_tensor(
                            q, nac_q_direction=q_dir)
                    else: # Specified q-vector
                        g, omega2 = self._get_gruneisen_tensor(
                            q, nac_q_direction=self._nac_q_direction)
                else: # If q is away from Gamma-point, then q-vector
                    g, omega2 = self._get_gruneisen_tensor(q, nac_q_direction=q)
            else: # Without NAC
                g, omega2 = self._get_gruneisen_tensor(q)
            gruneisen_parameters.append(g)
            frequencies.append(
                np.sqrt(abs(omega2)) * np.sign(omega2) * self._factor)

        return gruneisen_parameters, frequencies
            
    def _calculate_band_paths(self):
        gruneisen_parameters = []
        frequencies = []
        for path in self._band_paths:
            (gruneisen_at_path,
             frequencies_at_path) = self._calculate_at_qpoints(path)
            gruneisen_parameters.append(gruneisen_at_path)
            frequencies.append(frequencies_at_path)

        return gruneisen_parameters, frequencies

    def _get_gruneisen_tensor(self, q, nac_q_direction=None):
        if nac_q_direction is None:
            self._dm.set_dynamical_matrix(q)
        else:
            self._dm.set_dynamical_matrix(q, nac_q_direction)
        omega2, w = np.linalg.eigh(self._dm.get_dynamical_matrix())
        g = np.zeros((len(omega2), 3, 3), dtype=float)
        num_atom_prim = self._pcell.get_number_of_atoms()
        dDdu = self._get_dDdu(q)

        for s in range(len(omega2)):
            if (np.abs(q) < 1e-5).all() and s < 3:
                continue
            for i in range(3):
                for j in range(3):
                    for nu in range(num_atom_prim):
                        for pi in range(num_atom_prim):
                            g[s] += (w[nu * 3 + i, s].conjugate() * 
                                     dDdu[nu, pi, i, j] * w[pi * 3 + j, s]).real

            g[s] *= -1.0/2/omega2[s]

        return g, omega2

    def _get_dDdu(self, q):
        num_atom_prim = self._pcell.get_number_of_atoms()
        num_atom_super = self._scell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        s2p = self._pcell.get_supercell_to_primitive_map()
        vecs = self._shortest_vectors
        multi = self._multiplicity
        m = self._pcell.get_masses()
        dPhidu = self._dPhidu
        dDdu = np.zeros((num_atom_prim, num_atom_prim, 3, 3, 3, 3), dtype=complex)
        
        for nu in range(num_atom_prim):
            for pi, p in enumerate(p2s):
                for Ppi, s in enumerate(s2p):
                    if not s==p:
                        continue
                    phase = np.exp(2j * np.pi * np.dot(
                            vecs[Ppi,nu,:multi[Ppi, nu], :], q)
                                   ).sum() / multi[Ppi, nu]
                    dDdu[nu, pi] += phase * dPhidu[nu, Ppi]
                dDdu[nu, pi] /= np.sqrt(m[nu] * m[pi])

        return dDdu
                                    
    def _get_dPhidu(self):
        fc3 = self._fc3
        num_atom_prim = self._pcell.get_number_of_atoms()
        num_atom_super = self._scell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        dPhidu = np.zeros((num_atom_prim, num_atom_super, 3, 3, 3, 3),
                          dtype=float)

        for nu in range(num_atom_prim):
            Y = self._get_Y(nu)
            for pi in range(num_atom_super):
                for i in range(3):
                    for j in range(3):
                        for k in range(3):
                            for l in range(3):
                                for m in range(3):
                                    dPhidu[nu, pi, i, j, k, l] = (
                                        fc3[p2s[nu], pi, :, i, j, :] *
                                        Y[:, :, k, l]).sum()
                                    # (Y[:,:,k,l] + Y[:,:,l,k]) / 2).sum() # Symmetrization?

        return dPhidu

    def _get_Y(self, nu):
        P = self._fc2
        X = self._X
        vecs = self._shortest_vectors
        multi = self._multiplicity
        lat = self._pcell.get_cell()
        num_atom_super = self._scell.get_number_of_atoms()
        R = np.array(
            [np.dot(vecs[Npi, nu, :multi[Npi,nu], :].sum(axis=0) /
                    multi[Npi,nu], lat) for Npi in range(num_atom_super)])

        p2s = self._pcell.get_primitive_to_supercell_map()
        s2p = self._pcell.get_supercell_to_primitive_map()
        p2p = self._pcell.get_primitive_to_primitive_map()
        # s2p = self._scell_extra.get_supercell_to_unitcell_map()
        # p2p = {0:0, 32:1}



        Y = np.zeros((num_atom_super, 3, 3, 3), dtype=float)

        for Mmu in range(num_atom_super):
            for i in range(3):
                Y[Mmu, i, i, :] = R[Mmu, :]
            Y[Mmu] += X[p2p[s2p[Mmu]]]
            
        return Y

    def _get_X(self):
        num_atom_super = self._scell_extra.get_number_of_atoms()
        num_atom_prim = self._pcell.get_number_of_atoms()
        p2s = self._pcell.get_primitive_to_supercell_map()
        lat = self._pcell.get_cell()
        vecs = self._shortest_vectors_extra
        multi = self._multiplicity_extra
        X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float)
        G = self._get_Gamma()
        P = self._fc2

        for mu in range(num_atom_prim):
            for nu in range(num_atom_prim):
                R = np.array(
                    [np.dot(vecs[Npi, nu, :multi[Npi, nu], :].sum(axis=0) /
                            multi[Npi, nu], lat)
                     for Npi in range(num_atom_super)])
                for i in range(3):
                    for j in range(3):
                        for k in range(3):
                            for l in range(3):
                                X[mu, i, j, k] -= G[mu, nu, i, l] * \
                                    np.dot(P[p2s[nu], :, l, j], R[:, k])

        return X

    def _get_Gamma(self):
        num_atom_prim = self._pcell.get_number_of_atoms()
        m = self._pcell.get_masses()
        self._dm.set_dynamical_matrix([0, 0, 0])
        vals, vecs = np.linalg.eigh(self._dm.get_dynamical_matrix().real)
        G = np.zeros((num_atom_prim, num_atom_prim, 3, 3), dtype=float)

        for pi in range(num_atom_prim):
            for mu in range(num_atom_prim):
                for k in range(3):
                    for i in range(3):
                        # Eigenvectors are real.
                        # 3: means optical modes
                        G[pi, mu, k, i] = 1.0 / np.sqrt(m[pi] * m[mu]) * \
                            (vecs[pi * 3 + k, 3:] * vecs[mu * 3 + i, 3:] /
                             vals[3:]).sum()
        return G