From 125f86a0752b87e36b40bea8d6abfaef4052aaab Mon Sep 17 00:00:00 2001 From: antonio Date: Fri, 14 Oct 2022 15:45:40 +0200 Subject: [PATCH 01/18] Now the symmetrization of effective charges and Raman tensor work. Still need to add a proper test --- .ipynb_checkpoints/setup-checkpoint.py | 80 + .../cryst_to_car-checkpoint.f90 | 65 + .../fc_supercell_from_dyn-checkpoint.f90 | 276 + .../.ipynb_checkpoints/invmat-checkpoint.f90 | 47 + .../symdynph_gq_new-checkpoint.f90 | 235 + .../symm_base-checkpoint.f90 | 1054 ++++ .../symm_matrix-checkpoint.f90 | 97 + .../symmetry_high_rank-checkpoint.f90 | 1180 +++++ .../.ipynb_checkpoints/Methods-checkpoint.py | 1852 +++++++ .../Moro_object-checkpoint.py | 160 + .../.ipynb_checkpoints/Phonons-checkpoint.py | 4518 +++++++++++++++++ .../Structure-checkpoint.py | 2319 +++++++++ .../calculators-checkpoint.py | 525 ++ .../symmetries-checkpoint.py | 3027 +++++++++++ cellconstructor/Methods.py | 32 + cellconstructor/symmetries.py | 136 +- .../.ipynb_checkpoints/INFO-checkpoint.txt | 3 + .../test_diagsymmetries-checkpoint.py | 51 + .../test_supercell_fourier-checkpoint.py | 85 + .../test_spglib_symmetrization-checkpoint.py | 58 + .../test_symmetries_supercell-checkpoint.py | 105 + .../plot_dispersion-checkpoint.py | 71 + .../.ipynb_checkpoints/QHA-checkpoint.py | 55 + .../.ipynb_checkpoints/h2o-checkpoint.dyn | 1077 ++++ .../symmetry_qe-checkpoint.py | 52 + .../symmetry_spglib-checkpoint.py | 70 + 26 files changed, 17220 insertions(+), 10 deletions(-) create mode 100644 .ipynb_checkpoints/setup-checkpoint.py create mode 100644 FModules/.ipynb_checkpoints/cryst_to_car-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/invmat-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 create mode 100755 FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 create mode 100644 cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py create mode 100644 tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt create mode 100644 tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py create mode 100644 tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py create mode 100644 tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py create mode 100644 tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py create mode 100644 tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py create mode 100644 tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py create mode 100644 tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn create mode 100644 tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py create mode 100644 tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py diff --git a/.ipynb_checkpoints/setup-checkpoint.py b/.ipynb_checkpoints/setup-checkpoint.py new file mode 100644 index 00000000..6bab55d1 --- /dev/null +++ b/.ipynb_checkpoints/setup-checkpoint.py @@ -0,0 +1,80 @@ +from numpy.distutils.core import setup, Extension +import sys + +symph_ext = Extension(name = "symph", + sources = ["FModules/symdynph_gq_new.f90", "FModules/symm_base.f90", + "FModules/sgam_ph.f90", "FModules/invmat.f90", "FModules/set_asr.f90", + "FModules/error_handler.f90", "FModules/io_global.f90", + "FModules/flush_unit.f90", "FModules/symvector.f90", + "FModules/fc_supercell_from_dyn.f90", + "FModules/set_tau.f90", "FModules/cryst_to_car.f90", + "FModules/recips.f90", "FModules/q2qstar_out.f90", + "FModules/rotate_and_add_dyn.f90", "FModules/trntnsc.f90", + "FModules/star_q.f90", "FModules/eqvect.f90", + "FModules/symm_matrix.f90", "FModules/from_matdyn.f90", + "FModules/interp.f90", "FModules/q_gen.f90", "FModules/smallgq.f90", + "FModules/symmetry_high_rank.f90", + "FModules/unwrap_tensors.f90", + "FModules/get_latvec.f90", + "FModules/contract_two_phonon_propagator.f90", + "FModules/get_q_grid_fast.f90", + "FModules/kind.f90", + "FModules/constants.f90", + "FModules/eff_charge_interp.f90", + "FModules/get_translations.f90", + "FModules/get_equivalent_atoms.f90"], + libraries= ["lapack", "blas"], + extra_f90_compile_args = ["-cpp"] + ) + + +secondorder_ext = Extension(name = "secondorder", + sources = ["FModules/second_order_centering.f90", + "FModules/second_order_ASR.f90"], + libraries= ["lapack", "blas"], + extra_f90_compile_args = ["-cpp"] + ) + + +thirdorder_ext = Extension(name = "thirdorder", + sources = ["FModules/third_order_centering.f90", + "FModules/third_order_ASR.f90", + "FModules/third_order_interpol.f90", + "FModules/third_order_dynbubble.f90"], + libraries= ["lapack", "blas"], + extra_f90_compile_args = ["-cpp"] + ) + + + +# The C module extension actually depeds on the python version +WRAPPER = "CModules/wrapper3.c" +if sys.version_info[0] < 3: + print("Running python2, changing the C wrapper") + WRAPPER = "CModules/wrapper.c" + +cc_modules_ext = Extension(name = "cc_linalg", + sources = ["CModules/LinAlg.c", WRAPPER] + ) + + + + +setup( name = "CellConstructor", + version = "1.1", + description = "Python utilities that is interfaced with ASE for atomic crystal analysis", + author = "Lorenzo Monacelli", + url = "https://github.com/mesonepigreco/CellConstructor", + packages = ["cellconstructor"], + package_dir = {"cellconstructor": "cellconstructor"}, + package_data = {"cellconstructor": ["SymData/*.dat"]}, + setup_requires = ["numpy", "ase", "scipy"], + license = "MIT", + include_package_data = True, + scripts = ["scripts/symmetrize_dynmat.py", "scripts/cellconstructor_test.py", "scripts/view_scf_atoms.py"], + ext_modules = [symph_ext, cc_modules_ext, thirdorder_ext, secondorder_ext] + ) + +def readme(): + with open("README.md") as f: + return f.read() diff --git a/FModules/.ipynb_checkpoints/cryst_to_car-checkpoint.f90 b/FModules/.ipynb_checkpoints/cryst_to_car-checkpoint.f90 new file mode 100644 index 00000000..b4b98684 --- /dev/null +++ b/FModules/.ipynb_checkpoints/cryst_to_car-checkpoint.f90 @@ -0,0 +1,65 @@ +! +! Copyright (C) 2001-2003 PWSCF group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +! +!----------------------------------------------------------------------- +subroutine cryst_to_cart (nvec, vec, trmat, iflag) + !----------------------------------------------------------------------- + ! + ! This routine transforms the atomic positions or the k-point + ! components from crystallographic to cartesian coordinates + ! ( iflag=1 ) and viceversa ( iflag=-1 ). + ! Output cartesian coordinates are stored in the input ('vec') array + ! + ! + implicit none + ! + integer, intent(in) :: nvec, iflag + ! nvec: number of vectors (atomic positions or k-points) + ! to be transformed from crystal to cartesian and vice versa + ! iflag: gives the direction of the transformation + double precision, intent(in) :: trmat (3, 3) + ! trmat: transformation matrix + ! if iflag=1: + ! trmat = at , basis of the real-space lattice, for atoms or + ! = bg , basis of the reciprocal-space lattice, for k-points + ! if iflag=-1: the opposite + double precision, intent(inout) :: vec (3, nvec) + ! coordinates of the vector (atomic positions or k-points) to be + ! transformed - overwritten on output + ! + ! local variables + ! + integer :: nv, kpol + ! counter on vectors + ! counter on polarizations + double precision :: vau (3) + ! workspace + ! + ! Compute the cartesian coordinates of each vectors + ! (atomic positions or k-points components) + ! + do nv = 1, nvec + if (iflag.eq.1) then + do kpol = 1, 3 + vau (kpol) = trmat (kpol, 1) * vec (1, nv) + trmat (kpol, 2) & + * vec (2, nv) + trmat (kpol, 3) * vec (3, nv) + enddo + else + do kpol = 1, 3 + vau (kpol) = trmat (1, kpol) * vec (1, nv) + trmat (2, kpol) & + * vec (2, nv) + trmat (3, kpol) * vec (3, nv) + enddo + endif + do kpol = 1, 3 + vec (kpol, nv) = vau (kpol) + enddo + enddo + ! + return +end subroutine cryst_to_cart + diff --git a/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 b/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 new file mode 100644 index 00000000..d94cb126 --- /dev/null +++ b/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 @@ -0,0 +1,276 @@ +! +! This code transform in q space the real space dynamical matrix +! Made by Ion Errea +! Originally part of the sscha.x code +! +subroutine fc_supercell_from_dyn (phitot, q, tau, tau_sc, itau, phitot_sc, nat, nq) + + implicit none + + integer, intent(in) :: nq, nat + + double complex, dimension(nq,3,3,nat,nat), intent(in) :: phitot + double precision, dimension(3,nq), intent(in) :: q + double precision, dimension(3,nat), intent(in):: tau + double precision, dimension(3, nat*nq), intent(in) ::tau_sc + ! integer, dimension(nat), intent(in) :: ityp + integer, dimension(nat*nq), intent(in) :: itau + double precision, dimension(3,3,nat*nq,nat*nq), intent(out) :: phitot_sc + + integer :: natsc + integer :: i, j, alpha, beta, qtot + double precision, dimension(3) :: latvec + double complex :: im, one, complex_number + double precision :: twopi + + one = (1.0d0,0.0d0) + im = (0.0d0,1.0d0) + twopi = 6.283185307179586d0 + + natsc = nq * nat + + do i = 1, natsc + do j = 1, natsc + latvec(:) = -( tau_sc(:,i) - tau(:,itau(i)) - tau_sc(:,j) + tau(:,itau(j))) + do alpha = 1, 3 + do beta = 1, 3 + complex_number = (0.0d0,0.0d0) + do qtot = 1, nq + !print * , "THE Q POINT", qtot, "IS", q(:, qtot), & + ! "THE LATTICE VECTOR", i,j, "IS", latvec(:) + + complex_number = complex_number + & + exp( - im * twopi * dot_product(q(:,qtot),latvec)) * & + phitot(qtot,alpha,beta,itau(i),itau(j)) / & + dble(nq) + end do + if (abs(aimag(complex_number)) .gt. 1.0d-5) then + print *, complex_number + print *, '' + print *, ' ERROR: There are force constants in the supercell that ' + print *, ' are complex. This is not possible. ' + print *, ' Stopping... ' + print *, ' 2 ' + !stop + end if + phitot_sc(alpha,beta,i,j) = real(complex_number) + end do + end do + end do + end do + +end subroutine fc_supercell_from_dyn + +! This is a fast version of the Fourier transform +! Equal to the one implemented in python +! But much faster +subroutine fast_ft_real_space_from_dynq(unit_cell_coords, super_cell_coords, itau, nat, nat_sc, nq, q_tot, dynq, fc_supercell) + + integer, intent(in) :: nat, nat_sc, nq + integer, intent(in), dimension(nat_sc) :: itau + double precision, intent(in) :: unit_cell_coords(nat, 3), super_cell_coords(nat_sc, 3) + double precision, intent(in), dimension(nq, 3) :: q_tot + double complex, intent(in), dimension(nq, 3*nat, 3*nat) :: dynq + + double complex, intent(out), dimension(3*nat_sc, 3*nat_sc) :: fc_supercell + + + integer :: i, j, iq, i_uc, j_uc, h, k + double precision :: R(3), arg, twopi + + double complex :: im, phase + + im = (0.0d0,1.0d0) + twopi = 6.283185307179586d0 + + fc_supercell(:,:) = 0.0d0 + + do i = 1, nat_sc + i_uc = itau(i) + do j = 1, nat_sc + j_uc = itau(j) + + ! Get the distance vector between the two atoms + R(:) = super_cell_coords(i, :) - unit_cell_coords(i_uc,:) + R(:) = R(:) - super_cell_coords(j, :) + unit_cell_coords(j_uc, :) + + ! Perform the Fourier transform + do iq = 1, nq + arg = twopi * sum(q_tot(iq, :) * R) + phase = exp(im * arg) / nq + + do h = 1, 3 + do k = 1, 3 + fc_supercell(3*(i-1) + h, 3*(j-1) + k) = fc_supercell(3*(i-1) + h, 3*(j-1) + k) + & + dynq(iq, 3*(i_uc-1) + h, 3*(j_uc-1) + k) * phase + enddo + enddo + enddo + enddo + enddo + + ! Check if the fc supercell has an imaginary value + +end subroutine fast_ft_real_space_from_dynq + +! +!logical function eqvect1 (x, y) +! !----------------------------------------------------------------------- +! ! +! ! This function test if the difference x-y-f is an integer. +! ! x, y = 3d vectors in crystal axis, f = fractionary translation +! ! +! implicit none +! double precision, intent(in) :: x (3), y (3) +! double precision, parameter :: accep = 1.0d-4 ! acceptance parameter +! +! ! +! ! +! eqvect1 = abs( x(1)-y(1) - nint(x(1)-y(1)) ) < accep .and. & +! abs( x(2)-y(2) - nint(x(2)-y(2) )) < accep .and. & +! abs( x(3)-y(3) - nint(x(3)-y(3) ) ) < accep +! ! +! return +!end function eqvect1 +! + +! by Lorenzo Monacelli +! This subrouitne impose the translation in the supercell +! for the force constant matrix. +! Tau_sc must be in crystal coordinates with respect to the supercell basis! +subroutine impose_trans_sc(fc_sc, tau_sc_cryst, itau, nat_sc) + implicit none + + + integer, intent(in) :: nat_sc + double precision, dimension(3, 3, nat_sc, nat_sc), intent(inout) :: fc_sc + double precision, dimension(3, nat_sc), intent(in) :: tau_sc_cryst + integer, dimension(nat_sc), intent(in) :: itau + + ! ----- HERE THE CODE ----- + double precision, dimension(3, 3) :: fc_tmp + double precision, dimension(3, 3, nat_sc, nat_sc) :: fc_new + + double precision, dimension(3) :: latvec_1, latvec_2, zero_vec + integer :: i, j, h, k, counter + double precision, parameter :: small_value = 1d-6 + logical :: is_equivalent + + fc_new = 0.0d0 + do i = 1, nat_sc + do j = 1, nat_sc + + ! Average the force constant matrix for all lattice vectors in the supercell. + counter = 0 + fc_tmp = 0.0d0 + do h = 1, nat_sc + if (itau(h) /= itau(i)) cycle ! Check if they are the same atom + ! Get the lattice vector + latvec_1 = tau_sc_cryst(:, i) - tau_sc_cryst(:, h) + + do k = 1, nat_sc + if (itau(k) /= itau(j)) cycle ! Check if they are the same atom + + ! Get the second lattice vector + latvec_2 = tau_sc_cryst(:, j) - tau_sc_cryst(:, k) + + zero_vec = latvec_1 - latvec_2 + !print *, "DISTANCE:", zero_vec + + ! Check if the two lattice vectors are the same apart from + ! an unit cell vector + ! In that case the fc should be equal + is_equivalent = abs( latvec_1(1)-latvec_2(1) - nint(latvec_1(1)-latvec_2(1)) ) < small_value .and. & + abs( latvec_1(2)-latvec_2(2) - nint(latvec_1(2)-latvec_2(2) )) < small_value .and. & + abs( latvec_1(3)-latvec_2(3) - nint(latvec_1(3)-latvec_2(3) ) ) < small_value + if ( is_equivalent ) then + fc_tmp = fc_tmp + fc_sc(:, :, h, k) + counter = counter + 1 + print *, "ATOMS EQ TO:", i, j, "ARE:", h, k + end if + end do + end do + + ! Copy the symmetrized matrix into the original one + print *, "COUNTER:", counter + fc_new(:, :, i, j) = fc_tmp / counter + end do + end do + fc_sc = fc_new +end subroutine impose_trans_sc + +! The following subroutine instead perform the inverse transform +subroutine dyn_from_fc ( phitot_sc, q, tau, tau_sc, itau, dyn, nq, nat) + + implicit none + + + integer :: nq, nat + double precision, dimension(3,nq), intent(in) :: q + double precision, dimension(3,nat), intent(in) :: tau + double precision, dimension(3,3,nq*nat,nq*nat), intent(in) :: phitot_sc + double precision, dimension(3,nq*nat), intent(in) :: tau_sc + !integer, dimension(:), intent(in) :: nqs + !integer, dimension(:), intent(in) :: ityp + integer, dimension(nq*nat), intent(in) :: itau + double complex, dimension(nq,3,3,nat,nat), intent(out) :: dyn + + integer :: natsc + integer :: i, j, k, alpha, beta, qtot, R + integer :: ka + double precision, dimension(:,:), allocatable :: latvec + double precision, dimension(3) :: vecaux + double complex :: im, one, complex_number + double precision :: twopi, prec + + one = (1.0d0,0.0d0) + im = (0.0d0,1.0d0) + twopi = 6.283185307179586d0 + + prec = 1.0d-6 + + natsc = nq * nat + + allocate(latvec(nq,3)) + + ! Prepare list of lattice vectors + + ka = 0 + + do i = 1, natsc + if (itau(i) .ne. 1) cycle + ka = ka + 1 + latvec(ka,:) = tau_sc(:,i) - tau(:,1) + end do + + ! Print list of lattice vectors + + do i = 1, nq + print *, latvec(i,:) + end do + + do qtot = 1, nq + do i = 1, nat + do j = 1, nat + do alpha = 1, 3 + do beta = 1, 3 + complex_number = (0.0d0,0.0d0) + do R = 1, ka + ! Check what the atom in the supercell is + do k = 1, natsc + vecaux = tau(:,j) + latvec(R,:) - tau_sc(:,k) + if ( sqrt(dot_product(vecaux,vecaux)) .lt. prec ) then + complex_number = complex_number + & + exp( im * twopi * dot_product(q(:,qtot),latvec(R,:))) * & + phitot_sc(alpha,beta,i,k) + end if + end do + end do + dyn(qtot,alpha,beta,i,j) = complex_number + end do + end do + end do + end do + end do + +end subroutine dyn_from_fc diff --git a/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 b/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 new file mode 100644 index 00000000..79baaf6a --- /dev/null +++ b/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 @@ -0,0 +1,47 @@ +! +! Copyright (C) 2004 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +subroutine invmat (n, a, a_inv) + !----------------------------------------------------------------------- + ! computes the inverse "a_inv" of matrix "a", both dimensioned (n,n) + ! matrix "a" is unchanged on output - LAPACK + ! + implicit none + integer :: n + double precision, DIMENSION (n,n), intent(in) :: a + double precision, dimension(n,n), intent(out) :: a_inv + + ! + integer :: info, lda, lwork, ipiv (n) + ! info=0: inversion was successful + ! lda : leading dimension (the same as n) + ! ipiv : work space for pivoting (assumed of length lwork=n) + double precision :: work (n) + ! more work space + ! + lda = n + lwork=n + ! + a_inv(:,:) = a(:,:) + ! + call dgetrf (n, n, a_inv, lda, ipiv, info) + !call errore ('invmat', 'error in DGETRF', abs (info) ) + call dgetri (n, a_inv, lda, ipiv, work, lwork, info) + !call errore ('invmat', 'error in DGETRI', abs (info) ) + ! + ! if (n == 3) then + ! da = a(1,1)*(a(2,2)*a(3,3)-a(2,3)*a(3,2)) + & + ! a(1,2)*(a(2,3)*a(3,1)-a(2,1)*a(3,3)) + & + ! a(1,3)*(a(2,1)*a(3,2)-a(3,1)*a(2,2)) + ! IF (ABS(da) < 1.d-10) CALL errore(' invmat ',' singular matrix ', 1) + ! else + ! da = 0.d0 + ! end if + + return +end subroutine invmat + diff --git a/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 b/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 new file mode 100644 index 00000000..b4cbd35a --- /dev/null +++ b/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 @@ -0,0 +1,235 @@ +! +! Copyright (C) 2001-2012 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +!----------------------------------------------------------------------- + +! Source edited from the original QE version +! to be wrapped into a python library +subroutine symdynph_gq_new (xq, phi, s, invs, rtau, irt, irotmq, minus_q, & + nsymq, nat) + !----------------------------------------------------------------------- + ! + ! This routine receives as input an unsymmetrized dynamical + ! matrix expressed on the crystal axes and imposes the symmetry + ! of the small group of q. Furthermore it imposes also the symmetry + ! q -> -q+G if present. + ! + ! + implicit none + ! + ! The dummy variables + ! + integer, intent(in) :: nat, nsymq, irotmq + integer, intent(in) :: s (3, 3, 48), irt (48, nat), invs (48) + ! input: the number of atoms + ! input: the symmetry matrices + ! input: the rotated of each vector + ! input: the small group of q + ! input: the inverse of each matrix + ! input: the order of the small gro + ! input: the rotation sending q -> + double precision, intent(in) :: xq (3), rtau (3, 48, nat) + ! input: the q point + ! input: the R associated at each t + + logical, intent(in) :: minus_q + ! input: true if a symmetry q->-q+G + + double complex, intent(inout) :: phi (3, 3, nat, nat) + ! inp/out: the matrix to symmetrize + ! + ! local variables + ! + integer :: isymq, sna, snb, irot, na, nb, ipol, jpol, lpol, kpol, & + iflb (nat, nat) + ! counters, indices, work space + + double precision :: arg + ! the argument of the phase + + double complex :: phip (3, 3, nat, nat), work (3, 3), fase, faseq (nsymq) + + ! Define the 2pi constant + double precision :: tpi + tpi = 6.283185307179586 + + + ! Print all the input variables + ! print *, "-------------------------------------" + ! print *, "xq:", xq + ! print *, "PHI:" + ! do na = 1, nat + ! do nb = 1, nat + ! print *, na, nb + ! do jpol = 1, 3 + ! print *, phi(:, jpol, na, nb) + ! end do + ! end do + ! end do + ! print *, "N sym:", nsymq + ! print *, "Symmetries:" + ! do isymq = 1, nsymq + ! print *, " ", isymq + ! do jpol = 1, 3 + ! print *, s(:, jpol, isymq) + ! end do + ! print *, "irt:", irt(isymq, :) + ! end do + ! print *, "INVS:" + ! print *, invs(:) + ! print *, "MINUS Q:", minus_q, "IROTMQ:", irotmq + ! print *, "-------------------------------------" + + + + ! work space, phase factors + ! + ! We start by imposing hermiticity + ! + do na = 1, nat + do nb = 1, nat + do ipol = 1, 3 + do jpol = 1, 3 + phi (ipol, jpol, na, nb) = 0.5d0 * (phi (ipol, jpol, na, nb) & + + CONJG(phi (jpol, ipol, nb, na) ) ) + phi (jpol, ipol, nb, na) = CONJG(phi (ipol, jpol, na, nb) ) + enddo + enddo + enddo + enddo + ! + ! If no other symmetry is present we quit here + ! + if ( (nsymq == 1) .and. (.not.minus_q) ) return + ! + ! Then we impose the symmetry q -> -q+G if present + ! + if (minus_q) then + do na = 1, nat + do nb = 1, nat + do ipol = 1, 3 + do jpol = 1, 3 + work(:,:) = (0.d0, 0.d0) + sna = irt (irotmq, na) + snb = irt (irotmq, nb) + arg = 0.d0 + do kpol = 1, 3 + arg = arg + (xq (kpol) * (rtau (kpol, irotmq, na) - & + rtau (kpol, irotmq, nb) ) ) + enddo + arg = arg * tpi + fase = DCMPLX(cos (arg), sin (arg)) + do kpol = 1, 3 + do lpol = 1, 3 + work (ipol, jpol) = work (ipol, jpol) + & + s (ipol, kpol, irotmq) * s (jpol, lpol, irotmq) & + * phi (kpol, lpol, sna, snb) * fase + enddo + enddo + phip (ipol, jpol, na, nb) = (phi (ipol, jpol, na, nb) + & + CONJG( work (ipol, jpol) ) ) * 0.5d0 + enddo + enddo + enddo + enddo + phi = phip + endif + + ! + ! Here we symmetrize with respect to the small group of q + ! + if (nsymq == 1) return + + iflb (:, :) = 0 + do na = 1, nat + do nb = 1, nat + if (iflb (na, nb) == 0) then + work(:,:) = (0.d0, 0.d0) + do isymq = 1, nsymq + ! Print the symmetry + ! if (na .eq. 1 .and. nb .eq. 1) then + ! print *, "Symmetry", isymq + ! print *, s(:, 1, isymq) + ! print *, s(:, 2, isymq) + ! print *, s(:, 3, isymq) + ! print *, "" + ! end if + + + irot = isymq + sna = irt (irot, na) + snb = irt (irot, nb) + arg = 0.d0 + do ipol = 1, 3 + arg = arg + (xq (ipol) * (rtau (ipol, irot, na) - & + rtau (ipol, irot, nb) ) ) + enddo + arg = arg * tpi + faseq (isymq) = DCMPLX(cos (arg), sin (arg)) + do ipol = 1, 3 + do jpol = 1, 3 + do kpol = 1, 3 + do lpol = 1, 3 + work (ipol, jpol) = work (ipol, jpol) + & + s (ipol, kpol, irot) * s (jpol, lpol, irot) & + * phi (kpol, lpol, sna, snb) * faseq (isymq) + enddo + enddo + enddo + enddo + enddo + + ! ! Print the Phi matrix + ! print "(A10, I8,A10,I8)", "NA = ", na, "NB =", nb + ! print *, "Phi:" + ! print *, phi(:, 1, na, nb) + ! print *, phi(:, 2, na, nb) + ! print *, phi(:, 3, na, nb) + + ! print *, "" + ! print *, "Work:" + ! print *, work(:, 1) + ! print *, work(:, 2) + ! print *, work(:, 3) + + do isymq = 1, nsymq + irot = isymq + sna = irt (irot, na) + snb = irt (irot, nb) + do ipol = 1, 3 + do jpol = 1, 3 + phi (ipol, jpol, sna, snb) = (0.d0, 0.d0) + do kpol = 1, 3 + do lpol = 1, 3 + phi (ipol, jpol, sna, snb) = phi (ipol, jpol, sna, snb) & + + s (ipol, kpol, invs (irot) ) * s (jpol, lpol, invs (irot) ) & + * work (kpol, lpol) * CONJG(faseq (isymq) ) + enddo + enddo + enddo + enddo + iflb (sna, snb) = 1 + enddo + + endif + enddo + enddo + phi (:, :, :, :) = phi (:, :, :, :) / DBLE(nsymq) + + + ! print *, "OUT PHI:" + ! do na = 1, nat + ! do nb = 1, nat + ! print *, na, nb + ! do jpol = 1, 3 + ! print *, phi(:, jpol, na, nb) + ! end do + ! end do + ! end do + + return +end subroutine symdynph_gq_new diff --git a/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 b/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 new file mode 100644 index 00000000..69d61ec9 --- /dev/null +++ b/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 @@ -0,0 +1,1054 @@ +! +! Copyright (C) 2010-2011 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +!-------------------------------------------------------------------------- +! +MODULE symm_base + + ! + ! ... The variables needed to describe the symmetry properties + ! ... and the routines to find crystal symmetries + ! + ! ... these are acceptance criteria + ! + double precision, parameter :: eps1 = 1.0d-6, eps2 = 1.0d-5 + double precision :: accep + ! + SAVE + ! + PRIVATE :: accep + ! + ! ... Exported variables + ! + PUBLIC :: s, sr, sname, ft, ftau, nrot, nsym, nsym_ns, nsym_na, t_rev, & + no_t_rev, time_reversal, irt, invs, invsym, d1, d2, d3, & + allfrac, nofrac, nosym, nosym_evc + INTEGER :: & + s(3,3,48), &! symmetry matrices, in crystal axis + invs(48), &! index of inverse operation: S^{-1}_i=S(invs(i)) + ftau(3,48), &! fractional translations, in FFT coordinates + nrot, &! number of bravais lattice symmetries + nsym = 1, &! total number of crystal symmetries + nsym_ns = 0, &! nonsymmorphic (fractional translation) symms + nsym_na = 0 ! excluded nonsymmorphic symmetries because + ! fract. transl. is noncommensurate with FFT grid + DOUBLE PRECISION :: & + ft (3,48), &! fractional translations, in crystal axis + sr (3,3,48), &! symmetry matrices, in cartesian axis + at(3,3), &! Unit cell vectors (the first index is the cartesian coordinate, the second the vector) + bg(3,3) ! Reciprocal lattice vectors + ! + ! ... note: ftau are used for symmetrization in real space (phonon, exx) + ! ... in which case they must be commensurated with the FFT grid + ! + CHARACTER(LEN=45) :: sname(48) ! name of the symmetries + INTEGER :: & + t_rev(48) = 0 ! time reversal flag, for noncolinear magnetism + INTEGER, ALLOCATABLE :: & + irt(:,:) ! symmetric atom for each atom and sym.op. + LOGICAL :: & + time_reversal=.true., &! if .TRUE. the system has time reversal symmetry + invsym, &! if .TRUE. the system has inversion symmetry + nofrac= .FALSE., &! if .TRUE. fract. translations are not allowed + allfrac= .FALSE., &! if .TRUE. all fractionary transations allowed, + ! even those not commensurate with FFT grid + nosym = .FALSE., &! if .TRUE. no symmetry is used + nosym_evc = .FALSE., &! if .TRUE. symmetry is used only to symmetrize + ! k points + no_t_rev=.FALSE. ! if .TRUE. remove the symmetries that + ! require time reversal + double precision,TARGET :: & + d1(3,3,48), &! matrices for rotating spherical + d2(5,5,48), &! harmonics (d1 for l=1, ...) + d3(7,7,48) ! + ! + ! ... Exported routines + ! + PUBLIC :: find_sym, inverse_s, copy_sym, checkallsym, & + s_axis_to_cart, set_sym, set_sym_bl, set_at_bg + ! +CONTAINS + subroutine set_accep_threshold(thr) + ! Set the acceptance threshold for the symmetry operation. + implicit none + double precision, intent(in) :: thr + + accep = thr + end subroutine set_accep_threshold + + subroutine get_accep_threshold(thr) + ! Get the acceptance threshold for the symmetry operation + implicit none + double precision, intent(out) :: thr + thr = accep + end subroutine get_accep_threshold + + ! + SUBROUTINE set_at_bg(new_at, new_bg) + ! SETUP THE unit cell vector and reciprocal lattice + implicit none + double precision, dimension(3,3), intent(in) :: new_at, new_bg + at = new_at + bg = new_bg + END SUBROUTINE set_at_bg + + SUBROUTINE inverse_s ( ) + !----------------------------------------------------------------------- + ! + ! Locate index of S^{-1} + ! + IMPLICIT NONE + ! + INTEGER :: isym, jsym, ss (3, 3) + LOGICAL :: found + ! + DO isym = 1, nsym + found = .FALSE. + DO jsym = 1, nsym + ! + ss = MATMUL (s(:,:,jsym),s(:,:,isym)) + ! s(:,:,1) is the identity + IF ( ALL ( s(:,:,1) == ss(:,:) ) ) THEN + invs (isym) = jsym + found = .TRUE. + END IF + END DO + IF ( .NOT.found) stop "ERROR, not a group" + END DO + ! + END SUBROUTINE inverse_s + ! +!----------------------------------------------------------------------- +subroutine set_sym_bl ( ) + !----------------------------------------------------------------------- + ! + ! Provides symmetry operations for all bravais lattices + ! Tests first the 24 proper rotations for the cubic lattice; + ! then the 8 rotations specific for the hexagonal axis (special axis c); + ! then inversion is added + ! + implicit none + ! + ! sin3 = sin(pi/3), cos3 = cos(pi/3), msin3 = -sin(pi/3), mcos3 = -cos(pi/3) + ! + double precision, parameter :: sin3 = 0.866025403784438597d0, cos3 = 0.5d0, & + msin3 =-0.866025403784438597d0, mcos3 = -0.5d0 + double precision :: s0(3, 3, 32), overlap (3, 3), rat (3), rot (3, 3), value + ! s0: the s matrices in cartesian axis + ! overlap: inverse overlap matrix between direct lattice + ! rat: the rotated of a direct vector ( cartesian ) + ! rot: the rotated of a direct vector ( crystal axis ) + ! value: component of the s matrix in axis basis + integer :: jpol, kpol, mpol, irot + ! counters over the polarizations and the rotations + + character :: s0name (64) * 45 + ! full name of the rotational part of each symmetry operation + + data s0/ 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, & + -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, & + -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, & + 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, & + 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, & + 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, & + 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, & + 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, & + 0.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 0.d0, & + 0.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, 0.d0, & + 0.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, 0.d0, & + 0.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 0.d0, & + -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, & + -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, & + 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, & + 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, & + 0.d0, 0.d0, 1.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, & + 0.d0, 0.d0, -1.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, & + 0.d0, 0.d0, -1.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, & + 0.d0, 0.d0, 1.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, & + 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 1.d0, 0.d0, 0.d0, & + 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 1.d0, 0.d0, 0.d0, & + 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, -1.d0, 0.d0, 0.d0, & + 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, -1.d0, 0.d0, 0.d0, & + cos3, sin3, 0.d0, msin3, cos3, 0.d0, 0.d0, 0.d0, 1.d0, & + cos3, msin3, 0.d0, sin3, cos3, 0.d0, 0.d0, 0.d0, 1.d0, & + mcos3, sin3, 0.d0, msin3, mcos3, 0.d0, 0.d0, 0.d0, 1.d0, & + mcos3, msin3, 0.d0, sin3, mcos3, 0.d0, 0.d0, 0.d0, 1.d0, & + cos3, msin3, 0.d0, msin3, mcos3, 0.d0, 0.d0, 0.d0, -1.d0, & + cos3, sin3, 0.d0, sin3, mcos3, 0.d0, 0.d0, 0.d0, -1.d0, & + mcos3, msin3, 0.d0, msin3, cos3, 0.d0, 0.d0, 0.d0, -1.d0, & + mcos3, sin3, 0.d0, sin3, cos3, 0.d0, 0.d0, 0.d0, -1.d0 / + + data s0name/ 'identity ',& + '180 deg rotation - cart. axis [0,0,1] ',& + '180 deg rotation - cart. axis [0,1,0] ',& + '180 deg rotation - cart. axis [1,0,0] ',& + '180 deg rotation - cart. axis [1,1,0] ',& + '180 deg rotation - cart. axis [1,-1,0] ',& + ' 90 deg rotation - cart. axis [0,0,-1] ',& + ' 90 deg rotation - cart. axis [0,0,1] ',& + '180 deg rotation - cart. axis [1,0,1] ',& + '180 deg rotation - cart. axis [-1,0,1] ',& + ' 90 deg rotation - cart. axis [0,1,0] ',& + ' 90 deg rotation - cart. axis [0,-1,0] ',& + '180 deg rotation - cart. axis [0,1,1] ',& + '180 deg rotation - cart. axis [0,1,-1] ',& + ' 90 deg rotation - cart. axis [-1,0,0] ',& + ' 90 deg rotation - cart. axis [1,0,0] ',& + '120 deg rotation - cart. axis [-1,-1,-1] ',& + '120 deg rotation - cart. axis [-1,1,1] ',& + '120 deg rotation - cart. axis [1,1,-1] ',& + '120 deg rotation - cart. axis [1,-1,1] ',& + '120 deg rotation - cart. axis [1,1,1] ',& + '120 deg rotation - cart. axis [-1,1,-1] ',& + '120 deg rotation - cart. axis [1,-1,-1] ',& + '120 deg rotation - cart. axis [-1,-1,1] ',& + ' 60 deg rotation - cryst. axis [0,0,1] ',& + ' 60 deg rotation - cryst. axis [0,0,-1] ',& + '120 deg rotation - cryst. axis [0,0,1] ',& + '120 deg rotation - cryst. axis [0,0,-1] ',& + '180 deg rotation - cryst. axis [1,-1,0] ',& + '180 deg rotation - cryst. axis [2,1,0] ',& + '180 deg rotation - cryst. axis [0,1,0] ',& + '180 deg rotation - cryst. axis [1,1,0] ',& + 'inversion ',& + 'inv. 180 deg rotation - cart. axis [0,0,1] ',& + 'inv. 180 deg rotation - cart. axis [0,1,0] ',& + 'inv. 180 deg rotation - cart. axis [1,0,0] ',& + 'inv. 180 deg rotation - cart. axis [1,1,0] ',& + 'inv. 180 deg rotation - cart. axis [1,-1,0] ',& + 'inv. 90 deg rotation - cart. axis [0,0,-1] ',& + 'inv. 90 deg rotation - cart. axis [0,0,1] ',& + 'inv. 180 deg rotation - cart. axis [1,0,1] ',& + 'inv. 180 deg rotation - cart. axis [-1,0,1] ',& + 'inv. 90 deg rotation - cart. axis [0,1,0] ',& + 'inv. 90 deg rotation - cart. axis [0,-1,0] ',& + 'inv. 180 deg rotation - cart. axis [0,1,1] ',& + 'inv. 180 deg rotation - cart. axis [0,1,-1] ',& + 'inv. 90 deg rotation - cart. axis [-1,0,0] ',& + 'inv. 90 deg rotation - cart. axis [1,0,0] ',& + 'inv. 120 deg rotation - cart. axis [-1,-1,-1]',& + 'inv. 120 deg rotation - cart. axis [-1,1,1] ',& + 'inv. 120 deg rotation - cart. axis [1,1,-1] ',& + 'inv. 120 deg rotation - cart. axis [1,-1,1] ',& + 'inv. 120 deg rotation - cart. axis [1,1,1] ',& + 'inv. 120 deg rotation - cart. axis [-1,1,-1] ',& + 'inv. 120 deg rotation - cart. axis [1,-1,-1] ',& + 'inv. 120 deg rotation - cart. axis [-1,-1,1] ',& + 'inv. 60 deg rotation - cryst. axis [0,0,1] ',& + 'inv. 60 deg rotation - cryst. axis [0,0,-1] ',& + 'inv. 120 deg rotation - cryst. axis [0,0,1] ',& + 'inv. 120 deg rotation - cryst. axis [0,0,-1] ',& + 'inv. 180 deg rotation - cryst. axis [1,-1,0] ',& + 'inv. 180 deg rotation - cryst. axis [2,1,0] ',& + 'inv. 180 deg rotation - cryst. axis [0,1,0] ',& + 'inv. 180 deg rotation - cryst. axis [1,1,0] ' / + + ! compute the overlap matrix for crystal axis + + do jpol = 1,3 + do kpol = 1,3 + rot(kpol,jpol) = at(1,kpol)*at(1,jpol) +& + at(2,kpol)*at(2,jpol) +& + at(3,kpol)*at(3,jpol) + enddo + enddo + ! + ! then its inverse (rot is used as work space) + ! + call invmat (3, rot, overlap) + + nrot = 1 + do irot = 1,32 + ! + ! for each possible symmetry + ! + do jpol = 1,3 + do mpol = 1,3 + ! + ! compute, in cartesian coordinates the rotated vector + ! + rat(mpol) = s0(mpol,1,irot)*at(1,jpol) +& + s0(mpol,2,irot)*at(2,jpol) +& + s0(mpol,3,irot)*at(3,jpol) + enddo + + do kpol = 1,3 + ! + ! the rotated vector is projected on the direct lattice + ! + rot(kpol,jpol) = at(1,kpol)*rat(1) +& + at(2,kpol)*rat(2) +& + at(3,kpol)*rat(3) + enddo + enddo + ! + ! and the inverse of the overlap matrix is applied + ! + do jpol = 1,3 + do kpol = 1,3 + value = overlap(jpol,1)*rot(1,kpol) +& + & overlap(jpol,2)*rot(2,kpol) +& + & overlap(jpol,3)*rot(3,kpol) + if ( abs(DBLE(nint(value))-value) > eps1 ) then + ! + ! if a noninteger is obtained, this implies that this operation + ! is not a symmetry operation for the given lattice + ! + go to 10 + end if + s(kpol,jpol,nrot) = nint(value) + sname(nrot)=s0name(irot) + enddo + enddo + nrot = nrot+1 +10 continue + enddo + nrot = nrot-1 + ! + ! set the inversion symmetry ( Bravais lattices have always inversion + ! symmetry ) + ! + do irot = 1, nrot + do kpol = 1,3 + do jpol = 1,3 + s(kpol,jpol,irot+nrot) = -s(kpol,jpol,irot) + sname(irot+nrot) = s0name(irot+32) + end do + end do + end do + + nrot = 2*nrot + + return + ! +end subroutine set_sym_bl +! +!----------------------------------------------------------------------- +SUBROUTINE find_sym ( nat, tau, ityp, nr1, nr2, nr3, magnetic_sym, m_loc ) + !----------------------------------------------------------------------- + ! + ! This routine finds the point group of the crystal, by eliminating + ! the symmetries of the Bravais lattice which are not allowed + ! by the atomic positions (or by the magnetization if present) + ! + implicit none + ! + integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 + double precision, intent(in) :: tau (3,nat), m_loc(3,nat) + logical, intent(in) :: magnetic_sym + ! + logical :: sym (48) + ! if true the corresponding operation is a symmetry operation + ! + IF ( ALLOCATED(irt) ) DEALLOCATE(irt) + ALLOCATE( irt( 48, nat ) ) + irt( :, : ) = 0 + ! + ! Here we find the true symmetries of the crystal + ! + CALL sgam_at ( nat, tau, ityp, nr1, nr2, nr3, sym ) + ! + ! Here we check for magnetic symmetries + ! + IF ( magnetic_sym ) CALL sgam_at_mag ( nat, m_loc, sym ) + ! + ! If nosym_evc is true from now on we do not use the symmetry any more + ! + IF (nosym_evc) THEN + sym=.false. + sym(1)=.true. + ENDIF + ! + ! Here we re-order all rotations in such a way that true sym.ops + ! are the first nsym; rotations that are not sym.ops. follow + ! + nsym = copy_sym ( nrot, sym ) + ! + IF ( .not. is_group ( ) ) THEN + print *, "find_sym: Not a group! symmetry disabled" + nsym = 1 + END IF + ! + ! check if inversion (I) is a symmetry. + ! If so, it should be the (nsym/2+1)-th operation of the group + ! + invsym = ALL ( s(:,:,nsym/2+1) == -s(:,:,1) ) + ! + CALL inverse_s ( ) + ! + CALL s_axis_to_cart ( ) + ! + return + ! +END SUBROUTINE find_sym +! +!----------------------------------------------------------------------- +subroutine sgam_at ( nat, tau, ityp, nr1, nr2, nr3, sym ) + !----------------------------------------------------------------------- + ! + ! Given the point group of the Bravais lattice, this routine finds + ! the subgroup which is the point group of the considered crystal. + ! Non symmorphic groups are allowed, provided that fractional + ! translations are allowed (nofrac=.false), that the unit cell is + ! not a supercell, and that they are commensurate with the FFT grid + ! + ! On output, the array sym is set to .true.. for each operation + ! of the original point group that is also a symmetry operation + ! of the crystal symmetry point group + ! + implicit none + ! + integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 + ! nat : number of atoms in the unit cell + ! ityp : species of each atom in the unit cell + ! nr* : dimensions of the FFT mesh + ! + double precision, intent(in) :: tau (3, nat) + ! + ! tau : cartesian coordinates of the atoms + ! + ! output variables + ! + logical, intent(out) :: sym (48) + ! sym(isym) : flag indicating if sym.op. isym in the parent group + ! is a true symmetry operation of the crystal + ! + integer :: na, kpol, nb, irot, i, j + ! counters + double precision , allocatable :: xau (:,:), rau (:,:) + ! atomic coordinates in crystal axis + logical :: fractional_translations + double precision :: ft_(3), ft1, ft2, ft3 + ! + allocate(xau(3,nat)) + allocate(rau(3,nat)) + ! + ! Compute the coordinates of each atom in the basis of + ! the direct lattice vectors + ! + + do na = 1, nat + xau(:,na) = bg(1,:) * tau(1,na) + bg(2,:) * tau(2,na) + bg(3,:) * tau(3,na) + enddo + + + + ! + ! check if the identity has fractional translations + ! (this means that the cell is actually a supercell). + ! When this happens, fractional translations are disabled, + ! because there is no guarantee that the generated sym.ops. + ! form a group + ! + nb = 1 + irot = 1 + ! + fractional_translations = .not. nofrac +! *********************************************************************** +! Be careful here... it was commented... probably for odd3 symmetry stuff +! do na = 2, nat +! if ( fractional_translations ) then +! if (ityp (nb) == ityp (na) ) then +! ft_(:) = xau(:,na) - xau(:,nb) - nint( xau(:,na) - xau(:,nb) ) +! ! +! sym(irot) = checksym ( irot, nat, ityp, xau, xau, ft_ ) +! ! +! if ( sym (irot) .and. & +! (abs (ft_(1) **2 + ft_(2) **2 + ft_(3) **2) < 1.d-8) ) & +! call errore ('sgam_at', 'overlapping atoms', na) +! if (sym (irot) ) then +! fractional_translations = .false. +! WRITE( stdout, '(5x,"Found symmetry operation: I + (",& +! & 3f8.4, ")",/,5x,"This is a supercell,", & +! & " fractional translations are disabled")') ft_ +! endif +! endif +! end if +! enddo +! ! +! ********************************************************************* + nsym_ns = 0 + do irot = 1, nrot + ! COMMENTED BY LORENZO MONACELLI + ! ! + ! ! check that the grid is compatible with the S rotation + ! ! + ! if ( mod (s (2, 1, irot) * nr1, nr2) /= 0 .or. & + ! mod (s (3, 1, irot) * nr1, nr3) /= 0 .or. & + ! mod (s (1, 2, irot) * nr2, nr1) /= 0 .or. & + ! mod (s (3, 2, irot) * nr2, nr3) /= 0 .or. & + ! mod (s (1, 3, irot) * nr3, nr1) /= 0 .or. & + ! mod (s (2, 3, irot) * nr3, nr2) /= 0 ) then + ! sym (irot) = .false. + ! print '(3i4)', ( (s (i, j, irot) , j = 1, 3) , i = 1, 3) + ! goto 100 + ! endif + + do na = 1, nat + ! rau = rotated atom coordinates + rau (:, na) = s (1,:, irot) * xau (1, na) + & + s (2,:, irot) * xau (2, na) + & + s (3,:, irot) * xau (3, na) + enddo + ! + ! first attempt: no fractional translation + ! + ftau (:, irot) = 0 + ft (:, irot) = 0 + ft_(:) = 0.d0 + ! + sym(irot) = checksym ( irot, nat, ityp, xau, rau, ft_ ) + ! + if (.not.sym (irot) .and. fractional_translations) then + nb = 1 + do na = 1, nat + if (ityp (nb) == ityp (na) ) then + ! + ! second attempt: check all possible fractional translations + ! + ft_ (:) = rau(:,na) - xau(:,nb) - nint( rau(:,na) - xau(:,nb) ) + ! + sym(irot) = checksym ( irot, nat, ityp, xau, rau, ft_ ) + ! + if (sym (irot) ) then + nsym_ns = nsym_ns + 1 + ft (:,irot) = ft_(:) + go to 100 + end if + endif + enddo + + endif +100 continue + enddo + ! + ! convert ft to FFT coordinates, check if compatible with FFT grid + ! for real-space symmetrization (if done: currently, exx, phonon) + ! + nsym_na = 0 + do irot =1, nrot + if ( sym(irot) .AND. .NOT. allfrac ) then + ft1 = ft(1,irot) * nr1 + ft2 = ft(2,irot) * nr2 + ft3 = ft(3,irot) * nr3 + ! check if the fractional translations are commensurate + ! with the FFT grid, discard sym.op. if not + ! (needed because ph.x symmetrizes in real space) + ! COMMENTED BY LORENZO MONACELLI (WE DO NOT NEED THIS CHECK) + if (abs (ft1 - nint (ft1) ) / nr1 > eps2 .or. & + abs (ft2 - nint (ft2) ) / nr2 > eps2 .or. & + abs (ft3 - nint (ft3) ) / nr3 > eps2 ) then + ! WRITE( stdout, '(5x,"warning: symmetry operation", & + ! & " # ",i2," not allowed. fractional ", & + ! & "translation:"/5x,3f11.7," in crystal", & + ! & " coordinates")') irot, ft_ + sym (irot) = .false. + nsym_na = nsym_na + 1 + nsym_ns = nsym_ns - 1 + endif + ftau (1, irot) = nint (ft1) + ftau (2, irot) = nint (ft2) + ftau (3, irot) = nint (ft3) + end if + end do + ! + ! deallocate work space + ! + deallocate (rau) + deallocate (xau) + ! + return +end subroutine sgam_at +! +!----------------------------------------------------------------------- +subroutine sgam_at_mag ( nat, m_loc, sym ) + !----------------------------------------------------------------------- + ! + ! Find magnetic symmetries, i.e. point-group symmetries that are + ! also symmetries of the local magnetization - including + ! rotation + time reversal operations + ! + implicit none + ! + integer, intent(in) :: nat + double precision, intent(in) :: m_loc(3, nat) + ! + ! m_loc: local magnetization, must be invariant under the sym.op. + ! + logical, intent(inout) :: sym (48) + ! + ! sym(isym) = .true. if rotation isym is a sym.op. of the crystal + ! (i.e. not of the bravais lattice only) + ! + integer :: na, nb, irot + logical :: t1, t2 + double precision , allocatable :: mxau(:,:), mrau(:,:) + ! magnetization and rotated magnetization in crystal axis + ! + allocate ( mxau(3,nat), mrau(3,nat) ) + ! + ! Compute the local magnetization of each atom in the basis of + ! the direct lattice vectors + ! + do na = 1, nat + mxau (:, na)= bg (1, :) * m_loc (1, na) + & + bg (2, :) * m_loc (2, na) + & + bg (3, :) * m_loc (3, na) + enddo + ! + do irot = 1, nrot + ! + t_rev(irot) = 0 + ! + if ( sym (irot) ) then + ! + ! mrau = rotated local magnetization + ! + do na = 1, nat + mrau(:,na) = s(1,:,irot) * mxau(1,na) + & + s(2,:,irot) * mxau(2,na) + & + s(3,:,irot) * mxau(3,na) + enddo + if (sname(irot)(1:3)=='inv') mrau = -mrau + ! + ! check if this a magnetic symmetry + ! + t1 = .true. + t2 = .true. + do na = 1, nat + ! + nb = irt (irot,na) + if ( nb < 1 .or. nb > nat ) then + stop "check_mag_sym: internal error: out-of-bound atomic index" + end if + ! + t1 = ( abs(mrau(1,na) - mxau(1,nb)) + & + abs(mrau(2,na) - mxau(2,nb)) + & + abs(mrau(3,na) - mxau(3,nb)) < eps2 ) .and. t1 + t2 = ( abs(mrau(1,na) + mxau(1,nb))+ & + abs(mrau(2,na) + mxau(2,nb))+ & + abs(mrau(3,na) + mxau(3,nb)) < eps2 ) .and. t2 + ! + enddo + ! + if ( .not.t1 .and. .not.t2 ) then + ! not a magnetic symmetry + sym(irot) = .false. + else if( t2 .and. .not. t1 ) then + ! magnetic symmetry with time reversal, if allowed + IF (no_t_rev) THEN + sym(irot) = .false. + ELSE + t_rev(irot) = 1 + ENDIF + end if + ! + end if + ! + enddo + ! + ! deallocate work space + ! + deallocate ( mrau, mxau ) + ! + return +END SUBROUTINE sgam_at_mag +! +SUBROUTINE set_sym(nat, tau, ityp, nspin_mag, m_loc, nr1, nr2, nr3) + ! + ! This routine receives as input atomic types and positions, if there + ! is noncollinear magnetism and the initial magnetic moments, the fft + ! dimensions nr1, nr2, nr3; it sets the symmetry elements of this module. + ! Note that at and bg are those in cell_base. It sets nrot, nsym, s, + ! sname, sr, invs, ftau, irt, t_rev, time_reversal, and invsym + ! + !----------------------------------------------------------------------- + ! + IMPLICIT NONE + ! input + INTEGER, INTENT(IN) :: nat, ityp(nat), nspin_mag, nr1, nr2, nr3 + double precision, INTENT(IN) :: tau(3,nat) + DOUBLE PRECISION, INTENT(IN) :: m_loc(3,nat) + ! + time_reversal = (nspin_mag /= 4) + t_rev(:) = 0 + CALL set_sym_bl ( ) + CALL find_sym ( nat, tau, ityp, nr1, nr2, nr3, .not.time_reversal, m_loc ) + ! + RETURN + END SUBROUTINE set_sym +! + +INTEGER FUNCTION copy_sym ( nrot_, sym ) +!----------------------------------------------------------------------- + ! + implicit none + integer, intent(in) :: nrot_ + logical, intent(inout) :: sym(48) + ! + integer :: stemp(3,3), ftemp(3), ttemp, irot, jrot + double precision :: ft_(3) + integer, allocatable :: irtemp(:) + character(len=45) :: nametemp + ! + ! copy symm. operations in sequential order so that + ! s(i,j,irot) , irot <= nsym are the sym.ops. of the crystal + ! nsym+1 < irot <= nrot are the sym.ops. of the lattice + ! on exit copy_sym returns nsym + ! + allocate ( irtemp( size(irt,2) ) ) + jrot = 0 + do irot = 1, nrot_ + if (sym (irot) ) then + jrot = jrot + 1 + if ( irot > jrot ) then + stemp = s(:,:,jrot) + s (:,:, jrot) = s (:,:, irot) + s (:,:, irot) = stemp + ftemp(:) = ftau(:,jrot) + ftau (:, jrot) = ftau (:, irot) + ftau (:, irot) = ftemp(:) + ft_(:) = ft(:,jrot) + ft (:, jrot) = ft (:, irot) + ft (:, irot) = ft_(:) + irtemp (:) = irt (jrot,:) + irt (jrot,:) = irt (irot,:) + irt (irot,:) = irtemp (:) + nametemp = sname (jrot) + sname (jrot) = sname (irot) + sname (irot) = nametemp + ttemp = t_rev(jrot) + t_rev(jrot) = t_rev(irot) + t_rev(irot) = ttemp + endif + endif + enddo + sym (1:jrot) = .true. + sym (jrot+1:nrot_) = .false. + deallocate ( irtemp ) + ! + copy_sym = jrot + return + ! +END FUNCTION copy_sym + +! +!----------------------------------------------------------------------- +LOGICAL FUNCTION is_group ( ) + !----------------------------------------------------------------------- + ! + ! Checks that {S} is a group + ! + IMPLICIT NONE + ! + INTEGER :: isym, jsym, ksym, ss (3, 3) + DOUBLE PRECISION :: st(3), dt(3) + LOGICAL :: found + ! + DO isym = 1, nsym + DO jsym = 1, nsym + ! + ss = MATMUL (s(:,:,isym),s(:,:,jsym)) + st(:)= ft(:,jsym) + s(1,:,jsym)*ft(1,isym) + & + s(2,:,jsym)*ft(2,isym) + & + s(3,:,jsym)*ft(3,isym) + ! + ! here we check that the input matrices really form a group: + ! S(k) = S(i)*S(j) + ! ftau_k = S(j)*ftau_i+ftau_j (modulo a lattice vector) + ! + found = .false. + DO ksym = 1, nsym + dt(:) = ft(:,ksym) - st(:) - NINT( ft(:,ksym) - st(:) ) + IF ( ALL( s(:,:,ksym) == ss(:,:) ) .AND. & + ( ABS ( dt(1) ) < eps2 ) .AND. & + ( ABS ( dt(2) ) < eps2 ) .AND. & + ( ABS ( dt(3) ) < eps2 ) ) THEN + IF (found) THEN + is_group = .false. + RETURN + END IF + found = .true. + END IF + END DO + IF ( .NOT.found) then + is_group = .false. + RETURN + END IF + END DO + END DO + is_group=.true. + RETURN + ! +END FUNCTION is_group + +logical function eqvect (x, y, f) + !----------------------------------------------------------------------- + ! + ! This function test if the difference x-y-f is an integer. + ! x, y = 3d vectors in crystal axis, f = fractionary translation + ! + implicit none + double precision, intent(in) :: x (3), y (3), f (3) + ! + ! + eqvect = abs( x(1)-y(1)-f(1) - nint(x(1)-y(1)-f(1)) ) < accep .and. & + abs( x(2)-y(2)-f(2) - nint(x(2)-y(2)-f(2)) ) < accep .and. & + abs( x(3)-y(3)-f(3) - nint(x(3)-y(3)-f(3)) ) < accep + ! + return +end function eqvect + +! +!----------------------------------------------------------------------- +logical function checksym ( irot, nat, ityp, xau, rau, ft_ ) + !----------------------------------------------------------------------- + ! + ! This function receives as input all the atomic positions xau, + ! and the rotated rau by the symmetry operation ir. It returns + ! true if for each atom na, it is possible to find an atom nb + ! which is of the same type of na, and coincide with it after the + ! symmetry operation. Fractional translations are allowed. + ! + implicit none + ! + integer, intent(in) :: nat, ityp (nat), irot + ! nat : number of atoms + ! ityp: the type of each atom + double precision, intent(in) :: xau (3, nat), rau (3, nat), ft_(3) + ! xau: the initial vectors (in crystal coordinates) + ! rau: the rotated vectors (as above) + ! ft_: fractionary translation (as above) + ! + integer :: na, nb + + ! the testing function + ! + do na = 1, nat + do nb = 1, nat + checksym = ( ityp (na) == ityp (nb) .and. & + eqvect (rau (1, na), xau (1, nb), ft_) ) + if ( checksym ) then + ! + ! the rotated atom does coincide with one of the like atoms + ! keep track of which atom the rotated atom coincides with + ! + irt (irot, na) = nb + goto 10 + endif + enddo + ! + ! the rotated atom does not coincide with any of the like atoms + ! s(ir) + ft is not a symmetry operation + ! + return +10 continue + enddo + ! + ! s(ir) + ft is a symmetry operation + ! + return +end function checksym +! +!----------------------------------------------------------------------- +subroutine checkallsym ( nat, tau, ityp, nr1, nr2, nr3 ) + !----------------------------------------------------------------------- + ! given a crystal group this routine checks that the actual + ! atomic positions and bravais lattice vectors are compatible with + ! it. Used in relaxation/MD runs to check that atomic motion is + ! consistent with assumed symmetry. + ! + implicit none + ! + integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 + double precision, intent(in) :: tau (3, nat) + ! + integer :: na, kpol, isym, i, j, k, l + logical :: loksym (48) + double precision :: sx (3, 3), sy(3,3) + double precision , allocatable :: xau(:,:), rau(:,:) + ! + allocate (xau( 3 , nat)) + allocate (rau( 3 , nat)) + ! + ! check that s(i,j, isym) is an orthogonal operation + ! + do isym = 1, nsym + sx = DBLE( s(:,:,isym) ) + sy = matmul ( bg, sx ) + sx = matmul ( sy, transpose(at) ) + ! sx is s in cartesian axis + sy = matmul ( transpose ( sx ), sx ) + ! sy = s*transpose(s) = I + do i = 1, 3 + sy (i,i) = sy (i,i) - 1.0d0 + end do + if (any (abs (sy) > eps1 ) ) & + !call errore ('checkallsym', 'not orthogonal operation', isym) + stop "Checkallsym not orthogonal operation" + enddo + ! + ! Compute the coordinates of each atom in the basis of the lattice + ! + do na = 1, nat + do kpol = 1, 3 + xau (kpol, na) = bg (1, kpol) * tau (1, na) + & + bg (2, kpol) * tau (2, na) + & + bg (3, kpol) * tau (3, na) + enddo + enddo + ! + ! generate the coordinates of the rotated atoms + ! + do isym = 1, nsym + do na = 1, nat + do kpol = 1, 3 + rau (kpol, na) = s (1, kpol, isym) * xau (1, na) + & + s (2, kpol, isym) * xau (2, na) + & + s (3, kpol, isym) * xau (3, na) + enddo + enddo + ! + loksym(isym) = checksym ( isym, nat, ityp, xau, rau, ft(1,isym) ) + ! + enddo + ! + ! deallocate work space + ! + deallocate(rau) + deallocate(xau) + ! + do isym = 1,nsym + if (.not.loksym (isym) ) then + stop "chgeckallsym: the symmetry operation is not satisfied" + end if + + end do + if (ANY (.not.loksym (1:nsym) ) ) then + !call symmetrize_at (nsym, s, invs, ft, irt, nat, tau, at, bg, & + ! alat, omega) + stop "checkallsym some of the original symmetry operations not satisfied" + end if + ! + return +end subroutine checkallsym + +!---------------------------------------------------------------------- +subroutine s_axis_to_cart ( ) + !---------------------------------------------------------------------- + ! + ! This routine transforms symmetry matrices expressed in the + ! basis of the crystal axis into rotations in cartesian axis + ! + implicit none + ! + integer :: isym + double precision:: sa(3,3), sb(3,3) + ! + do isym = 1,nsym + sa (:,:) = DBLE ( s(:,:,isym) ) + sb = MATMUL ( bg, sa ) + sr (:,:, isym) = MATMUL ( at, TRANSPOSE (sb) ) + enddo + ! + end subroutine s_axis_to_cart + + + subroutine smallg_q (aq, modenum, sym, minus_q) + !----------------------------------------------------------------------- + ! + ! This routine selects, among the symmetry matrices of the point group + ! of a crystal, the symmetry operations which leave q unchanged. + ! Furthermore it checks if one of the above matrices send q --> -q+G. + ! In this case minus_q is set true. + ! + ! input-output variables + ! + implicit none + + double precision, intent(in) :: aq (3) + ! input: the q point of the crystal + ! IN CRYSTAL UNITS (REMEMBER TO CONVERT IT) + + integer, intent(in) :: modenum + ! input: main switch of the program, used for + ! q<>0 to restrict the small group of q + ! to operation such that Sq=q (exactly, + ! without G vectors) when iswitch = -3. + ! Note, initialize it with true up to the crystal symmetry + logical, intent(inout) :: sym (48) + logical, intent(out) :: minus_q + ! input-output: .true. if symm. op. S q = q + G + ! output: .true. if there is an op. sym.: S q = - q + G + ! + ! local variables + ! + + double precision :: raq (3), zero (3) + ! q vector in crystal basis + ! the rotated of the q vector + ! the zero vector + + integer :: irot, ipol, jpol + ! counter on symmetry op. + ! counter on polarizations + ! counter on polarizations + + ! logical function, check if two vectors are equa + ! + ! return immediately (with minus_q=.true.) if xq=(0,0,0) + ! + minus_q = .true. + if ( (aq (1) == 0.d0) .and. (aq (2) == 0.d0) .and. (aq (3) == 0.d0) ) & + return + ! + ! Set to zero some variables + ! + minus_q = .false. + zero(:) = 0.d0 + ! + ! Transform xq to the crystal basis + ! + ! aq = xq + ! call cryst_to_cart (1, aq, at, - 1) + ! ! + ! Test all symmetries to see if this operation send Sq in q+G or in -q+G + ! + do irot = 1, nrot + if (.not.sym (irot) ) goto 100 + raq(:) = 0.d0 + do ipol = 1, 3 + do jpol = 1, 3 + raq(ipol) = raq(ipol) + DBLE( s(ipol,jpol,irot) ) * aq( jpol) + enddo + enddo + sym (irot) = eqvect (raq, aq, zero) + ! + ! if "iswitch.le.-3" (modenum.ne.0) S must be such that Sq=q exactly ! + ! + if (modenum.ne.0 .and. sym(irot) ) then + do ipol = 1, 3 + sym(irot) = sym(irot) .and. (abs(raq(ipol)-aq(ipol)) < 1.0d-5) + enddo + endif + if (.not.minus_q) then ! ION ERREA's change +! if (sym(irot).and..not.minus_q) then + raq = - raq + minus_q = eqvect (raq, aq, zero) + endif +100 continue + enddo + ! + ! if "iswitch.le.-3" (modenum.ne.0) time reversal symmetry is not included ! + ! + if (modenum.ne.0) minus_q = .false. + ! + return +end subroutine smallg_q + + +END MODULE symm_base diff --git a/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 b/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 new file mode 100644 index 00000000..96f4a868 --- /dev/null +++ b/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 @@ -0,0 +1,97 @@ +SUBROUTINE symmatrix ( matr, s, nsym, at, bg) + !----------------------------------------------------------------------- + ! Symmetrize a function f(i,j), i,j=cartesian components + ! e.g. : stress, dielectric tensor (in cartesian axis) + ! + IMPLICIT NONE + ! + double precision, intent(INOUT) :: matr(3,3) + integer, intent(IN) :: nsym + integer, intent(IN) :: s(3,3,48) + double precision, dimension(3,3), intent(in) :: at, bg + ! + INTEGER :: isym, i,j,k,l + double precision :: work (3,3) + ! + IF (nsym == 1) RETURN + ! + ! bring matrix to crystal axis + ! + CALL cart_to_crys_mat ( matr, at ) + ! + ! symmetrize in crystal axis + ! + work (:,:) = 0.0d0 + DO isym = 1, nsym + DO i = 1, 3 + DO j = 1, 3 + DO k = 1, 3 + DO l = 1, 3 + work (i,j) = work (i,j) + & + s (i,k,isym) * s (j,l,isym) * matr (k,l) + END DO + END DO + END DO + END DO + END DO + matr (:,:) = work (:,:) / DBLE(nsym) + ! + ! bring matrix back to cartesian axis + ! + CALL crys_to_cart_mat ( matr, bg ) + ! + END SUBROUTINE symmatrix + + + SUBROUTINE cart_to_crys_mat ( matr, at ) + !----------------------------------------------------------------------- + ! + IMPLICIT NONE + ! + double precision, intent(INOUT) :: matr(3,3) + double precision, intent(IN) :: at(3,3) + ! + double precision:: work(3,3) + INTEGER :: i,j,k,l + ! + work(:,:) = 0.0d0 + DO i = 1, 3 + DO j = 1, 3 + DO k = 1, 3 + DO l = 1, 3 + work(i,j) = work(i,j) + matr(k,l) * at(k,i) * at(l,j) + END DO + END DO + END DO + END DO + ! + matr(:,:) = work(:,:) + ! +END SUBROUTINE cart_to_crys_mat +! +SUBROUTINE crys_to_cart_mat ( matr, bg) + !----------------------------------------------------------------------- + ! + IMPLICIT NONE + ! + double precision, intent(INOUT) :: matr(3,3) + double precision, intent(in) :: bg(3,3) + ! + double precision :: work(3,3) + INTEGER :: i,j,k,l + ! + work(:,:) = 0.0d0 + DO i = 1, 3 + DO j = 1, 3 + DO k = 1, 3 + DO l = 1, 3 + work(i,j) = work(i,j) + & + matr(k,l) * bg(i,k) * bg(j,l) + END DO + END DO + END DO + END DO + matr(:,:) = work(:,:) + ! +END SUBROUTINE crys_to_cart_mat +! \ No newline at end of file diff --git a/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 b/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 new file mode 100755 index 00000000..fd8138dd --- /dev/null +++ b/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 @@ -0,0 +1,1180 @@ + +! This subroutine calculates which atom in the supercell +! is related by a translation vector of the supercell +! to another atom of the supercell + +subroutine get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec, nat_sc, nr ) + + implicit none + + double precision, dimension(3,nat_sc), intent(in) :: tau_sc + double precision, dimension(nr, 3), intent(in) :: latvec + double precision, dimension(3,3), intent(in) :: at_sc + integer, dimension(nat_sc,nr), intent(out) :: tau_sc_latvec + + integer :: nr, nat_sc + double precision, dimension(3) :: diff + double precision, dimension(27,3) :: superlatvec + double precision :: prec + logical, parameter :: debug = .true. + + integer :: ka, i, j, k, r + + ! Define precision for scalar product that + ! decides if two positions are the same + + if (debug) then + print *, "=== DEBUG get_tau_sc_latvec ===" + print *, "NAT_SC:", nat_sc + print *, "NR:", NR + print *, "" + call flush() + endif + + prec = 1.0d-6 + + ! Get integers + + !nr = size(latvec(:,1)) + !nat_sc = size(tau_sc(1,:)) + + ! Create the supercell lattice vectors + + ka = 0 + + do i = -1, 1 + do j = -1, 1 + do k = -1, 1 + ka = ka + 1 + superlatvec(ka,:) = dble(i) * at_sc(:,1) + dble(j) * at_sc(:,2) + dble(k) * at_sc(:,3) + end do + end do + end do + + ! Calculate which is the atom of the supercell related to a given + ! lattice vector + + do i = 1, nat_sc + do r = 1, nr + do j = 1, nat_sc + do ka = 1, 27 + diff(:) = tau_sc(:,i) + latvec(r,:) - tau_sc(:,j) + superlatvec(ka,:) + if (dot_product(diff,diff) .lt. prec) then + tau_sc_latvec(i,r) = j + print *, '' + print '(a,i3,a,3f16.8)', ' Supercell atom ', i, ' : ', tau_sc(:,i) + print '(a,i3,a,3f16.8)', ' Translation ', r, ' : ', latvec(r,:) + print '(a,i3,a,3f16.8)', ' Translate atom ', j, ' : ', tau_sc(:,j) + end if + end do + end do + end do + end do + +end subroutine get_tau_sc_latvec + + +! This subroutine imposes the permutation symmetry in the +! third order force constant matrices. The input is given +! with three indices, where each index represents an atom and +! a Cartesian index + +subroutine permute_v3 (v3,n) + + implicit none + + double precision, dimension(n,n,n), intent(inout) :: v3 + + integer :: n + integer :: a, b, c + + ! Assign permutation symmetry + + do a = 1, n + do b = 1, n + do c = 1, n + v3(a,b,c) = (v3(a,b,c) + v3(a,c,b) + v3(b,a,c) + v3(b,c,a) + v3(c,a,b) + v3(c,b,a)) / 6.0d0 + v3(a,c,b) = v3(a,b,c) + v3(b,a,c) = v3(a,b,c) + v3(b,c,a) = v3(a,b,c) + v3(c,a,b) = v3(a,b,c) + v3(c,b,a) = v3(a,b,c) + end do + end do + end do + +end subroutine permute_v3 + +! This subroutine imposes the permutation symmetry in the +! fourth order force constant matrices. The input is given +! with four indices, where each index represents an atom and +! a Cartesian index + +subroutine permute_v4 (v4, n) + + implicit none + + double precision, dimension(n,n,n,n), intent(inout) :: v4 + + integer :: n + integer :: a, b, c, d + + + ! Assign permutation symmetry + + do a = 1, n + do b = 1, n + do c = 1, n + do d = 1, n + v4(a,b,c,d) = ( v4(a,b,c,d) + v4(a,b,d,c) + v4(a,c,b,d) + v4(a,c,d,b) + v4(a,d,b,c) + v4(a,d,c,b) & + + v4(b,a,c,d) + v4(b,a,d,c) + v4(b,d,a,c) + v4(b,d,c,a) + v4(b,c,a,d) + v4(b,c,d,a) & + + v4(c,a,b,d) + v4(c,a,d,b) + v4(c,b,a,d) + v4(c,b,d,a) + v4(c,d,a,b) + v4(c,d,b,a) & + + v4(d,a,b,c) + v4(d,a,c,b) + v4(d,b,a,c) + v4(d,b,c,a) + v4(d,c,a,b) + v4(d,c,b,a) ) / 24.0d0 + v4(a,b,d,c) = v4(a,b,c,d) + v4(a,c,b,d) = v4(a,b,c,d) + v4(a,c,d,b) = v4(a,b,c,d) + v4(a,d,b,c) = v4(a,b,c,d) + v4(a,d,c,b) = v4(a,b,c,d) + ! + v4(b,a,c,d) = v4(a,b,c,d) + v4(b,a,d,c) = v4(a,b,c,d) + v4(b,d,a,c) = v4(a,b,c,d) + v4(b,d,c,a) = v4(a,b,c,d) + v4(b,c,a,d) = v4(a,b,c,d) + v4(b,c,d,a) = v4(a,b,c,d) + ! + v4(c,a,b,d) = v4(a,b,c,d) + v4(c,a,d,b) = v4(a,b,c,d) + v4(c,b,a,d) = v4(a,b,c,d) + v4(c,b,d,a) = v4(a,b,c,d) + v4(c,d,a,b) = v4(a,b,c,d) + v4(c,d,b,a) = v4(a,b,c,d) + ! + v4(d,a,b,c) = v4(a,b,c,d) + v4(d,a,c,b) = v4(a,b,c,d) + v4(d,b,a,c) = v4(a,b,c,d) + v4(d,b,c,a) = v4(a,b,c,d) + v4(d,c,a,b) = v4(a,b,c,d) + v4(d,c,b,a) = v4(a,b,c,d) + end do + end do + end do + end do + +end subroutine permute_v4 + + +! This subroutine imposes the translational symmetry to the +! second order force constants. +! + +subroutine trans_v2 ( v2, tau_sc_latvec, nat_sc, nr ) + + implicit none + + double precision, dimension(3, 3, nat_sc,nat_sc), intent(inout) :: v2 + integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec + integer :: nat_sc, nr + + integer :: ka, i, j, k, l, r, is, js, la, r1, r2 + double precision, dimension(3,3) :: mat_aux + logical, parameter :: debug = .true. + + !nat = size(tau(1,:)) + !nat_sc = size(tau_sc(1,:)) + + if (debug) then + print *, "=== DEBUG TRANS V2 ===" + print *, "NAT_SC:", nat_sc + print *, "NR:", nr + call flush() + endif + + + ! Impose translational symmetry + + do i = 1, nat_sc + do j = 1, nat_sc + mat_aux = 0.0d0 + do r = 1, nr + mat_aux(:,:) = mat_aux(:,:) & + + v2(:, :, tau_sc_latvec(i,r),tau_sc_latvec(j,r)) + end do + mat_aux(:,:) = mat_aux(:,:) / dble(nr) + do r = 1, nr + v2(:, :, tau_sc_latvec(i,r),tau_sc_latvec(j,r)) = mat_aux(:,:) + end do + end do + end do + +end subroutine trans_v2 + +! This subroutine imposes the translational symmetry to the +! third order force constants. +! +! Both in the input and output the third order force constants +! are given only with three indexes, each representing both +! an atom and a Cartesian index, but inside it is used +! with 6 indexes, separating cartesian and atom indexes. +subroutine trans_v3 ( v3, tau_sc_latvec, nat_sc, nr)!tau, tau_sc, itau, at_sc, nat, nat_sc ) + + implicit none + + double precision, dimension(nat_sc*3,nat_sc*3,nat_sc*3), intent(inout) :: v3 + integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec + !double precision, dimension(3,nat), intent(in) :: tau + !double precision, dimension(3,nat_sc), intent(in) :: tau_sc + !integer, dimension(nat_sc), intent(in) :: itau + !double precision, dimension(3,3), intent(in) :: at_sc + + integer :: nat_sc, nr + !double precision, dimension(3) :: cholat, vect, diff + double precision, dimension(:,:,:,:,:,:), allocatable :: v3_6 + !double precision, dimension(:,:), allocatable :: latvec + double precision :: prec + !integer, dimension(:,:), allocatable :: tau_sc_latvec + !logical, dimension(:), allocatable :: assigned + integer :: ka, i, j, k, l, r, is, js, la, r1, r2 + double precision, dimension(3,3,3) :: mat_aux + logical, parameter :: debug = .true. + + prec = 1.0d-6 + + !nat = size(tau(1,:)) + !nat_sc = size(tau_sc(1,:)) + + !nr = nat_sc / nat + + if (debug) then + print *, "=== DEBUG TRANS V3 ===" + print *, "NAT_SC:", nat_sc + !print *, "NAT:", nat + print *, "NR:", nr + call flush() + endif + + !allocate( assigned(nr) ) + allocate( v3_6(nat_sc,nat_sc,nat_sc,3,3,3) ) + !allocate( latvec(nr,3) ) + !allocate( tau_sc_latvec(nat_sc,nr) ) + + ! Get the lattice vectors of the supercell + + !call get_latvec ( tau_sc, tau, itau, latvec, nat, nat_sc, nr ) + + ! Build the 3rd order force-constant matrices + ! in 6 rank tensor + + call threetosix_real ( v3, v3_6, nat_sc) + + ! Assign which is the transformed atom in the supercell + ! given a particular translation vector + + !call get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec, nat_sc, nr ) + + ! Impose translational symmetry + + do i = 1, nat_sc + do j = 1, nat_sc + do k = 1, nat_sc + mat_aux = 0.0d0 + do r = 1, nr + mat_aux(:,:,:) = mat_aux(:,:,:) & + + v3_6(tau_sc_latvec(i,r),tau_sc_latvec(j,r),tau_sc_latvec(k,r),:,:,:) + end do + mat_aux(:,:,:) = mat_aux(:,:,:) / dble(nr) + do r = 1, nr + v3_6(tau_sc_latvec(i,r),tau_sc_latvec(j,r),tau_sc_latvec(k,r),:,:,:) = mat_aux(:,:,:) + end do + end do + end do + end do + + ! Return to the rank 3 tensor of the third order force constants + ! matrices + + call sixtothree_real ( v3_6, v3, nat_sc) + +end subroutine trans_v3 + +! This subroutine imposes the translational symmetry to the +! fourth order force constants. +! +! Both in the input and output the third order force constants +! are given only with three indexes, each representing both +! an atom and a Cartesian index, but inside it is used +! with 6 indexes, separating cartesian and atom indexes. +! TODO: TO BE CONVERTED IN PYTHONIC +!( v3, tau_sc_latvec, nat_sc, nr) +subroutine trans_v4 ( v4, tau_sc_latvec, nat_sc, nr ) + + implicit none + + double precision, dimension(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc), intent(inout) :: v4 + integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec + + ! double precision, dimension(:,:), intent(in) :: tau, tau_sc + ! integer, dimension(:), intent(in) :: itau + ! double precision, dimension(3,3), intent(in) :: at_sc + + integer :: nat, nat_sc, nr + + ! double precision, dimension(3) :: cholat, vect, diff + double precision, dimension(:,:,:,:,:,:), allocatable :: v3_6 + ! double precision, dimension(:,:), allocatable :: latvec + double precision :: prec + logical, dimension(:), allocatable :: assigned + integer :: ka, i, j, k, l, r, is, js, la, r1, r2 + double precision, dimension(3,3,3,3) :: mat_aux + logical, parameter :: debug = .true. + + prec = 1.0d-6 + + nat = nat_sc / nr + + if (debug) then + print *, "=== DEBUG TRANS_V4 ===" + print *, "NAT_SC:", nat_sc + print *, "NR:", nr + print *, "NAT:", nat + call flush() + endif + + + ! allocate( assigned(nr) ) + ! allocate( latvec(nr,3) ) + ! allocate( tau_sc_latvec(nat_sc,nr) ) + + ! ! Get the lattice vectors of the supercell + + ! call get_latvec ( tau_sc, tau, itau, latvec, nat, nat_sc, nr ) + + ! ! Assign which is the transformed atom in the supercell + ! ! given a particular translation vector + + ! call get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec ) + + ! Impose translational symmetry + + do i = 1, nat_sc + do j = 1, nat_sc + do k = 1, nat_sc + do l = 1, nat_sc + mat_aux = 0.0d0 + do r = 1, nr + mat_aux(:,:,:,:) = mat_aux(:,:,:,:) & + + v4((3*(tau_sc_latvec(i,r)-1)+1):(3*(tau_sc_latvec(i,r)-1)+3), & + (3*(tau_sc_latvec(j,r)-1)+1):(3*(tau_sc_latvec(j,r)-1)+3), & + (3*(tau_sc_latvec(k,r)-1)+1):(3*(tau_sc_latvec(k,r)-1)+3), & + (3*(tau_sc_latvec(l,r)-1)+1):(3*(tau_sc_latvec(l,r)-1)+3)) + end do + mat_aux(:,:,:,:) = mat_aux(:,:,:,:) / dble(nr) + do r = 1, nr + v4((3*(tau_sc_latvec(i,r)-1)+1):(3*(tau_sc_latvec(i,r)-1)+3), & + (3*(tau_sc_latvec(j,r)-1)+1):(3*(tau_sc_latvec(j,r)-1)+3), & + (3*(tau_sc_latvec(k,r)-1)+1):(3*(tau_sc_latvec(k,r)-1)+3), & + (3*(tau_sc_latvec(l,r)-1)+1):(3*(tau_sc_latvec(l,r)-1)+3)) = mat_aux(:,:,:,:) + end do + end do + end do + end do + end do + +end subroutine trans_v4 + +! This subroutine imposes the point group symmetry in the second-order +! force-constants + +subroutine sym_v2 ( v2, at_sc, bg_sc, s, irt, nsym, nat_sc) + + implicit none + + double precision, dimension(3,3,nat_sc,nat_sc), intent(inout) :: v2 + double precision, dimension(3,3), intent(in) :: at_sc + double precision, dimension(3,3), intent(in) :: bg_sc + ! Symmetry stuff + + integer, dimension(3,3,48), intent(in) :: s + integer, dimension(48,nat_sc), intent(in) :: irt + integer :: nsym, nat_sc + + INTEGER :: na, nb, nc, isym, nar, nbr, ncr + double precision, ALLOCATABLE :: work (:,:,:,:) + !double precision, dimension(3,3) :: bg_sc + + integer :: iq, i, j, k, alpha, beta, gamm + logical, parameter :: debug = .true. + + if (debug) then + print *, "=== DEBUG SYM_V2 ===" + print *, "NSYM:", nsym + print *, "NAT_SC:", nat_sc + call flush() + endif + + !logical :: prnt_sym + + ! Extract integers + + !nat_sc = size(tau_sc(1,:)) + + ! Allocate variables + + ! Create reciprocal lattice vectors of supercell + + !CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) + + ! Assign values to print fake dynamical matrix in the supercell + + ! Write fake dynamical matrix + +! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & +! fildyn_prefix, ibrav, celldm, tau_sc, & +! type_name, at_sc, lrigid, epsil, zeu) + + ! Extract all information about symmetries + + + ! Symmetrize the third order force constant matrix + + ALLOCATE (work(3,3,nat_sc,nat_sc)) + ! + ! bring third-order matrix to crystal axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + work(alpha,beta,na,nb) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + work(alpha,beta,na,nb) = work(alpha,beta,na,nb) + & + v2(i,j,na,nb)*at_sc(i,alpha)*at_sc(j,beta) + end do + end do + end do + END DO + END DO + END DO + ! + ! symmetrize in crystal axis + ! + v2 = 0.0d0 + DO na = 1, nat_sc + DO nb = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + DO isym = 1, nsym + nar = irt (isym, na) + nbr = irt (isym, nb) + do i = 1, 3 + do j = 1, 3 + v2(alpha,beta,na,nb) = v2(alpha,beta,na,nb) + & + work(i,j,nar,nbr)*s(alpha,i,isym)*s(beta,j,isym) + end do + end do + end do + end do + END DO + END DO + END DO + work (:,:,:,:) = v2 (:,:,:,:) / DBLE(nsym) + ! + ! bring vector back to cartesian axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + v2(alpha,beta,na,nb) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + v2(alpha,beta,na,nb) = v2(alpha,beta,na,nb) + & + work(i,j,na,nb)*bg_sc(alpha,i)*bg_sc(beta,j) + end do + end do + end do + END DO + END DO + END DO + ! + DEALLOCATE (work) + +end subroutine sym_v2 + +! ========================= +! This subroutine imposes the point group symmetry in the third-order +! force-constants + +subroutine sym_v3 ( v3, at_sc, s, irt, nsym, nat_sc ) + + implicit none + + double precision, dimension(nat_sc*3,nat_sc*3,nat_sc*3), intent(inout) :: v3 + !integer, dimension(nat_sc), intent(in) :: ityp_sc + !double precision, dimension(ntyp), intent(in) :: amass + !integer, intent(in) :: ibrav + !double precision, dimension(6), intent(in) :: celldm + !double precision, dimension(3,nat_sc), intent(in) :: tau_sc + !character (len=3), dimension(:), intent(in) :: type_name + double precision, dimension(3,3), intent(in) :: at_sc + !integer, dimension(nat_sc), intent(in) :: itau + + ! The symmetries + integer, dimension(3,3,48), intent(in) :: s + integer, dimension(48, nat_sc), intent(in) :: irt + + integer :: nsym + integer :: nat_sc + + !double complex, dimension(:,:,:,:,:), allocatable :: phitot + !double precision, dimension(3,1) :: q + !integer, dimension(1) :: nqs + !logical :: lrigid + !character (len=50) :: fildyn_prefix + !character (len=512) :: fildyn + !double precision, dimension(3,3) :: epsil + !double precision, dimension(:,:,:), allocatable :: zeu + !character(len=6), EXTERNAL :: int_to_char + + ! Symmetry stuff + + !integer, dimension(48) :: invs, irgq, isq + !double precision, dimension(3,48) :: sxq + !double precision, dimension(:,:,:), allocatable :: rtau + !integer :: nsymq, irotmq, nsym, imq + !logical :: minus_q + + INTEGER :: na, nb, nc, isym, nar, nbr, ncr + double precision, ALLOCATABLE :: work (:,:,:,:,:,:) + double precision, dimension(3,3) :: bg_sc + + double precision, dimension(:,:,:,:,:,:), allocatable :: v32 + + integer :: iq, i, j, k, alpha, beta, gamm + + ! Extract integers + + !nat_sc = size(tau_sc(1,:)) + + ! Allocate variables + + allocate(v32(nat_sc,nat_sc,nat_sc,3,3,3)) + + !allocate(phitot(1,3,3,nat_sc,nat_sc)) + !allocate(zeu(3,3,nat_sc)) + + !allocate(rtau(3,48,nat_sc)) + !allocate(irt(48,nat_sc)) + + ! Create reciprocal lattice vectors of supercell + + CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) + + ! Write third-order force constants with 6 indexes + + call threetosix_real (v3,v32, nat_sc) + + ! Assign values to print fake dynamical matrix in the supercell + +! q = 0.0d0 +! nqs = 1 +! lrigid = .false. +! epsil = 0.0d0 +! phitot = (0.0d0,0.0d0) +! zeu = 0.0d0 +! fildyn_prefix = 'fake_dyn' + +! ! Write fake dynamical matrix + +! ! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & +! ! fildyn_prefix, ibrav, celldm, tau_sc, & +! ! type_name, at_sc, lrigid, epsil, zeu) +! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & +! fildyn_prefix, 0, celldm, tau_sc, & +! type_name, at_sc, lrigid, epsil, zeu) + +! ! Extract all information about symmetries + +! print *, '' +! print *, ' Extracting symmetries of the supercell... ' +! print *, '' + +! iq = 1 + +! fildyn = trim(fildyn_prefix) // int_to_char(iq) + +! call symmdynmat ( fildyn, nat_sc, phitot(1,:,:,:,:), q, & +! s, invs, rtau, irt, irgq, & +! nsymq, irotmq, minus_q, nsym, nqs, isq, & +! imq, sxq, lrigid, epsil, zeu ) + + call print_symm ( s, nsym, irt, .true., nat_sc) + + ! Symmetrize the third order force constant matrix + + ALLOCATE (work(nat_sc,nat_sc,nat_sc,3,3,3)) + ! + ! bring third-order matrix to crystal axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + work(na,nb,nc,alpha,beta,gamm) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + work(na,nb,nc,alpha,beta,gamm) = work(na,nb,nc,alpha,beta,gamm) + & + v32(na,nb,nc,i,j,k)*at_sc(i,alpha)*at_sc(j,beta)*at_sc(k,gamm) + end do + end do + end do + end do + end do + end do + END DO + END DO + END DO + ! + ! symmetrize in crystal axis + ! + v32 = 0.0d0 + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + DO isym = 1, nsym + nar = irt (isym, na) + nbr = irt (isym, nb) + ncr = irt (isym, nc) + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + v32(na,nb,nc,alpha,beta,gamm) = v32(na,nb,nc,alpha,beta,gamm) + & + work(nar,nbr,ncr,i,j,k)*s(alpha,i,isym)*s(beta,j,isym)*s(gamm,k,isym) + end do + end do + end do + END DO + end do + end do + end do + END DO + END DO + END DO + work (:,:,:,:,:,:) = v32 (:,:,:,:,:,:) / DBLE(nsym) + ! + ! bring vector back to cartesian axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + v32(na,nb,nc,alpha,beta,gamm) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + v32(na,nb,nc,alpha,beta,gamm) = v32(na,nb,nc,alpha,beta,gamm) + & + work(na,nb,nc,i,j,k)*bg_sc(alpha,i)*bg_sc(beta,j)*bg_sc(gamm,k) + end do + end do + end do + end do + end do + end do + END DO + END DO + END DO + ! + DEALLOCATE (work) + + ! Write third-order force constants back with 3 indexes + + call sixtothree_real (v32,v3, nat_sc) + + ! Deallocate stuff + + ! deallocate(phitot) + ! deallocate(zeu) + ! deallocate(rtau) + ! deallocate(irt) +end subroutine sym_v3 + +! This subroutine imposes the point group symmetry in the fourth-order +! force-constants + +subroutine sym_v4 ( v4, at_sc, s, irt, nsym, nat_sc ) + + implicit none + + double precision, dimension(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc), intent(inout) :: v4 + !integer, intent(in) :: ntyp + !integer, dimension(:), intent(in) :: ityp_sc + !double precision, dimension(:), intent(in) :: amass + !integer, intent(in) :: ibrav + !double precision, dimension(6), intent(in) :: celldm + !double precision, dimension(:,:), intent(in) :: tau_sc + !character (len=3), dimension(:), intent(in) :: type_name + double precision, dimension(3,3), intent(in) :: at_sc + integer, dimension(3,3,48), intent(in) :: s + integer, dimension(48, nat_sc), intent(in) :: irt + integer, intent(in) :: nsym + !integer, dimension(:), intent(in) :: itau + + + !double complex, dimension(:,:,:,:,:), allocatable :: phitot + !double precision, dimension(3,1) :: q + !integer, dimension(1) :: nqs + !logical :: lrigid + !character (len=50) :: fildyn_prefix + !character (len=512) :: fildyn + !double precision, dimension(3,3) :: epsil + !double precision, dimension(:,:,:), allocatable :: zeu + !character(len=6), EXTERNAL :: int_to_char + + ! Symmetry stuff + + ! integer, dimension(48) :: invs, irgq, isq + ! double precision, dimension(3,48) :: sxq + ! double precision, dimension(:,:,:), allocatable :: rtau + ! integer :: nsymq, irotmq, nsym, imq + ! logical :: minus_q + + INTEGER :: na, nb, nc, nd, isym, nar, nbr, ncr, ndr + double precision, ALLOCATABLE :: work (:,:,:,:) + double precision, dimension(3,3) :: bg_sc + + integer :: nat_sc + + integer :: iq, i, j, k, l, alpha, beta, gamm, delt + logical, parameter :: debug = .true. + + ! Extract integers + + !nat_sc = size(tau_sc(1,:)) + if (debug) then + print *, "=== DEBUG SYM_V4 ===" + print *, "NAT_SC:", nat_sc + print *, "NSYM:", nsym + call flush() + end if + + ! Allocate variables + + ! allocate(phitot(1,3,3,nat_sc,nat_sc)) + ! allocate(zeu(3,3,nat_sc)) + + ! allocate(rtau(3,48,nat_sc)) + ! allocate(irt(48,nat_sc)) + + ! Create reciprocal lattice vectors of supercell + + CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) + + ! Assign values to print fake dynamical matrix in the supercell + +! q = 0.0d0 +! nqs = 1 +! lrigid = .false. +! epsil = 0.0d0 +! phitot = (0.0d0,0.0d0) +! zeu = 0.0d0 +! fildyn_prefix = 'fake_dyn' + +! ! Write fake dynamical matrix + +! ! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & +! ! fildyn_prefix, ibrav, celldm, tau_sc, & +! ! type_name, at_sc, lrigid, epsil, zeu) +! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & +! fildyn_prefix, 0, celldm, tau_sc, & +! type_name, at_sc, lrigid, epsil, zeu) + +! ! Extract all information about symmetries + +! print *, '' +! print *, ' Extracting symmetries of the supercell... ' +! print *, '' + +! iq = 1 + +! fildyn = trim(fildyn_prefix) // int_to_char(iq) + +! call symmdynmat ( fildyn, nat_sc, phitot(1,:,:,:,:), q, & +! s, invs, rtau, irt, irgq, & +! nsymq, irotmq, minus_q, nsym, nqs, isq, & +! imq, sxq, lrigid, epsil, zeu ) + + call print_symm ( s, nsym, irt, .true., nat_sc) + + ! Symmetrize the fourth order force constant matrix + + ALLOCATE (work(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc)) + ! + ! bring third-order matrix to crystal axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + DO nd = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + do delt = 1, 3 + work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + do l = 1, 3 + work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & + work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & + v4(3*(na-1)+i,3*(nb-1)+j,3*(nc-1)+k,3*(nd-1)+l)* & + at_sc(i,alpha)*at_sc(j,beta)*at_sc(k,gamm)*at_sc(l,delt) + end do + end do + end do + end do + end do + end do + end do + end do + end do + END DO + END DO + END DO + ! + ! symmetrize in crystal axis + ! + v4 = 0.0d0 + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + DO nd = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + do delt = 1, 3 + DO isym = 1, nsym + nar = irt (isym, na) + nbr = irt (isym, nb) + ncr = irt (isym, nc) + ndr = irt (isym, nd) + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + do l = 1, 3 + v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & + v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & + work(3*(nar-1)+i,3*(nbr-1)+j,3*(ncr-1)+k,3*(ndr-1)+l) * & + s(alpha,i,isym)*s(beta,j,isym)*s(gamm,k,isym)*s(delt,l,isym) + end do + end do + end do + end do + end do + end do + END DO + end do + end do + end do + END DO + END DO + END DO + work (:,:,:,:) = v4 (:,:,:,:) / DBLE(nsym) + ! + ! bring vector back to cartesian axis + ! + DO na = 1, nat_sc + DO nb = 1, nat_sc + DO nc = 1, nat_sc + DO nd = 1, nat_sc + do alpha = 1, 3 + do beta = 1, 3 + do gamm = 1, 3 + do delt = 1, 3 + v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = 0.0d0 + do i = 1, 3 + do j = 1, 3 + do k = 1, 3 + do l = 1, 3 + v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & + v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & + work(3*(na-1)+i,3*(nb-1)+j,3*(nc-1)+k,3*(nd-1)+l) * & + bg_sc(alpha,i)*bg_sc(beta,j)*bg_sc(gamm,k)*bg_sc(delt,l) + end do + end do + end do + end do + end do + end do + end do + end do + end do + END DO + END DO + END DO + ! + DEALLOCATE (work) + + ! Deallocate stuff + + ! deallocate(phitot) + ! deallocate(zeu) + ! deallocate(rtau) + ! deallocate(irt) + +end subroutine sym_v4 + +! This subroutine creates replicas for one vector (i.e. forces or displacements) +! in the supercell based on the pointgroup symmetries. The subroutine inputs +! the vector for all the random configuration +! and it outputs in a new array the vector replicated in the new structures + +! subroutine sym_replica (v, at_sc, irt, s, nsym, tau_sc_latvec, trans_replica, vr) + +! implicit none + +! double precision, dimension(:,:,:), intent(in) :: v ! Input vector +! ! Dimension (n_random,nat_sc,3) +! double precision, dimension(3,3), intent(in) :: at_sc ! Lattice vectors of the supercell +! integer, dimension(:,:), intent(in) :: irt ! Rotated atom by a symmetry operation +! integer, dimension(3,3,48), intent(in) :: s ! Symmetry operation matrix +! integer, intent(in) :: nsym ! Number of symmetry operations +! integer, dimension(:,:), allocatable :: tau_sc_latvec ! Tells which is the transformed atom by a particular translation +! logical, intent(in) :: trans_replica ! Logical variable to determine if translational replica are included +! double precision, dimension(:,:,:), intent(out) :: vr ! Output vector +! ! Dimension (n_random*nsym,nat_sc,3) + +! ! Work variables for the subroutine +! integer :: n_random, nat_sc, ntrans +! integer :: na, nar, i, alpha, ran, nr, isym, nt +! double precision, dimension(3,3) :: bg_sc +! double precision, dimension(:,:), allocatable :: work1, work2 +! double precision, dimension(3) :: work_aux + + +! ! Get integers + +! n_random = size (v(:,1,1)) +! nat_sc = size (v(1,:,1)) +! ntrans = size (tau_sc_latvec(1,:)) + +! ! Allcoate arrays + +! allocate ( work1 (nat_sc,3) ) +! allocate ( work2 (nat_sc,3) ) + +! ! Create reciprocal lattice vectors of supercell + +! CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) + +! ! Do loop on random configurations and create replicas + +! nr = 0 ! Counter on replicas + +! do ran = 1, n_random +! ! Now we create replicas based on point group symmetries +! do isym = 1, nsym +! ! Bring vector to crystal axis +! work1 = 0.0d0 +! do na = 1, nat_sc +! do alpha = 1, 3 +! work1(na,alpha) = 0.0d0 +! do i = 1, 3 +! work1(na,alpha) = work1(na,alpha) + v(ran,na,i) * at_sc(i,alpha) +! end do +! end do +! end do +! work2 = 0.0d0 +! ! Make replica +! do na = 1, nat_sc +! do alpha = 1, 3 +! nar = irt (isym, na) +! do i = 1, 3 +! work2(na,alpha) = work2(na,alpha) + work1(nar,i) * s(alpha,i,isym) +! end do +! end do +! end do +! ! Bring replica to cartesian units +! work1 = 0.0d0 +! do na = 1, nat_sc +! do alpha = 1, 3 +! do i = 1, 3 +! work1(na,alpha) = work1(na,alpha) + work2(na,i) * bg_sc (alpha,i) +! end do +! end do +! end do +! ! Now we create replicas based on translations +! if (trans_replica) then +! do nt = 1, ntrans +! nr = nr + 1 +! do na = 1, nat_sc +! vr(nr,tau_sc_latvec(na,nt),:) = work1(na,:) +! end do +! end do +! else +! nr = nr + 1 +! vr(nr,:,:) = work1(:,:) +! end if +! end do +! end do + +! ! Deallcoate arrays + +! deallocate ( work1, work2 ) + +! end subroutine sym_replica + +! ! This subroutine creates replicas for one vector (i.e. forces or displacements) +! ! in the supercell based on the pointgroup symmetries. The subroutine inputs +! ! the vector for all the random configuration +! ! and it outputs in a new array the vector replicated in the new structures + +! subroutine sym_replica2 (v, at_sc, irt, s, nsym, tau_sc_latvec, trans_replica, vr) + +! implicit none + +! double precision, dimension(:,:,:), intent(in) :: v ! Input vector +! ! Dimension (n_random,nat_sc,3) +! double precision, dimension(3,3), intent(in) :: at_sc ! Lattice vectors of the supercell +! integer, dimension(:,:), intent(in) :: irt ! Rotated atom by a symmetry operation +! integer, dimension(3,3,48), intent(in) :: s ! Symmetry operation matrix +! integer, intent(in) :: nsym ! Number of symmetry operations +! integer, dimension(:,:), allocatable :: tau_sc_latvec ! Tells which is the transformed atom by a particular translation +! logical, intent(in) :: trans_replica ! Logical variable to determine if translational replica are included +! double precision, dimension(:,:,:), intent(out) :: vr ! Output vector +! ! Dimension (n_random*nsym,nat_sc,3) + +! ! Work variables for the subroutine +! integer :: n_random, nat_sc, ntrans +! integer :: na, nar, i, alpha, ran, nr, isym, nt +! double precision, dimension(3,3) :: bg_sc +! double precision, dimension(:,:), allocatable :: work1, work2 +! double precision, dimension(3) :: work_aux + + +! ! Get integers + +! n_random = size (v(:,1,1)) +! nat_sc = size (v(1,:,1)) +! ntrans = size (tau_sc_latvec(1,:)) + +! ! Allcoate arrays + +! allocate ( work1 (nat_sc,3) ) +! allocate ( work2 (nat_sc,3) ) + +! ! Create reciprocal lattice vectors of supercell + +! CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) + +! ! Do loop on random configurations and create replicas + +! nr = 0 ! Counter on replicas + +! do ran = 1, n_random +! ! Now we create replicas based on point group symmetries +! do isym = 1, nsym +! ! Bring vector to crystal axis +! work1 = 0.0d0 +! do na = 1, nat_sc +! do alpha = 1, 3 +! work1(na,alpha) = 0.0d0 +! do i = 1, 3 +! work1(na,alpha) = work1(na,alpha) + v(ran,na,i) * bg_sc(i,alpha) +! end do +! end do +! end do +! work2 = 0.0d0 +! ! Make replica +! do na = 1, nat_sc +! do alpha = 1, 3 +! nar = irt (isym, na) +! do i = 1, 3 +! work2(na,alpha) = work2(na,alpha) + work1(nar,i) * s(alpha,i,isym) +! end do +! end do +! end do +! ! Bring replica to cartesian units +! work1 = 0.0d0 +! do na = 1, nat_sc +! do alpha = 1, 3 +! do i = 1, 3 +! work1(na,alpha) = work1(na,alpha) + work2(na,i) * at_sc (alpha,i) +! end do +! end do +! end do +! ! Now we create replicas based on translations +! if (trans_replica) then +! do nt = 1, ntrans +! nr = nr + 1 +! do na = 1, nat_sc +! vr(nr,tau_sc_latvec(na,nt),:) = work1(na,:) +! end do +! end do +! else +! nr = nr + 1 +! vr(nr,:,:) = work1(:,:) +! end if +! end do +! end do + +! ! Deallcoate arrays + +! deallocate ( work1, work2 ) + +! end subroutine sym_replica2 + + +! This subroutine prints out the symmetries. It prints point group matrix +! and which atom is related to that in the unit cell + +subroutine print_symm ( s, nsym, irt, supercell, nat) + + implicit none + + integer, dimension(3,3,48), intent(in) :: s + integer, intent(in) :: nsym + integer, dimension(48,nat), intent(in) :: irt + logical, intent(in) :: supercell + !integer, dimension(nat), intent(in) :: itau + + integer :: isym, alpha, na + integer :: nat + + !nat = size(irt(1,:)) + + print *, '' + print *, ' Printing symmetries... ' + print *, '' + print '(a,i3)', ' Symmetries found : ', nsym + print *, '' + + do isym = 1, nsym + print '(a,i3)', ' Symmetry ', isym + print '(a)', ' -------- ' + print *, '' + print *, ' point group matrix:' + do alpha = 1, 3 + print '(3i3)', s(alpha,1:3,isym) + end do + print *, ' rotated atoms:' + do na = 1, nat + print '(i3,a,i3)', na , ' -> ', irt(isym,na) + end do + ! if ( supercell ) then + ! print *, ' rotated atoms brought to unit cell:' + ! do na = 1, nat + ! print '(i3,a,i3)', itau(na) , ' -> ', itau(irt(isym,na)) + ! end do + ! end if + end do + +end subroutine print_symm diff --git a/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py new file mode 100644 index 00000000..cfb6b047 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py @@ -0,0 +1,1852 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 6 10:45:50 2018 + +@author: pione +""" +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division +from hashlib import new +from importlib.resources import path + +from numpy import * +import numpy as np +import sys, os +import symph + +import warnings + +#from . import Structure + +BOHR_TO_ANGSTROM = 0.529177249 +__EPSILON__ = 1e-6 + + +__all__ = ["covariant_coordinates", "from_dynmat_to_spectrum", + "put_into_cell", "get_minimal_orthorombic_cell", + "write_dynmat_in_qe_format", "get_gr", "cell2abc_alphabetagamma", + "DistanceBetweenStructures"] + + +def covariant_coordinates(basis, vectors): + """ + Covariant Coordinates + ===================== + + This method returns the covariant coordinates of the given vector in the chosen basis. + Covariant coordinates are the coordinates expressed as: + .. math:: + + \\vec v = \\sum_i \\alpha_i \\vec e_i + + + where :math:`\\vec e_i` are the basis vectors. Note: the :math:`\\alpha_i` are not the + projection of the vector :math:`\\vec v` on :math:`\\vec e_i` if the basis is not orthogonal. + + + Parameters + ---------- + - basis : ndarray(size = (N,N)) + The basis. each :math:`\\vec e_i` is a row. + - vector : ndarray(size = (N_vectors, N)) + The vectors expressed in cartesian coordinates. + It coould be just one ndarray(size=N) + + Results + ------- + - cov_vector : Nx float + The :math:`\\alpha_i` values. + + """ + + M, N = np.shape(basis) + + metric_tensor = np.zeros((N,N)) + + metric_tensor = basis.dot(basis.T) + # for i in range(0, N): + # for j in range(i, N): + # metric_tensor[i, j] = metric_tensor[j,i] = basis[i,:].dot(basis[j, :]) + + imt = np.linalg.inv(metric_tensor) + + contra_vect = vectors.dot(basis.T) + return contra_vect.dot(imt) + +def cryst_to_cart(unit_cell, cryst_vectors): + """ + Convert a vector from crystalline to cartesian. + Many vectors counld be pased toghether, in that case the last axis must be the one with the vector. + + Parameters + ---------- + unit_cell : ndarray((3,3)) + The unit cell vectors. + The i-th cell vector is unit_cell[i, :] + cryst_vectors : ndarray((N_vectors, 3)) or ndarray(3) + The vector(s) in crystalline coordinates that you want to + transform in cartesian coordinates + + Results + ------- + cart_vectors : ndarray((N_vectors, 3)) or ndarray(3) + The vector(s) in cartesian coordinates + """ + + return cryst_vectors.dot(unit_cell) + +def cart_to_cryst(unit_cell, cart_vectors): + """ + Convert a vector from cartesian to crystalline. + Many vectors counld be pased toghether, in that case the last axis must be the one with the vector. + + Parameters + ---------- + unit_cell : ndarray((3,3)) + The unit cell vectors. + The i-th cell vector is unit_cell[i, :] + cart_vectors : ndarray((N_vectors, 3)) or ndarray(3) + The vector(s) in cartesian coordinates that you want to + transform in crystalline coordinates + + Results + ------- + cryst_vectors : ndarray((N_vectors, 3)) or ndarray(3) + The vector(s) in crystalline coordinates + """ + return covariant_coordinates(unit_cell, cart_vectors) + +def get_equivalent_vectors(unit_cell, vectors, target, index = None): + """ + This function returns an array mask of the vectors that are + equivalent to the target vector. + + Parameters + ---------- + - unit_cell : ndarray (size = (3,3)) + unit_cell[i, :] is the i-th lattice vector + - vectors : ndarray(size = (n_vects, 3)) + The vectors to be compared to the target + - target : ndarray(size = 3) + The target vector + + Returns + ------- + - eq_mask : ndarray(size = n_vects, dtype = bool) + A mask that is True if the vectors[i, :] is equivalent + to target. + """ + + # Get the inverse metric tensor + M, N = np.shape(unit_cell) + + metric_tensor = np.zeros((N,N)) + for i in range(0, N): + for j in range(i, N): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + imt = np.linalg.inv(metric_tensor) + + # Transform matrix + # This matrix transforms from cartesian to crystal coordinates + transform = imt.dot(unit_cell) + + # Get the crystal coordinates for each vector + crystal_vectors = vectors.dot(transform.T) + + # Get the crystal coordinates for the target + crystal_target = transform.dot(target) + + + # For each crystal vector, subtract the target + crystal_vectors -= crystal_target + + # Get the mask of those vectors whose components are integers + mask_on_coords = np.abs((crystal_vectors - np.floor(crystal_vectors + .5))) < 1e-5 + + # Here we count, for each vector, how many coordinates are not integers in crystal units + # Then we select only those whose count is 0 (all crystal coordinats are integers) + mask_equal = np.sum(mask_on_coords.astype(int), axis = 1) == 3 + + return mask_equal + + +def get_min_dist_into_cell(unit_cell, v1, v2): + """ + This function obtain the minimum distance between two vector, considering the given unit cell + + + Parameters + ---------- + unit_cell : ndarray 3x3 + The unit cell + v1 : ndarray 3 + Vector 1 + v2 : ndarray 3 + Vector 2 + + Results + ------- + float + The minimum distance between the two fector inside the given unit cell + """ + + + # Get the covariant components + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + imt = np.linalg.inv(metric_tensor) + + # Get contravariant components + contra_vect = np.zeros(3) + for i in range(3): + contra_vect[i] = v1.dot(unit_cell[i, :]) + + # Invert the metric tensor and obtain the covariant coordinates + covect1 = imt.dot(contra_vect) + + contra_vect = np.zeros(3) + for i in range(3): + contra_vect[i] = v2.dot(unit_cell[i, :]) + + # Invert the metric tensor and obtain the covariant coordinates + covect2 = imt.dot(contra_vect) + + covect_distance = covect1 - covect2 + + # Bring the distance as close as possible to zero + covect_distance -= (covect_distance + np.sign(covect_distance)*.5).astype(int) + + # Compute the distance using the metric tensor + return np.sqrt(covect_distance.dot(metric_tensor.dot(covect_distance))) + +def identify_vector(unit_cell, vector_list, target, epsil = 1e-8): + """ + Identify whichone in vector_list is equivalent to the target (given the supercell) + If no vector is identified, then raise a warning and return None. + + This function is much more efficient than calling get_min_dist_into_cell in a loop. + + Parameters + ---------- + unit_cell : ndarray 3x3 + The unit cell + vector_list : ndarray (N_vectors, 3) + The array of vector you want to compare + target : ndarray 3 + The target vector + + Results + ------- + int + the index of the vector_list that is equivalent to target (withint the cell) + """ + + # Get the covariant components + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + imt = np.linalg.inv(metric_tensor) + + N_vectors = vector_list.shape[0] + + # Get contravariant components + crystal_list = vector_list.dot(unit_cell.T) + crystal_list = crystal_list.dot(imt) + + crystal_target = target.dot(unit_cell.T) + crystal_target = imt.dot(crystal_target) + + crystal_distance = crystal_list - np.tile(crystal_target, (N_vectors, 1)) + + # Bring the distance as close as possible to zero + crystal_distance -= (crystal_distance + np.sign(crystal_distance)*.5).astype(int) + + # Compute the distances using the metric tensor + distances = np.einsum("ai, ai -> a", crystal_distance.dot(metric_tensor), crystal_distance) + + min_index = np.argmin(distances) + dist = distances[min_index] + + if dist > epsil: + warnings.warn("Warning in identify_vector, no equivalent atoms found within a threshold of {}".format(epsil)) + return None + + return min_index + + + +def get_reciprocal_vectors(unit_cell): + """ + GET THE RECIPROCAL LATTICE VECTORS + ================================== + + Gives back the reciprocal lattice vectors given the + unit cell. + + P.S. + The output is in rad / alat^-1 + where alat is the unit of measurement of the unit_cell. + + Parameters + ---------- + unit_cell : ndarray( size = (3,3), dtype = np.float64) + The unit cell, rows are the vectors. + + Results + ------- + reciprocal_vectors : ndarray(size = (3,3), dtype = np.float64) + The reciprocal lattice vectors + """ + + reciprocal_vectors = np.zeros( (3,3), dtype = np.float64) + reciprocal_vectors[:,:] = np.transpose(np.linalg.inv(unit_cell)) + return reciprocal_vectors + + +def from_dynmat_to_spectrum(dynmat, struct): + """ + This method takes as input the dynamical matrix and the atomic structure of the system and + returns the spectrum. + + Parameters + ---------- + - dynmat : float, 3*N_atoms x 3*N_atoms + Numpy array that contains the real-space dynamical matrix (Hartree). + - struct : Structure + The structure of the system. The masses must be initialized. + + Results + ------- + - Frequencies : float, 3*N_atoms + Numpy array containing the frequencies in cm-1 + """ + + n_atoms = struct.N_atoms + + # Construct the matrix to be diagonalized + new_phi = np.zeros(np.shape(dynmat)) + for i in range(n_atoms): + M_i = struct.masses[struct.atoms[i]] + for j in range(n_atoms): + M_j = struct.masses[struct.atoms[j]] + new_phi[3*i : 3*(i+1), 3*j : 3*(j+1)] = dynmat[3*i : 3*(i+1), 3*j : 3*(j+1)] / (M_i * M_j) + + + # Diagonalize the matrix + eigval, eigvect = np.linalg.eig(new_phi) + eigval *= 220000. # conversion to cm-1 + + return np.sort(eigval) + + + +def put_into_cell(cell, vector): + """ + This function take the given vector and gives as output the corresponding + one inside the specified cell. + + Parameters + ---------- + - cell : double, 3x3 matrix + The unit cell, a 3x3 matrix whose rows specifies the cell vectors + - vector : double, 3 elements ndarray + The vector to be shifted into the unit cell. + + Results + ------- + - new_vector : double, 3 elements ndarray + The corresponding vector into the unit cell + """ + + # Check if the system has unit cell + if np.linalg.det(cell) == 0: + ERROR_MSG = """ + Error, the structure has no unit cell (or vectors are linearly dependent). + """ + raise ValueError(ERROR_MSG) + + # Put the vector inside the unit cell + # To do this, just obtain the covariant vector coordinates. + + # Get the metric tensor + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = cell[i,:].dot(cell[j, :]) + + # Get contravariant components + contra_vect = np.zeros(3) + for i in range(3): + contra_vect[i] = vector.dot(cell[i, :]) + + # Invert the metric tensor and obta + covect = np.linalg.inv(metric_tensor).dot(contra_vect) + + # print "" + # print "Translating into the unit cell:" + # print "MT:" + # print metric_tensor + # print "IMT:" + # print linalg.inv(metric_tensor) + # print "vector:", vector + # print "contra variant:", contra_vect + # print "covariant:", covect + + for i in range(3): + covect[i] = covect[i] - int(covect[i]) + if covect[i] < 0: + covect[i] += 1 + + + + # Go back + final_vect = np.zeros(3) + for i in range(3): + final_vect += covect[i] * cell[i,:] + + # print "covariant new:", covect + # print "final:", final_vect + # print "" + + return final_vect + + +def get_minimal_orthorombic_cell(euclidean_cell, ita=36): + """ + This function, given an euclidean cell with 90 90 90 angles, returns the minimal + cell. The minimal cell will not have 90 90 90 angles. + + Parameters + ---------- + - euclidean_cell : matrix 3x3, double precision + The rows of this matrix are the unit cell vectors in euclidean cell. + - ita : integer + The group class in ITA standard (36 = Cmc21) + + Results + ------- + - minimal_cell : matrix 3x3, double precision + The rows of this matrix are the new minimal unit cell vectors. + """ + + + # Take the last vector and project into the last-one. + + minimal_cell = euclidean_cell.copy() + + if (ita == 36): + last_vector = .5 * euclidean_cell[1,:] + .5 * euclidean_cell[0,:] + minimal_cell[1,:] = last_vector + else: + raise ValueError("Error on input, ITA = %d not yet implemented." % ita) + + return minimal_cell + + +# ------- +# Compute the g(r) +def _get_gr_slow(structures, type1, type2, r_max, dr): + """ +Radial distribution function +============================ + +Computes the radial distribution function for the system. The +:math:`g_{AB}(r)` is defined as + +.. math:: + + + g_{AB}(r) = \\frac{\\rho_{AB}^{(2)}(r)}{\\rho_A(r) \\rho_B(r)} + + +where :math:`A` and :math:`B` are two different types + +Parameters +---------- + - structures : list type(Structure) + A list of atomic structures on which compute the :math:`g_{AB}(r)` + - type1 : character + The character specifying the :math:`A` atomic type. + - type2 : character + The character specifying the :math:`B` atomic type. + - r_max : float + The maximum cutoff value of :math:`r` + - dr : float + The bin value of the distribution. + +Results +------- + - g_r : ndarray.shape() = (r/dr + 1, 2) + The :math:`g(r)` distribution, in the first column the :math:`r` value + in the second column the corresponding value of g(r) + """ + + # Get the r axis + N_bin = int( r_max / float(dr)) + 1 + r_min = np.linspace(0, r_max, N_bin) + + real_dr = np.mean(np.diff(r_min)) + #print("REAL DR:", real_dr) + real_r = r_min + real_dr * .5 + + # Define the counting array + N_r = np.zeros(N_bin) + + # Count how many atoms are in each shell + for i, struct in enumerate(structures): + # Cycle for all atoms + for first in range(0, struct.N_atoms- 1): + other_type = "" + if struct.atoms[first] == type1: + other_type = type2 + elif struct.atoms[first] == type2: + other_type = type1 + else: + continue + + for second in range(first + 1, struct.N_atoms): + if struct.atoms[second] == other_type: + # Get the distance vector between the two atoms + r_vec = struct.coords[first, :] - struct.coords[second,:] + #print("nat: {}, indexes: {}, {}".format(struct.N_atoms, first, second)) + #print("r_vec:", r_vec) + if struct.has_unit_cell: + r_vec = get_closest_vector(struct.unit_cell, r_vec) + #r = struct.get_min_dist(first, second) + r = np.sqrt(r_vec.dot(r_vec)) # The modulus + index_pos = int( r / real_dr) + if index_pos < N_bin: + N_r[index_pos] += 1 + + + + + # Now get the g(r) from N_r + N_tot = sum(N_r) + V = 4 * np.pi * r_max**3 / 3. + rho = N_tot / V + g_r = N_r / (4 * np.pi * real_r**2 * real_dr * rho) + + # Get the final data and return + data = np.zeros((N_bin, 2)) + data[:, 0] = real_r + data[:, 1] = g_r + return data + + +def get_gr(structures, type1, type2, r_min, r_max, N_r): + """ +Radial distribution function +============================ + +Computes the radial distribution function for the system. The +:math:`g_{AB}(r)` is defined as + +.. math:: + + + g_{AB}(r) = \\frac{\\rho_{AB}^{(2)}(r)}{\\rho_A(r) \\rho_B(r)} + + +where :math:`A` and :math:`B` are two different types + +Parameters +---------- + - structures : list type(Structure) + A list of atomic structures on which compute the :math:`g_{AB}(r)` + - type1 : character + The character specifying the :math:`A` atomic type. + - type2 : character + The character specifying the :math:`B` atomic type. + - r_min, r_max : float + The minimum and maximum cutoff value of :math:`r` + - N_r : float + The number of bins for the distributions + +Results +------- + - g_r : ndarray.shape() = (r/dr + 1, 2) + The :math:`g(r)` distribution, in the first column the :math:`r` value + in the second column the corresponding value of g(r) + """ + n_structs = len(structures) + nat = structures[0].N_atoms + cells = np.zeros((n_structs, 3, 3), order= "F", dtype = np.double) + coords = np.zeros( (n_structs, nat, 3), order = "F", dtype = np.double) + ityp = structures[0].get_ityp() + 1 + t1 = structures[0].get_ityp_from_species(type1) + 1 + t2 = structures[0].get_ityp_from_species(type2) + 1 + + #print(t1, t2, ityp) + + for i in range(n_structs): + cells[i, :, :] = structures[i].unit_cell + coords[i, :, :] = structures[i].coords + + + real_r, g_r = symph.get_gr_data(cells, coords, ityp, t1, t2, r_min, r_max, N_r) + + # Get the final data and return + data = np.zeros((N_r, 2)) + data[:, 0] = real_r + data[:, 1] = g_r + return data + + + + +def cell2abc_alphabetagamma(unit_cell): + """ +This methods return a list of 6 elements. The first three are the three lengths a,b,c of the cell, while the other three +are the angles alpha (between b and c), beta (between a and c) and gamma(between a and b). + +Parameters +---------- + - unit_cell : 3x3 ndarray (a float dtype) + The unit cell in which the lattice vectors are the rows. + +Results +------- + - cell : 6 length ndarray (size = 6, dtype = type(unit_cell)) + The array containing the a,b,c length followed by alpha,beta and gamma (in degrees) + """ + + cell = np.zeros(6, dtype = unit_cell.dtype) + + # Get a,b,c + for i in range(3): + cell[i] = np.sqrt(unit_cell[i, :].dot(unit_cell[i, :])) + + # Get alpha beta gamma + for i in range(3): + j = (i + 1) % 3 + k = (i + 2) % 3 + cosangle = unit_cell[j,:].dot(unit_cell[k, :]) / (cell[j] * cell[k]) + + cell[i + 3] = np.arccos(cosangle) * 180 / np.pi + + return cell + + + +def DistanceBetweenStructures(strc1, strc2, ApplyTrans=True, ApplyRot=False, Ordered = True): + """ +This method computes the distance between two structures. +It is usefull to check the similarity between two structure. + +Note: +Ordered = False is not yet implemented + +Parameters +---------- + - strc1 : type(Structure) + The first structure. It commutes with the strc2. + - strc2 : type(Structure) + The second structure. + - ApplyTrans: bool, default = False + If true both the structures are shifted in a common origin (The first atom). + This works only if the atoms are ordered to match properly. + - ApplyRot : bool, default = False + If true the structure are rotated to reduce the rotational freedom. + - Ordered: bool, default = True + If true the order in which the atoms appears is supposed to match in the two structures. + + +Results +------- + - Similarities: float + Similarity between the two provided structures + """ + + + if not Ordered: + raise ValueError("Error, Ordered = False not yet implemented. Sorry.") + + if strc1.N_atoms != strc2.N_atoms: + print( "Strc1 has ", strc1.N_atoms, " atoms") + print( "Strc2 has ", strc2.N_atoms, " atoms") + raise ValueError("Error, the number of atoms are not the same in the given structures.") + + nat = strc1.N_atoms + coord1 = np.zeros(nat*3) + coord2 = np.zeros(nat*3) + + if ApplyTrans: + # Shift both the strcture so that the first atom is in the origin. + if Ordered: + for i in range(nat): + coord1[3*i : 3*i + 3] = strc1.coords[i,:] - strc1.coords[0,:] + coord2[3*i : 3*i + 3] = strc2.coords[i,:] - strc2.coords[0,:] + else: + for i in range(nat): + coord1[3*i : 3*i + 3] = strc1.coords[i,:] + coord2[3*i : 3*i + 3] = strc2.coords[i,:] + + + # Compute the distance between the two coordinates + return np.sqrt(sum((coord1 - coord2)**2)) + + +def get_unit_cell_from_ibrav(ibrav, celldm): + """ + OBTAIN THE UNIT CELL WITH QUANTUM ESPRESSO IBRAV + ================================================ + + This subroutine reads the quantum espresso variables ibrav and celldm + and built the unit cell according to them. + + NOTE: not all the version are still supported, they will be + added as the developing of the code will go on. + + + Look at quantum espresso pw.x input for a clear explanation + on how they works. + Note the unit of QE are bohr, so we expect the celldm[0] to be + written in bohr. However the output cell will be in angstrom. + + Parameters + ---------- + ibrav : int + This is the ibrav number identification of the cell type. + Note if it is a float, it will be rounded to the closest integer. + For example, 1 means simple cubic, 13 is the base-centered monoclinic ... + + celldm : ndarray (float, 6) + It contains a list of 6 floats that defines the cell axis length and + angles. Their precise meaning can be found on quantum-espresso documentation. + We refer at 6.2.1 version. + + Results + ------- + unit_cell : ndarray (3x3) + The unit cell in angstrom. the i-th cell vector is unit_cell[i,:] + + """ + # Avoid trivial problems if the ibrav is a float + ibrav = int(ibrav + .5) + + #SUPPORTED_IBRAV = [13] + + # Check if the ibrav is in the supported ibrav + #if not ibrav in SUPPORTED_IBRAV: + # raise ValueError("Error, the specified ibrav %d is not supported." % ibrav) + + + # Check if celldm is of the correct length + if len(celldm) != 6: + raise ValueError("Error, celldm shoud be an ndarray of size 6") + + # Get the cell + unit_cell = np.zeros((3,3)) + if ibrav == 1: + # Simple cubic + a = celldm[0] * BOHR_TO_ANGSTROM + unit_cell[0,:] = np.array([1, 0, 0]) * a + unit_cell[1,:] = np.array([0, 1, 0]) * a + unit_cell[2,:] = np.array([0, 0, 1]) * a + elif ibrav == 2: + # Cubic fcc + a = celldm[0] * BOHR_TO_ANGSTROM + unit_cell[0,:] = np.array([-1, 0, 1]) * a / 2 + unit_cell[1,:] = np.array([0, 1, 1]) * a /2 + unit_cell[2,:] = np.array([-1, 1, 0]) * a/2 + elif ibrav == 3: + # Cubic bcc + a = celldm[0] * BOHR_TO_ANGSTROM + unit_cell[0,:] = np.array([1, 1, 1]) * a / 2 + unit_cell[1,:] = np.array([-1, 1, 1]) * a /2 + unit_cell[2,:] = np.array([-1, -1, 1]) * a/2 + elif ibrav == -3: + # Cubic bcc other kind + a = celldm[0] * BOHR_TO_ANGSTROM + unit_cell[0,:] = np.array([-1, 1, 1]) * a / 2 + unit_cell[1,:] = np.array([1, -1, 1]) * a /2 + unit_cell[2,:] = np.array([1, 1, -1]) * a/2 + elif ibrav == 4: + # Hexagonal + a = celldm[0] * BOHR_TO_ANGSTROM + c = celldm[2] * a + + unit_cell[0, :] = np.array([1, 0, 0]) * a + unit_cell[1, :] = np.array([-0.5, np.sqrt(3)/2, 0]) * a + unit_cell[2, :] = np.array([0, 0, 1]) * c + elif ibrav == 5: + a = celldm[0] * BOHR_TO_ANGSTROM + c = celldm[3] + + tx = np.sqrt( (1 - c) / 2.) + ty = np.sqrt( (1-c) / 6.) + tz = np.sqrt( (1+2*c)/3.) + + unit_cell[0, :] = np.array([tx, -ty, tz]) * a + unit_cell[1, :] = np.array([0, 2*ty, tz]) * a + unit_cell[2, :] = np.array([-tx, -ty, tz]) * a + + elif ibrav == 6: + a = celldm[0] * BOHR_TO_ANGSTROM + c = celldm[2] * a + + unit_cell[0, :] = a * np.array([1, 0, 0]) + unit_cell[1, :] = a * np.array([0, 1, 0]) + unit_cell[2, :] = c * np.array([0, 0, 1]) + + elif ibrav == 7: + # Tetragonal I + a = celldm[0] * BOHR_TO_ANGSTROM + c = celldm[2] * a + + unit_cell[0, :] = np.array([a*0.5, -a*0.5, c*0.5]) + unit_cell[1, :] = np.array([a*0.5, a*0.5, c*0.5]) + unit_cell[2, :] = np.array([-a*0.5, -a*0.5, c*0.5]) + + elif ibrav == 8: + # Orthorombic + a = celldm[0] * BOHR_TO_ANGSTROM + b = celldm[1] * a + c = celldm[2] * a + + unit_cell[0, :] = a * np.array([1,0,0]) + unit_cell[1, :] = b * np.array([0,1,0]) + unit_cell[2, :] = c * np.array([0,0,1]) + + elif ibrav == 9: + # Orthorombinc base centered + a = celldm[0] * BOHR_TO_ANGSTROM + b = celldm[1] * a + c = celldm[2] * a + + unit_cell[0, :] = np.array([a/2, b/2, 0]) + unit_cell[1, :] = np.array([-a/2, b/2, 0]) + unit_cell[2, :] = np.array([0, 0, c]) + elif ibrav == -9: + # Orthorombinc base centered (the same but change the first two vectors) + a = celldm[0] * BOHR_TO_ANGSTROM + b = celldm[1] * a + c = celldm[2] * a + + unit_cell[0, :] = np.array([-a/2, b/2, 0]) + unit_cell[1, :] = np.array([a/2, b/2, 0]) + unit_cell[2, :] = np.array([0, 0, c]) + elif ibrav == 91: + # Orthorhombic one-face base-centered A-type + # celldm(2)=b/a + # celldm(3)=c/a + # v1 = (a, 0, 0), v2 = (0,b/2,-c/2), v3 = (0,b/2,c/2) + a = celldm[0] * BOHR_TO_ANGSTROM + b = celldm[1] * a + c = celldm[2] * a + unit_cell[0,:] = np.array([a,0,0]) + unit_cell[1,:] = np.array([0, b/2, -c/2]) + unit_cell[2,:] = np.array([0, b/2, c/2]) + elif ibrav == 13: + # Monoclinic base-centered + + # Create cell + a = celldm[0] * BOHR_TO_ANGSTROM + b = a * celldm[1] + c = a * celldm[2] + cos_ab = celldm[3] + sin_ab = np.sqrt(1 - cos_ab**2) + + unit_cell[0,:] = np.array( [a/2., 0, -c/2.]) + unit_cell[1,:] = np.array( [b * cos_ab, b*sin_ab, 0]) + unit_cell[2,:] = np.array( [a/2., 0, c/2.]) + + elif ibrav == 14: + a = celldm[0] * BOHR_TO_ANGSTROM + b = a * celldm[1] + c = a * celldm[2] + cos_alpha = celldm[3] + cos_beta = celldm[4] + cos_gamma = celldm[5] + + sin_alpha = np.sqrt(1 - cos_alpha**2) + sin_beta = np.sqrt(1 - cos_beta**2) + sin_gamma = np.sqrt(1 - cos_gamma**2) + + unit_cell[0, :] = np.array([a, 0, 0], dtype = np.double) + unit_cell[1, :] = np.array([b * cos_gamma, b * sin_gamma, 0], dtype = np.double) + unit_cell[2, :] = np.array([c * cos_beta, c * (cos_alpha - cos_beta * cos_gamma) / sin_gamma, + c * np.sqrt( 1 + 2*cos_alpha*cos_beta*cos_gamma - cos_alpha**2 - cos_beta**2 - cos_gamma**2) / sin_gamma]) + + else: + raise ValueError("Error, the specified ibrav %d is not supported." % ibrav) + + return unit_cell + +def is_inside(index, indices): + """ + Returns whether idex is inside a couple of indices. + Usefull to check if something is inside or not something like + parenthesys or quotes + """ + if len(indices) == 0: + return False + + a = np.array(indices, dtype = int) + + new_a = (index > a).astype(int) + result = np.sum(new_a) + + if result % 2 == 0: + return False + return True + +def read_namelist(line_list): + """ + READ NAMELIST + ============= + + + This function will read the quantum espresso namelist format from a list of lines. + The info are returned in a dictionary: + + &control + type_cal = "wrong" + ecutrho = 140 + &end + + will be converted in a python dictionary + dict = {"control" : {"type_cal" : "wrong", "ecutrho" : 140}} + + Then the dictionary is returned. Comments must start as in fortran with the '!' + + NOTE: Fotran is not case sensitive, therefore all the keys are converted in lower case + + + + Parameters + ---------- + line_list : list or string (path) + A list of lines read in a file. They should be the row output of f.readlines() function + where f is a file obtained as f = open("something", "r"). You can also directly pass + a string to path + + Returns + ------- + dict : + The dictionary of the namelist + """ + + if isinstance(line_list, str): + if not os.path.exists(line_list): + raise IOError("Error, file %s not found." % line_list) + + # Read the file + fread = open(line_list, "r") + line_list = fread.readlines() + fread.close() + + + inside_namespace = False + current_namespace = "" + namespace = {} + total_dict = {} + + # Start reading + for line in line_list: + # Avoid case sensitivity turning everithing in lower case + #line = line.lower() + # Get thee string content and avoid parsing that + quotes_indices = [] + last_found = 0 + while True: + last_found = line.find('"', last_found + 1) + if last_found != -1: + quotes_indices.append(last_found) + else: + break + + + + + # Delete the line after the comment + # If it is not inside double quotes + if not is_inside(line.find("!"), quotes_indices): + # Delete the comment + line = line[:line.find("!")] + + # Clear the line of tailoring white spaces + line = line.strip() + + # Skip if the line is white + if len(line) == 0: + continue + + # Check if the line begins with an "&" sign + if line[0] == "&": + # Check if we are closing an existing namespace + if line[1:].lower() == "end": + if not inside_namespace: + raise IOError("Error, trying to close a namespace without having open it.") + + total_dict[current_namespace] = namespace.copy() + current_namespace = "" + inside_namespace = False + namespace.clear() + + continue + + # New namelist --- + + # Check if we already are inside a namespace + if inside_namespace: + raise IOError("Error, the namespace %s has not been closed." % current_namespace) + + current_namespace = line[1:].lower() + inside_namespace = True + + # Check if the namespace has a valid name + if len(current_namespace) == 0: + raise IOError("Error, non valid name for a namespace") + else: + # Check if the old namespace closure is used + if line[0] == "/": + if not inside_namespace: + raise IOError("Error, trying to close a namespace without having open it.") + + total_dict[current_namespace] = namespace.copy() + current_namespace = "" + inside_namespace = False + namespace.clear() + continue + + # First of all split for quotes + value = None + new_list_trial = line.split('"') + if len(new_list_trial) == 3: + value = '"' + new_list_trial[1] + '"' + else: + new_list_trial = line.split("'") + if len(new_list_trial) == 3: + value = '"' + new_list_trial[1] + '"' + + # Get the name of the variable + new_list = line.split("=") + + if len(new_list) != 2 and value is None: + raise IOError("Error, I do not understand the line %s" % line) + elif len(new_list) < 2: + raise IOError("Error, I do not understand the line %s" % line) + + variable = new_list[0].strip().lower() + if value is None: + value = new_list[1].strip() + + # Remove ending comma and otehr tailoring space + if value[-1] == ",": + value = value[:-1].strip() + + + # Convert fortran bool + if value.lower() == ".true.": + value = True + elif value.lower() == ".false.": + value = False + elif '"' == value[0]: # Get a string content + # If it is a string cancel the " or ' or , + value = value.replace("\"", "") + elif "'" == value[0]: + value = value.replace("'", "") + elif value.count(" ") >= 1: + value = [float(item) for item in value.split()] + else: + # Check if it is a number + try: + value = float(value.lower().replace("d", "e")) + except: + pass + if inside_namespace: + namespace[variable] = value + else: + total_dict[variable] = value + + # The file has been analyzed + if inside_namespace: + raise IOError("Error, file endend before %s was closed" % current_namespace) + + return total_dict + + + + + +def write_namelist(total_dict): + """ + WRITE ESPRESSO NAMELIST + ======================= + + Given a particular dictionary this subroutine will transform it into an espresso dictionary + + Parameters + ---------- + total_dict : dict + A dictionary of the namespaces in the namelist + + Results + ------- + list + A list of lines that can be written into a file + """ + + + lines = [] + keys = list(total_dict) + new_keys = [] + if "control" in keys: + new_keys.append("control") + keys.remove("control") + if "system" in keys: + new_keys.append("system") + keys.remove("system") + + for k in keys: + new_keys.append(k) + + for key in new_keys: + if type(total_dict[key]) == dict: + # Namelist + lines.append("&%s\n" % key) + for key2 in total_dict[key].keys(): + value = total_dict[key][key2] + valuestr = "" + if isinstance(value, list): + valuestr = " ".join(value) + elif isinstance(value, str): + valuestr = "\"%s\"" % value + elif isinstance(value, bool): + valuestr = ".{}.".format(str(value).lower()) + else: + valuestr = str(value) + + line = "\t%s = %s\n" % (key2, valuestr) + lines.append(line) + + lines.append("&end\n") + else: + value = total_dict[key] + valuestr = "" + if type(value) == list: + valuestr = " ".join(value) + else: + valuestr = str(value) + + line = "\t%s = %s\n" % (key, valuestr) + lines.append(line) + + + return lines + + + +def get_translations(pols, masses): + """ + GET TRANSLATIONS + ================ + + This subroutine analyzes the polarization vectors of a dynamical matrix to recognize the translations. + It is usefull to carefully remove the translations from the frequencies where w -> 0 gives an error in an equation. + + Parmaeters + ---------- + pols : ndarray 2 rank + The polarization vectors as they came out from DyagDinQ(0) method from Phonons. + masses : ndarray (size nat) + The mass of each atom. + + Returns + ------- + is_translation_mask : ndarray(3 * N_atoms) + A bool array of True if the i-th polarization vectors correspond to a translation, false otherwise. + + + Example + ------- + + In this example starting from the frequencies, the translations are removed (let dyn to be Phonons()): + + >>> w, pols = dyn.DyagDinQ(0) + >>> t_mask = get_translations(pols) + >>> w_without_trans = w[ ~t_mask ] + + The same, of course, can be applied to polarization vectors: + + >>> pols = pols[ :, ~t_mask ] + + The ~ caracter is used to get the bit not operation over the t_mask array (to mark False the translational modes and True all the others) + """ + n_atoms = len(pols[:, 0]) // 3 + n_pols = len(pols[0,:]) + + # Check if the masses array is good + if len(masses) * 3 != np.size(pols[:,0]): + raise ValueError("Error, the size of the two array masses and pols are not compatible.") + + # Prepare a mask filled with false + is_translation = np.zeros( n_pols, dtype = bool) + + # Cast to bool + is_translation[:] = symph.get_translations(pols, masses, n_pols, n_atoms) + + return is_translation + + +def _get_translations(pols, masses): + """ + OLD slow implemetation of get_translations + """ + + # Check if the masses array is good + if len(masses) * 3 != np.size(pols[:,0]): + raise ValueError("Error, the size of the two array masses and pols are not compatible.") + + + # Get the number of atoms and the number of polarization vectors + n_atoms = len(pols[:, 0]) // 3 + n_pols = len(pols[0,:]) + + # Prepare a mask filled with false + is_translation = np.zeros( n_pols).astype (bool) + + for i in range(n_pols): + # Check if the polarization vector is oriented in the same way for each atom + thr_val = 0 + for j in range(n_atoms): + thr_val += np.sum( np.abs(pols[3 * j : 3 * j + 3, i]/np.sqrt(masses[j]) - + pols[:3, i] / np.sqrt(masses[0]))**2) + + thr_val = np.sqrt(thr_val) + + if thr_val < __EPSILON__ : + is_translation[i] = True + + return is_translation + + + +def convert_matrix_cart_cryst(matrix, unit_cell, cryst_to_cart = False): + """ + This methods convert the 3x3 matrix into crystalline coordinates using the metric tensor defined by the unit_cell. + + This method is a specular implementation of Quantum ESPRESSO. + This subroutine transforms matrices obtained as open product between vectors. + If you want to transform a linear operator, then use + convert_matrix_cart_cryst2 + + For example, use this to transform a dynamical matrix, + use convert_matrix_cart_cryst2 to transform a symmetry operation. + + .. math:: + + g_{\\alpha\\beta} = \\left< \\vec v_\\alpha | \\vec v_\\beta\\right> + + F_{\\alpha\\beta} = g_{\\alpha\\gamma} g_{\\beta\\delta} F^{\\gamma\\delta} + + F^{\\alpha\\beta} = g^{\\alpha\\gamma} g^{\\beta\\delta} F_{\\gamma\\delta} + + F_{\\alpha\\beta} = \\frac{\\partial E}{\\partial u^\\alpha \\partial u^\\beta} + + F^{\\alpha\\beta} = \\frac{\\partial E}{\\partial u_\\alpha \\partial u_\\beta} + + + Parameters + ---------- + matrix : ndarray 3x3 + The matrix to be converted + unit_cell : ndarray 3x3 + The cell containing the vectors defining the metric (the change between crystalline and cartesian coordinates) + cryst_to_cart : bool, optional + If False (default) the matrix is assumed in cartesian coordinates and converted to crystalline. If True + otherwise. + + Results + ------- + new_matrix : ndarray(3x3) + The converted matrix into the desidered format + """ + + + # Get the metric tensor from the unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + # Choose which conversion perform + comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) + comp_matrix_inv = np.linalg.inv(comp_matrix) + + if cryst_to_cart: + return comp_matrix.T.dot( np.dot(matrix, comp_matrix)) + + return comp_matrix_inv.T.dot( np.dot(matrix, comp_matrix_inv)) + +def convert_matrix_cart_cryst2(matrix, unit_cell, cryst_to_cart = False): + """ + This methods convert the 3x3 matrix into crystalline coordinates using the metric tensor defined by the unit_cell. + + This perform the exact transform. + With this method you get a matrix that performs the transformation directly in the other space. + If I have a matrix that transforms vectors in crystalline coordinates, then with this I get the same operator between + vectors in cartesian space. + + This subroutine transforms operators, while the previous one transforms matrices (obtained as open product between vectors) + + For example, use convert_matrix_cart_cryst to transform a dynamical matrix, + use convert_matrix_cart_cryst2 to transform a symmetry operation. + + + Parameters + ---------- + matrix : ndarray 3x3 + The matrix to be converted + unit_cell : ndarray 3x3 + The cell containing the vectors defining the metric (the change between crystalline and cartesian coordinates) + cryst_to_cart : bool, optional + If False (default) the matrix is assumed in cartesian coordinates and converted to crystalline. If True + otherwise. + + Results + ------- + new_matrix : ndarray(3x3) + The converted matrix into the desidered format + """ + + + # Get the metric tensor from the unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + # Choose which conversion perform + comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) + comp_matrix_inv = np.linalg.inv(comp_matrix) + + if cryst_to_cart: + return comp_matrix_inv.dot( np.dot(matrix, comp_matrix)) + + return comp_matrix.dot( np.dot(matrix, comp_matrix_inv)) + + +def convert_3tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): + """ + Convert to crystal coordinates + ============================== + + This subroutine converts the 3 rank tensor to crystal coordinates + and vice versa. + + Paramters + --------- + tensor : ndarray( size = (3,3,3)) + The 3rank tensor to be converted + unit_cell : ndarray + The unit cell of the structure + cryst_to_cart : bool + If true, reverse convert crystal to cartesian. + """ + + # Get the metric tensor from the unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) + if not cryst_to_cart: + comp_matrix = np.linalg.inv(comp_matrix) + + return np.einsum("ia, jb, kc, ijk -> abc", comp_matrix, comp_matrix, comp_matrix, tensor) + + +def convert_4tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): + """ + Convert to crystal coordinates + ============================== + + This subroutine converts the 4 rank tensor to crystal coordinates and vice versa. + + Paramters + --------- + tensor : ndarray( size = (3,3,3,3)) + The 4rank tensor to be converted + unit_cell : ndarray + The unit cell of the structure + cryst_to_cart : bool + If true, reverse convert crystal to cartesian. + """ + + # Get the metric tensor from the unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) + if not cryst_to_cart: + comp_matrix = np.linalg.inv(comp_matrix) + + return np.einsum("ia, jb, kc, ld, ijkl -> abcd", comp_matrix, comp_matrix, comp_matrix, comp_matrix, tensor) + + + +def convert_fc(fc_matrix, unit_cell, cryst_to_cart = False): + """ + This method converts the force constant matrix from cartesian to crystal and the opposite. + Check the method convert_matrix_cart_cryst to see more details. + + Parameters + ---------- + fc_matrix : ndarray (3 nat x 3 nat) + The original force constant matrix + unit_cell : ndarray (3x3) + The unit cell of the system + cryst_to_cart : bool, optional, default False + If true convert from crystal to cartesian, the opposite otherwise. + + Results + ------- + new_fc_matrix : ndarray (shape(fc_matrix)) + The new force constant matrix after the conversion. + """ + + # Check if the fc_matrix is a good candidate + if np.shape(fc_matrix)[0] != np.shape(fc_matrix)[1]: + raise ValueError("Error, the force constant matrix must be a square array") + + if len(np.shape(fc_matrix)) != 2: + raise ValueError("Error, the fc_matrix must be a matrix.") + + n_indices = np.shape(fc_matrix)[0] + + if n_indices %3 != 0: + raise ValueError("Error, the size of the force constant matrix must be a multiple of 3") + + # Get the number of atoms + nat = n_indices / 3 + + # Prepare the output matrix + new_fc_matrix = np.zeros(np.shape(fc_matrix)) + for na in range(nat): + for nb in range(na, nat): + # Convert the single matrix + in_mat = fc_matrix[3 * na : 3*(na+1), 3*nb : 3*(nb+1)] + out_mat = convert_matrix_cart_cryst(in_mat, unit_cell, cryst_to_cart) + new_fc_matrix[3 * na : 3*(na+1), 3*nb : 3*(nb+1)] = out_mat + + # Apply hermitianity + if na != nb: + new_fc_matrix[3 * nb : 3*(nb+1), 3*na : 3*(na+1)] = np.conjugate(out_mat.transpose()) + + return new_fc_matrix + + +def get_directed_nn(structure, atom_id, direction): + """ + This function get the closest neighbour along a direction of the atom. + It returns the index of the atom in the structure that is the closest to atom_id in the + specified direction (a vector identifying the direction). + + The algorithm selects a cone around the atom oriented to the direction, and selects + the first atom in the structure inside the cone (the one with lowest projection on the cone axis) + + The method returns -1 if no near neighbour along the specified direction is detected + """ + + nat = structure.N_atoms + new_coords = structure.coords - structure.coords[atom_id, :] + + + + # Center the atoms if the unit cell is present. + if structure.has_unit_cell: + # Put the atom at the center of the cell + new_coords[:,:] += 0.5 * np.sum(structure.unit_cell, axis = 0) + for i in range(nat): + new_coords[i, :] = put_into_cell(structure.unit_cell, new_coords[i, :]) + + # Shift back + new_coords -= new_coords[atom_id, :] + + + + + # Pop the atoms not in the cone + versor_cone = direction / np.sqrt(direction.dot(direction)) + + # Project the coordinate on the versor + p_cone = new_coords.dot(versor_cone) + #print p_cone + + + # Now discard atoms not inside the cone + good_atoms = [] + good_d = [] + for i in range(nat): + if p_cone[i] > 0: + r_vect = new_coords[i, :] - p_cone[i] * versor_cone + r = np.sqrt(r_vect.dot(r_vect)) + #print "%d) p = %.4e, r = %.4e, r_vect = " % (i, p_cone[i], r), r_vect + if r < p_cone[i]: + good_atoms.append(i) + good_d.append(r**2 + p_cone[i]**2) + + #print good_atoms + + if len(good_atoms) == 0: + return -1 + + # Get the minimum value of p_cone among the good atoms + i_best = np.argmin(good_d) + + return good_atoms[i_best] + +def get_closest_vector(unit_cell, v_dist): + """ + This subroutine computes the periodic replica of v_dist + that minimizes its modulus. + + Parameters + ---------- + - unit_cell : ndarray(size = (3,3)) + The unit cell vector, unit_cell[i,:] is the i-th cell vector + - v_dist : ndarray(size = 3) + The distance vector that you want to optimize + + Returns + ------- + - new_v_dist: ndarray(size = 3) + The replica of v_dist that has the minimum modulus + """ + + # Define the metric tensor + g = unit_cell.dot(unit_cell.T) + alphas = covariant_coordinates(unit_cell, v_dist) + + # Define the minimum function + def min_f(n): + tmp = g.dot(2 * alphas + n) + return n.dot(tmp) + + # Get the starting guess for the vector + n_start = -np.floor(alphas + .5) + n_min = n_start.copy() + tot_min = min_f(n_min) + + # Get the supercell shift that minimizes the vector + for n_x in [-1,0,1]: + for n_y in [-1,0,1]: + for n_z in [-1,0,1]: + n_v = n_start + np.array([n_x, n_y, n_z]) + new_min = min_f(n_v) + if new_min < tot_min: + tot_min = new_min + n_min = n_v + + # Get the new vector + new_v = v_dist + n_min.dot(unit_cell) + return new_v + +def three_to_one_len(v,v_min,v_len): + """ + This subroutine converts a triplet index v, + of length v_len and starting from v_min, + into a single progressive index starting from 0 + """ + res=(v[0]-v_min[0])*v_len[1]*v_len[2]+(v[1]-v_min[1])*v_len[2]+(v[2]-v_min[2]) + return int(res) + +def three_to_one(v,v_min,v_max): + """ + This subroutine converts a triplet index v, + going from v_min to v_max, into a single progressive + index starting from 0 + """ + v_len=np.array(v_max)-np.array(v_min)+np.ones(3,dtype=int) + res=(v[0]-v_min[0])*v_len[1]*v_len[2]+(v[1]-v_min[1])*v_len[2]+(v[2]-v_min[2]) + return int(res) + +def one_to_three_len(J,v_min,v_len): + """ + This subroutine converts a single progressive + index J starting from 0, to a triplet of progressive + indexes starting from v_min, of length v_len + """ + x = J // (v_len[2] * v_len[1]) + v_min[0] + y = ( J % (v_len[2] * v_len[1])) // v_len[2] + v_min[1] + z = J % v_len[2] + v_min[2] + return np.array([x,y,z],dtype=int) + +def one_to_three(J,v_min,v_max): + """ + This subroutine coonverts a single progressive + index J starting from 0, to a triplet of progressive + indexes going from v_min to v_max + """ + v_len=np.array(v_max)-np.array(v_min)+np.ones(3,dtype=int) + x = J // (v_len[2] * v_len[1]) + v_min[0] + y = ( J % (v_len[2] * v_len[1])) // v_len[2] + v_min[1] + z = J % v_len[2] + v_min[2] + return np.array([x,y,z],dtype=int) + + + +def is_gamma(unit_cell, q): + """ + Defines if the q point in cartesian (A^-1) is gamma or not + given the unit cell + + Parameters + ---------- + unit_cell : ndarray (size =3,3) + The unit cell of the structure + q : ndarray(size=3) + The q point that you want to check + + Results + ------- + is_gamma : bool + """ + + bg = get_reciprocal_vectors(unit_cell) + new_q = get_closest_vector(bg, q) + + return (np.abs(new_q) < 1e-6).all() + + +def save_qe(dyn,q,dynq,freqs, pol_vects,fname): + """ + SAVE THE DYNMAT + =============== + + This subroutine saves the dynamical matrix in the quantum espresso file format. + The dynmat is the force constant matrix in Ry units. + + .. math:: + + \\Phi_{ab} = \\sum_\\mu \\omega_\\mu^2 e_\\mu^a e_\\mu^b \\sqrt{M_a M_b} + + Where :math:`\\Phi_{ab}` is the force constant matrix between the a-b atoms (also cartesian + indices), :math:`\\omega_\\mu` is the phonon frequency and :math:`e_\\mu` is the + polarization vector. + """ + + A_TO_BOHR = 1.889725989 + RyToCm=109737.37595 + RyToTHz=3289.84377 + + + # Open the file + fp = open(fname, "w") + fp.write("Dynamical matrix file\n") + + # Get the different number of types + types = [] + n_atoms = dyn.structure.N_atoms + for i in range(n_atoms): + if not dyn.structure.atoms[i] in types: + types.append(dyn.structure.atoms[i]) + n_types = len(types) + + # Assign an integer for each atomic species + itau = {} + for i in range(n_types): + itau[types[i]] = i +1 + + # Write the comment line + fp.write("File generated with CellConstructor\n") + fp.write("%d %d %d %22.16f %22.16f %22.16f %22.16f %22.16f %22.16f\n" % + (n_types, n_atoms, 0, dyn.alat * A_TO_BOHR, 0, 0, 0, 0, 0) ) + + # Write the basis vector + fp.write("Basis vectors\n") + # Get the unit cell + for i in range(3): + fp.write(" ".join("%22.16f" % x for x in dyn.structure.unit_cell[i,:] / dyn.alat) + "\n") + + # Set the atom types and masses + for i in range(n_types): + fp.write("\t{:d} '{:<3s}' {:>24.16f}\n".format(i +1, types[i], dyn.structure.masses[types[i]])) + + # Setup the atomic structure + for i in range(n_atoms): + # Convert the coordinates in alat + coords = dyn.structure.coords[i,:] / dyn.alat + fp.write("%5d %5d %22.16f %22.16f %22.16f\n" % + (i +1, itau[dyn.structure.atoms[i]], + coords[0], coords[1], coords[2])) + + # Here the dynamical matrix starts + fp.write("\n") + fp.write(" Dynamical Matrix in cartesian axes\n") + fp.write("\n") + fp.write(" q = ( {:11.9f} {:11.9f} {:11.9f} )\n".format(q[0] * dyn.alat , + q[1] * dyn.alat, q[2] * dyn.alat )) + fp.write("\n") + + # Now print the dynamical matrix + for i in range(n_atoms): + for j in range(n_atoms): + # Write the atoms + fp.write("%5d%5d\n" % (i + 1, j + 1)) + for x in range(3): + line = "%23.16f%23.16f %23.16f%23.16f %23.16f%23.16f" % \ + ( np.real(dynq[3*i + x, 3*j]), + np.imag(dynq[3*i + x, 3*j]), + np.real(dynq[3*i + x, 3*j+1]), + np.imag(dynq[3*i+x, 3*j+1]), + np.real(dynq[3*i + x, 3*j+2]), + np.imag(dynq[3*i+x, 3*j+2]) ) + + fp.write(line + "\n") + + # Print the diagnoalization of the matrix + fp.write("\n") + fp.write(" Diagonalizing the dynamical matrix\n") + fp.write("\n") + fp.write(" q = ( {:11.9f} {:11.9f} {:11.9f} )\n".format(q[0] *dyn.alat , + q[1] *dyn.alat, q[2] *dyn.alat)) + fp.write("\n") + fp.write("*" * 75 + "\n") + + nmodes = len(freqs) + for mu in range(nmodes): + # Print the frequency + fp.write("%7s (%5d) = %14.8f [THz] = %14.8f [cm-1]\n" % + ("freq", mu+1, freqs[mu] * RyToTHz, freqs[mu] * RyToCm)) + + # Print the polarization vectors + for i in range(n_atoms): + fp.write("( %10.6f%10.6f %10.6f%10.6f %10.6f%10.6f )\n" % + (np.real(pol_vects[3*i, mu]), np.imag(pol_vects[3*i,mu]), + np.real(pol_vects[3*i+1, mu]), np.imag(pol_vects[3*i+1,mu]), + np.real(pol_vects[3*i+2, mu]), np.imag(pol_vects[3*i+1,mu]))) + fp.write("*" * 75 + "\n") + fp.close() + +def transform_voigt(tensor, voigt_to_mat = False): + """ + Transforms the voigt notation. + If voit_to_mat is True, the tensor is assumed to be in voigt format. + Otherwise it assumed as a 3x3 symmetric matrix (upper triangle will be read). + """ + + if voigt_to_mat: + assert len(tensor) == 6 + new_tensor = np.zeros((3,3), dtype = type(tensor[0])) + for i in range(3): + new_tensor[i,i] = tensor[i] + + new_tensor[1,2] = new_tensor[2,1] = tensor[3] + new_tensor[0,2] = new_tensor[2,0] = tensor[4] + new_tensor[1,0] = new_tensor[0,1] = tensor[5] + else: + assert tensor.shape == (3,3) + new_tensor = np.zeros(6, dtype = type(tensor[0,0])) + + for i in range(3): + new_tensor[i] = tensor[i,i] + new_tensor[3] = tensor[1,2] + new_tensor[4] = tensor[0,2] + new_tensor[5] = tensor[0,1] + + return new_tensor + + +def get_bandpath(unit_cell, path_string, special_points, n_points = 1000): + """ + GET THE BANDPATH + ================ + + Given the structure, get the kpoints in cartesian coordinates that reproduce the bandpath. + + This method is usefull to plot the phonon dispersion. + + Parameters + ---------- + unit_cell :: ndarray(size = (3,3)) + The primitive cell on which to simulate the bandpath + path_string :: str + The string of the path (for example GXWKG) + special_points : dict + A dictionary containing all the special points in the path and the respective coordinates in crystalline axis (relative to the reciprocal vectors). + n_points : int + The total number of points in which the path is divided. + + + Results + ------- + qpath : ndarray(sizeof=(n_points, 3)) + The q path in cartesian coordinates + (xaxis, xticks, xlabels) : + The xaxis that represent the lenght of the qpath from the first point. + xlabels is the labels of each ticks and xticks + + """ + + # Get the reciprocal lattice + bg = get_reciprocal_vectors(unit_cell) + + + new_special_points = {x : np.array(special_points[x], dtype = np.double).dot(bg) for x in special_points} + print(new_special_points) + + if len(path_string) < 2: + raise ValueError("Error, at least 2 q points needs to be processed") + + path_points = np.zeros((len(path_string), 3), dtype = np.double) + for i, c in enumerate(path_string): + path_points[i, :] = new_special_points[c] + + #print('BG:', bg * 2 * np.pi) + #print('UC:', unit_cell) + #print('SPECIAL POINTS:', {x : new_special_points[x] * 2 * np.pi for x in new_special_points}) + + single_lenghts = np.linalg.norm(np.diff(path_points, axis = 0), axis = 1) + total_lenght = np.sum(single_lenghts) + + xaxis = np.linspace(0, total_lenght, n_points) + xticks = np.zeros(len(path_string)) + for i, ll in enumerate(single_lenghts): + xticks[i+1] = xticks[i] + ll + + xlabels = [x.replace('G', r'$\Gamma$') for x in path_string] + + + q_path = np.zeros((n_points, 3), dtype = np.double) + q_path[-1, :] = path_points[-1,:] # Set the starting point in the path + dq = total_lenght / n_points + counter = 0 + visited = [] + for i in range(1, n_points): + + # Identify in which line it is + xval = xaxis[i] + index = 0 + while xval >= single_lenghts[index] + __EPSILON__: + + print(xval, index, single_lenghts) + xval -= single_lenghts[index] + index += 1 + + + # If the line is changed, add a counter + if not index in visited: + visited.append(index) + counter = 0 + else: + counter += 1 + + q_versor = (path_points[index+1,:] - path_points[index,:]) / single_lenghts[index] + + q_path[i-1, :] = path_points[index, :] + counter * dq * q_versor + + return q_path, (xaxis, xticks, xlabels) diff --git a/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py new file mode 100644 index 00000000..ba3aa243 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# untitled.py +# +# Copyright 2022 Diego Martinez Gutierrez +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# +# --------------------------- +# Importación de los módulos +# --------------------------- +import numpy as np +from scipy.stats import qmc +import matplotlib.pyplot as plt +import math +# ------- +# Clases +# ------- +class Moro(object): + def __init__(self): + self.a0 = 2.50662823884 + self.a1 = -18.61500062529 + self.a2 = 41.39119773534 + self.a3 = -25.44106049637 + self.b0 = -8.47351093090 + self.b1 = 23.08336743743 + self.b2 = -21.06224101826 + self.b3 = 3.13082909833 + self.c0 = 0.3374754822726147 + self.c1 = 0.9761690190917186 + self.c2 = 0.1607979714918209 + self.c3 = 0.0276438810333863 + self.c4 = 0.0038405729373609 + self.c5 = 0.0003951896511919 + self.c6 = 0.0000321767881768 + self.c7 = 0.0000002888167364 + self.c8 = 0.0000003960315187 + + def gauss(self,u): + y = u - 0.5 + if (abs(y) < 0.42): + r = y * y + #x = y * (((self.a3*r+self.a2)*r+self.a1)*r+self.a0)/((((self.b3*r+self.b2)*r+self.b1)*r+self.b0)*r+1) + x = y * np.polyval([self.a3,self.a2,self.a1,self.a0],r)/np.polyval([self.b3,self.b2,self.b1,self.b0,1],r) + else: + r = u + if (y > 0): + r = 1-u + r = np.log(-np.log(r)) + #x = self.c0+r*(self.c1+r*(self.c2+r*(self.c3+r*(self.c4+r*(self.c5+r*(self.c6+r*(self.c7+r*self.c8)))))) + x = np.polyval([self.c8,self.c7,self.c6,self.c5,self.c4,self.c3,self.c2,self.c1,self.c0],r) + if (y < 0): + x = -x + return x + + def normalize(self,u): + x = np.zeros(len(u)) + for i in range(len(u)): + x[i] = self.gauss(u[i]) + return x + + def sobol(self,size,n_modes): + sampler = qmc.Sobol(d=1, scramble=False) + size_sobol = int(math.ceil(np.log(size)/np.log(2))) + print ('size=',size,'size_sobol=',size_sobol,'ss=',2**size_sobol) + # x = [] + # for i in range(n_modes): + # x.append(data) + sample0 = sampler.random_base2(m=size_sobol) + sample = sampler.random_base2(m=size_sobol) +# print ('sample=',sample) +# plt.hist(sample, bins=int(size/2)) +# plt.show() +# plt.scatter(sample,range(len(sample))) +# plt.show() +# data = self.normalize((sample+0.01)%1) + data = self.normalize(sample) +# print ('data=',data) +# plt.hist(data, bins=int(size/2)) +# plt.show() +# plt.scatter(data,range(len(data))) +# plt.show() + m = len(data)-size #****Diegom_test**** cut extra points (may work?) + data1 = data[m:] + x = np.resize(data1,(n_modes,size)) + print ('exit data:') + print (x) + return x + + def sobol_modes(self,size,n_modes,scramble): + sampler = qmc.Sobol(d=n_modes, scramble=scramble) + size_sobol = int(math.ceil(np.log(size)/np.log(2))) + sample0 = sampler.random_base2(m=size_sobol) + sample = sampler.random_base2(m=size_sobol) + print ('size=',size,'size_sobol=',size_sobol,'ss=',2**size_sobol) + #print (sample) + data = np.zeros(shape=(size,n_modes)) + for i in range(size): + for j in range(n_modes): + data[i][j] = self.gauss(sample[i][j]) + m = len(data)-size #****Diegom_test**** cut extra points (may work?) + x = data[m:] + #print ('exit data:') + #print (x) + + return x + + def sobol_big(self,size,n_modes,scramble): # in case the number of vibrational modes excedes the max setting of the generator. + if (n_modes>21201): + number = n_modes-21201 + #if (number>21201): #Do it recursive??? + x1 = self.sobol_modes(size = size, n_modes = 21201 , scramble = scramble) + x2 = self.sobol_big(size = size, n_modes = number, scramble = scramble) + x = x1.append(x2) + else: + x = self.sobol_modes(size = size, n_modes = n_modes , scramble = scramble) + return x + +# ---------- +# Funciones +# ---------- + +def main(args): + size = 32 + n_modes = 3 + Sobol = Moro() + # data = Sobol.sobol(size,n_modes) + # for i in (range(n_modes)): + # plt.hist(data[i], bins=20)#int(size/2)) + # plt.show() + # plt.scatter(data[i],range(len(data[i]))) + # plt.show() + + data = Sobol.sobol_modes(size,n_modes) + for i in (range(n_modes)): + plt.hist(data.T[i], bins=20)#int(size/2)) + plt.show() + plt.scatter(data.T[i],range(len(data.T[i]))) + plt.show() + + return 0 + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv)) diff --git a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py new file mode 100644 index 00000000..5844307a --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py @@ -0,0 +1,4518 @@ + +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 6 10:29:32 2018 +@author: pione +""" + +from __future__ import print_function +from __future__ import division +from cellconstructor import Settings + +import numpy as np +import os, sys +import scipy, scipy.optimize +from scipy.stats import qmc + +import itertools, math +import cellconstructor.Structure as Structure +import cellconstructor.symmetries as symmetries +import cellconstructor.ForceTensor as ForceTensor +import cellconstructor.Methods as Methods +from cellconstructor.Units import * + +import cellconstructor.calculators as calculators +from cellconstructor.Moro_object import Moro + +import warnings + +# Import the Fortran Code +import symph + +import time + + +try: + from mpi4py import MPI + __MPI__ = True +except: + __MPI__ = False + +try: + import spglib + __SPGLIB__ = True +except: + __SPGLIB__ = False + +__EPSILON__ = 1e-5 +__EPSILON_W__ = 3e-9 + +class Phonons: + """ + Phonons + ================ + + + This class contains the phonon of a given structure. + It can be used to show and display dinamical matrices, as well as for operating + with them + """ + def __init__(self, structure = None, nqirr = 1, full_name = False, use_format = False, force_real = False): + """ + INITIALIZE PHONONS + ================== + + The dynamical matrix for a given structure. + + Parameters + ---------- + - structure : type(Structure) or type(string) + This is the atomic structure for which you want to use the phonon calculation. + It is needed to correctly initialize all the arrays. + It can be both the Structure, or a filepath containing a quantum ESPRESSO + dynamical matrix. Up to now only ibrav0 dymat are supported. + - nqirr : type(int) , default 1 + The number of irreducible q point of the supercell on which you want + to compute the phonons. + Use 1 if you want to perform a Gamma point calculation. + - full_name : bool + If full_name is True, then the structure is loaded without appending the + q point index. This is compatible only with nqirr = 1. + - force_real : bool + If True, force the dynamical matrix allocated to be real. This is usefull to spare memory when + generating a dynamical matrix at Gamma in real space, that is real by construction. + + Results + ------- + - Phonons : this + It returns the Phonon class initializated. + """ + + # Initialize standard variables + self.dynmats = [] + self.nqirr = nqirr + # Q tot contains the total q points (also those belonging to the same star) + self.q_tot = [] + + # Prepare additional information that can be loaded + self.dielectric_tensor = None # (3x3 matrix) + self.effective_charges = None # 3-rank (Natoms, pol electric field, atomic coords) = (nat, 3, 3) + self.raman_tensor = None # 3-rank (incoming field, outcoming field, atomic coords) = (3,3, 3*nat) + + # This alat is read just from QE, but not used + self.alat = 1 + + # If this is true then the dynmat can be used + self.initialized = False + + # This contains all the q points in the stars of the irreducible q point + self.q_stars = [] + self.structure = None + + dtype = np.complex128 + if force_real: + dtype = np.float64 + + # Check whether the structure argument is a path or a Structure + if (type(structure) == type("hello there!")): + # Quantum espresso + self.LoadFromQE(structure, nqirr, full_name = full_name, use_format = use_format) + elif (type(structure) == type(Structure.Structure())): + # Get the structure + self.structure = structure + + if structure.N_atoms <= 0: + raise ValueError("Error, the given structure cannot be empty.") + + # Check that nqirr has a valid value + if nqirr <= 0: + raise ValueError("Error, nqirr argument must be a strictly positive number.") + + self.dynmats = [] + for i in range(nqirr): + # Create a dynamical matrix + self.dynmats.append(np.zeros((3 * structure.N_atoms, 3*structure.N_atoms), dtype = dtype)) + + # Initialize the q vectors + self.q_stars.append([np.zeros(3, dtype = np.float64)]) + self.q_tot.append(np.zeros(3, dtype = np.float64)) + + + def LoadFromQE(self, fildyn_prefix, nqirr=1, full_name = False, use_format= False, is_fulltext = False): + r""" + This Function loads the phonons information from the quantum espresso dynamical matrix. + the fildyn prefix is the prefix of the QE dynamical matrix, that must be followed by numbers from 1 to nqirr. + All the dynamical matrices are loaded. + + + Parameters + ---------- + - fildyn_prefix : type(string) + Quantum ESPRESSO dynmat prefix (the files are followed by the q irreducible index) + - nqirr : type(int), default 1 + Number of irreducible q points in the space group (supercell phonons). + If 0 or negative an exception is raised. + - full_name : bool, optional + If it is True, then the dynamical matrix is loaded without appending the q index. + This is compatible only with gamma point matrices. + - use_format : bool + If true, the IQ index of the dynamical matrix is replaced in the specified format, i.e. + a standard matrix with prefix dyn (dyn1, dyn2, ...) will be dyn{} with the format notation. + This allows the user to insert the IQ index in many formats and any position of the file name. + - is_fulltext : bool + If true (default false), the fildyn_prefix is ment to be the full text of the dynamical matrix + instead to the path of the file. + """ + + # Check if the nqirr is correct + if nqirr <= 0: + raise ValueError("Error, the specified nqirr is not valid: it must be positive!") + + if full_name and nqirr > 1: + raise ValueError("Error, with full_name only gamma matrices are loaded.") + + if is_fulltext and nqirr > 1: + raise ValueError("Error, with is_fulltext only gamma single dynamical matrices can be loaded.") + + # Initialize the atomic structure + self.structure = Structure.Structure() + + # Start processing the dynamical matrices + for iq in range(nqirr): + # Check if the selected matrix exists + if not is_fulltext: + if use_format: + filepath = fildyn_prefix.format(iq+1) + else: + if not full_name: + filepath = "%s%i" % (fildyn_prefix, iq + 1) + else: + filepath = fildyn_prefix + + if not os.path.isfile(filepath): + raise ValueError("Error, file %s does not exist." % filepath) + + # Load the matrix as a regular file + dynfile = open(filepath, "r") + dynlines = [line.strip() for line in dynfile.readlines()] + dynfile.close() + else: + dynlines = [x.strip() for x in fildyn_prefix.split("\n")] + + if (iq == 0): + # This is a gamma point file, generate the structure + # Go to the third line + struct_info = dynlines[2].split() + + # Check if the ibrav is 0 + ibrav = int(struct_info[2]) + celldm = np.zeros(6) + celldm[0] = float(struct_info[3]) + celldm[1] = float(struct_info[4]) + celldm[2] = float(struct_info[5]) + celldm[3] = float(struct_info[6]) + celldm[4] = float(struct_info[7]) + celldm[5] = float(struct_info[8]) + +# if ibrav != 0: +# raise ValueError("Error, only ibrav 0 supported up to now") + + nat = int(struct_info[1]) + ntyp = int(struct_info[0]) + self.alat = np.float64(struct_info[3]) * BOHR_TO_ANGSTROM # We want a structure in angstrom + + # Allocate the coordinates + self.structure.N_atoms = nat + self.structure.coords = np.zeros((nat, 3)) + + # Read the unit cell + unit_cell = np.zeros((3,3)) + if ibrav == 0: + for i in range(3): + unit_cell[i, :] = np.array([np.float64(item) for item in dynlines[4 + i].split()]) * self.alat + else: + unit_cell = Methods.get_unit_cell_from_ibrav(ibrav, celldm) + # Insert 4 lines to match the same number of lines as in ibrav = 0 + dynlines.insert(3, "") + dynlines.insert(3, "") + dynlines.insert(3, "") + dynlines.insert(3, "") + + # Read the atomic type + atoms_dict = {} + masses_dict = {} + for atom_index in range(1, ntyp + 1): + atm_line = dynlines[6 + atom_index] + atoms_dict[atom_index] = atm_line.split("'")[1].strip() + + # Get also the atomic mass + masses_dict[atoms_dict[atom_index]] = np.float64(atm_line.split("'")[-1].strip()) + + self.structure.set_masses(masses_dict) + + + self.structure.unit_cell = unit_cell + self.structure.has_unit_cell = True + + # Read the atoms + for i in range(nat): + # Jump the lines up to the structure + line_index = 7 + ntyp + i + atom_info = np.array([np.float64(item) for item in dynlines[line_index].split()]) + self.structure.atoms.append(atoms_dict[int(atom_info[1])]) + self.structure.coords[i, :] = atom_info[2:] * self.alat + + + # From now start reading the dynamical matrix ----------------------- + reading_dyn = True + q_star = [] + + # Pop the beginning of the matrix + while reading_dyn: + # Pop the file until you reach the dynamical matrix + if "cartesian axes" in dynlines[0]: + reading_dyn = False + dynlines.pop(0) + + # Get the small q point + reading_dyn = True + index = -1 + current_dyn = np.zeros((3*self.structure.N_atoms, 3*self.structure.N_atoms), dtype = np.complex128) + + # The atom indices + atm_i = 0 + atm_j = 0 + coordline = 0 + + dielectric_read = 0 + pol_read = 0 + + # Info about what I'm reading + reading_dielectric = False + reading_eff_charges = False + reading_raman = False + + while reading_dyn: + # Advance in the reading + index += 1 + + if index >= len(dynlines): + reading_dyn = False + self.dynmats.append(current_dyn.copy()) + continue + + # Setup what I'm reading + if "Diagonalizing" in dynlines[index]: + reading_dyn = False + self.dynmats.append(current_dyn.copy()) + + continue + if "Dielectric" in dynlines[index]: + reading_dielectric = True + reading_eff_charges = False + reading_raman = False + + # Reset the dielectric tensor + self.dielectric_tensor = np.zeros((3,3)) + dielectric_read = 0 + + continue + elif "Effective" in dynlines[index]: + reading_dielectric = False + reading_eff_charges = True + reading_raman = False + + + # Reset the effective charges + self.effective_charges = np.zeros((self.structure.N_atoms, 3, 3)) + + continue + elif "Raman" in dynlines[index]: + reading_dielectric = False + reading_eff_charges = False + reading_raman = True + + # Reset the raman tensor + self.raman_tensor = np.zeros((3,3, 3*self.structure.N_atoms)) + continue + elif "q = " in dynlines[index]: + #Read the q + qpoint = np.array([float(item) for item in dynlines[index].replace("(", ")").split(')')[1].split()]) + q_star.append(qpoint / self.alat) + self.q_tot.append(qpoint / self.alat) + reading_dielectric = False + reading_eff_charges = False + reading_raman = False + continue + elif "ynamical" in dynlines[index]: + # Save the dynamical matrix + self.dynmats.append(current_dyn.copy()) + reading_dielectric = False + reading_eff_charges = False + reading_raman = False + continue + + + # Read what is needed + numbers_in_line = dynlines[index].split() + if len(numbers_in_line) == 0: + continue + + if reading_dielectric: + # Reading the dielectric + if len(numbers_in_line) == 3: + self.dielectric_tensor[dielectric_read, :] = np.array([np.float64(x) for x in numbers_in_line]) + dielectric_read += 1 + elif reading_eff_charges: + if numbers_in_line[0].lower() == "atom": + atm_i = int(numbers_in_line[2]) - 1 + dielectric_read = 0 + elif len(numbers_in_line) == 3: + self.effective_charges[atm_i, dielectric_read,:] = np.array([np.float64(x) for x in numbers_in_line]) + dielectric_read += 1 + elif reading_raman: + if numbers_in_line[0].lower() == "atom": + atm_i = int(numbers_in_line[2]) - 1 + pol_read = int(numbers_in_line[4]) - 1 + dielectric_read = 0 + elif len(numbers_in_line) == 3: + self.raman_tensor[dielectric_read,:, 3*atm_i + pol_read] = np.array([np.float64(x) for x in numbers_in_line]) + dielectric_read += 1 + else: + # Read the numbers + if (len(numbers_in_line) == 2): + # Setup which atoms are + atm_i = int(numbers_in_line[0]) - 1 + atm_j = int(numbers_in_line[1]) - 1 + coordline = 0 + elif(len(numbers_in_line) == 6): + # Read the dynmat + for k in range(3): + current_dyn[3 * atm_i + coordline, 3*atm_j + k] = np.float64(numbers_in_line[2*k]) + 1j*np.float64(numbers_in_line[2*k + 1]) + coordline += 1 + + + # Append the new stars for the irreducible q point + self.q_stars.append(q_star) + + + # Ok, the matrix has been initialized + self.initialized = True + + def DyagDinQ(self, iq, force_real_at_gamma = True): + """ + Dyagonalize the dynamical matrix in the given q point index. + This methods returns both frequencies and polarization vectors. + The frequencies and polarization are ordered. Negative frequencies are to + be interpreted as instabilities and imaginary frequency, as for QE. + + They are returned. + + NOTE: The normalization is forced, as it is problematic for degenerate modes + NOTE: if the q point is gamma, then the matrix is forced to be real + + Parameters + ---------- + - iq : int + Tbe index of the q point of the matrix to be dyagonalized. + - force_real_at_gamma : bool, optional + If True (default) the matrix is forced to be real during the + dyagonalization (if q = 0). This assures to have real eigenvectors. + This is usefull for supercells. + + + Results + ------- + - frequencies : ndarray (float) + The frequencies (square root of the eigenvalues divided by the masses). + These are in Ry units. + - pol_vectors : ndarray (N_modes x 3)^2 + The polarization vectors for the dynamical matrix. They are returned + in a Fortran fashon order: pol_vectors[:, i] is the i-th polarization vector. + """ + + + + # First of all get correct dynamical matrix by dividing per the masses. + real_dyn = np.zeros((3* self.structure.N_atoms, 3*self.structure.N_atoms), dtype = np.complex128) + for i, atm_type1 in enumerate(self.structure.atoms): + m1 = self.structure.masses[atm_type1] + for j, atm_type2 in enumerate(self.structure.atoms): + m2 = self.structure.masses[atm_type2] + real_dyn[3*i : 3*i + 3, 3*j : 3*j + 3] = 1 / np.sqrt(m1 * m2) + + + real_dyn *= self.dynmats[iq] + + q_vec = self.q_tot[iq] + if np.sqrt(q_vec.dot(q_vec)) < __EPSILON__: + eigvals, pol_vects = np.linalg.eigh(np.real(real_dyn)) + else: + eigvals, pol_vects = np.linalg.eigh(real_dyn) + + f2 = eigvals + + # Check for imaginary frequencies (unstabilities) and return them as negative + frequencies = np.zeros(len(f2), dtype = np.double) + frequencies[f2 > 0] = np.sqrt(f2[f2 > 0]) + frequencies[f2 < 0] = -np.sqrt(-f2[f2 < 0]) + + # Order the frequencies and the polarization vectors + sorting_mask = np.argsort(frequencies) + frequencies = frequencies[sorting_mask] + pol_vects = pol_vects[:, sorting_mask] + + # Force normalization + for i in range(3 * self.structure.N_atoms): + # Check the normalization + norm = np.sqrt(pol_vects[:, i].dot(np.conj(pol_vects[:, i]))) + if abs(norm - 1) > __EPSILON__: + sys.stderr.write("WARNING: Phonon mode %d at q point %d not normalized!\n" % (i, iq)) + print ("WARNING: Normalization of the phonon %d mode at %d q = %16.8f" % (i, iq, norm)) + + # Check if it is an eigenvector + not_eigen = np.sqrt(np.sum( abs(real_dyn.dot(pol_vects[:, i]) - eigvals[i] * pol_vects[:, i])**2)) + + if not_eigen > 1e-2: + sys.stderr.write("WARNING: Phonon mode %d at q point %d not an eigenvector!\n" % (i, iq)) + print ("WARNING: Error of the phonon %d mode eigenvector %d q = %16.8f" % (i, iq, not_eigen)) + + pol_vects[:, i] /= norm + + return frequencies, pol_vects + + def Copy(self): + """ + Return an exact copy of itself. + This will implies copying all the dynamical matricies and structures inside. + So take care if the structure is big, because it will overload the memory. + + + """ + + ret = Phonons() + ret.structure = self.structure.copy() + ret.q_tot = [x.copy() for x in self.q_tot] + ret.nqirr = self.nqirr + ret.initialized = self.initialized + ret.q_stars = [] + for qstar in self.q_stars: + ret.q_stars.append([x.copy() for x in qstar]) + + ret.alat = self.alat + + for i, dyn in enumerate(self.dynmats): + ret.dynmats.append(dyn.copy()) + + if not self.effective_charges is None: + ret.effective_charges = self.effective_charges.copy() + if not self.raman_tensor is None: + ret.raman_tensor = self.raman_tensor.copy() + if not self.dielectric_tensor is None: + ret.dielectric_tensor = self.dielectric_tensor.copy() + + return ret + + def CheckCompatibility(self, other): + """ + This function checks the compatibility between two dynamical matrices. + The check includes the number of atoms and the atomic type. + Parameters + ---------- + - other : Phonons.Phonons() + The other dynamical matrix to check the compatibility. + + Returns + ------- + bool + """ + + # First of all, check if other is a dynamical matrix: + if type(other) != type(self): + return False + + # Check if the two structures shares the same number of atoms: + if self.structure.N_atoms != other.structure.N_atoms: + return False + + # Check if they belong to the same supercell: + if self.nqirr != other.nqirr: + return False + + # Then they are compatible + return True + + def GetUpsilonMatrix(self, T, min_w_threshold = __EPSILON_W__, debug = False, verbose = False, w_pols = None): + """ + This subroutine returns the inverse of the correlation matrix. + It is computed as following + + .. math:: + + \\Upsilon_{ab} = \\sqrt{M_aM_b}\\sum_\\mu \\frac{2\\omega_\\mu}{(1 + 2n_\\mu)\\hbar} e_\\mu^a e_\\mu^b + + It is used to compute the probability of a given atomic displacement. + The resulting matrix is a 3N x 3N one ordered as the dynamical matrix here. + The result is in bohr^-2, please be carefull. + + + Parameters + ---------- + T : float + Temperature of the calculation (Kelvin) + min_w_threshold: float + The threshold for frequency under which the modes are considered fixed and neglected (as Gamma acoustic modes). + w_pols: (list of w and pols) + If different from None, contains the frequencies and polarization vectors of this matrix. + Usefull to avoid multiple diagonalizations + Returns + ------- + ndarray(3N x3N), dtype = np.float64 + The inverse of the correlation matrix in the supercell. + N is the number of atoms in the supercell + """ + K_to_Ry=6.336857346553283e-06 + + if T < 0: + raise ValueError("Error, T must be posititive (or zero)") +# +# if self.nqirr != 1: +# raise ValueError("Error, this function yet not supports the supercells.") + + # We need frequencies and polarization vectors + if w_pols is None: + t1 = time.time() + w, pols = self.DiagonalizeSupercell() #self.DyagDinQ(iq) + t2 = time.time() + if verbose: + print("[GET UPS] Time to diagonalize the dynamical matrix {} s".format(t2-t1)) + else: + w = w_pols[0] + pols = w_pols[1] + # Transform the polarization vector into real one + #pols = np.real(pols) + + # Remove translations if we are at Gamma + type_cal = np.float64#np.complex128 + + super_struct = self.structure.generate_supercell(self.GetSupercell()) + t3 = time.time() + trans_mask = Methods.get_translations(pols, super_struct.get_masses_array()) + + t4 = time.time() + if verbose: + print("[GET UPS] Time to prepare the supercell structure: {} s".format(t3-t2)) + print("[GET UPS] Time to get translations: {} s".format(t4-t3)) + + # Exclude also other w = 0 modes + locked_original = np.abs(w) < min_w_threshold + if np.sum(locked_original.astype(int)) > np.sum(trans_mask.astype(int)): + trans_mask = locked_original + + no_trans = ~trans_mask + + # Discard translations + w = w[no_trans] + pols = pols[:, no_trans] + + + pols_conj = np.conj(pols) + + # Get the bosonic occupation number + nw = np.zeros(np.shape(w)) + if T < __EPSILON__: + nw = np.float64(0) + #print "T = 0" + else: + nw = 1. / (np.exp(w/(K_to_Ry * T)) -1) + #print "T > 0" + + # Compute the matrix + factor = 2 * w / (1. + 2*nw) + t1 = time.time() + + if verbose: + print("[GET UPS] Time to prepare the upsilon computation: {} s".format(t1-t3)) + + pols_mod = np.einsum("ab,b -> ab", pols_conj, factor) + Upsilon = pols.dot(pols_mod.T) + t2 = time.time() + + if verbose: + print("[GET UPS] Time to build the Upsilon matrix: {} s".format(t2 - t1)) + + if debug: + Upsilon_old = np.einsum( "i, ji, ki", factor, pols, pols_conj, dtype = type_cal) + assert np.max(np.abs(Upsilon - Upsilon_old)) < 1e-10, "Error, the new Upsilon calculation is wrong" + #_p1_, _p1vect_ = np.linalg.eigh(Upsilon) + #np.savetxt("factor.dat", np.transpose([factor * RY_TO_CM / 2, _p1_[3:]* RY_TO_CM / 2])) + + # Get the masses for the final multiplication + t1 = time.time() + mass_sqrt = np.sqrt(np.tile(super_struct.get_masses_array(), (3,1)).T.ravel()) + + #mass1 = np.zeros( 3*super_struct.N_atoms) + #for i in range(self.structure.N_atoms): + # mass1[ 3*i : 3*i + 3] = np.sqrt(self.structure.masses[ super_struct.atoms[i]]) + + _m1_ = np.tile(mass_sqrt, (3 * super_struct.N_atoms, 1)) + _m2_ = np.tile(mass_sqrt, (3 * super_struct.N_atoms, 1)).transpose() + + Upsilon *= _m1_ * _m2_ + t2 = time.time() + if verbose: + print("[GET UPS] Time to multiply the masses: {} s".format(t2 -t1)) + return Upsilon + + def GetProbability(self, displacement, T, upsilon_matrix = None, normalize = True, return_braket_vals = False): + """ + This function, given a particular displacement, returns the probability density + of finding the system around that displacement. This in practical computes + density matrix of the system in this way + + .. math:: + + \\rho(\\vec u) = \\sqrt{\\det(\\Upsilon / 2\\pi)} \\times \\exp\\left[-\\frac 12 \\sum_{ab} u_a \\Upsilon_{ab} u_b\\right] + + Where :math:`\\vec u` is the displacement, :math:`\\Upsilon` is the inverse of the covariant matrix + computed through the method self.GetUpsilonMatrix(). + + NOTE: I think there is an error in the implementation, in fact the Upsilon matrix is in bohr^-2 while displacements are in Angstrom. + + Parameters + ---------- + displacement : ndarray(3xN) or ndarray(N, 3) + The displacement on which you want to compute the probability. + It can be both an array of dimension 3 x self.structure.N_atoms or + a bidimensional array of structure (N_atoms, 3). + T : float + Temperature (Kelvin) for the calculation. It will be discarded + if a costum upsilon_matrix is provided. + upsilon_matrix : ndarray (3xN)^2, optional + If you have to compute many times this probability it can be convenient + to compute only once the upsilon matrix, and recycle it. If it is + None (as default) the upsilon matrix will be recomputed each time. + normalize : bool, optional + If false (default true) the probability distribution will not be normalized. + Useful to check if the exponential weight is the same after some manipulation + return_braket_vals : bool, optional + If true the value returned is only the braket followed by the + eigenvalues of the Upsilon matrix. + + Returns + ------- + float + The probability density of finding the system in the given displacement. + + """ + + + disp = np.zeros( 3 * self.structure.N_atoms) + + # Reshape the displacement + if len(np.shape(displacement)) == 2: + disp = displacement.reshape( len(disp)) + else: + disp = displacement + + + if upsilon_matrix is None: + upsilon_matrix = self.GetUpsilonMatrix(T) + + # Compute the braket + braket = np.einsum("i, ij, j", disp, upsilon_matrix, disp) + + # Get the normalization + vals = np.linalg.eigvals(upsilon_matrix) + vals = vals[np.argsort(np.abs(vals))] + + vals /= 2*np.pi + det = np.prod(vals[3:]) + + if return_braket_vals: + return braket, vals + + if normalize: + return np.sqrt(det) * np.exp(-braket) + else: + return np.exp(-braket) + + def GetRatioProbability(self, structure, T, dyn0, T0): + """ + IMPORTANCE SAMPLING + =================== + + This method compute the ration of the probability of extracting a given structure at temperature T + generated with dyn0 at T0 if the extraction is made with the self dynamical matrix. + + It is very usefull to perform importance sampling tests. + + .. math:: + + w(\\vec u) = \\frac{\\rho_{D_1}(\\vec u, T)}{\\rho_{D_0}(\\vec u, T_0)} + + Where :math:`D_1` is the current dynamical matrix, while :math:`D_0` is the + dynamical matrix that has been actually used to generate dyn0 + + TODO: It seems to return wrong results + NOTE: This subroutine seems to return fake results, please be carefull. + + Parameters + ---------- + structure : Structure.Structure() + The atomic structure generated according to dyn0 and T0 to evaluate the statistical significance ratio. + T : float + The target temperature + dyn0 : Phonons.Phonons() + The dynamical matrix used to generate the given structure. + T0 : float + The temperature used in the generation of the structure + + Results + ------- + float + The ratio :math:`w(\\vec u)` between the probabilities. + """ + K_to_Ry = 6.336857346553283e-06 + + if not self.CheckCompatibility(dyn0): + raise ValueError("Error, dyn0 and the current dyn are incompatible") + + # Get the displacement respect the two central atomic positions + disp1 = structure.get_displacement(self.structure) + disp0 = structure.get_displacement(dyn0.structure) + + # # TODO: Improve the method with a much more reliable one + # # In fact the ratio between them is much easier (this can be largely affected by rounding) + # #print "disp1:", disp1 + # #print "Ratio1:", self.GetProbability(disp1, T) , "Ratio2:", dyn0.GetProbability(disp0, T0) + + # b1, v1 = self.GetProbability(disp1, T, return_braket_vals = True) + # b2, v2 = dyn0.GetProbability(disp0, T0, return_braket_vals = True) + # new_v = v1[3:] / v2[3:] + # ret = np.exp(b2- b1) * np.prod(np.sqrt(new_v)) + + # #print "comparison:", ret, self.GetProbability(disp1, T) / dyn0.GetProbability(disp0, T0) + + + # This should be the fastest way + w1, pols1 = self.DyagDinQ(0) + w0, pols0 = dyn0.DyagDinQ(0) + + # Remove translations (acustic modes in gamma) + tmask1 = Methods.get_translations(pols1, self.structure.get_masses_array()) + tmask0 = Methods.get_translations(pols0, dyn0.structure.get_masses_array()) + + + w1 = w1[ ~tmask1 ] + pols1 = pols1[:, ~tmask1] + w0 = w0[~tmask0] + pols0 = pols0[:, ~tmask0] + + #print "TMASK:", tmask0, tmask1 + + + _m1_ = np.zeros(self.structure.N_atoms * 3) + _m0_ = np.zeros(dyn0.structure.N_atoms * 3) + + for i in range(self.structure.N_atoms): + _m1_[3*i : 3*i + 3] = self.structure.masses[self.structure.atoms[i]] + _m0_[3*i : 3*i + 3] = dyn0.structure.masses[dyn0.structure.atoms[i]] + + # Get the q values + q1 = np.real(np.einsum("i, ij, i", np.sqrt(_m1_), pols1, disp1.reshape(3 * self.structure.N_atoms))) + q0 = np.real(np.einsum("i, ij, i", np.sqrt(_m0_), pols0, disp0.reshape(3 * self.structure.N_atoms))) + + a1 = np.zeros(np.shape(w1)) + a0 = np.zeros(np.shape(w0)) + + if T == 0: + a1 = 1 / np.sqrt(2* w1) + else: + beta = 1 / (K_to_Ry*T) + a1 = 1 / np.sqrt( np.tanh(beta*w1 / 2) *2* w1) + + if T0 == 0: + a0 = 1 / np.sqrt(2* w0) + else: + beta = 1 / (K_to_Ry*T0) + a0 = 1 / np.sqrt( np.tanh(beta*w0 / 2) *2* w0) + + weight = np.prod((a0 / a1) * np.exp(- (q1 / (a1))**2 + (q0 / (a0))**2)) + + #print "COMPARISON:", ret, weight + + return weight + + def AdjustToNewCell(self, new_cell, symmetrize = True): + """ + ADJUST THE DYNAMICAL MATRIX IN A NEW CELL + ========================================= + + This method is used, if you want to change the unit cell, + to adjust the dynamical matrix, as the q points, in the new cell. + + The method forces also the symmetrization after the strain + + Parameters + ---------- + new_cell : ndarray(size=(3,3), dtype=np.float64) + The new unit cell + """ + + new_qs = symmetries.GetNewQFromUnitCell(self.structure.unit_cell, new_cell, self.q_tot) + + # Get the new structure + self.structure.change_unit_cell(new_cell) + + # Get the new q points + for iq, q in enumerate(new_qs): + self.q_tot[iq] = q + + count = 0 + for iqirr in range(len(self.q_stars)): + for iq in range(len(self.q_stars[iqirr])): + self.q_stars[iqirr][iq] = new_qs[count] + count += 1 + + self.AdjustQStar() + + # Force the symmetrization in the new structure + # NOTE: This will rise an exception if something is wrong + if symmetrize: + qe_sym = symmetries.QE_Symmetry(self.structure) + fcq = np.array(self.dynmats, dtype = np.complex128) + qe_sym.SymmetrizeFCQ(fcq, self.q_stars) + for iq, q in enumerate(self.q_tot): + self.dynmats[iq] = fcq[iq, :, :] + + def GetStrainMatrix(self, new_cell, T,threshold=1e-5,x_start = 0.01): + """ + STRAIN THE DYNAMICAL MATRIX + =========================== + + This function strains the dynamical matrix to fit into the new cell. + It will modify both the polarization vectors and the frequencies. + + The strain is performed on the covariance matrix. + + .. math:: + + {\\Upsilon_{axby}^{-1}}' = \\sum_{\\alpha,\\beta = x,y,z} \\varepsilon_{x\\alpha}\\varepsilon_{y\\beta}\\Upsilon_{a\\alpha b\\beta}^{-1} + + Then the new :math:`\\Upsilon^{-1}` matrix is diagonalized, eigenvalues and eigenvector are built, + and from them the new dynamical matrix is computed. + + NOTE: This works only at Gamma + I think there is a bug if T != 0 in the solver. BE CAREFULL! + + Parameters + ---------- + new_cell : ndarray 3x3 + The new unit cell after the strain. + T : float + The temperature of the strain (default 0) + threshold : float + The threshold for the convergence of the newton algorithm to find the + frequencies given the eigenvalues of the upsilon matrix. + x_start : float + The initial guess for the newton algorithm. + + Results + ------- + dyn : Phonons.Phonons() + A new dynamical matrix strained. Note, the current dynamical matrix will not be modified. + """ + K_to_Ry=6.336857346553283e-06 + + if T < 0: + raise ValueError("Error, the temperature must be positive.") + + # Get the polarization vectors and frequencies + w, pol_vects = self.DyagDinQ(0) + + n_modes = len(w) + + # Strain the polarization vectors + new_vect = np.zeros(np.shape(pol_vects)) + for i in range(3, n_modes): + for j in range(self.structure.N_atoms): + # Get the crystal representation of the polarization vector + cov_coord = Methods.covariant_coordinates(self.structure.unit_cell, + pol_vects[3*j: 3*(j+1), i]) + + # Transform the crystal representation into the cartesian in the new cell + new_vect[3*j: 3*(j+1), i] = np.einsum("ij, i", new_cell, cov_coord) + + # Now prepare the new Covariance Matrix + factor = np.zeros(n_modes) + if T == 0: + factor[3:] = 1 / (2. * w[3:]) + else: + n = 1 / (np.exp(w[3:] / (K_to_Ry * T)) - 1) + factor[3:] = (1. + 2*n) / (2*w[3:]) + + cmat = np.einsum("i, hi,ki", factor, new_vect, new_vect) + + # Diagonalize once again + newf, new_pols = np.linalg.eig(cmat) +# +# # DEBUG PRINT +# prova1 = np.sort(newf) +# prova2 = np.sort(factor) +# for i in range(n_modes): +# print "New: %e | Old: %e" % (prova1[i], prova2[i]) +# + + # Sort the results + sort_mask = np.argsort(newf) + newf = newf[sort_mask] + new_pols = new_pols[:, sort_mask] + + # Initialize the array of the new frequencies + new_w = np.zeros(n_modes) + new_w[3:] = 1. / (2 * newf[3:]) + + + + # If the temperature is different from zero, we must obtain a new frequency + # using a numerical nonlinear solver + if T != 0: + + #def opt_func(w): + # ret = 2*w*newf - 1./( 1 - np.exp(w / (K_to_Ry * T))) + # if not np.shape(w): + # if np.abs(w) < __EPSILON__: + # return 0 + # else: + # ret[np.abs(w) < __EPSILON__] = 0 + # return ret + + + + + + #try: + # for k in range(len(new_w)): + # def new_func(x): + # _x_ = np.ones(np.shape(newf)) * x + # return opt_func(_x_)[k] + # if np.abs(new_w[k]) < __EPSILON__: + # continue + # new_w[k] = scipy.optimize.anderson(new_func, new_w[k], verbose = True) + + for k in range(3,36): + def g(w): + f1= 2*w*newf[k]-1/np.tanh(w*0.5/(K_to_Ry*T)) + return f1 + + def g_prime(w): + f2=2*newf[k]+0.5/(K_to_Ry*T*(np.sinh(w*0.5/(K_to_Ry*T)))**2) + return f2 + + x_old=x_start + while True : + x_new=x_old-g(x_old)/g_prime(x_old) + if np.abs(g(x_new)) < threshold : + break + else: + x_old=x_new + new_w[k]=x_new + + #except ValueError: + # print "Error, Nan encountered during the scipy minimization (T != 0)" + # print "Starting w value:" + # print new_w + # print "new_f value:" + # print newf + # print "T:", T + # raise ValueError("Aborting, error in scipy minimization.") + + + + # + # print "Compare frequencies:" +# for i in range(0,n_modes): +# print "New: %e | Old: %e" % (new_w[i], w[i]) + + + # Sort once again + sort_mask = np.argsort(new_w) + new_w = new_w[sort_mask] + new_pols = new_pols[:, sort_mask] + + + # Now we can rebuild the dynamical matrix + out_dyn = self.Copy() + out_dyn.structure.change_unit_cell(new_cell) + out_dyn.dynmats[0] = np.einsum("i, hi, ki", new_w**2, new_pols, new_pols) + + # Get the masses for the final multiplication + mass1 = np.zeros( 3*self.structure.N_atoms) + for i in range(self.structure.N_atoms): + mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] + + _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) + _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() + + out_dyn.dynmats[0] *= np.sqrt( _m1_ * _m2_ ) + + return out_dyn + + + def save_qe(self, filename, full_name = False): + """ + SAVE THE DYNMAT + =============== + + This subroutine saves the dynamical matrix in the quantum espresso file format. + The dynmat is the force constant matrix in Ry units. + + .. math:: + + \\Phi_{ab} = \\sum_\\mu \\omega_\\mu^2 e_\\mu^a e_\\mu^b \\sqrt{M_a M_b} + + Where :math:`\\Phi_{ab}` is the force constant matrix between the a-b atoms (also cartesian + indices), :math:`\\omega_\\mu` is the phonon frequency and :math:`e_\\mu` is the + polarization vector. + + + Parameters + ---------- + filename : string + The path in which the quantum espresso dynamical matrix will be written. + full_name : bool + If true only the gamma matrix will be saved, and the irreducible q + point index will not be appended. Otherwise all the file filenameIQ + where IQ is an integer between 0 and self.nqirr will be generated. + filename0 will contain all the information about the Q points and the supercell. + """ + #A_TO_BOHR = 1.889725989 + #RyToCm=109737.37595 + RyToTHz=3289.84377 + + # Check if all the dynamical matrix must be saved, or only the + nqirr = self.nqirr + if full_name: + nqirr = 1 + + # The following counter counts the total number of q points + count_q = 0 + for iq in range(nqirr): + # Prepare the file name appending the q point index + fname = filename + if not full_name: + fname += str(iq+1) + + # Open the file + fp = open(fname, "w") + fp.write("Dynamical matrix file\n") + + # Get the different number of types + types = [] + n_atoms = self.structure.N_atoms + for i in range(n_atoms): + if not self.structure.atoms[i] in types: + types.append(self.structure.atoms[i]) + n_types = len(types) + + # Assign an integer for each atomic species + itau = {} + for i in range(n_types): + itau[types[i]] = i +1 + + # Write the comment line + fp.write("File generated with the CellConstructor by Lorenzo Monacelli\n") + fp.write("%d %d %d %22.16f %22.16f %22.16f %22.16f %22.16f %22.16f\n" % + (n_types, n_atoms, 0, self.alat * A_TO_BOHR, 0, 0, 0, 0, 0) ) + + # Write the basis vector + fp.write("Basis vectors\n") + # Get the unit cell + for i in range(3): + fp.write(" ".join("%22.16f" % x for x in self.structure.unit_cell[i,:] / self.alat) + "\n") + + # Set the atom types and masses + for i in range(n_types): + fp.write("\t%d '%s ' %22.16f\n" % (i +1, types[i], self.structure.masses[types[i]])) + + # Setup the atomic structure + for i in range(n_atoms): + # Convert the coordinates in alat + coords = self.structure.coords[i,:] / self.alat + fp.write("%5d %5d %22.16f %22.16f %22.16f\n" % + (i +1, itau[self.structure.atoms[i]], + coords[0], coords[1], coords[2])) + + # Iterate over all the q points in the star + nqstar = len(self.q_stars[iq]) + q_star = self.q_stars[iq] #* self.alat + + # Store the first matrix index of the star + # This will be used to dyagonalize the matrix in the end of the file + dyag_q_index = count_q + + for jq in range(nqstar): + # Here the dynamical matrix starts + fp.write("\n") + fp.write(" Dynamical Matrix in cartesian axes\n") + fp.write("\n") + fp.write(" q = ( %18.12f %18.12f %18.12f )\n" % + (q_star[jq][0] * self.alat , q_star[jq][1]*self.alat, q_star[jq][2]*self.alat )) + fp.write("\n") + + # Now print the dynamical matrix + for i in range(n_atoms): + for j in range(n_atoms): + # Write the atoms + fp.write("%5d%5d\n" % (i + 1, j + 1)) + for x in range(3): + line = "%23.16f%23.16f %23.16f%23.16f %23.16f%23.16f" % \ + ( np.real(self.dynmats[count_q][3*i + x, 3*j]), np.imag(self.dynmats[count_q][3*i + x, 3*j]), + np.real(self.dynmats[count_q][3*i + x, 3*j+1]), np.imag(self.dynmats[count_q][3*i+x, 3*j+1]), + np.real(self.dynmats[count_q][3*i + x, 3*j+2]), np.imag(self.dynmats[count_q][3*i+x, 3*j+2]) ) + + fp.write(line + "\n") + + # Go to the next q point + count_q += 1 + + # Here save the Dielectric tensor, the effective charges and the Raman response + if not self.dielectric_tensor is None: + fp.write("\n") + fp.write(" Dielectric Tensor:\n") + fp.write("\n") + for i in range(3): + fp.write("{:24.12f} {:24.12f} {:24.12f}\n".format(*list(self.dielectric_tensor[i,:]))) + + if not self.effective_charges is None: + fp.write("\n") + fp.write(" Effective Charges E-U: Z_{alpha}{s,beta}\n") + fp.write("\n") + for i in range(self.structure.N_atoms): + fp.write(" atom # {:5d}\n".format(i+1)) + for j in range(3): + fp.write("{:24.12e} {:24.12e} {:24.12e}\n".format(*list(self.effective_charges[i, j, :]))) + + if not self.raman_tensor is None: + fp.write("\n") + fp.write(" Raman tensor (A^2)\n") + fp.write("\n") + for i_atm in range(self.structure.N_atoms): + for j_pol in range(3): + fp.write(" atom # {:5d} pol. {:2d}\n".format(i_atm+1, j_pol+1)) + for k in range(3): + fp.write("{:24.12e} {:24.12e} {:24.12e}\n".format(*list(self.raman_tensor[k, :, 3*i_atm + j_pol]))) + + + + # Print the diagnoalization of the matrix + fp.write("\n") + fp.write(" Diagonalizing the dynamical matrix\n") + fp.write("\n") + fp.write(" q = ( %18.12f %18.12f %18.12f )\n" % + (q_star[0][0] *self.alat , q_star[0][1] *self.alat, q_star[0][2] *self.alat)) + fp.write("\n") + fp.write("*" * 75 + "\n") + + # Diagonalize the dynamical matrix + freqs, pol_vects = self.DyagDinQ(dyag_q_index) + + # Compute the displacemets from the polarization vectors + _m_ = self.structure.get_masses_array() + _m_ = np.tile(_m_, (3,1)).T.ravel() + + # Compute the atomic displacements + atomic_disp = np.einsum("ab, a -> ab", pol_vects, 1 / np.sqrt(_m_) ) + # Normalize the displacements + atomic_disp[:,:] /= np.tile( np.sqrt(np.sum(np.abs(atomic_disp)**2, axis = 0)), (self.structure.N_atoms * 3, 1)) + + nmodes = len(freqs) + for mu in range(nmodes): + # Print the frequency + fp.write("%7s (%5d) = %14.8f [THz] = %14.8f [cm-1]\n" % + ("freq", mu+1, freqs[mu] * RyToTHz, freqs[mu] * RY_TO_CM)) + + # Print the polarization vectors + for i in range(n_atoms): + fp.write("( %10.6f%10.6f %10.6f%10.6f %10.6f%10.6f )\n" % + (np.real(atomic_disp[3*i, mu]), np.imag(atomic_disp[3*i,mu]), + np.real(atomic_disp[3*i+1, mu]), np.imag(atomic_disp[3*i+1,mu]), + np.real(atomic_disp[3*i+2, mu]), np.imag(atomic_disp[3*i+1,mu]))) + fp.write("*" * 75 + "\n") + fp.close() + + def save_phononpy(self, *args, **kwargs): + "Mapping to save_phonopy" + warnings.warn("[DEPRECATION WARNING] save_phononpy is deprecated: use save_phonopy instead.") + self.save_phonopy(*args, **kwargs) + + def save_phonopy(self, path = ".", supercell_size = None): + """ + EXPORT THE DYN IN THE PHONONPY FORMAT + ===================================== + + This tool export the dynamical matrix into the PHONONPY plain text format. + We save them in Ry/bohr^2, as the quantum espresso format. Please, remember + this when using Phononpy for the conversion factors. + + It will create a file called FORCE_CONSTANTS, one called unitcell.in + with the info on the structure + + Parameters + ---------- + path: str + Path to the directory in which the FORCE_CONSTANTS and unitcell.in files are created. + supercell_size : list of 3 + The supercell that defines the dynamical matrix, note phononpy + works in the supercell. If none, it is inferred from the q points + + + """ + if supercell_size is None: + supercell_size = self.GetSupercell() + + # Save it into the phononpy in the supercell + superdyn = self.GenerateSupercellDyn(supercell_size) + filename = os.path.join(path, "FORCE_CONSTANTS") + + nat_sc = superdyn.structure.N_atoms + nat = self.structure.N_atoms + + # This is the text to be written + lines = [] + lines.append("%d %d\n" % (nat_sc, nat_sc)) + for i in range(nat_sc): + for j in range(nat_sc): + lines.append("%4d\t%4d\n" % (i, j)) + mat = np.real(superdyn.dynmats[0][3*i : 3*i+ 3, 3*j: 3*j+3]) + lines.append("%16.8f %16.8f %16.8f\n" % (mat[0,0], mat[0,1], mat[0,2])) + lines.append("%16.8f %16.8f %16.8f\n" % (mat[1,0], mat[1,1], mat[1,2])) + lines.append("%16.8f %16.8f %16.8f\n" % (mat[2,0], mat[2,1], mat[2,2])) + + # Write to the file + f = open(filename, "w") + f.writelines(lines) + f.close() + + # Produce the unit cell + lines = [] + lines.append("&system\n") + lines.append("ibrav = 0\n") + lines.append("celldm(1) = 1.889726125836928\n") + lines.append("nat = %d\n" % self.structure.N_atoms) + + typs = self.structure.masses.keys() + lines.append("ntyp = %d\n" % len(typs)) + lines.append("&end\n") + + # Write the atomic species + lines.append("ATOMIC_SPECIES\n") + for i in typs: + m = self.structure.masses[i] + lines.append("%s %16.8f XXX\n" % (i, m / 911.444243096)) + + # Write the unit cell + lines.append("CELL_PARAMETERS alat\n") + for i in range(3): + uc_v = self.structure.unit_cell[i, :] #* 1.889726125836928 + lines.append("%16.8f %16.8f %16.8f\n" % (uc_v[0], uc_v[1], uc_v[2])) + + lines.append("ATOMIC_POSITIONS crystal\n") + for i in range(nat): + atm = self.structure.atoms[i] + cov_vect = Methods.covariant_coordinates(self.structure.unit_cell, self.structure.coords[i, :]) + lines.append("%s %16.8f %16.8f %16.8f\n" % (atm, cov_vect[0], cov_vect[1], cov_vect[2])) + + + f = open(os.path.join(path, "unitcell.in"), "w") + f.writelines(lines) + f.close() + + def load_phonopy(self, yaml_filename = "phonopy.yaml", fc_filename = None): + """ + LOAD FROM PHONOPY FORCE CONSTANTS + ================================= + + This subroutine load the dynamical matrix from the phonopy FORCE_CONSTANT file. + It needs two files: the file with the structure information, + and the file with the force constant matrix. + + Parameters + ---------- + yaml_filename : string + Path to the YAML file, this contains the info of the structure and the supercell. + fc_filename: string + Path to the FORCE_CONSTANTS file. If None, a file called FORCE_CONSTANTS in the same directory + as phonopy.yaml will be looked for. + """ + + unit_cell = np.zeros((3,3), dtype = np.double) + supercell = np.zeros(3, dtype = np.intc) + coords = [] + atoms = [] + masses = {} + + superstruct = None + unit_cell_itau = [] + + with open(yaml_filename, "r") as fp: + + read_primitive_cell = False + read_coord = False + read_lattice = False + read_supercell = False + read_superstruct = False + counter = 0 + for line in fp.readlines(): + + line = line.strip() + if not line: + continue + + data = line.replace(",","").split() + + if line == "supercell_matrix:": + read_supercell = True + counter = 0 + continue + + if read_supercell and len(data) == 6: + supercell[counter] = int(data[2 + counter]) + counter += 1 + + if counter == 3: + counter = 0 + read_supercell = False + + if line == "unit_cell:": + read_primitive_cell = True + continue + + if line == "lattice:": + read_lattice = True + counter = 0 + continue + + if read_lattice and len(data) == 8: + unit_cell[counter, :] = [float(data[x]) for x in range(2, 5)] + counter += 1 + if counter == 3: + counter = 0 + read_lattice = False + + if line == "points:": + read_coord = True + atoms = [] + coords = [] + continue + + if read_coord: + if "symbol" in line: + atoms.append(data[2]) + if "coordinates" in line: + vector = np.array([float(data[x]) for x in range(2, 5)]) + coords.append(Methods.cryst_to_cart(unit_cell, vector)) + if "mass" in line: + if not atoms[-1] in masses: + masses[atoms[-1]] = float(data[1]) / MASS_RY_TO_UMA + if "reduced_to" in line: + if read_primitive_cell: + unit_cell_itau.append(int(data[1]) - 1) + + if "supercell" in line: + if read_primitive_cell: + self.structure = Structure.Structure(len(atoms)) + self.structure.atoms = atoms + self.structure.coords[:,:] = np.array(coords) * BOHR_TO_ANGSTROM + self.structure.masses = masses + self.structure.has_unit_cell = True + self.structure.unit_cell = unit_cell.copy() * BOHR_TO_ANGSTROM + read_coord = False + read_lattice = False + read_primitive_cell = False + read_superstruct = True + continue + + # Now create the superstructure + if read_superstruct: + superstruct = Structure.Structure(len(atoms)) + superstruct.atoms = atoms + superstruct.coords[:,:] = np.array(coords) * BOHR_TO_ANGSTROM + superstruct.masses = masses + superstruct.unit_cell = unit_cell.copy() * BOHR_TO_ANGSTROM + superstruct.has_unit_cell = True + + # Get the Equivalent atoms in the unit cell + itau = superstruct.get_itau(self.structure) - 1 + + # Now load the Force constant matrix + if fc_filename is None: + fc_filename = os.path.join(os.path.dirname(yaml_filename), "FORCE_CONSTANTS") + + fc = np.zeros( (superstruct.N_atoms * 3, superstruct.N_atoms * 3), dtype = np.double) + FC_TMP = np.zeros((3,3), dtype = np.double) + + with open(fc_filename, "r") as fp: + + x = 0 + y = 0 + counter = 0 + FC = np.zeros((3,3), dtype = np.double) + for i, line in enumerate(fp.readlines()): + line = line.strip() + data = line.split() + + + if i == 0: + nat_prim = int(data[0]) + nat_tot = int(data[1]) + continue + + iteration = (i - 1) // 4 + counter = (i-1) % 4 + x = iteration // nat_tot + y = iteration % nat_tot + + if counter > 0: + for new_x in np.arange(superstruct.N_atoms)[itau == x]: + fc[3 * new_x + counter -1, 3*y: 3*y + 3] = [float(fx) for fx in data] + fc[3*y: 3*y + 3, 3 * new_x + counter -1] = [float(fx) for fx in data] + # counter += 1 + + # if counter == 3: + # # Save the FC in the correct blocks + # counter = 0 + # for ia, ib in blocks: + # fc[3*ia : 3*ia + 3, 3*ib: 3*ib + 3] = FC_TMP + # fc[3*ib : 3*ib + 3, 3*ia: 3*ia + 3] = FC_TMP + + + # if len(data) == 2: + # #x = int(data[0]) - 1 + # #y = int(data[1]) - 1 + # #x = itau[x] + # counter = 0 + + # # Get the blocks + # blocks = [] + # #print(x, y) + # DR = self.structure.coords[x, :] - superstruct.coords[y,:] + # for ia in range(superstruct.N_atoms): + # if unit_cell_itau[itau[ia]] != x: + # continue + # for ib in range(superstruct.N_atoms): + # if unit_cell_itau[itau[ib]] != unit_cell_itau[itau[y]]: + # continue + + # # Check if the two ia and ib are the correct block + # delta_r = superstruct.coords[ia, :] - superstruct.coords[ib, :] + # dist = Methods.get_closest_vector(superstruct.unit_cell, DR - delta_r) + # if np.linalg.norm(dist) < __EPSILON__: + # blocks.append((ia,ib)) + + # elif len(data) == 3: + # FC_TMP[counter, :] = [float(fx) for fx in data] + # counter += 1 + + # if counter == 3: + # # Save the FC in the correct blocks + # counter = 0 + # for ia, ib in blocks: + # fc[3*ia : 3*ia + 3, 3*ib: 3*ib + 3] = FC_TMP + # fc[3*ib : 3*ib + 3, 3*ia: 3*ia + 3] = FC_TMP + + + # Now transform back in real space + q_tot = symmetries.GetQGrid(self.structure.unit_cell, supercell) + dynq = GetDynQFromFCSupercell(fc, np.array(q_tot), self.structure, superstruct, itau) + self.dynmats = [None] * len(q_tot) + self.q_tot = q_tot + self.q_stars = [q_tot] + + for iq in range(len(q_tot)): + self.dynmats[iq] = dynq[iq, :, :] + + self.AdjustQStar() + + + def ForcePositiveDefinite(self): + """ + FORCE TO BE POSITIVE DEFINITE + ============================= + + This method force the matrix to be positive defined. + Usefull if you want to start with a matrix for a SCHA calculation. + + It will take the Dynamical matrix and rebuild it as + + .. math:: + + \\Phi'_{ab} = \\sqrt{M_aM_b}\sum_{\mu} |\omega_\mu^2| e_\\mu^a e_\\mu^b + + + In this way the dynamical matrix will be always positive definite. + """ + + # Prepare the masses matrix + mass1 = np.zeros( 3*self.structure.N_atoms) + for i in range(self.structure.N_atoms): + mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] + + _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) + _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() + + for iq in range(len(self.dynmats)): + # Diagonalize the matrix + w, pols = self.DyagDinQ(iq) + + matrix = np.einsum("i, ji, ki", w**2, pols, np.conj(pols)) * np.sqrt(_m1_ * _m2_) + self.dynmats[iq] = matrix + + + def ForcePositiveDefinite_2(self): + """ + FORCE TO BE POSITIVE DEFINITE + ============================= + + This method force the matrix to be positive defined. + Usefull if you want to start with a matrix for a SCHA calculation. + + It will take the Dynamical matrix and rebuild it as + + .. math:: + + \\Phi'_{ab} = \\sqrt{M_aM_b}\sum_{\mu} (\omega_\mu + \\min_\\mu \\omega)^2 e_\\mu^a e_\\mu^b + + + In this way the dynamical matrix will be always positive definite. + """ + + # Prepare the masses matrix + mass1 = np.zeros( 3*self.structure.N_atoms) + for i in range(self.structure.N_atoms): + mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] + + _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) + _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() + + + numq=len(self.dynmats) + w=np.zeros((numq,3*self.structure.N_atoms), dtype = np.float64) + pols=np.zeros((numq,3*self.structure.N_atoms,3*self.structure.N_atoms), dtype = np.complex128 ) + + for iq in range(numq): + # Diagonalize the matrix + w[iq,:], pols[iq,:,:] = self.DyagDinQ(iq) + + + fact=np.amin(w) + + if fact < 0.0 : + w+=np.abs(fact)*0.1 + + for iq in range(numq): + v=pols[iq,:,:] + fr=w[iq,:] + matrix = np.einsum("i, ji, ki", fr**2, v, np.conj(v)) * np.sqrt(_m1_ * _m2_) + self.dynmats[iq] = matrix + + + + def GetRamanResponce(self, pol_in, pol_out, T = 0): + r""" + RAMAN RESPONSE + ============== + + Evaluate the raman response using the Mauri-Lazzeri equation. + This subroutine needs the Raman tensor to be defined, and computes the intensity for each mode. + It returns a list of intensity associated to each mode. + + .. math:: + + I_{\nu} = \left| \sum_{xy} \epsilon_x^{(1)} A^\nu_{xy} \epsilon_y^{(2)}\right|^2 \frac{n_\nu + 1}{\omega_\nu} + + Where :math:`\epsilon` are the polarization vectors of the incoming/outcoming light, :math:`n_\nu` is the bosonic + occupation number associated to the :math:`\nu` mode, and :math:`A^\nu_{xy}` is the Raman tensor in the mode rapresentation + + Parameters + ---------- + pol_in : ndarray 3 + The polarization versor of the incominc electric field + pol_out : ndarray 3 + The polarization versor of the outcoming electric field + T : float + The tempearture of the calculation + + Results + ------- + ndarray (nmodes) + Intensity for each mode of the current dynamical matrix. + """ + + K_to_Ry=6.336857346553283e-06 + + + if self.raman_tensor is None: + raise ValueError("Error, to get the raman responce the raman tensor must be defined") + + w, pol_vects = self.DyagDinQ(0) + + # Get the mass array + _m_ = np.zeros( 3*self.structure.N_atoms) + for i in range(self.structure.N_atoms): + _m_[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] + + # Apply translation + trans = Methods.get_translations(pol_vects, self.structure.get_masses_array()) + pol_vects[:, trans] = 0 + + # The super sum + #print np.shape(self.raman_tensor), np.shape(pol_vects), np.shape(_m_), np.shape(pol_in), np.shape(pol_out) + I = np.einsum("ijk, kl, k, i, j", self.raman_tensor, pol_vects, 1/np.sqrt(_m_), pol_in, pol_out) + + # Get the bosonic occupation number + n = np.zeros(len(w)) + if T > 0: + beta = 1 / (K_to_Ry*T) + n = 1 / (np.exp(beta * w) - 1.) + + return np.abs(I**2) * (1. + n) / w + + def GetIRIntensities(self): + """ + GET THE IR INTENSITIES + ====================== + + This function uses the effective charges to compute the infrared responce. + + A list of value is returned, at each index the IR intensity of the + relative mode. + """ + + if self.effective_charges is None: + raise ValueError("Error, I cannot compute IR intensities without effective charges") + + w, pols = self.DyagDinQ(0) + m = self.structure.get_masses_array() + + + # Get the eigendisplacement z + nat3, nmodes = np.shape(pols) + z = np.zeros( (nmodes, self.structure.N_atoms, 3), dtype = np.float64) + for i in range(self.structure.N_atoms): + z[:, i, :] = pols[3*i: 3*(i+1), :].T / np.sqrt(m[i]) + + # Get the I_mu,i where mu is the mode and i is the polarization of the light + I = np.einsum("cbd, acd->ab", self.effective_charges, z) + # Average over polarizations + I = np.sum( I*I, axis = 1) * 2 + + return I + + def GetIRActivityVector(self): + """ + GET THE IR VECTOR + ================= + + This vector returns the activity of the infrared mode. + It is the matrix element to compute the responce function of the IR experiment. + + Results + ------- + v_ir : ndarray(size = (3, 3*natoms), dtype = np.double) + The ir activity amplitude for each polarization mode, for each polarizations of the incoming field + """ + + if self.effective_charges is None: + raise ValueError("Error, I cannot compute IR intensities without effective charges") + + w, pols = self.DyagDinQ(0) + m = self.structure.get_masses_array() + + # Get the eigendisplacement z + nat3, nmodes = np.shape(pols) + z = np.zeros( (nmodes, self.structure.N_atoms, 3), dtype = np.float64) + for i in range(self.structure.N_atoms): + z[:, i, :] = pols[3*i: 3*(i+1), :].T / np.sqrt(m[i]) + + # Get the I_mu,i where mu is the mode and i is the polarization of the light + v_ir = np.einsum("cbd, acd->ba", self.effective_charges, z) + + return v_ir + + + def GetRamanVector(self, pol_in, pol_out): + r""" + GET THE RAMAN VECTOR + ==================== + + Get the Raman vector. It is the vector obtained from the Raman Tensor: + + .. math:: + + v_\nu = \sum_{xy} \epsilon^{(1)}_x \epsilon_y^{(2)} A^{\nu}_{xy} + + This is defined in real space. + + Parameters + ---------- + pol_in : ndarray(size = 3) + Incoming polarization + pol_out : ndarray(size = 3) + Outcoming polarization + Results + ------- + vnu : ndarray(size = 3*nat) + The raman intensity vector along each atomic displacement. + """ + + if self.raman_tensor is None: + raise ImportError("Error, the raman tensor is not defined.") + + v = np.einsum("ija, i, j", self.raman_tensor, pol_in, pol_out) + + # Take out the translations from v + #t1 = np.tile(np.array([1,0,0], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() + #t2 = np.tile(np.array([0,1,0], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() + #t3 = np.tile(np.array([0,0,1], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() + + #v -= t1.dot(v) + #v -= t2.dot(v) + #v -= t3.dot(v) + + nat = np.shape(self.raman_tensor)[-1] // 3 + dtype = type(v[0]) + + trans = np.eye(3*nat, dtype = dtype) + for i in range(3): + v1 = np.zeros(3*nat, dtype = dtype) + v1[3*np.arange(nat)+i] = 1 + v1 /= np.sqrt(v1.dot(v1)) + + trans -= np.outer(v1,v1) + + return trans.dot(v) + + def GetRamanActive(self, use_spglib = False): + """ + This simple subroutines tries to guess by symmetry analisys which mode is active or not. + If a raman tensor is present, it will be used to test the activity, otherwise, a random one will + be generated + + Parameters + ---------- + use_spblib: bool + If True the spglib library is used to initialize symmetries. + Usefull if the phonon matrix is in a super cell. + + Results + ------- + raman_activity_mask : ndarray(size = (3*nat), dtype = bool) + A mask that is False or True if a mode in the unit cell is Raman-active or not. + """ + + there_is_raman_tensor = True + if self.raman_tensor is None: + there_is_raman_tensor = False + self.raman_tensor = np.zeros((3,3, 3*self.structure.N_atoms), dtype = np.double) + self.raman_tensor[:,:,:] = np.random.uniform( size = self.raman_tensor.shape) + + # Get the symmetries + qe_sym = symmetries.QE_Symmetry(self.structure) + if use_spglib: + qe_sym.SetupFromSPGLIB() + else: + qe_sym.SetupQPoint() + + # Symmetrize the effective charges + qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) + + # Save a debugging one + self.save_qe("Raman") + + # Simulate the Raman signal for all possible incoming and outcoming polarizations + res = np.zeros(self.structure.N_atoms * 3, dtype = np.double) + for i, j in itertools.product(range(3) , range(3)): + pol_in = np.zeros(3) + pol_in[i] = 1 + pol_out = np.zeros(3) + pol_out[j] = 1 + + res += self.GetRamanResponce(pol_in, pol_out) + + print("total_raman_res:", res) + + is_raman_active = res > 1e-5 + + # Delete the random raman tensor if any + if not there_is_raman_tensor: + self.raman_tensor = None + + return is_raman_active + + + + def GenerateSupercellDyn(self, supercell_size, img_thr = 1e-6): + """ + GENERATE SUPERCEL DYN + ===================== + + This method returns a Phonon structure as it was computed directly in the supercell. + + + NOTE: For now this neglects bohr effective charges + + Parameters + ---------- + supercell_size : array int (size=3) + the dimension of the cell on which you want to generate the new + Phonon + + Results + ------- + dyn_supercell : Phonons() + A Phonons class of the supercell + + """ + # First check if the q vectors are compatible with the supercell + if not symmetries.CheckSupercellQ(self.structure.unit_cell, supercell_size, self.q_tot): + print("Q points:", self.q_tot) + print("Supercell size:", supercell_size) + print("Unit cell:", self.structure.unit_cell) + raise ValueError("Error, the list of q point does not match the given supercell.") + + super_struct = self.structure.generate_supercell(supercell_size) + + dyn_supercell = Phonons(super_struct, nqirr = 1, force_real = True) + + dyn_supercell.dynmats[0] = self.GetRealSpaceFC(supercell_size, img_thr = img_thr) + + return dyn_supercell + + + def GetMatrixCFFT(self): + """ + Generate the dynamical matrix ready for the Fast Fourier Transform. + This is an alternative way to go in real space. + NOTE: Use only for debug purpouses + """ + + s1, s2, s3 = self.GetSupercell() + nat = self.structure.N_atoms + output_dyn = np.zeros((s1, s2, s3, 3 * nat, 3 * nat), dtype = np.complex128, order = "F") + + super_struct = self.structure.generate_supercell((s1,s2,s3)) + bg = super_struct.get_reciprocal_vectors() / (2 * np.pi) + + for iq, q in enumerate(self.q_tot): + x_vect = Methods.covariant_coordinates(bg, q) + x1 = int((x_vect[0] + s1) % s1 + .5) + x2 = int((x_vect[1] + s2) % s2 + .5) + x3 = int((x_vect[2] + s3) % s3 + .5) + + #print("Q = ", q, "| xv:", x_vect, "x = ", x1, x2,x3) + + output_dyn[x1, x2, x3, :, :] = self.dynmats[iq] + + return output_dyn + + + def ExtractRandomStructures(self, size=1, T=0, isolate_atoms = [], project_on_vectors = None, + lock_low_w = False, remove_non_isolated_atoms = False, sobol = False, sobol_scramble = False, sobol_scatter = 0.0): + """ + EXTRACT RANDOM STRUCTURES + ========================= + + This method is used to extract a pool of random structures according to the current dinamical matrix. + + Parameters + ---------- + size : int + The number of structures to be generated + T : float + The temperature for the generation of the ensemble + isolate_atoms : list, optional + A list of the atom index. Only the atoms present in this list will be randomize. + If remove_non_isolated_atoms is True, then the output structures contain only non isolated atoms. + project_on_vectors : ndarray + Vectors in Cartesian Space on which the random displacements are projected. Usefull if you want to remove some + mode or atomic motion. + lock_low_w : bool + If True, frequencies below __EPSILON_W__ are fixed. + remove_non_isolated_atoms : bool + If true it removes atoms non included in the isolate_atoms list (if not empty) + sobol : bool, optional (Default = False) + Defines if the calculation uses random Gaussian generator or Sobol Gaussian generator. + sobol_scramble : bool, optional (Default = False) + Set the optional scrambling of the generated numbers taken from the Sobol sequence. + sobol_scatter : real (0.0 to 1) (Deafault = 0.0) + Set the scatter parameter to displace the Sobol positions randommly. + + Returns + ------- + list + A list of Structure.Structure() + """ + K_to_Ry=6.336857346553283e-06 + + def sobol_norm_rand(size,n_modes,scramble=False,sobol_salt=0.0): # **** Diegom_test **** adding random 'salt' + Sobol = Moro() + #data = Sobol.sobol_modes(size,n_modes,scramble=scramble) +# If n_modes is bigger than 21201 comment upper line and uncomment lower line. This will be a strange ocurrence due to the fact that 21201 vibrational eigenmodes implies a dinamical matrix with more than 449482401 elements. + data = Sobol.sobol_big(size,n_modes,scramble=scramble) + if (sobol_salt!=0.0): + for i in range(size): + for j in range(n_modes): + data[i][j]=data[i][j]+(np.random.rand()-0.5)*sobol_salt + return data + + # Check if isolate atoms is good + if len(isolate_atoms): + if np.max(isolate_atoms) >= self.structure.N_atoms: + raise ValueError("Error, index in isolate_atoms out of boundary") + + # Now extract the values + ws, pol_vects = self.DiagonalizeSupercell() + super_structure, itau = self.structure.generate_supercell(self.GetSupercell(), get_itau= True) + + # get the new isolated_atoms in the supercell + if len(isolate_atoms): + new_isolate_atoms = [] + for i, it in enumerate(itau): + if it in isolate_atoms: + new_isolate_atoms.append(i) + + + # Remove translations + trans_mask = Methods.get_translations(pol_vects, super_structure.get_masses_array()) + + # Exclude also other w = 0 modes + if lock_low_w: + locked_original = np.abs(ws) < __EPSILON_W__ + if np.sum(locked_original.astype(int)) > np.sum(trans_mask.astype(int)): + trans_mask = locked_original + + ws = ws[~trans_mask] + pol_vects = pol_vects[:, ~trans_mask] + + nat = self.structure.N_atoms * np.prod(self.GetSupercell()) + + # Check that the matrix is positive definite + if any([w < 0 for w in ws]): + ERR_MSG = """ + Error, the current matrix is not positive definite. + I cannot extract a random ensamble. + If you want to skip this error, + consider calling the method ForcePositiveDefinite() before extracting the ensemble. + + It could also be a consequence of a sum rule not well imposed. + Try to run Symmetrize() to force the sum rule. + """ + + raise ValueError(ERR_MSG) + + n_modes = len(ws) + if T == 0: + a_mu = 1 / np.sqrt(2* ws) * BOHR_TO_ANGSTROM + else: + beta = 1 / (K_to_Ry*T) + a_mu = 1 / np.sqrt( np.tanh(beta*ws / 2) *2* ws) * BOHR_TO_ANGSTROM + + # Prepare the random numbers + size = int(size) + if (not sobol): + rand = np.random.normal(size = (size, n_modes)) + elif (sobol): + rand = sobol_norm_rand(size, n_modes, scramble = sobol_scramble, sobol_salt = sobol_scatter) # ***** Diegom_test ****** + else: + raise ValueError('sobol is not True or False') # This should never raise + + # Get the masses for the final multiplication + mass1 = np.tile(super_structure.get_masses_array(), (3, 1)).T.ravel() + + # TODO: I believe this is the heavy part of the extraction + total_coords = np.einsum("ij, i, j, kj->ik", pol_vects, 1/np.sqrt(mass1), a_mu, rand) + + + + # Project the displacements along the selected modes + if not project_on_vectors is None: + check, N_proj = np.shape(project_on_vectors) + if check != 3*nat: + print("Expected nat: " + str(nat) + " project_on_modes nat: " + str(check/3)) + raise ValueError("Error, the input project_on_modes has a wrong shape") + + for confid in range(size): + new_coords = np.zeros( nat*3, dtype = np.float64) + for i in range(N_proj): + new_coords += project_on_vectors[:, i].dot(total_coords[:, confid]) * project_on_vectors[:, i] + + total_coords[:, confid] = new_coords + + # Prepare the structures + final_structures = [] + for i in range(size): + tmp_str = super_structure.copy() + # Prepare the new atomic positions + + + # TODO: THis is the heavy part, probably we can replace this for loop + tmp_str.coords[:,:] += total_coords[:,i].reshape((tmp_str.N_atoms, 3)) + #for k in range(tmp_str.N_atoms): + # tmp_str.coords[k,:] += total_coords[3*k : 3*(k+1), i] + + # Check if you must to pop some atoms: + if len (isolate_atoms): + + if remove_non_isolated_atoms: + tmp_str = tmp_str.isolate_atoms(new_isolate_atoms) # Use the list in the supercell + else: + tmp_str.N_atoms = len(isolate_atoms) * np.prod(self.GetSupercell()) + new_coords = tmp_str.coords.copy() + for j, x in enumerate(isolate_atoms): + tmp_str.coords[j,:] = new_coords[x,:] + final_structures.append(tmp_str) + + + return final_structures + + def GetHarmonicFreeEnergy(self, T, allow_imaginary_freq = False, w_pols = None): + """ + COMPUTE THE HARMONIC QUANTUM FREE ENERGY + ======================================== + + The dynamical matrix can be used to obtain the vibrational contribution + to the Free energy. + + ..math:: + + F(\\Phi) = \\sum_\mu \\left[\\frac{\\hbar \\omega_\\mu}{2} + kT \\ln\\left(1 + e^{-\\beta \hbar\\omega_\\mu}\\right)\\right] + + + Acustic modes at Gamma are discarded from the summation. + An exception is raised if there are imaginary frequencies. + + Parameter + --------- + T : float + Temperature (in K) of the system. + w_pols : (w, pols) + If given, it should be a len=2 tuple with the frequencies and the polarization + vectors as obtaind from DiagonalizeSupercell method + + Returns + ------- + fe : float + Free energy (in Ry) at the given temperature. + """ + + K_to_Ry=K_B / RY_TO_EV#6.336857346553283e-06 + + if w_pols is None: + w, pols = self.DiagonalizeSupercell() + else: + w = w_pols[0].copy() + pols = w_pols[1].copy() + + # Remove translations + tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) + + # Exclude also other w = 0 modes (good for rotations) + locked_original = np.abs(w) < __EPSILON__ + if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): + tmask = locked_original + + w = w[ ~tmask ] + + # if imaginary frequencies are allowed, put w->0 + if allow_imaginary_freq: + w[w<0] = __EPSILON__ + + if len(w[w < 0]) >= 1: + raise ValueError("Error while computing the free energy, the dynamical matrix has imaginary frequencies") + + # Zero point energy + free_energy = np.sum( w / 2) + + # Add also the entropy + if T > 0: + beta = 1 / (K_to_Ry * T) + free_energy += np.sum( 1 / beta * np.log(1 - np.exp(-beta * w))) + + return free_energy + + def get_harmonic_entropy(self, T, w_pols = None, small_w_freq = __EPSILON_W__, allow_imaginary_freq = False): + """ + Get the harmonic entropy. + + Parameters + ---------- + T : float + Temperature in K + w_pols : (ndarray, ndarray) + Frequencies and polarization vectors of the diagonalized dynamical matrix. + Obtained from self.DiagonalizeSupercell + This way the diagonalization is performed only once if computed in a cycle. + small_w_freq : float + If provided, all the frequencies below this value are neglected + allow_imaginary_freq : bool + If true, imaginary frequencies are ignored. + + Results + ------- + entropy : float + The entropy in Ry / K for the whole supercell structure + """ + + if w_pols is None: + w, pols = self.DiagonalizeSupercell() + else: + w, pols = w_pols + + # Remove translations + tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) + + # Exclude also other w = 0 modes (good for rotations) + locked_original = np.abs(w) < __EPSILON_W__ + if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): + tmask = locked_original + + w = w[ ~tmask ] + + if allow_imaginary_freq: + w = w[w > 0] + + # Check the presence of imaginary frequencie + if not np.all( w>0): + raise ValueError("Error, the entropy is not defined when the dynamical matrix has imaginary frequencies!") + + beta = RY_TO_KELVIN / T + Kb_ry = K_B / RY_TO_EV + + + # Compute the entropy for each mode + exp_factor = np.exp(-beta * w) + entropy = -Kb_ry * np.log(1 - exp_factor) + Kb_ry* beta*w * (exp_factor / (1 - exp_factor)) + #av_energy = Kb_ry * beta * w / (2 * np.tanh(beta * w / 2)) + #entropy = av_energy - Kb_ry * np.log(2*np.sinh(beta * w / 2)) + + + return np.sum(entropy) + + def get_harmonic_heat_capacity(self, T, w_pols = None, small_w_freq = __EPSILON_W__, allow_imaginary_freq = False): + r""" + HEAT CAPACITY + ============= + + Compute the (quantum) harmonic heat capacity by deriving the entropy with respect to temperature + + + .. math:: + + C_v = \sum_\mu k_b \beta^2\omega_\mu^2 \frac{e^{\beta\omega_\mu}}{(e^{\beta\omega_\mu} - 1)^2} + + Parameters + ---------- + T : float + Temperature in K + w_pols : (ndarray, ndarray) + Frequencies and polarization vectors of the diagonalized dynamical matrix. + Obtained from self.DiagonalizeSupercell + This way the diagonalization is performed only once if computed in a cycle. + small_w_freq : float + If provided, all the frequencies below this value are neglected + allow_imaginary_freq : bool + If true, imaginary frequencies are ignored. + + Results + ------- + heat_capacity : float + The heat_capacity in Ry / K for the whole supercell structure + """ + + if T < __EPSILON__: + return 0 + + + if w_pols is None: + w, pols = self.DiagonalizeSupercell() + else: + w, pols = w_pols + + # Remove translations + tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) + + # Exclude also other w = 0 modes (good for rotations) + locked_original = np.abs(w) < __EPSILON_W__ + if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): + tmask = locked_original + + w = w[ ~tmask ] + + if allow_imaginary_freq: + w = w[w > 0] + + # Check the presence of imaginary frequencie + if not np.all( w>0): + raise ValueError("Error, the entropy is not defined when the dynamical matrix has imaginary frequencies!") + + beta = RY_TO_KELVIN / T + Kb_ry = K_B / RY_TO_EV + + # Compute the specific heat for each mode + exp_factor2 = np.exp(beta * w) + cv = Kb_ry * (beta*w)**2 * exp_factor2 / (exp_factor2 - 1)**2 + + # Sum the result in the full supercell + return np.sum(cv) + + + + def get_phonon_dos(self, w_array, smearing, exclude_acoustic = True, use_cm = False, w_pols = None): + r""" + GET THE PHONON DOS + ================== + + This function plots the phonon dos. + + Parameters + ---------- + w_array : ndarray + The frequencies at which you want to compute the phonon dos [in Ry] (or cm-1, see use_cm). + smearing : float + The smearing [in Ry] (or cm-1, see use_cm). + exclude_acoustic : bool + If true, the acoustic modes at gamma are excluded. + use_cm : bool + If true, the frequency array and the smearing is supposed to be given in cm-1 + instead of Ry. + w_pols : (frequencies, polarizations) + If provided, it avoids performing a new diagonalization + + Results + ------- + dos : ndarray(size = (w_array), dtype = np.float64) + The phonon density of state. + """ + + if use_cm: + w_array = w_array.copy() / RY_TO_CM + smearing /= RY_TO_CM + + dos = np.zeros(np.shape(w_array), dtype = np.float64) + if w_pols is None: + w, p = self.DiagonalizeSupercell() + trans = Methods.get_translations(p, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) + w = w[~trans] + else: + w, p = w_pols + + for w0 in w: + dos += np.exp( -(w_array - w0)**2 / (2 * smearing*smearing)) / np.sqrt(2 * np.pi * smearing*smearing) + + + return dos + + + def get_two_phonon_dos(self, w_array, smearing, temperature, q_index = 0, exclude_acoustic = True): + r""" + COMPUTE THE TWO PHONON DOS + ========================== + + This subroutine compute the two phonon DOS of the given dynamical matrix. + It analyzes all possible phonon-phonon scattering and decayment to + build the two body density of states. This can be used to get an idea how much + each phonon can interact with the other in presence of anharmonicity just + considering energy conservation law and Bose-Einstein statistic. + + The DOS equation is + + .. math :: + + \rho^{(2)}(q, \omega) = \int d^3k_1d^3k_2\sum_{\mu\nu}\left[(n_\mu + n_\nu + 1)\delta(\omega - \omega_\mu(k_1) - \omega_\nu(k_2))\delta^3(\vec k_1 + \vec k_2 - \vec q)\right. + + \left. + 2 (n_\mu - n_\nu)\delta(\omega - \omega_\mu(k_1) + \omega_\nu(k_2))\delta^3(\vec q + \vec k_1 - \vec k_2)\right] + + + Where the Delta function are replaced by the Lorenzian shape to consider a smearing. + + Parameters + ---------- + w_array : ndarray + The frequency of the dos + smearing : float + The smearing used to compute the DOS. + To converge the smearing you need to study the limit + :math:`\lim_{\sigma\rightarrow 0} \lim_{N_q\rightarrow\infty} DOS` + q_index : int + The q point in which to compute the phonon DOS. + You must pass the index that matches the q_tot list. + exclude_acustic : bool, default = False + If True the acoustic modes at gamma are neglected in the DOS. + NOTE: if you have few q points, you will not see the frequencies of the real mode in the DOS! + + Results + ------- + dos : ndarray + The array of the density of state returned. Same shape as w_array + """ + K_to_Ry=6.336857346553283e-06 + + + q_vector = self.q_tot[q_index] + bg = Methods.get_reciprocal_vectors(self.structure.unit_cell) + + nat = self.structure.N_atoms + + + DOS = np.zeros( np.shape(w_array), dtype = np.float64) + for k1_i, k1 in enumerate(self.q_tot): + # Get the k vectors from the delta relations + k2_dists = [Methods.get_min_dist_into_cell(bg, k1, q_vector - x) for x in self.q_tot] + k2p_dists = [Methods.get_min_dist_into_cell(bg, k1, x - q_vector) for x in self.q_tot] + + k2_i = np.argmin(k2_dists) + k2p_i = np.argmin(k2p_dists) + + k2 = self.q_tot[k2_i] + k2p = self.q_tot[k2p_i] + + # Get the frequencies at the correct Q points + _wmu_, _pmu_ = self.DyagDinQ(k1_i) + _wnu_, _pnu_ = self.DyagDinQ(k2_i) + _wnu2_, _pnu2_ = self.DyagDinQ(k2p_i) + + trans1 = Methods.get_translations(_pmu_, self.structure.get_masses_array()) + trans2 = Methods.get_translations(_pnu_, self.structure.get_masses_array()) + trans3 = Methods.get_translations(_pnu2_, self.structure.get_masses_array()) + + # Sum over mu nu + for mu in range(3*nat): + if exclude_acoustic and trans1[mu]: + continue + w_mu = _wmu_[mu] + n_mu = 0 + if temperature > 0: + n_mu = 1 / (np.exp(w_mu / (temperature * K_to_Ry)) - 1) + for nu in range(3*nat): + w_nu = _wnu_[nu] + n_nu = 0 + if temperature > 0: + n_nu = 1 / (np.exp(w_nu / (temperature * K_to_Ry)) - 1) + + chi1 = 0 + if not (exclude_acoustic and trans2[nu]): + chi1 = 2*smearing * w_array * (w_mu + w_nu) * (n_nu + n_mu + 1) + chi1 /= 4 * smearing**2*w_array**2 + ( (w_mu + w_nu)**2 - w_array**2)**2 + chi1 /= w_mu * w_nu + + w_nu = _wnu2_[nu] + if temperature > 0: + n_nu = 1 / (np.exp(w_nu / (temperature * K_to_Ry)) - 1) + + chi2 = 0 + if not (exclude_acoustic and trans3[nu]): + chi2 = 2 * smearing * w_array * (w_mu - w_nu) * (n_nu - n_mu) + chi2 /= 4*smearing**2 *w_array**2 + ( (w_nu - w_mu)**2 - w_array**2)**2 + chi2 /= w_mu*w_nu + + DOS += chi1 + chi2 + + return DOS / 2 # We need a 1/2 factor + + + def get_phonon_propagator(self, w_array, smearing = 1e-5, only_gamma = False): + r""" + GET THE SINGLE PHONON PROPAGATOR + ================================ + + This method computes the single phonon harmonic propagator. + It is computed in the supercell + + .. math:: + + G_{ab}(\omega) = \sum_{\mu}\frac{e_\mu^a e_\mu^b}{(\omega - i\eta)^2 - \omega_\mu^2} + + This is in real space + + Parameters + ---------- + - w_array : ndarray + The frequencies at which you want to compute the propagator. + In [Ry] + - smearing : float + The :math:`\eta` value. + - only_gamma : bool + If True, only the phonons at gamma will be used + + Results + ------- + - G_abw : ndarray(size = (3nat, 3nat, len(w))) + The real space green function + + """ + + if not only_gamma: + w, pols = self.DiagonalizeSupercell() + + super_struct = self.structure.generate_supercell(self.GetSupercell()) + trans = Methods.get_translations(pols, super_struct.get_masses_array()) + nat = super_struct.N_atoms + else: + w, pols = self.DyagDinQ(0) + trans = Methods.get_translations(pols, self.structure.get_masses_array()) + nat = self.structure.N_atoms + + G_final = np.zeros( (3*nat, 3*nat, len(w_array)), dtype = np.complex128) + + w = w[~trans] + pols = pols[:, ~trans] + + nmodes = len(w) + for mu in range(nmodes): + epol = np.outer(pols[:, mu], pols[:, mu]) + freq = 1 / ((w_array + 1j*smearing)**2 - w[mu]**2) + G_final[:,:,:] += np.einsum("ab,c ->abc", epol, freq) + + return G_final + + def get_two_phonon_propagator(self, w, T, smearing = 1e-5): + r""" + GET THE TWO PHONONS PROPAGATOR + ========================= + + This subroutine computes the two phonons propagator defined as + + .. math :: + + \chi_{\mu\nu}(z, q) = \frac{1}{\beta} \sum_{l} G_\mu(i\Omega_l, \vec k) G_\nu(z - i\Omega_l, \vec q - \vec k) + + \chi_{\mu\nu}(z, q) = \frac{\hbar}{2\omega_\mu\omega_\nu}\left[ \frac{(\omega_\nu + \omega_\mu)[1 + n_\nu + n_\mu]}{(\omega_\nu + \omega_\mu)^2 - z^2} - \frac{(\omega_\nu - \omega_\mu)[n_\nu - n_\mu]}{(\omega_\nu - \omega_\mu)^2 -z^2}\right] + + This is the phonon dynamical bubble. + This is computed in the polarization basis. + The translational modes are discarted. + + + + Parameters + ---------- + w : ndarray + The values of the dynamical frequency to compute the phonon propagator. + T : float + The temperature to compute the bosonic occupation numbers :math:`n_\mu`. + semaring : float, default = 1e-5 + The smearing [Ry] to achieve a faster convergence with the k-mesh sampling. + + Result + ------ + chi : ndarray(size=(3*nat, 3*nat), dtype = np.complex128) + The bubble phonon propagator + """ + + + K_to_Ry=6.336857346553283e-06 + + + # Get the frequencies at the correct Q points + _w_, _p_ = self.DiagonalizeSupercell() + + # Get the translational vectors + trans = Methods.get_translations(_p_, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) + + _w_ = _w_[~trans] + _p_ = _p_[~trans] + + nmodes = len(_w_) + ChiMuNu = np.zeros( (nmodes, nmodes, len(w)), dtype = np.complex128) + + # Sum over mu nu + for mu, w_mu in enumerate(_w_): + n_mu = 0 + dn_dw = 0 + if T > __EPSILON__: + n_mu = 1 / (np.exp(w_mu / (T * K_to_Ry)) - 1) + dn_dw = -n_mu / (T * K_to_Ry * (1 - np.exp(-w_mu / (T * K_to_Ry)))) + for nu, w_nu in enumerate(_w_): + n_nu = 0 + if T > __EPSILON__: + n_nu = 1 / (np.exp(w_nu / (T * K_to_Ry)) - 1) + + chi1 = np.zeros(np.shape(w), dtype = np.complex128) + chi2 = np.zeros(np.shape(w), dtype = np.complex128) + chi1 = (w_mu + w_nu) * (n_nu + n_mu + 1) + chi1 /= ( (w_mu + w_nu)**2 - (w - 1j*smearing)**2 ) + chi1 /= 2*w_mu*w_nu + + #if np.abs(w) < __EPSILON__ and np.abs(w_nu - w_mu) < __EPSILON__: + # chi2 = - dn_dw / (2*w_mu*w_nu) + #else: + chi2 = (w_mu - w_nu) * (n_nu - n_mu) + chi2 /= ( (w_nu - w_mu)**2 - (w - 1j*smearing)**2 ) + chi2 /= 2*w_mu*w_nu + + if np.isnan(chi1 + chi2).any(): + print("NaN value found in the propagator.") + print("NaN value error details:") + print("chi1: ", chi1) + print("chi2: ", chi2) + print("mu = %d, nu = %d" % (mu, nu)) + print("w_mu = %10.4f, n_mu = %10.4f" % (w_mu * RY_TO_CM, n_mu)) + print("w_nu = %10.4f, n_nu = %10.4f" % (w_nu * RY_TO_CM, n_nu)) + raise ValueError("Error, the propagator is NAN, check stdout for details.") + ChiMuNu[mu, nu, :] = chi1 + chi2 + + return ChiMuNu + + def get_energy_forces(self, structure, vector1d = False, real_space_fc = None, super_structure = None, supercell = None, + displacement = None, use_unit_cell = True, w_pols = None): + """ + COMPUTE ENERGY AND FORCES + ========================= + + This subroutine computes the harmonic energy and the forces + for the given dynamical matrix at harmonic level. + + .. math:: + + E = \frac 12 \\sum_{\\alpha\\beta} \\left(r_{\\alpha} - r^0_{\\alpha}\\right)\\Phi_{\\alpha\\beta} \\left(r_\\beta - r^0_\\beta\right) + + F_\\alpha = \\sum_{\\beta} \\Phi_{\\alpha\\beta} \\left(r_\\beta - r^0_\\beta\right) + + The energy is given in Rydberg, while the force is given in Ry/Angstrom + + NOTE: In this very moment it has been tested only at Gamma (unit cell) + + Parameters + ---------- + structure : Structure.Structure() + A unit cell structure in which energy and forces on atoms are computed + vector1d : bool, optional + If true the forces are returned in a reshaped 1d vector. + real_space_fc : ndarray 3nat_sc x 3nat_sc, optional (default None) + If provided the real space force constant matrix is not recomputed each time the + method is called. Usefull if you have to repeat this calculation many times. + You can get the real_space_fc using the method GetRealSpaceFC. + super_structure : Structure.Structure() + Optional, not required. If given is the superstructure used to compute the distance from the + target one. You can pass it to avoid regenerating it each time this subroutine is called. + If you do not pass it, you must provide the supercell size (if different than the unit cell) + super_cell : list of 3 items + This is the supercell on which compute the energy and force. If none it is inferred by the dynamical matrix. + displacement: + The displacements from the self average position to be used. It is not + necessary since they can be recomputed, however if provided, the calculation is faster. + It must be in Angstrom. + To speedup the calculations, many displacements can be provided, in the form: + displacement.shape = (N_config, 3*nat_sc) + where N_config are the number of configurations, nat_sc the atoms in the supercell + use_unit_cell : bool + If ture, do not compute the real space force constant matrix on the super cell. This is the fastest option. + Put it to false only for debugging purpouses. + w_pols : list of (w, pols) + If given, the frequencies and polarization vectors are not recomputed from scratch + + Returns + ------- + energy : float (or ndarray.shape(N_config)) + The harmonic energy (in Ry) of the structure + force : ndarray N_atoms x 3 or N_config, nat_sc, 3) + The harmonic forces that acts on each atoms (in Ry / A) + """ + + if supercell is None: + supercell = self.GetSupercell() + + # Convert the displacement vector in bohr + #A_TO_BOHR=np.float64(1.889725989) + if super_structure is None: + super_structure = self.structure.generate_supercell(supercell) + + # Get the displacement vector (bohr) + if displacement is None: + rv = structure.get_displacement(super_structure).reshape(structure.N_atoms * 3) * A_TO_BOHR + else: + rv = displacement * A_TO_BOHR + + # Check how many configurations + many_configs = False + if len(rv.shape) > 1: + many_configs = True + n_configs = rv.shape[0] + + # Fast computation + if use_unit_cell: + if w_pols is not None: + w = w_pols[0] + pols = w_pols[1] + else: + w, pols = self.DiagonalizeSupercell() + + # Correctly account for not positive definite dynamical matrices + w2 = w**2 * np.sign(w) + + m = np.tile(super_structure.get_masses_array(), (3,1)).T.ravel() + m_sqrt = np.sqrt(m) + + epols = np.einsum("ab, a -> ab", pols, m_sqrt) + x_mu = rv.dot(epols) + + # Check if more configurations needs to be used + + # TODO: add the possibility to pass several structures toghether + # to avoid computing many times the same passages + # This works only if the displacements are passed + if not many_configs: + energy = 0.5 * np.sum(x_mu**2 * w2) + forces = - epols.dot( w2 * x_mu) + else: + w2_tile = np.tile(w2, (n_configs, 1)) + energy = 0.5 * np.sum(x_mu**2 * w2_tile, axis = 1) + forces = - (w2_tile * x_mu).dot(epols.T) + else: + if real_space_fc is None: + real_space_fc = self.GetRealSpaceFC(supercell) + + if many_configs: + raise NotImplementedError("Error, use the use_unit_cell = True if you want to compute many configurations.") + + # Get the energy + energy = 0.5 * rv.dot ( np.real(real_space_fc)).dot(rv) + + # Get the forces (Ry/ bohr) + forces = - real_space_fc.dot(rv) + + nat_sc = self.structure.N_atoms * np.prod(supercell) + + # Translate the force in Ry / A + forces *= A_TO_BOHR + if not vector1d: + if not many_configs: + forces = forces.reshape( (nat_sc, 3)) + else: + forces = forces.reshape( (n_configs, nat_sc, 3)) + + return energy, forces + + + def GetRealSpaceFC(self, supercell_array = (1,1,1), super_structure = None, img_thr = 1e-6): + """ + GET THE REAL SPACE FORCE CONSTANT + ================================= + + This subroutine uses the fourier transformation to get the real space force constant, + starting from the fourer space matrix. + + .. math:: + + C_{k\\alpha,k'\\beta}(0, b) = \\frac{1}{N_q} \\sum_q \\tilde C_{k\\alpha k'\\beta}(q) e^{i\\vec q \\cdot \\vec R_b} + + Then the translationa property is applied. + + .. math:: + + C_{k\\alpha,k'\\beta}(a, b) = C_{k\\alpha,k'\\beta}(0, b-a) + + Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\\alpha` is the + cartesian indices. + + NOTE: This method just call the GetSupercellFCFromDyn, look at its documentation for further info. + + Returns + ------- + fc_supercell : ndarray 3nat_sc x 3nat_sc + The force constant matrix in the supercell. + If it is a supercell structure, it is use that structure to determine the supercell array + super_structure : Structure() + If given, it is used to generate the supercell. Note that in this + case the supercell_array argument is ignored + + """ + + nq = len(self.q_tot) + nat = self.structure.N_atoms + nat_sc = nat * nq + + if super_structure is None: + super_structure = self.structure.generate_supercell(supercell_array) + + # Check the consistency of the argument with the number of q point + if nat_sc != super_structure.N_atoms: + raise ValueError("Error, the super_structure number of atoms %d does not match %d computed from the q points." % (super_structure.N_atoms, nat_sc)) + + dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") + + # Fill the dynamical matrix + for i, q in enumerate(self.q_tot): + dynmat[i, :,:] = self.dynmats[i] + + fc = GetSupercellFCFromDyn(dynmat, np.array(self.q_tot), self.structure, super_structure, img_thr = img_thr) + return fc + +# +# # Define the number of q points, atoms and unit cell atoms +# nq = len(self.q_tot) +# nat = self.structure.N_atoms +# nat_sc = nq*nat +# +# # Check if the supercell array matches the number of q points +# if np.prod(supercell_array) != nq: +# raise ValueError("Error, the number of supercell %d must match the number of q points %d." % (np.prod(supercell_array), nq)) +# +# dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") +# fc = np.zeros((3*nat_sc, 3*nat_sc), dtype = np.complex128) +# +# print "NQ:", nq +# +# R_vectors_cart = np.zeros((nq,3), dtype = np.float64, order = "F") +# q_vect = np.zeros((nq,3), dtype = np.float64, order = "F") +# +# +# +# # Fill the dynamical matrix +# for i, q in enumerate(self.q_tot): +# dynmat[i, :,:] = self.dynmats[i] +# +# a_x = i % supercell_array[0] +# a_y = (i / supercell_array[0]) % supercell_array[1] +# a_z = i / (supercell_array[0] * supercell_array[1]) +# R_vectors_cart[i,:] = a_x * self.structure.unit_cell[0,:] + a_y * self.structure.unit_cell[1,:] + a_z * self.structure.unit_cell[2,:] +# +# q_vect[i,:] = 2*np.pi * q / self.alat +# +# +# # For now, to test, just the unit cell +# for i in range(nq): +# start_index = 3 * nat * i +# for j in range(nq): +# end_index = 3 * nat * j +# q_dot_R = np.einsum("ab, b", q_vect, R_vectors_cart[j,:] - R_vectors_cart[i,:]) +# #print "%d, %d => q dot R = " % (i, j), np.exp(1j * q_dot_R) +# fc[end_index: end_index + 3*nat, start_index: start_index + 3*nat ] += np.einsum("abc, a", dynmat, np.exp(1j* q_dot_R)) / nq +# +# +# #np.sum(dynmat * np.exp(), axis = 0) / nq +# print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) +# +# return fc + + def GetSupercell(self): + """ + GET SUPERCELL + ============= + + Return the supercell along which this matrix has been generated. + + Results + ------- + supercell : list of 3 int + The supercell in each direction. + """ + return symmetries.GetSupercellFromQlist(self.q_tot, self.structure.unit_cell) + + def InterpolateMesh(self, mesh_dim, lo_to_splitting = False): + """ + INTERPOLATE THE DYNAMICAL MATRIX IN A FINER Q MESH + ================================================== + + This method employs the Tensor2 interpolateion functions + from the ForceTensor module to perform the interpolation. + + Parameters + ---------- + mesh_dim : list of int + The dimension of the q-mesh on which perform the interpolation. + + Results + ------- + new_dyn : Phonons.Phonons() + A new dynamical matrix defined on the desidered mesh. + """ + + # Setup the force constant tensor + current_mesh = self.GetSupercell() + t2 = ForceTensor.Tensor2(self.structure, self.structure.generate_supercell(current_mesh), current_mesh) + t2.SetupFromPhonons(self) + + out_dyn = t2.GeneratePhonons(mesh_dim, lo_to_splitting=lo_to_splitting) + return out_dyn + + + + + + def Interpolate(self, coarse_grid, fine_grid, support_dyn_coarse = None, + support_dyn_fine = None, symmetrize = False): + """ + INTERPOLATE THE DYNAMICAL MATRIX IN A FINER Q MESH + ================================================== + + This method interpolates the dynamical matrix in a finer mesh. + It is possible to use a different dynamical matrix as a support, + then only the difference of the current dynamical matrix + with the support is interpolated. In this way you can easier achieve convergence. + + NOTE: This method ignores effective charges. + If you want to account for effective charges you should use the ForceTensor.Tensor2 class + to interpolate. + + NOTE: This is going to be replaced with the InterpolateMesh function, + accounting properly for effective charges + + Parameters + ---------- + coarse_grid : ndarray(size=3, dtype = int) + The current q point mesh size + fine_grid : ndarray(size=3, dtype = int) + The final q point mesh size + support_dyn_coarse : Phonons(), optional + A dynamical matrix used as a support in the same q grid as this one. + Note that the q points must coincide with the one of this matrix. + support_dyn_fine : Phonons(), optional + The support dynamical matrix in the finer cell. + If given, the fine_grid is read + by the q points of this matrix, and must be compatible + with the fine_grid. + symmetrize : bool, optional + If true activate the symmetrization for the new matrix + + Results + ------- + interpolated_dyn : Phonons() + The dynamical matrix interpolated. + """ + + + # Check if the support dynamical matrix is given: + is_dync = support_dyn_coarse is not None + is_dynf = support_dyn_fine is not None + if is_dync != is_dynf: + raise ValueError("Error, you must provide both support matrix") + + nqtot = np.prod(fine_grid) + + # Get the q list + q_list = symmetries.GetQGrid(self.structure.unit_cell, fine_grid) + #print "The q list:" + #print q_list + + if is_dync and is_dynf: + # Check if the is_dynf has the correct number of q points + if nqtot != len(support_dyn_fine.q_tot): + raise ValueError("Error, the number of q points of the support must coincide with the fine grid") + + assert self.GetSupercell() == support_dyn_coarse.GetSupercell(), """ +Error, support dyn is defined on a different supercell + supercell self: {} + supercell support_dyn_coarse: {} +""".format(self.GetSupercell(), support_dyn_coarse.GetSupercell()) + + assert self.structure.N_atoms == support_dyn_coarse.structure.N_atoms, """ +Error, support_dyn is defined on a wrong structure. +""" + + # # Check if the support dyn course q points coincides + # bg = Methods.get_reciprocal_vectors(self.structure.unit_cell) + # for iq, q in enumerate(self.q_tot): + # if Methods.get_min_dist_into_cell(bg, q, support_dyn_coarse.q_tot[iq]) > __EPSILON__: + # # Get the NQIRR + # limit = iq + # for nqirr in range(len(self.q_stars)): + # limit -= len(self.q_stars[nqirr]) + # if limit < 0: + # break + + # print ("ERROR, NOT MATCHING Q IN STAR NUMBER ({}):".format(nqirr +1)) + # print ("self q1 = ", q) + # print ("support coarse q2 = ", support_dyn_coarse.q_tot[iq]) + # raise ValueError("Error, the coarse support grid as a q point that does not match the self one") + + + # Overwrite the q list + q_list = support_dyn_fine.q_tot[:] + + + # Prepare the super variables + if not is_dynf: + new_dynmat = Phonons(self.structure.copy(), nqtot) + new_dynmat.q_stars = [[]] + new_dynmat.initialized = True + new_dynmat.nqirr = 1 + new_dynmat.alat = self.alat + else: + new_dynmat = support_dyn_fine.Copy() + + + super_structure = self.structure.generate_supercell(fine_grid) + superstruct_coarse = self.structure.generate_supercell(coarse_grid) + + nat = self.structure.N_atoms + fcq = np.zeros( (len(self.q_tot), 3 * nat, 3*nat), dtype = np.complex128) + for iq, q in enumerate(self.q_tot): + fcq[iq, :, :] = self.dynmats[iq].copy() + #if is_dync: + # fcq[iq, :, :] -= support_dyn_coarse.dynmats[iq] + + # Get the real space force constant matrix + #r_fcq = GetSupercellFCFromDyn(fcq, np.array(self.q_tot), self.structure, super_structure) + r_fcq = GetSupercellFCFromDyn(fcq, np.array(self.q_tot), self.structure, superstruct_coarse) + + + if is_dync: + fcq = np.zeros( (len(self.q_tot), 3 * nat, 3*nat), dtype = np.complex128) + for iq, q in enumerate(self.q_tot): + fcq[iq, :, :] = support_dyn_coarse.dynmats[iq].copy() + r_fcq -= GetSupercellFCFromDyn(fcq, np.array(support_dyn_coarse.q_tot), self.structure, superstruct_coarse) + + #r_fcq = self.GetRealSpaceFC(coarse_grid) + + + q_star_i = 0 + passed_qstar = 0 + for iq, q in enumerate(q_list): + new_dynmat.q_tot[iq][:] = q + + # Use the same star as the support matrix + if is_dynf: + if iq - passed_qstar == len(support_dyn_fine.q_stars[q_star_i]): + q_star_i += 1 + passed_qstar = iq + + print ("WORKING ON:", q) + new_dynmat.q_stars[q_star_i].append(q) + new_dynmat.dynmats[iq] += InterpolateDynFC(r_fcq, coarse_grid, self.structure, self.structure.generate_supercell(coarse_grid), q) + + + new_dynmat.AdjustQStar() + + if symmetrize: + new_dynmat.Symmetrize() + + + if self.effective_charges is not None: + WARN_TXT=""" +WARNING: Effective charges are not accounted by this method + You should generate a ForceTensor.Tensor2 object + To account for the interpolation of long-range forces. + """ + + print(WARN_TXT) + warnings.warn(WARN_TXT, DeprecationWarning) + + + return new_dynmat + + + def AdjustQStar(self, use_spglib = False): + """ + ADJUST THE Q STAR + ================= + + This function uses the quantum espresso symmetry finder to + divide the q points into the proper q stars, reordering the current dynamical matrix. + + + Parameters + ---------- + use_spglib : bool + If true, the SPGLIB is used to perform the symmetrization. + Otherwise the quantum espresso default symmetry route is used. + """ + + # Initialize the symmetries + qe_sym = symmetries.QE_Symmetry(self.structure) + + if use_spglib: + #raise NotImplementedError("Error, the symmetry module from SPGLIB is not yet able to compute the q star") + + qe_sym.SetupFromSPGLIB() + else: + qe_sym.SetupQPoint() + + i_gamma = -1 + for iq, q in enumerate(self.q_tot): + if np.max(np.abs(q)) < __EPSILON__: + i_gamma = iq + + if i_gamma != 0: + mydyn = self.dynmats[0].copy() + self.dynmats[0] = self.dynmats[iq].copy() + self.dynmats[iq] = mydyn + self.q_tot[iq] = self.q_tot[0].copy() + self.q_tot[0][:] = 0 + + + # Get the q_stars + q_stars, q_order = qe_sym.SetupQStar(self.q_tot) + + # Reorder the dynamical matrix + new_dynmats = [] + q_tot = [] + for i in range(len(q_order)): + iq = q_order[i] + q = self.q_tot[iq] + new_dynmats.append(self.dynmats[iq]) + + self.dynmats = new_dynmats + self.q_stars = q_stars + self.q_tot = [y for x in q_stars for y in x] # Unwrap the q points + self.nqirr = len(q_stars) + + # # Now, the q_stars respect the correct fourier convention + # q_tot = [] + # for q_star in q_stars: + # for q in q_star: + # q_tot.append(q) + # self.q_tot = q_tot + + def SwapQPoints(self, other_dyn): + """ + Adjust the order of the q points of this dynamical matrix (self) to match the one of the passed dynamical matrix. + This is usefull if you want to compare the two dynamical matrices. + + The method also checks if the q points are in different brilluin zones. + + NOTE: this method will match the q points, this means that the q star could be destroyed. + You need to call AdjustQStar to correctly generate the star after this method. + + """ + + ## Check of consistency between the dynamical matrices + assert len(self.q_tot) == len(other_dyn.q_tot) + assert self.GetSupercell() == other_dyn.GetSupercell() + + order_mask = [] + bg = self.structure.get_reciprocal_vectors() / (2*np.pi) + for i, qi in enumerate(other_dyn.q_tot): + found = False + for j, qj in enumerate(self.q_tot): + # Skip if it has already been identified + if j in order_mask: + continue + + # Check if qi and qj are the same vector + dist = Methods.get_min_dist_into_cell(bg, qi, qj) + if dist < __EPSILON__: + order_mask.append(j) + found = True + break + + assert found, "Error, mismatching between q points: this matrix has q = {} missing in the other one".format(qi) + + # Reorder the dynamical matrix + print("Order: {}".format(order_mask)) + self.dynmats = [ self.dynmats[x] for x in order_mask ] + self.q_tot = [ self.q_tot[x] for x in order_mask ] + self.q_stars = [ self.q_tot ] + + + + + + + + def SymmetrizeSupercell(self, supercell_size = None): + """ + Testing function, it applies symmetries in the supercell. + """ + + if supercell_size == None: + supercell_size = self.GetSupercell() + + + if not __SPGLIB__: + raise ImportError("Error, the SymmetrizeSupercell method of the Phonon class requires spglib") + + superdyn = self.GenerateSupercellDyn(supercell_size) + + # Apply the sum rule + symmetries.CustomASR(superdyn.dynmats[0]) + + qe_sym = symmetries.QE_Symmetry(superdyn.structure) + qe_sym.SetupFromSPGLIB() + #qe_sym.SetupQPoint() + qe_sym.ApplySymmetriesToV2(superdyn.dynmats[0]) + + #spgsym = spglib.get_symmetry(superdyn.structure.get_ase_atoms()) + #syms = symmetries.GetSymmetriesFromSPGLIB(spgsym, False) + #superdyn.ForceSymmetries(syms) + + # Get the dynamical matrix back + fcq = GetDynQFromFCSupercell_parallel(superdyn.dynmats[0], np.array(self.q_tot), self.structure, superdyn.structure) + + for iq, q in enumerate(self.q_tot): + self.dynmats[iq] = fcq[iq, :, :] + + # Symmetrize also the effective charges and the Raman Tensor if any + # To do this, the symmetries must be initialized once again in the unit cell + qe_sym = symmetries.QE_Symmetry(self.structure) + qe_sym.SetupFromSPGLIB() + if not self.effective_charges is None: + qe_sym.ApplySymmetryToEffCharge(self.effective_charges) + if not self.raman_tensor is None: + qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) + + def Symmetrize(self, verbose = False, asr = "custom", use_spglib = False): + """ + SYMMETRIZE THE DYNAMICAL MATRIX + =============================== + + This subroutine uses the QE symmetrization procedure to obtain + a full symmetrized dynamical matrix. + + Parameters + ---------- + verbose : bool + If true a lot of info regarding the symmetrization are printed. + asr : string + The kind of the acustic sum rule. Allowed are 'crystal', 'simple' or 'custom'. + for crystal and simple refer to the quantum-espresso guide. + use_spglib : bool + If True, the simmetrization is performed with SPGLIB in the supercell + """ + + if use_spglib: + self.SymmetrizeSupercell() + else: + qe_sym = symmetries.QE_Symmetry(self.structure) + fcq = np.array(self.dynmats, dtype = np.complex128) + qe_sym.SymmetrizeFCQ(fcq, self.q_stars, asr = asr, verbose = verbose) + + for iq,q in enumerate(self.q_tot): + self.dynmats[iq] = fcq[iq, :, :] + + # Symmetrize also the effective charges and the Raman Tensor if any + if not self.effective_charges is None: + qe_sym.ApplySymmetryToEffCharge(self.effective_charges) + if not self.raman_tensor is None: + qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) + + + + def ApplySumRule(self, kind = "custom"): + """ + ACUSTIC SUM RULE + ================ + + The acustic sum rule is a way to impose translational symmetries on the dynamical matrix. + It affects also the effective charges if any (the total effective charge must be zero). + For the dynamical matrix it allows to have the self interaction terms: + .. math:: + + \\Phi_{n_a, n_a}^{x,y} = - \\sum_{n_b \\neq n_a} \\Phi_{n_a,n_b}^{x,y} + + Parameters + ---------- + kind : string + - "custom" : The polarization vectors asigned to the translation are removed from the + gamma dynamical matrix. + - "normal" : The equation written in this doc_string is applied. + A NotImplementedError is raised if kind differs from these types. + """ + + # Apply the sum rule on the dynamical matrix + if kind == "custom": + # Apply the sum rule + symmetries.CustomASR(self.dynmats[0]) + elif kind == "normal": + nb = np.arange(self.structure.N_atoms) + for i in range(9): + x = i / 3 + y = i % 3 + for na in range(self.structure.N_atoms): + sum_value = np.sum(self.dynmats[0][3 * na + x, 3 * nb[(nb != na)] + y]) + self.dynmats[0][3 * na + x, 3 * na + y] = - sum_value + else: + raise NotImplementedError("Error, the specified kind for the sum rule is unknown {}".format(kind)) + + + # Apply the sum rule on the effective charge + if self.effective_charges is not None: + total_charge = np.sum(self.effective_charges, axis = 0) + + # Subtract to each atom an average of the total charges + self.effective_charges = np.einsum("aij, ij -> aij", self.effective_charges, - total_charge / self.structure.N_atoms) + + + def GetIRActive(self, use_spglib = False): + """ + GET IF A MODE IS IR ACTIVE + ========================== + + This subroutine uses group theory to get if a mode is IR active. + + Parameters + ---------- + use_spglib : bool + If True, spglib is used for group theory. + Good if you are in a supercell. + + Results + ------- + is_ir_active : ndarray (size = 3*nat) + Returns a bool array with True for each mode at gamma + that is IR active. + """ + + there_are_eff_charges = True + if self.effective_charges is None: + there_are_eff_charges = False + self.effective_charges = np.zeros((self.structure.N_atoms, 3,3), dtype = np.double) + self.effective_charges = np.random.uniform( size = self.effective_charges.shape) + + # Get the symmetries + qe_sym = symmetries.QE_Symmetry(self.structure) + if use_spglib: + qe_sym.SetupFromSPGLIB() + else: + qe_sym.SetupQPoint() + + # Symmetrize the effective charges + qe_sym.ApplySymmetryToEffCharge(self.effective_charges) + + # Simulate the IR signal + Ir = self.GetIRIntensities() + is_ir_active = Ir > 1e-8 + + # Delete the random effective charges if added + if not there_are_eff_charges: + self.effective_charges = None + + return is_ir_active + + + + def ApplySymmetry(self, symmat, irt = None): + """ + APPLY SYMMETRY + ============== + + This function apply a symmetry to the force constant matrix + The matrix must be a 3 rows x 4 columns array containing the rotation and the subsequent translation of the vectors. + + The symmetry check is performed by comparing the two force constant matrix within the given threshold. + + .. math:: + + \\Phi_{s(a)s(b)}^{ij} = \\sum_{h,k = 1}^3 S_{ik} S_{jh} \\Phi_{ab}^{kh} + + \\Phi = S \\Phi S^\\dagger + + where :math:`s(a)` is the atom in which the :math:`a` atom is mapped by the symmetry. + + Note: this works only in supercells at gamma point + + Parameters + ---------- + symmat : ndarray 3x4 + The symmetry matrix to be checked. the last column contains the translations. Trans + irt : ndarray (size = N_atoms) + The atoms the symmetry is mapping to. + + Results + ------- + ndarray 3Nat x 3Nat + The new force constant matrix after the application of the symmetries + """ + #A_TO_BOHR = 1.889725989 + + + # Check if the matrix has been initialized + if len(self.dynmats) == 0: + raise ValueError("Error, the phonon force constant has not been initialized. Please consider loading the phonon info.") + + if self.nqirr != 1: + raise ValueError("Error, this method only works for gamma point calculations") + + + # Get the way atoms are echanged + if irt is None: + aux_struct = self.structure.copy() + aux_struct.apply_symmetry(symmat, delete_original = True) + aux_struct.fix_coords_in_unit_cell() + + eq_atoms = self.structure.get_equivalent_atoms(aux_struct) + else: + eq_atoms = irt + #print eq_atoms + + # Get the number of atoms + n_atoms = self.structure.N_atoms + + # Get only the rotational part of the symmetry + new_s_mat = symmat[:3, :3] + + out_fc = np.zeros(np.shape(self.dynmats[0]), dtype = np.complex128) + in_fc = self.dynmats[0] + + # Apply the symmetry to the force constant matrix + for na in range(n_atoms): + for nb in range(0, n_atoms): + # Get the atoms projection of the symmetries + s_na = eq_atoms[na] + s_nb = eq_atoms[nb] + + # Extract the matrix referring to na and nb atoms + current_m = in_fc[3 * na : 3*na + 3, 3*nb : 3*nb + 3] + + # Conver the matrix in crystalline + new_m = Methods.convert_matrix_cart_cryst(current_m, self.structure.unit_cell * A_TO_BOHR) + + # Apply the symmetry + #new_m_sym = new_s_mat.dot(new_m.dot( new_s_mat.transpose())) + new_m_sym = new_s_mat.transpose().dot(new_m.dot( new_s_mat)) + + #new_m_sym =new_m.copy() + + # Convert back to cartesian coordinates + new_m = Methods.convert_matrix_cart_cryst(new_m_sym, self.structure.unit_cell * A_TO_BOHR, cryst_to_cart=True) + + #print "%d -> %d , %d -> %d)" % (na, s_na, nb, s_nb)#, "d = %.5f" % np.real(np.sqrt(np.sum( (new_m - current_m)**2))) + + # Write the matrix into the output + out_fc[3 * s_na : 3*s_na + 3, 3*s_nb : 3* s_nb + 3] = new_m.copy() + + + #out_fc[3 * s_nb : 3*s_nb + 3, 3*s_na : 3 * s_na + 3] = np.conj(new_m.copy().transpose()) + #print "Test of the transpose. d = ", np.real(np.sqrt(np.sum( (in_fc[3 * nb : 3*nb + 3, 3*na : 3*na + 3].transpose() - out_fc[3 * nb : 3*nb + 3, 3*na : 3 * na + 3])**2))) + + # Return the symmetrized result + #print "Total distance:", np.sqrt(np.sum( (out_fc - np.real(in_fc))**2)) + return out_fc + + + + def ForceSymmetries(self, symmetries, irt = None, apply_sum_rule = True): + """ + FORCE THE PHONON TO RESPECT THE SYMMETRIES + ========================================== + + This method forces the phonon dynamical matrix to respect + the given symmetries. + + It uses the method ApplySymmetry to manipulate the force constant matrix. + + Note: This method only affect the force constant matrix, the structure is supposed to respect the symmetries. + + Note: This works only with gamma matrices (i.e. supercells) + + Parameters + ---------- + symmetries : list of ndarray 3x4 + List of the symmetries matrices. The last column is the fractional translation. + irt : ndarray(size = (N_sym, N_atoms_sc), dtype = np.intc) + For each symmetry s, the atom i is mapped into the atom irt[s, i] + If None, irt is recomputed with the symmetries module. + apply_sum_rule: bool + If true the default sum rule is applied. + """ + + # Apply the symmetries + new_fc = np.zeros( np.shape(self.dynmats[0]), dtype = np.complex128 ) + + + self.structure.fix_coords_in_unit_cell() + for i, sym in enumerate(symmetries): + # Check if the structure satisfy the symmetry + if not self.structure.check_symmetry(sym): + print (sym) + new_sym = sym.copy() + new_sym[:, :3] = np.transpose( sym[:, :3]) + print ("Satisfy transpose?", self.structure.check_symmetry(new_sym)) + raise ValueError("Error, the given structure do not satisfy the %d-th symmetry." % (i+1)) + + # Get the force constant + current_irt = None + if not irt is None: + current_irt = irt[i, :] + current_fc = self.ApplySymmetry(sym, irt = current_irt) + + print (i) + + # Try to add the sum rule here + #newP = self.Copy() + #newP.dynmats[0] = current_fc +# #newP.ApplySumRule() +# +# distance = np.sum( (self.dynmats[0] - current_fc)**2) +# distance = np.real(np.sqrt(distance)) +# + #print "%d) d = " % (i+1), distance + + new_fc += current_fc + + # Average all the symmetrized structures + new_fc /= len(symmetries) + + + print ("DIST_SYM_FORC:", np.sqrt(np.sum( (new_fc - self.dynmats[0])**2))) + self.dynmats[0] = new_fc.copy() + + + # Print the phonons all toghether + #print "\n".join( ["\t".join("%.4e" % (xval - freqs[0,j]) for xval in freqs[:, j]) for j in range(3 * self.structure.N_atoms)]) + + # Apply the acustic sum rule + if apply_sum_rule: + self.ApplySumRule() + + def DiagonalizeSupercell(self, verbose = False, lo_to_split = None): + r""" + DYAGONALIZE THE DYNAMICAL MATRIX IN THE SUPERCELL + ================================================= + + This method dyagonalizes the dynamical matrix using the supercell approach. + + In this way we simply generate the polarization vector in the supercell + using those in the unit cell. + + This is performed using the following equation: + + .. math :: + + e_\mu^0(R_0) = \frac{\sqrt{|\tilde e_{q\nu}^a|^2}}{N_q} + + e_\mu^a(R_a) = \frac{\cos(\vec q\cdot \Delta R_{a0}) \Re\left[\tilde e_{q\nu}^a\tilde {e_{q\nu}^b}^\dagger\right] - \sin(\vec q\cdot \Delta R_{a0}) \Im\left[\tilde e_{q\nu}^a\tilde {e_{q\nu}^b}^\dagger\right]}{e_\mu^0(R_0)N_q} + + Here the :math:`\tilde e_{q\nu}` are the complex polarization vectors in the q point so that :math:`\omega_{q\nu} = \omega_{\mu}`. + + Parameters + ---------- + lo_to_split : string or ndarray + Could be a string with random, or a ndarray indicating the direction on which the + LO-TO splitting is computed. If None it is neglected. + If LO-TO is specified but no effective charges are present, then a warning is print and it is ignored. + Results + ------- + w_mu : ndarray( size = (n_modes), dtype = np.double) + Frequencies in the supercell + e_mu : ndarray( size = (3*Nat_sc, n_modes), dtype = np.double, order = "F") + Polarization vectors in the supercell + """ + + supercell_size = len(self.q_tot) + nat = self.structure.N_atoms + + nmodes = 3*nat*supercell_size + nat_sc = nat*supercell_size + + w_array = np.zeros( nmodes, dtype = np.double) + e_pols_sc = np.zeros( (nmodes, nmodes), dtype = np.double, order = "F") + + # Get the structure in the supercell + super_structure = self.structure.generate_supercell(self.GetSupercell()) + + # Get the supercell correspondence vector + itau = super_structure.get_itau(self.structure) - 1 # Fort2Py + + # Get the itau in the contracted indices (3*nat_sc -> 3*nat) + itau_modes = (np.tile(np.array(itau) * 3, (3,1)).T + np.arange(3)).ravel() + + # Get the position in the supercell + R_vec = np.zeros((nmodes, 3), dtype = np.double) + for i in range(nat_sc): + R_vec[3*i : 3*i+3, :] = np.tile(super_structure.coords[i, :] - self.structure.coords[itau[i], :], (3,1)) + + i_mu = 0 + bg = self.structure.get_reciprocal_vectors() / (2*np.pi) + for iq, q in enumerate(self.q_tot): + # Check if the current q point has been seen (we do not distinguish between q and -q) + skip_this_q = False + for jq, q_prev in enumerate(self.q_tot): + if jq >= iq: + break + + # Check if q and q_prev are related by a G-q operation + dist = Methods.get_min_dist_into_cell(bg, -q, q_prev) + if dist < __EPSILON__: + skip_this_q = True + break + + if skip_this_q: + continue + + + # Check if this q = -q + G + is_minus_q = False + if Methods.get_min_dist_into_cell(bg, q, -q) < 1e-6: + is_minus_q = True + + # The dynamical matrix must be real + re_part = np.real(self.dynmats[iq]) + + assert np.max(np.abs(np.imag(self.dynmats[iq]))) < __EPSILON__, "Error, at point {} (q = -q + G) the dynamical matrix is complex".format(iq) + + # Enforce reality to avoid complex polarization vectors + self.dynmats[iq] = re_part + + # Check if this is gamma (to apply the LO-TO splitting) + if Methods.get_min_dist_into_cell(bg, q, np.zeros(3)) < 1e-16 and lo_to_split is not None: + if self.effective_charges is None: + warnings.warn("WARNING: Requested LO-TO splitting without effective charges. LO-TO ignored.") + + # Initialize the Force Constant + t2 = ForceTensor.Tensor2(self.structure, self.structure.generate_supercell(self.GetSupercell()), self.GetSupercell()) + t2.SetupFromPhonons(self) + + if lo_to_split.lower() == "random": + fc_gamma = t2.Interpolate(np.zeros(3)) + else: + fc_gamma = t2.Interpolate(np.zeros(3), q_direct= -lo_to_split) + + _m_ = np.tile(self.structure.get_masses_array(), (3,1)).T.ravel() + d_gamma = fc_gamma / np.sqrt(np.outer(_m_, _m_)) + wq2, eq = np.linalg.eigh(d_gamma) + + wq = np.sqrt(np.abs(wq2)) * np.sign(wq2) + else: + # Diagonalize the matrix in the given q point + wq, eq = self.DyagDinQ(iq) + + # Iterate over the frequencies of the given q point + nm_q = i_mu + for i_qnu, w_qnu in enumerate(wq): + + tilde_e_qnu = eq[:, i_qnu] + + # If this is a minus_q, enforce reality of the vector + # To correctly fix the gauge + if is_minus_q: + # Get the phase factor from the first non zero value + phase_gauge = 0 + for e_a in tilde_e_qnu: + if np.abs(e_a) > __EPSILON__: + phase_gauge = np.angle(e_a) + break + + # Work only if it is not already real + if np.abs(phase_gauge) > __EPSILON__ and np.abs(phase_gauge - np.pi) > __EPSILON__: + # Apply the phase factor to the polarization vector + tilde_e_qnu *= np.exp(-1j * phase_gauge) + + # Check if the polarization vector is real + re_tilde_e_qnu = np.real(tilde_e_qnu) + + print("Phase:", phase_gauge) + print("Vector:", tilde_e_qnu) + assert np.max(np.abs(re_tilde_e_qnu - tilde_e_qnu)) < __EPSILON__, "Error while enforcing reality of {}".format(tilde_e_qnu) + + tilde_e_qnu = re_tilde_e_qnu + + phase = R_vec.dot(q) * 2 * np.pi + c_e_sc = tilde_e_qnu[itau_modes] * np.exp(1j*phase) / np.sqrt(supercell_size) + c_e_sc_mq = np.conj(c_e_sc) + + # Get the real and imaginary part + evec_1 = np.real(.5 * (c_e_sc + c_e_sc_mq)) + evec_2 = np.real((c_e_sc - c_e_sc_mq) / ( 2*1j)) + + # Check if they are not zero + norm1 = evec_1.dot(evec_1) + norm2 = evec_2.dot(evec_2) + scalar_dot = 0 + EPSILON = 1e-5 + + if norm2 > EPSILON and norm1 > EPSILON: + scalar_dot = evec_1.dot(evec_2) / np.sqrt(norm1 * norm2) + + if verbose: + print("IQ: {}, MODE: {} has norm1 = {} | norm2 = {} | scalar_dot = {}".format(iq, i_qnu, np.sqrt(norm1), np.sqrt(norm2), scalar_dot)) + + # Check if add to the polarization both 1 and 2 + add_1 = False + add_2 = False + + if norm1 > EPSILON: + add_1 = True + + if norm2 > EPSILON: + add_2 = True + + if is_minus_q: + if add_1 and add_2: + if np.abs(np.abs(scalar_dot) - 1) > EPSILON: + raise ValueError("Error, with q = -q + G, the two vectors should be linearly dependent") + + # In this case remove the one with lower norm (higher numerical accuracy) + if norm1 > norm2: + add_2 = False + else: + add_1 = False + + + + # If this is a q != -q point, this q point must contribute also for -q + # Thus twice the elements should be present. + if not is_minus_q: + if not (add_1 and add_2): + raise ValueError("Error, the q_point = {} {} {} should contribute also for -q, something went wrong".format(*list(q))) + + + if add_1 and add_2: + # Since both real and imaginary should match in this case + # Add only one of them + if is_minus_q: + add_2 = False + + if verbose: + print(" add_1 = {}; add_2 = {}".format(add_1, add_2)) + + + # Add the vectors + if add_1: + w_array[i_mu] = w_qnu + e_pols_sc[:, i_mu] = evec_1 / np.sqrt(norm1) + i_mu += 1 + if add_2: + w_array[i_mu] = w_qnu + e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) + i_mu += 1 + + + # # Add the second vector + # if norm1 > EPSILON: + # #q_cryst = Methods.covariant_coordinates(bg, q) + # #print ("IMU: {}, IQ: {}, IQNU: {}, TOTQ: {}, Q = {}, N1 = {:.3e}, N2 = {:.3e}, DOT = {:.3e}".format(i_mu, iq, i_qnu, len(self.q_tot), q_cryst, norm1, norm2, evec_1.dot(evec_2))) + # w_array[i_mu] = w_qnu + # e_pols_sc[:, i_mu] = evec_1 / np.sqrt(norm1) + # i_mu += 1 + + # # If there is another q point + # if not is_minus_q: #scalar_dot < EPSILON: + # if norm2 < EPSILON: + # raise ValueError("Error, the q_point = {} {} {} should contribute also for -q, something went wrong".format(*list(q))) + + + # w_array[i_mu] = w_qnu + # e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) + # i_mu += 1 + # else: + # w_array[i_mu] = w_qnu + # e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) + # i_mu += 1 + + # Print how many vectors have been extracted + if verbose: + print("The {} / {} q point produced {} nodes".format(iq, len(self.q_tot), i_mu - nm_q)) + + + + + # Sort the frequencies + sort_mask = np.argsort(w_array) + w_array = w_array[sort_mask] + e_pols_sc = e_pols_sc[:, sort_mask] + + + # Get the check for the polarization vector normalization + assert np.max(np.abs(np.einsum("ab, ab->b", e_pols_sc, e_pols_sc) - 1)) < __EPSILON__ + + return w_array, e_pols_sc + + + + + def ReadInfoFromESPRESSO(self, filename, read_dielectric_tensor = True, read_eff_charges = True, read_raman_tensor = True): + """ + READ INFO FROM ESPRESSO + ======================= + + This method reads the effective charges, the dielectric tensor as well as + the Raman tensor from an espresso phonon output file. + It is usefull if you want to run the electric field perturbation without computing + all the phonon spectrum, deriving only with respect to the electric field. + + + Parameters + ---------- + filename : string + Path to the standard output of the ph.x calculation. + read_dielectric_constant: bool + If False, the dielectric tensor will be ignored + read_effective_charge : bool + If False, the effective charges will be ignored + read_raman_tensor : bool + If False, the Raman tensor will be ignored. + """ + + if not os.path.exists(filename): + raise IOError("Error, the given file {} does not exist".format(filename)) + + # Read all the file + f = open(filename, "r") + lines = [l.strip() for l in f.readlines()] + f.close() + + # The triggers to know what I am reading + reading_dielectric = False + reading_eff_charges = False + reading_raman = False + + reading_index = 0 + reading_atom = 0 + reading_pol = 0 + + + if read_dielectric_tensor and len([x for x in lines if "Dielectric constant in " in x]): + self.dielectric_tensor = np.zeros((3,3), dtype = np.double) + if read_eff_charges and len([x for x in lines if "Effective charges" in x]): + self.effective_charges = np.zeros( (self.structure.N_atoms, 3, 3), dtype = np.double) + if read_raman_tensor and len([x for x in lines if "Raman tensor" in x]): + self.raman_tensor = np.zeros((3,3, 3* self.structure.N_atoms), dtype = np.double) + + # Start the analysis + for line in lines: + data = line.split() + if len(data) == 0: + continue + + # Check the number of atoms coincides + if "atoms/cell" in line: + nat = int(data[4]) + if nat != self.structure.N_atoms: + raise ValueError("Error, this Phonon has {} atoms, while the {} calculations contains {} atoms".format(self.structure.N_atoms, filename, nat)) + + # Check if we are reading the dielectric + if "Dielectric constant in " in line: + reading_dielectric = True + reading_eff_charges = False + reading_raman = False + reading_index = 0 + reading_atom = 0 + reading_pol = 0 + elif "Effective charges" in line: + reading_dielectric = False + reading_eff_charges = True + reading_raman = False + reading_index = 0 + reading_atom = 0 + reading_pol = 0 + elif "Raman tensor (A^2)" in line: + reading_dielectric = False + reading_eff_charges = False + reading_raman = True + reading_index = 0 + reading_atom = 0 + reading_pol = 0 + + + # Check if we must read the dielectric file + if reading_dielectric and read_dielectric_tensor: + if len(data) == 5 and data[0] == "(": + self.dielectric_tensor[reading_index, :] = [float(x) for x in data[1:4]] + reading_index += 1 + + if reading_eff_charges and read_eff_charges: + if data[0] == "atom": + reading_atom = int(data[1]) - 1 + reading_index = 0 + # Check the consistency of the atom type + atm_type = data[2] + if self.structure.atoms[reading_atom] != atm_type: + error = """ +Error while reading {}: + atom index {} shoud be {}, while it is {} (index {}) +""".format(filename, reading_atom, self.structure.atoms[reading_atom], atm_type, reading_atom+1) + raise ValueError(error) + if len(data) == 6 and data[0][0] == 'E': + self.effective_charges[reading_atom, reading_index, :] = [float(x) for x in data[2:5]] + reading_index += 1 + + # Check if we ended + if reading_atom == self.structure.N_atoms - 1 and reading_index == 3: + reading_eff_charges = False + + + # Reading the raman + if reading_raman and read_raman_tensor: + if data[0] == "atom": + reading_atom = int(data[2]) - 1 + reading_index = 0 + reading_pol = int(data[4]) - 1 + + if reading_atom >= self.structure.N_atoms: + error_msg = """ + Error, trying to read atom {} from inputfile {}. + I expect a maximum of {} atoms from this structure. +""".format(reading_atom + 1, filename, self.structure.N_atoms) + raise ValueError(error_msg) + + if len(data) == 3: + is_good_line = False + try: + float(data[0]) + is_good_line = True + except: + pass + + if is_good_line: + numbers = [float(x) for x in data] + self.raman_tensor[reading_index, :, 3 * reading_atom + reading_pol] = numbers + reading_index += 1 + + + + + + +def ImposeSCTranslations(fc_supercell, unit_cell_structure, supercell_structure, itau = None): + """ + IMPOSE TRANSLATION IN THE SUPERCELL + =================================== + + This subroutine imposes the unit cell translations of the supercell force constant matrix. + Note that it is very different from the acustic sum rule. + + .. math:: + + C_{k\\alpha,k'\\beta}(a,b) = C_{k\\alpha,k'\\beta}(0, b-a) + + + Parameters + ---------- + fc_supercell : ndarray (3nat_sc x 3nat_sc) + The input-output force constant matrix in real space. + unit_cell_structure: Structure() + The structure in the unit cell + supercell_structure : Structure() + The structure of the supercell + itau : optional, ndarray (int) + The equivalence between unit_cell and supercell atoms. If None it is + extracted by the given structures. Note it must be in fortran language + """ + + + if itau is None: + # Get the fortran one + itau = supercell_structure.get_itau(unit_cell_structure) + + nat_sc = supercell_structure.N_atoms + fc_tmp = np.zeros( (3,3, nat_sc, nat_sc), dtype = np.float64, order = "F") + tau_sc_cryst = np.zeros( (3, nat_sc), dtype = np.float64, order = "F") + + for i in range(nat_sc): + tau_sc_cryst[:,i] = Methods.covariant_coordinates(supercell_structure.unit_cell, supercell_structure.coords[i, :]) + + # Fill the force constant matrix + for i in range(nat_sc): + for j in range(nat_sc): + fc_tmp[:,:, i, j] = fc_supercell[3*i : 3*i + 3, 3*j: 3*j+3] + + # Call the fortran suboruitne + symph.impose_trans_sc(fc_tmp, tau_sc_cryst, itau, nat_sc) + + #Revert it in the original force constant matrix + for i in range(nat_sc): + for j in range(nat_sc): + fc_supercell[3*i : 3*i + 3, 3*j: 3*j+3] = fc_tmp[:,:, i, j] + + + + + +def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structure, itau = None, img_thr = 1e-5): + """ + GET THE REAL SPACE FORCE CONSTANT + ================================= + + This subroutine uses the fourier transformation to get the real space force constant, + starting from the fourer space matrix. + + .. math:: + + C_{k\\alpha,k'\\beta}(0, b) = \\frac{1}{N_q} \\sum_q \\tilde C_{k\\alpha k'\\beta}(q) e^{i\\vec q \\cdot \\vec R_b} + + Then the translationa property is applied. + + .. math:: + + C_{k\\alpha,k'\\beta}(a, b) = C_{k\\alpha,k'\\beta}(0, b-a) + + Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\\alpha` is the + cartesian indices. + + + Parameters + ---------- + dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) + The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. + q_tot : ndarray ( nq, 3) + The q vectors in Angstrom^-1 + unit_cell_structure : Structure() + The reference structure of the unit cell. + supercell_structure : Structure() + The reference structure of the supercell. It is used to keep the same indices of the atomic positions. + Note, it is required that consecutive atoms are placed sequently + itau : Ndarray(nat_sc) , optional + the correspondance between the supercell atoms and the unit cell one. + If None is recomputed + Returns + ------- + fc_supercell : ndarray 3nat_sc x 3nat_sc + The force constant matrix in the supercell. + + """ + + # Define the number of q points, atoms and unit cell atoms + nq = len(q_tot) + nat = np.shape(dynmat)[1] //3 + nat_sc = nq*nat + + + if itau is None: + itau = supercell_structure.get_itau(unit_cell_structure)-1 + + #dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") + fc = np.zeros((3*nat_sc, 3*nat_sc), dtype = np.complex128) + + + fc = symph.fast_ft_real_space_from_dynq(unit_cell_structure.coords, supercell_structure.coords, itau+1, np.array(q_tot), dynmat, unit_cell_structure.N_atoms, supercell_structure.N_atoms, q_tot.shape[0]) + + + + """ + for i in range(nat_sc): + i_uc = itau[i] + t1 = time.time() + for j in range(nat_sc): + j_uc = itau[j] + R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] + R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] + + # q_dot_R is 1d array that for each q contains the scalar product with R + q_dot_R = q_tot.dot(R) + + t2 = time.time() + fc[3*i : 3*i + 3, 3*j : 3*j + 3] += np.einsum("abc, a", dynmat[:, 3*i_uc : 3*i_uc + 3, 3*j_uc: 3*j_uc + 3], np.exp(1j * 2*np.pi * q_dot_R)) / nq + t3 = time.time() + + print("Time to do a single cycle: ", t3 - t2) + print("Total number of cycles = {} / {}".format(nat_sc * i + j + 1, nat_sc**2)) + print("Time for a whole cycle: ", t3 - t1) """ +# +# # For now, to test, just the unit cell +# for i in range(nq): +# start_index = 3*nat*i +# for j in range(nq): +# end_index = 3*nat*j +# +# q_dot_R = np.sum(q_tot[i,:] * R_vectors_cart[j,:]) +# +# fc[end_index: end_index + 3*nat, start_index: start_index + 3*nat ] += dynmat[i,:,:] * np.exp(1j* 2 * np.pi* q_dot_R) / nq +# + + #np.sum(dynmat * np.exp(), axis = 0) / nq + #print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) + + # Check the imaginary part + imag = np.sqrt(np.sum(np.imag(fc)**2)) + ASSERT_ERROR = """ + Error, the imaginary part of the real space force constant + is not zero. IMAG={} + """ + assert imag < img_thr, ASSERT_ERROR.format(imag) + + # Remove anyway the imaginary part + return fc - 1j*np.imag(fc) + + + +def GetDynQFromFCSupercell(fc_supercell, q_tot, unit_cell_structure, supercell_structure, itau = None, fc2 = None): + r""" + GET THE DYNAMICAL MATRICES + ========================== + + This subroutine uses the fourier transformation to get the dynamical matrices, + starting from the real space force constant. + + .. math:: + + \tilde C_{k\alpha k'\beta}(q) = \sum_{b}C_{k\alpha,k'\beta}(0, b)e^{i\vec q \cdot \vec R_b} + + + Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\alpha` is the + cartesian indices. + + + Parameters + ---------- + fc_supercell : ndarray 3nat_sc x 3nat_sc + The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. + q_tot : ndarray ( nq, 3) + The q vectors in Angstrom^-1 + unit_cell_structure : Structure() + The structure of the unit cell + supercell_structure : Structure() + The structure of the supercell + Returns + ------- + dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) + The force constant matrix in the supercell. + + """ + + # Define the number of q points, atoms and unit cell atoms + nq = np.shape(q_tot)[0] + nat_sc = np.shape(fc_supercell)[0]//3 + nat = nat_sc // nq + + if itau is None: + itau = supercell_structure.get_itau(unit_cell_structure)-1 + + + #dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") + dynmat = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) + + if fc2 is not None: + dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) + #print "NQ:", nq + + for i in range(nat_sc): + i_uc = itau[i] + for j in range(nat_sc): + j_uc = itau[j] + R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] + R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] + + # q_dot_R is 1d array that for each q contains the scalar product with R + q_dot_R = q_tot.dot(R) + + dynmat[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc_supercell[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq + + if fc2 is not None: + dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq + +# +# # Fill the dynamical matrix +# for i in range(nq): +# +# a_x = i % supercell_array[0] +# a_y = (i / supercell_array[0]) % supercell_array[1] +# a_z = i / (supercell_array[0] * supercell_array[1]) +# R_vectors_cart[i,:] = a_x * unit_cell[0,:] + a_y * unit_cell[1,:] + a_z * unit_cell[2,:] +# +# +# +# # For now, to test, just the unit cell +# for i in range(nq): +# start_index = 3 * nat * i +# q_dot_R = np.einsum("ab, b", q_tot, R_vectors_cart[i,:]) +# #print "%d, %d => q dot R = " % (i, j), np.exp(1j * q_dot_R) +# dynmat[:,:,:] += np.einsum("bc, a->abc", fc_supercell[:3 *nat, start_index : start_index + 3*nat], np.exp(1j* 2 * np.pi* q_dot_R)) + + + + if fc2 is not None: + return dynmat, dynmat2 + else: + return dynmat + + +def GetDynQFromFCSupercell_parallel(fc_supercell, q_tot, unit_cell_structure, supercell_structure, itau = None, fc2 = None): + r""" + Look at GetDynQFromFCSupercell. This is the mpi enabled version of that subroutine. + TODO: Still to be tested properly + + Parameters + ---------- + fc_supercell : ndarray 3nat_sc x 3nat_sc + The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. + q_tot : ndarray ( nq, 3) + The q vectors in Angstrom^-1 + unit_cell_structure : Structure() + The structure of the unit cell + supercell_structure : Structure() + The structure of the supercell + Returns + ------- + dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) + The force constant matrix in the supercell. + + """ + + # Define the number of q points, atoms and unit cell atoms + nq = np.shape(q_tot)[0] + nat_sc = np.shape(fc_supercell)[0]//3 + nat = nat_sc // nq + + if itau is None: + itau = supercell_structure.get_itau(unit_cell_structure)-1 + + def fourier_transform_reduction(ij_pair): + i, j = ij_pair + i_uc = itau[i] + j_uc = itau[j] + R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] + R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] + + # q_dot_R is 1d array that for each q contains the scalar product with R + q_dot_R = q_tot.dot(R) + + dynmat = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) + dynmat[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] = np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc_supercell[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq + #if fc2 is not None: + # dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) + # dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq + + return dynmat + + # Prepare the inputs TODO: this can be speedup + list_of_inputs = [] + for i in range(nat_sc): + for j in range(nat_sc): + list_of_inputs.append([i,j]) + + dynmat = Settings.GoParallel(fourier_transform_reduction, list_of_inputs, "+") + + if fc2 is not None: + def fourier_transform_reduction2(ij_pair): + i, j = ij_pair + i_uc = itau[i] + j_uc = itau[j] + R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] + R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] + + # q_dot_R is 1d array that for each q contains the scalar product with R + q_dot_R = q_tot.dot(R) + + dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) + dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] = np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq + + return dynmat2 + + dynmat2 = Settings.GoParallel(fourier_transform_reduction2, list_of_inputs, "+") + + return dynmat, dynmat2 + + return dynmat + + + + +def InterpolateDynFC(starting_fc, coarse_grid, unit_cell_structure, super_cell_structure, q_point): + """ + INTERPOLATE FORCE CONSTANT MATRIX + ================================= + + Interpolate the real space force constant matrix in a bigger supercell. + This can be used to obtain a dynamical matrix in many other q points. + This function uses the quantum espresso matdyn.x subroutines. + + Parameters + ---------- + starting_fc : ndarray(size=(3*natsc , 3*natsc), dtype = float64) + Array of the force constant matrix in real space. + coarse_grid : ndarray(size=3, dtype=int) + The dimension of the supercell that defines the starting_fc. + unit_cell_structure : Structure() + The structure in the unit cell + super_cell_structure : Structure() + The structure in the super cell + q_point : ndarray(size=3, dtype=float64) + The q point in which you want to interpolate the dynamical matrix. + + Results + ------- + dyn_mat : ndarray(size=(3*nat, 3*nat), dtype = complex128) + The interpolated dynamical matrix in the provided q point. + """ + # Get some info about the size + supercell_size = np.prod(coarse_grid) + natsc = np.shape(starting_fc)[0] // 3 + nat = natsc // supercell_size + + #print "nat:", nat + #print "natsc:", natsc + + + # Get the force constant in an appropriate supercell + QE_frc = np.zeros((coarse_grid[0], coarse_grid[1], coarse_grid[2], 3, 3, nat, nat), dtype = np.float64, order = "F") + QE_fc = np.zeros((3,3,natsc, natsc), dtype = np.float64, order = "F") + QE_itau = super_cell_structure.get_itau(unit_cell_structure) + QE_tau = np.zeros((3, nat), dtype = np.float64, order = "F") + QE_tau_sc = np.zeros((3, natsc), dtype = np.float64, order = "F") + QE_at = np.zeros((3,3), dtype = np.float64, order = "F") + QE_at_sc = np.zeros((3,3), dtype = np.float64, order = "F") + + for i in range(natsc): + for j in range(natsc): + QE_fc[:,:, i, j] = starting_fc[3*i : 3*(i+1), 3*j : 3*(j+1)] + + + QE_at[:,:] = unit_cell_structure.unit_cell.transpose() + QE_at_sc[:,:] = super_cell_structure.unit_cell.transpose() + QE_tau[:,:] = unit_cell_structure.coords.transpose() + QE_tau_sc[:,:] = super_cell_structure.coords.transpose() + + #print "ENTERING IN GET_FRC" + QE_frc[:,:,:,:,:,:,:] = symph.get_frc(QE_fc, QE_tau, QE_tau_sc, QE_at, QE_itau, + coarse_grid[0], coarse_grid[1], coarse_grid[2], nat, natsc) + #print "EXITING IN GET_FRC" + + # Initialize the interpolation + nrwsx = 200 + QE_rws = np.zeros((4, nrwsx), dtype = np.float64, order = "F") + #print "ENTERING IN WSINIT" + nrws = symph.wsinit(QE_rws, QE_at_sc, nrwsx) + #print "EXTING FROM WSINIT" + + # Perform the interpolation + QE_q = np.array(q_point, dtype = np.float64) + #print "ENTERING:" + #print "TAU SHAPE:", np.shape(QE_tau) + #print "FRC SHAPE:", np.shape(QE_frc) + new_dyn = symph.frc_blk(QE_q, QE_tau, QE_frc, QE_at, QE_rws, nrws, nat, + coarse_grid[0], coarse_grid[1], coarse_grid[2]) + + # Conver the dynamical matrix in the Cellconstructor format + output_dyn = np.zeros( (3*nat, 3*nat), dtype = np.complex128) + for i in range(nat): + for j in range(nat): + output_dyn[3*i : 3*(i+1), 3*j: 3*(j+1)]= new_dyn[:,:, i, j] + + return output_dyn + + + + +def get_dyn_from_ase_phonons(ase_ph, adjust_qstar = True): + """ + GET THE DYNAMICAL MATRIX FROM ASE + ================================= + + This function converts an ASE phonons object into the cellconstructor Phonons. + + Parameters + ---------- + ase_ph : ase.phonons.Phonons() + The ASE Phonons. It must be already computed + adjust_qstar : bool + If true the q points are ordered in star, preparing the dynamical matrix + for the symmetrization + + Results + ------- + dyn : CC.Phonons.Phonons() + The dynamical matrix + """ + + + FC = ase_ph.get_force_constant() + + supercell_size = ase_ph.N_c + + # Get the structure + structure = Structure.Structure() + structure.generate_from_ase_atoms(ase_ph.atoms) + + # Check if the structure has the unit cell + if np.linalg.det(structure.unit_cell) == 0: + ERROR_MSG = """ + Error, the ASE strucure passed to method 'get_dyn_from_ase_phonons' + does not have a valid unit cell. + If you are computing a isolated molecule, + you have to define an unit_cell that contains the molecule + (it will not affect the calculation) + """ + raise ValueError(ERROR_MSG) + + # Get the supercell structure and itau + nat_sc = structure.N_atoms * np.prod(supercell_size) + supercell_structure = structure.generate_supercell(supercell_size) + # Get the equivalent atom in the unit cell vs atoms in the supercell + itau = supercell_structure.get_itau(structure) - 1 # Fort -> Py (indexing) + + # Get the lattice vectors + R_cN = ase_ph.lattice_vectors() + R_cN = np.array(R_cN).T + + # Get the lattice in cartesian units + R_cN = R_cN.dot(structure.unit_cell) + + N_sup = np.prod(supercell_size) + + q_grid = symmetries.GetQGrid(structure.unit_cell, supercell_size) + + # Put gamma as the first vector + gamma_index = np.argmin(np.sum(np.array(q_grid)**2, axis = 1)) + q_grid[gamma_index] = q_grid[0].copy() + q_grid[0] = np.zeros(3, dtype = np.double) + + + # Prepare the dynamical matrix + dyn = Phonons(structure, len(q_grid)) + dyn.q_tot = q_grid + + # Each q point in a different star + dyn.q_stars = [ [q] for q in q_grid] + + # Generate the dynamical matrix in the supercell + fc_sup = np.zeros( (3*nat_sc, 3*nat_sc), dtype = np.double) + + # TODO: This can be slow + # It could be speeded up by getting directly the eigenmodes at the wanted q points. + for ia in range(nat_sc): + for ib in range(ia, nat_sc): + # lattice vector + R_1 = supercell_structure.coords[ia, :] - structure.coords[itau [ia], :] + R_2 = supercell_structure.coords[ib, :] - structure.coords[itau [ib], :] + delta_R = R_2 - R_1 + + i_block = Methods.identify_vector(supercell_structure.unit_cell, R_cN, delta_R) + + if i_block == None: + ERR_MSG=""" +ERROR, the ASE Phonons seems not to contain a supercell vector needed for the + force constant matrix: +Lattice vector needed : {:16.8f} {:16.8f} {:16.8f} +List of ASE vectors: {}""".format(delta_R[0], delta_R[1], delta_R[2], R_cN) + raise ValueError(ERR_MSG) + + for xa in range(3): + for xb in range(3): + # We found the block + fc_sup[3*ia + xa, 3*ib + xb] = FC[i_block, 3*itau[ia] + xa, 3*itau[ib] + xb] + fc_sup[3*ib + xb, 3*ia + xa] = fc_sup[3*ia + xa, 3*ib + xb] + + # Now get the dynamical matrix in the correct q point + dynq = GetDynQFromFCSupercell(fc_sup, np.array(dyn.q_tot), structure, supercell_structure) + + for iq in range(len(dyn.q_tot)): + # Convert from eV/A^2 into Ry/Bohr^2 + dyn.dynmats[iq] = dynq[iq, :, :] * BOHR_TO_ANGSTROM**2 / RY_TO_EV + + # Now adjust the q stars to match the symmetries + if adjust_qstar: + dyn.AdjustQStar() + + return dyn + + + + + +def compute_phonons_finite_displacements(structure, ase_calculator, epsilon = 0.05, supercell = (1,1,1), progress = -1, progress_bar = False): + """ + COMPUTE THE FORCE CONSTANT MATRIX + ================================= + + Use finite displacements to compute the force constant matrix. + (Works only at Gamma) + + Parameters + ---------- + structure : CC.Structure.Structure + The structure on the parameters + ase_calculator : ase.calculators.calculator + The ase calculator to compute energy and forces + epsilon : double + The finite displacement + progress : int + If positive, prints the status each tot structures + progress_bar : bool + If True, overwrite the progress line each structure + + Results + ------- + phonons : CC.Phonons.Phonons() + The dynamical matrix + """ + + + super_structure = structure.generate_supercell(supercell) + final_dyn = Phonons(super_structure) + + nat3 = 3 * super_structure.N_atoms + fc = np.zeros( (nat3, nat3), dtype = np.double) + + # Enable the parallel calculation + ase_calculator.directory = "calc_{}".format(Settings.get_rank()) + ase_calculator.set_label("label_{}".format(Settings.get_rank())) + + + #atm = structure.get_ase_atoms() + #atm.set_calculator(ase_calculator) + fc[:,:] = np.zeros((nat3, nat3), np.double) + if progress > 0: + print() + print("Computing phonons with finite differences.") + + + list_of_calculations = [] + + for i in range(super_structure.N_atoms): + for j in range(3): + list_of_calculations.append((i,j)) + + def compute_force(indices): + i, j = indices + #Settings.all_print("Computing indices:", i, j) + + if progress > 0: + if (3*i + j) % progress == 0: + if progress_bar and Settings.am_i_the_master(): + sys.stdout.write("\rProgress {:4.1f} % ... ".format(100 * (3*i + j + 1) / nat3)) + sys.stdout.flush() + else: + Settings.all_print("Finite displacement of structure {} / {}".format(3*i + j + 1, nat3)) + + + + s = super_structure.copy() + s.coords[i, j] += epsilon + + + ase_calculator.set_label("disp_{}".format(3*i + j)) + ase_calculator.directory = "disp_{}".format(3*i + j) + energy, forces = calculators.get_energy_forces(ase_calculator, s) + fc_tmp = np.zeros((nat3, nat3), dtype = np.double) + fc_tmp[3*i+j,:] -= forces.ravel() + + #print("FORCE ({}) = ".format(3*i+j), forces) + return fc_tmp + #atm = s.get_ase_atoms() + #atm.set_calculator(ase_calculator) + fc[3*i + j, :] -= forces.ravel() + + fc = Settings.GoParallel(compute_force, list_of_calculations, reduce_op='+') + + #if Settings.am_i_the_master(): + # np.savetxt("FC_before_subtraction.dat", fc) + + energy = None + forces = None + if Settings.am_i_the_master(): + energy, forces = calculators.get_energy_forces(ase_calculator, super_structure) + fc[:,:] += np.tile(forces.ravel(), (nat3, 1)) + Settings.barrier() + fc = Settings.broadcast(fc) + + #if Settings.am_i_the_master(): + # np.savetxt("FC_after_subtraction.dat", fc) + + if progress > 0: + print() + print("Done.") + + # Impose hermitianity + fc = .5 * (fc + fc.T) / epsilon + + # Convert to the correct units + final_dyn.dynmats[0] = fc / RY_TO_EV * BOHR_TO_ANGSTROM**2 + + + # Now we have the dynamical matrix in the supercell, get the dynamical matrix in the correct unit cell + if np.prod(supercell) > 1: + correct_dyn = Phonons(structure, nqirr = np.prod(supercell)) + q_tot = symmetries.GetQGrid(structure.unit_cell, supercell) + dynq = GetDynQFromFCSupercell(final_dyn.dynmats[0], np.array(q_tot), structure, super_structure) + for iq, q in enumerate(q_tot): + correct_dyn.dynmats[iq] = dynq[iq, :,:] + correct_dyn.q_tot[iq] = q + + correct_dyn.AdjustQStar() + final_dyn = correct_dyn + + return final_dyn diff --git a/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py new file mode 100644 index 00000000..ac4313c5 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py @@ -0,0 +1,2319 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 6 10:44:27 2018 + +@author: pione +""" +from __future__ import print_function +from __future__ import absolute_import + +import numpy as np +try: + __ASE__ = True + import ase + import ase.io +except: + __ASE__ = False + +try: + import phonopy, phonopy.structure.atoms + __PHONOPY__ = True +except: + __PHONOPY__ = False + + +import sys, os + +import cellconstructor.Methods as Methods +import cellconstructor.symmetries as SYM +from cellconstructor.Units import * +import cellconstructor.Timer as Timer + +#import ase.visualize + +import symph +import warnings + + + + +__all__ = ["Structure"] +BOHR_TO_ANGSTROM=0.529177249 + +class Structure(object): + def __init__(self, nat=0): + self.N_atoms=nat + # Coordinates are always express in chartesian axis + self.coords = np.zeros((self.N_atoms, 3), dtype = np.float64) + self.atoms = ["H"] * nat + self.unit_cell = np.zeros((3,3)) # Note: setting the unit cell to something different from zero automatically put has_unit_cell to true + self.has_unit_cell = False + self.masses = {} + self.ita = 0 # Symmetry group in ITA standard + + # Setup the attribute control + self.__total_attributes__ = [item for item in self.__dict__.keys()] + self.fixed_attributes = True # This must be the last attribute to be setted + + + def __setattr__(self, name, value): + """ + This method is used to set an attribute. + It will raise an exception if the attribute does not exists (with a suggestion of similar entries) + """ + + + if "fixed_attributes" in self.__dict__: + if name in self.__total_attributes__: + super(Structure, self).__setattr__(name, value) + elif self.fixed_attributes: + similar_objects = str( difflib.get_close_matches(name, self.__total_attributes__)) + ERROR_MSG = """ + Error, the attribute '{}' is not a member of '{}'. + Suggested similar attributes: {} ? + """.format(name, type(self).__name__, similar_objects) + + raise AttributeError(ERROR_MSG) + else: + super(Structure, self).__setattr__(name, value) + + + # Here check the consistency of the input + if name == "unit_cell": + if np.abs(np.linalg.det(value)) > 1e-8: + self.has_unit_cell = True + + + def get_volume(self): + """ + Returns the volume of the unit cell + """ + ERR_MSG = """ +Error, to compute the volume the structure must have a unit cell initialized: +(i.e. the has_unit_cell attribute must be True).""" + + assert self.has_unit_cell, ERR_MSG + + return np.abs(np.linalg.det(self.unit_cell)) + + def generate_from_ase_atoms(self, atoms, get_masses = True): + """ + This subroutines generate the current structure + from the ASE Atoms object + + Parameters + ---------- + atoms : the ASE Atoms object + get_masses : bool + If true, also build the masses. + Note that massess are saved in Ry units (electron mass) + + """ + + self.unit_cell = atoms.get_cell() + self.has_unit_cell = True + self.atoms = atoms.get_chemical_symbols() + self.N_atoms = len(self.atoms) + self.coords = atoms.positions.copy() + + if get_masses: + self.masses = {} + mass = atoms.get_masses() + for i, ma in enumerate(mass): + if not self.atoms[i] in self.masses: + self.masses[self.atoms[i]] = ma / MASS_RY_TO_UMA + + + def build_masses(self): + """ + Use the ASE database to build the masses. + The masses will be in [Ry] units (the electron mass) + """ + + ase_struct = self.get_ase_atoms() + + + self.masses = {} + mass = ase_struct.get_masses() + for i, ma in enumerate(mass): + if not self.atoms[i] in self.masses: + self.masses[self.atoms[i]] = ma / MASS_RY_TO_UMA + + + def copy(self): + """ + This method simply returns a copy of the current structure + + Results + ------- + - aux : Structure + A copy of the self structure. + """ + + aux = Structure() + aux.N_atoms = self.N_atoms + aux.coords = self.coords.copy() + aux.atoms = [atm for atm in self.atoms] + aux.unit_cell = self.unit_cell.copy() + aux.has_unit_cell = self.has_unit_cell + + # Deep copy of the masses + aux.masses = {} + for k in self.masses.keys(): + aux.masses[k] = self.masses[k] + + aux.ita = self.ita + return aux + + def set_masses(self, masses): + """ + This method set up the masses of the system. It requires a dictionary containing the + symobl and the value of the masses in a.u. (mass of the electron) + + Parameters + ---------- + - masses : dictionary + A dictionary containing the label and the corresponding mass in a.u. + Ex. masses = {'H' : 918.68, 'O' : 14582.56} + """ + + self.masses = masses + + def get_masses_array(self): + """ + Convert the masses of the current structure + in a numpy array of size N_atoms + + NOTE: This method will rise an exception if the masses are not initialized well + + Results + ------- + masses : ndarray (size self.N_atoms) + The array containing the mass for each atom of the system. + """ + + masses = np.zeros( self.N_atoms) + for i in range(self.N_atoms): + masses[i] = self.masses[ self.atoms[i] ] + + return masses + + def get_ityp_from_species(self, species): + """ + Get the integer of the atomic type from the species (string) + """ + + ityp = self.get_ityp() + for i in range(self.N_atoms): + if self.atoms[i] == species: + return ityp[i] + + raise ValueError("Error, species {} not present in this structure.".format(species)) + + + def get_atomic_types(self): + """ + Get an array of integer, starting from 1, for each atom of the structure, + so that two equal atoms share the same index. + + This is how different types are stored in Quantum ESPRESSO and it + is usefull for the wrapping Fortran => Python. + + Result + ------ + ityp : ndarray dtype=(numpy.intc) + The type array + """ + + ityp = [] + dictionary = {} + count = 1 + for i, atm in enumerate(self.atoms): + #ityp.append(cont) + if not atm in dictionary: + dictionary[atm] = count + count += 1 + + ityp = [dictionary[x] for x in self.atoms] + + # For fortran and C compatibility parse the array + return np.array(ityp, dtype = np.intc) + + def read_xyz(self, filename, alat = False, epsilon = 1e-8, frame_id = 0): + """ + This function reads the atomic position from a xyz file format. + if the passed file contains an animation, only the frame specified by frame_id + will be processed. + + Parameters + ---------- + - filename : string + The path of the xyz file, read access is required + - alat : bool, optional + If true the coordinates will be rescaled with the loaded unit cell, otherwise + cartesian coordinates are supposed (default False). + - frame_id : int + The id of the frame to be processed in an animation. + - epsilon : double precision, optional + Each value below this is considered to be zero (defalut 1e-8) + """ + # Check if the input is consistent + if (alat and not self.has_unit_cell): + sys.stderr.write("ERROR, alat setted to true, but no unit cell initialized\n") + raise ValueError("Function read_xyz, alat = True but no unit cell.") + + ## Check if the unit cell can be used with alat (only for orthorombic diagonal unit cell) + # if (alat): + # if (sum(self.unit_cell ** 2) - sum(diag(self.unit_cell)**2) < epsilon): + # sys.stderr.write("ERROR, alat compatible only with diagonal unit cell.\n") + # raise ErrorInUnitCell("Function read_xyz, alat parameters not combatible with cell.") + + + # Open the file + xyz = open(filename, "r") + + # Jump to the correct frame id + for k in range(frame_id): + njump = int(xyz.readline()) + 1 + for jl in range(njump): + xyz.readline() + + + self.N_atoms = int(xyz.readline()) + self.coords = np.zeros((self.N_atoms, 3)) + + # Read the comment line + xyz.readline() + + for i in range(self.N_atoms): + line = xyz.readline() + atom, x, y, z = line.split() + + self.atoms.append(atom) + self.coords[i,0] = np.float64(x) + self.coords[i,1] = np.float64(y) + self.coords[i,2] = np.float64(z) + + # Rescale the coordinates with the unit cell if requested + if alat: + # Not shure if the dot product must be done with the transposed unit cell matrix + self.coords[i, :] = np.dot( np.transpose(self.unit_cell), self.coords[i, :]) + + # Close the xyz file + xyz.close() + + def read_scf(self, filename, alat=1, read_string = False, read_espresso = False): + """ + Read the given filename in the quantum espresso format. + Note: + The file must contain only the part reguarding ATOMIC POSITIONS. + + Parameters + ---------- + - filename : str + The filename containing the atomic positions + - alat : double + If present the system will convert both the cell and the atoms position + by this factor. If it is also specified in the CELL_PARAMETERS line, + the one specified in the file will be used. + - read_string : bool + If true the filename is interpreted directly as a scf string, not as a file to be read. + - read_espresso : bool + If true, the SCF data are expected in between of a quantum espresso PW input. + """ + + # Check if the specified filename exists + if not read_string: + if not os.path.exists(filename): + raise ValueError("File %s does not exist" % filename) + + # Read the input filename + fp = open(filename, "r") + lines = fp.readlines() + fp.close() + else: + lines = filename.split("\n") + + n_atoms = 0 + #good_lines = [] + + # First read + read_cell = False + cell_index = 0 + read_atoms = True + if read_espresso: + read_atoms = False + cell_present = False + + read_crystal = False + + #print "ALAT:", alat + + #atom_index = 0 + cell = np.zeros((3,3), dtype = np.float64) + tmp_coords = [] + for line in lines: + line = line.strip() + + #Skipp comments + if len(line) == 0: + continue + + if line[0] == "!": + continue + + # Split the line into the values + values = line.split() + if values[0] == "CELL_PARAMETERS": + read_cell = True + read_atoms = False + self.has_unit_cell = True + cell_present = True + + # Check if the alat value is specified here + if "alat=" in line.lower().replace(" ", ""): + value_alat = np.float64(line[ line.find("=") + 1:].strip().replace(")","")) + alat = value_alat * BOHR_TO_ANGSTROM + + continue + if values[0] == "ATOMIC_POSITIONS": + self.atoms = [] + read_cell = False + read_atoms = True + if "crystal" in values[1].lower(): + read_crystal = True + + continue + + + if read_cell and cell_index < 3: + cell[cell_index, :] = [np.float64(v)*alat for v in values] + cell_index += 1 + elif cell_index == 3: + read_cell = False + + if read_atoms: + self.atoms.append(values[0]) + if not read_crystal: + tmp_coords.append([np.float64(v)*alat for v in values[1:4]]) + else: + # Read the crystal coordinate without taking care of alat + tmp_coords.append([np.float64(v) for v in values[1:4]]) + + n_atoms += 1 + + + + # Initialize the structure + self.coords = np.zeros((n_atoms, 3), dtype = np.float64) + self.N_atoms = n_atoms + + if cell_present: + self.has_unit_cell = True + self.unit_cell = cell + + for i, coord in enumerate(tmp_coords): + self.coords[i,:] = np.array(coord, dtype = np.float64) + + # Transform the coordinates if crystal + if read_crystal: + if not cell_present: + raise ValueError("Error, read crystal coordinates but no cell given in %s" % filename) + + self.coords[i,:] = np.einsum("ij, i", self.unit_cell, self.coords[i,:]) + + #print "COORDS:", self.coords + + + def read_generic_file(self, filename): + """ + This reader use ASE to parse the input and build the appropriate structure. + Any ASE accepted file is welcome. + This very simple reader uses the ase environment. + """ + + if not __ASE__: + print("ASE library not found.") + raise ImportError("Error, ASE library is required to read generic file.") + + atoms = ase.io.read(filename) + + # Now obtain all the information + self.generate_from_ase_atoms(atoms) + + + def set_unit_cell(self, filename, delete_copies = False, rescale_coords = False): + """ + Read the unit cell from the filename. + The rows of the filename are the unit cell vectors! + + Parameters + ---------- + - filename : string + The path of the file that contains the unit cell, in the numpy datafile format (text) + - delete_copies : bool, optional + If true the delete_copies subroutine is lounched after the creation of the unit cell + (default False) + - rescale_coords : bool, optional + If true ths system will be multiplied rows by column by the unit cell (default False) + """ + + # Load the unit cell + self.unit_cell = np.loadtxt(filename) + self.has_unit_cell = True + + if delete_copies: + self.delete_copies(verbose = False) + + if rescale_coords: + for i in range(self.N_atoms): + self.coords[i,:] = self.unit_cell.dot(self.coords[i,:]) + + def change_unit_cell(self, unit_cell): + """ + This method change the unit cell of the structure keeping fixed the crystal coordinates. + + NOTE: the unit_cell argument will be copied, so if the unit_cell variable is modified, this will not + affect the unit cell of this structure. + + Parameters + ---------- + unit_cell : numpy ndarray (3x3) + The new unit cell + """ + if not self.has_unit_cell: + raise ValueError("Error, the structure must already have a unit cell initialized.") + + # Get the crystal coordinates + crys_coord = np.zeros(np.shape(self.coords)) + for i in range(self.N_atoms): + crys_coord[i,:] = Methods.covariant_coordinates(self.unit_cell, self.coords[i,:]) + + # Setup the new unit cell + self.unit_cell = unit_cell.copy() + + # Modify the coordinates + for i in range(self.N_atoms): + self.coords[i,:] = np.einsum("ij, i", self.unit_cell, crys_coord[i,:]) + + + def export_unit_cell(self, filename): + """ + This method save the unit cell on the given file. + The rows will be the direct lattice vectors. + + Parameters + ---------- + - filename : string + The filename in which to save the unit cell + + """ + + np.savetxt(filename, self.unit_cell, header = "Rows are the unit cell vectors") + + def get_reciprocal_vectors(self): + """ + RECIPROCAL LATTICE + ================== + + Get the vectors of the reciprocal lattice. The self structure + must have the unit cell initialized (A NoUnitCell exception will be reised otherwise). + + Results + ------- + - reciprocal_vectors : float ndarray 3x3 + A matrix whose rows are the vectors of the reciprocal lattice + """ + + if not self.has_unit_cell: + raise ValueError("Error: the specified structure has not the unit cell.") + + return Methods.get_reciprocal_vectors(self.unit_cell) * 2 * np.pi + #return np.transpose(np.linalg.inv(self.unit_cell)) * 2 * np.pi + + def strain(self, strain_tensor, voigt = False, fix_volume = False): + r""" + APPLY A STRAIN TO THE STRUCTURE + =============================== + + This method applies a strain tensor to the structure. + Note, it will not affect the current structure, + but it returns a new strained strcture. + + Note: in the voigt representation, the off-diagonal terms of the strain tensor are intended as the sum + of the two symmetric components of the tensor. + + .. math :: + + \begin{pmatrix} \epsilon_1 \\ \epsilon_2 \\ \epsilon_3 \\ + 2\epsilon_4 \\ 2\epsilon_5 \\ 2\epsilon_6 \end{pmatrix} = + \begin{pmatrix} \epsilon_1 & \epsilon_6 & \epsilon_5 \\ + \epsilon_6 & \epsilon_2 & \epsilon_4 \\ + \epsilon_5 & \epsilon_4 & \epsilon_3 \end{pmatrix} + + + + Parameters + ---------- + strain_tensor: ndarray (size = (3,3), dtype = np.double) + The strain tensor. It must be a 3x3 tensor. + If you want to use the Voigt notation, set voigt to true + voigt : bool + If true, the strain_tensor is assumed in the voigt notation + (a 6 element array). + fix_volume : bool + If true, impose a hard volume constrain, that prevents the train to + change the total volume of the system. + + Results + ------- + strained_structure : CC.Structure.Structure() + The result of the strain. + """ + + if voigt: + strain_tensor[3:] /= 2 + strain_tensor = Methods.transform_voigt(strain_tensor, voigt_to_mat = True) + + + I = np.eye(3) + unit_cell = self.unit_cell.dot( I + strain_tensor.transpose()) + + if fix_volume: + # Fix the volume + unit_cell[:,:] *= (self.get_volume() / np.abs(np.linalg.det(unit_cell)))**(1/np.float64(3)) + + strained_struct = self.copy() + strained_struct.change_unit_cell(unit_cell) + + return strained_struct + + + + def delete_copies(self, minimum_dist=1e-6, verbose=False): + """ + This method checks if double atoms are present in the structure, + and delete them. + + Parameters + ---------- + - minimum_dist : double precision, optional + the minimum distance between two atoms of the same type allowed in the structure. + - verbose : bool (logical), optional + if True print on stdout how many atoms have been deleted (default False) + """ + + list_pop = [] + for i in range(self.N_atoms-1): + # Avoid to consider replica atoms already found + if (i in list_pop): + continue + + # If the atom is not a replica, then found if there are its replica missing + for j in range(i+1, self.N_atoms): + if (self.atoms[i] != self.atoms[j]): + continue + if j in list_pop: + continue + + # Get the axis + v1 = self.coords[i, :] + v2 = self.coords[j, :] + + d = np.sqrt(np.sum( (v1-v2)**2)) + if (self.has_unit_cell): + d = Methods.get_min_dist_into_cell(self.unit_cell, v1, v2) + + # # Apply the unit cell if necessary + # distances = [] + # if (self.has_unit_cell): + # # For each vector in the unit cell, add a distance + # shifts = [-1,0,1] + # for i_x, x_u in enumerate(shifts): + # new_x = x1 + x_u * self.unit_cell[i_x, :] + # for i_y, y_u in enumerate(shifts): + # new_y = y1 + y_u * self.unit_cell[i_y, :] + # for i_z, z_u in enumerate(shifts): + # new_z = z1 + z_u * self.unit_cell[i_z, :] + + # # Add the transformed distance + # distances.append( np.sqrt((x-new_x)**2 + (y - new_y)**2 + (z - new_z)**2)) + # else: + # # Get the first distance between atoms + # distances.append(np.sqrt( (x-x1)**2 + (y-y1)**2 + (z-z1)**2 )) + + + if (d < minimum_dist): + # Add the atom as a replica + list_pop.append(j) + + + # Print how many replica have been found + N_rep = len(list_pop) + if verbose: + print("Found %d replica" % N_rep) + + # Delete the replica + #list_pop = list(set(list_pop)) # Avoid duplicate indices + list_pop.sort(reverse=True) + #print list_pop, self.N_atoms + for index in list_pop: + #print index + del self.atoms[index] + + self.coords = np.delete(self.coords, list_pop, axis = 0) + self.N_atoms -= N_rep + + def apply_symmetry(self, sym_mat, delete_original = False, thr = 1e-6, timer = Timer.Timer()): + """ + This function apply the symmetry operation to the atoms + of the current structure. + + Parameters + ---------- + - sym_mat : (matrix 3x4) + The matrix of the symemtri operation, the final column is the translation + + - delete_original : bool, default False + If true only the atoms after the symmetry application are left (good to force symmetry) + + - thr : float, optional + The threshold for two atoms to be considered the same in the reduction process + (must be smaller than the minimum distance between two generic atoms in the struct, + but bigger than the numerical error in the wyckoff positions of the structure). + """ + + if not self.has_unit_cell: + raise ValueError("The structure has no unit cell!") + + if delete_original: + #self.N_atoms *= 2 + new_atoms = np.zeros( (self.N_atoms, 3)) + timer.execute_timed_function(self.fix_coords_in_unit_cell, delete_copies = False) + + old_coords = timer.execute_timed_function(Methods.covariant_coordinates, self.unit_cell, self.coords) + new_coords = sym_mat[:, :3].dot(old_coords.T).T + new_coords += np.tile( sym_mat[:, 3], (self.N_atoms, 1)) + + self.coords = new_coords.dot(self.unit_cell) + + timer.execute_timed_function(self.fix_coords_in_unit_cell, delete_copies = False) + else: + + + for i in range(self.N_atoms): + # Convert the coordinates into covariant + old_coords = Methods.covariant_coordinates(self.unit_cell, self.coords[i, :]) + + # Apply the symmetry + new_coords = sym_mat[:, :3].dot(old_coords) + new_coords += sym_mat[:, 3] + + # Return into the cartesian coordinates + coords = np.dot( np.transpose(self.unit_cell), new_coords) + + # Put the atoms into the unit cell + new_atoms[i, :] = Methods.put_into_cell(self.unit_cell, coords) + + # Add also the atom type + if not delete_original: + self.atoms.append(self.atoms[i]) + + self.N_atoms *= 2 + self.coords = np.concatenate( (self.coords, new_atoms), axis = 0) + self.delete_copies(verbose = False, minimum_dist = thr) + + def check_symmetry(self, sym_mat, thr = 1e-6): + """ + This method check if the provided matrix is actually a symmetry for the given system + + Parameters + ---------- + - sym_mat: a 3 rows by 4 columns matrix (float) + It contains the rotation matrix (the first 3x3 block) + and the traslation vector (the last column) of the symmetry + - thr : float, optional + The threshold for two atoms to be considered the same. + + Results + ------- + - check : bool + It is true if the given matrix is a real symmetry of the system. + """ + + # Copy the struct + new_struct = self.copy() + + # Apply the symmetry + new_struct.apply_symmetry(sym_mat, delete_original=True) + + # Get the equivalence + eq_atoms = new_struct.get_equivalent_atoms(self) + + # Exchange the atoms + new_struct.coords[eq_atoms, :] = new_struct.coords.copy() + + # Fix the structure in the unit cell + new_struct.fix_coords_in_unit_cell() + + # Get the displacements + u_vect = self.get_displacement(new_struct) + + # Get the distance between the structures + dist = np.sqrt(np.sum(u_vect ** 2)) + + if dist > thr: + return False + return True + + + def set_ita_group(self, group): + """ + This function setup the ita group of the cell, + the unit cell must be initialized and the ITA group must be + inside the supported one. + All the symmetries of the specified group are applied. + + Parameters + ---------- + - group : int + The ITA identifier of the symmetry group. + """ + + # Apply all the symmetries + sym_mats = SYM.get_symmetries_from_ita(group) + self.ita = group + + for mat in sym_mats: + self.apply_symmetry(mat) + + def load_symmetries(self, filename, progress_bar=False, verbose = False): + """ + This function loads the symmetries operation from a specific file + and applies them to the system. + The file must init with the total number of symmetries, and followed by + N 3 rows x 4 columns matrices that represent the symmetry application. + + Parameters + ---------- + filename : string + The path in which the symmetries are stored, a text file. + progress_bar : bool + If true a progress bar on stderr is shown, usefull if the system is very large and + this function can take a while. + """ + + + # Get the number of symmetries + symfile = open(filename) + N_sym = int(symfile.readline().strip()) + symfile.close() + + # Get the symmetries + symdata = np.loadtxt(filename, skiprows = 1) + + if (progress_bar): print() + + for i in range(N_sym): + sym_mat = symdata[3*i:3*(i+1), :] + + self.apply_symmetry(sym_mat) + + if (progress_bar): + if not verbose: + sys.stderr.write("\rProgress computing symmetries... %d of %d %%" % (i, N_sym) ) + else: + sys.stderr.write("\rSymmetry %d out of %d, %d atoms" % (i, N_sym, self.N_atoms ) ) + + sys.stderr.flush() + + if (progress_bar): print() + + def impose_symmetries(self, symmetries, threshold = 1.0e-6, verbose = True): + """ + This methods impose the list of symmetries found in the given filename. + It solves a self-consistente equation: Sx = x. If this equation is not satisfied at precision + of the initial_threshold the method will raise an exception. + + Parameters + ---------- + - symmetries : list + The simmetries to be imposed as a list of 3x4 ndarray matrices. The last column is the + fractional translations + - threshold : float + The threshold for the self consistent equation. The algorithm stops when Sx = x is satisfied + up to the given threshold value for all the symmetries. + - verbose : bool + If true the system will print on stdout info about the self-consistent threshold + + """ + + # An array storing which symmetry operation has reached the threshold + aux_struct = self.copy() + + + # Start the self consistent algorithm + running = True + index = 0 + while running: + old_coords = np.zeros( np.shape(self.coords)) + + for sym in symmetries: + aux_struct = self.copy() + aux_struct.apply_symmetry(sym, delete_original = True) + aux_struct.fix_coords_in_unit_cell() + + # Get the equivalent atoms + eq_atoms = self.get_equivalent_atoms(aux_struct) + + #ase.visualize.view(self.get_ase_atoms()) + #ase.visualize.view(aux_struct.get_ase_atoms()) + + # Order the atoms + aux_struct.atoms = [aux_struct.atoms[item] for item in eq_atoms] + aux_struct.coords = aux_struct.coords[eq_atoms,:] + + # Get the displacements + old_coords += self.get_displacement(aux_struct) + + # Average + old_coords /= len(symmetries) + + r = np.max(np.sqrt(np.sum((old_coords)**2, axis = 1))) +# +# if verbose: +# print np.sqrt(np.sum((old_coords - self.coords)**2, axis = 1)) +# print "Self:" +# print self.coords +# print "New:" +# print old_coords + + + self.coords -= old_coords + if r < threshold: + running = False + + index += 1 + if (verbose): + print("Self-consistent iteration %d -> r = %.3e | threshold = %.3e" % (index, r, threshold)) + + if (verbose): + print("Symmetrization reached in %d steps." % index) + + + + def get_equivalent_atoms(self, target_structure, return_distances = False, debug = False): + """ + GET EQUIVALENT ATOMS BETWEEN TWO STRUCTURES + =========================================== + + + This function returns a list of the atom index in the target structure that + correspond to the current structure. + NOTE: This method assumes that the two structures are equal. + + + Parameters + ---------- + target_structure : Structure() + This is the target structure to be used to get the equivalent atoms. + return_distances : bool + If True it returns also the list of the distances between the atoms + + Results + ------- + list + list of int. Each integer is the atomic index of the target_structure equivalent to the i-th element + of the self structure. + """ + + # Check if the structures are compatible + if self.N_atoms != target_structure.N_atoms: + raise ValueError("Error, the target structure must be of the same type of the current one") + + for typ in self.atoms: + if not typ in target_structure.atoms: + raise ValueError("Error, the target structure must be of the same type of the current one") + if self.atoms.count(typ) != target_structure.atoms.count(typ): + raise ValueError("Error, the target structure must be of the same type of the current one") + + eq_atm = list(symph.get_equivalent_atoms(self.coords, target_structure.coords, self.unit_cell, self.get_ityp(), target_structure.get_ityp())) + + if debug or return_distances: + equiv_atoms = [] + effective_distances = [] + for i in range(self.N_atoms): + i_typ = self.atoms[i] + + # Select the possible equivalent atoms in the target structure + target_indices = [x for x in range(self.N_atoms) if target_structure.atoms[x] == i_typ and not (x in equiv_atoms)] + + # For each possible equivalent atoms get the minimum distance + d = [] + for j in target_indices: + #v = Methods.get_closest_vector(self.unit_cell, self.coords[i,:] - target_structure.coords[j, :]) + #d.append(np.sqrt(np.sum(v**2))) + d.append(Methods.get_min_dist_into_cell(self.unit_cell, self.coords[i,:], target_structure.coords[j, :])) + + # Pick the minimum + j_min = target_indices[ np.argmin(d) ] + effective_distances.append(np.min(d)) + + # Set the equivalent atom index + equiv_atoms.append(j_min) + + #print "Max distance:", np.max(effective_distances) + + assert all(eq_atm == equiv_atoms) + + if return_distances: + return equiv_atoms, effective_distances + + return eq_atm + + + def sort_molecules(self, distance = 1.3): + """ + This method sorts the atom lists to have the atoms in the same molecule written subsequentially. + + Parameters + ---------- + - distance : double precision, optional + The distance below wich two atoms are considered to be bounded. The unit is in Argstrom. + """ + + molecules = [] + pop_indices = [] + for i in range(self.N_atoms): + if i in pop_indices: continue + + molecule = [i] + + # Get the closest molecules + for j in range(i+1, self.N_atoms): + if j in pop_indices: continue + + if np.sqrt(np.sum( (self.coords[i,:] - self.coords[j,:])**2 )) < distance: + molecule.append(j) + pop_indices.append(j) + + molecules.append(molecule) + + # Resort the atoms + coords = np.zeros( (self.N_atoms, 3)) + atoms = ["X"] * self.N_atoms + + cont = 0 + for mol in molecules: + for index in mol: + atoms[cont] = self.atoms[index] + coords[cont, :] = self.coords[index,:] + cont += 1 + self.atoms = atoms + self.coords = coords + + def save_xyz(self, filename, comment="Generated with BUC", overwrite = True): + """ + This function write the structure on the given filename in the xyz file format + + Parameters + ---------- + filename : string + The path of the file in which to save the structure. The user must have write access + comment : string, optional + This line is written in the comment line of the xyz file. + NOTE: this string is followed by the unit cell info is present + overwrite : bool, optional + If true any precedent file will be erased, otherwise the structure is appended + on the bottom of the previous one. In this way it is possible to save videos. + + """ + + if overwrite: + xyz = open(filename, "w") + else: + xyz = open(filename, "a") + + # Write the number of atoms + xyz.write("%d\n" % self.N_atoms) + + # Write the comment line + unit_cell_string = "" + if self.has_unit_cell: + unit_cell_string = " cell: " + for i in range(3): + unit_cell_string += chr( ord('A') + i) + " ".join([str(x_val) for x_val in self.unit_cell[i,:]]) + " " + xyz.write("%s\n" % (comment + unit_cell_string)) + + # Write the strcture + lines = [] + for i in range(self.N_atoms): + label = self.atoms[i] + x, y, z = self.coords[i, :] + + line = " ".join([label, str(x), str(y), str(z)]) + "\n" + lines.append(line) + xyz.writelines(lines) + xyz.close() + + def save_bcs(self, filename, symmetry_file = ""): # STILL NOT WORKING + """ + Save the current structure in the Bilbao Crystallographic Server file format + This is very usefull since the BCS website provide a conversor between + BCS with most of widely used crystallographic file format. + + NOTE: + remember to specify the correct ITA group symmetry in the structure. + You can find more about ITA on BCS website. Otherwise you must specify a file with symmetries + + Parameters + ---------- + - filename : str + The path of the bcs file in which you want to save the structure + - symmetry_file : str, optional + The path to a file containing the symmetries operations of the group space + This is not needed if a ITA grup has been specified. + """ + + fp = open(filename, "w") + fp.write("# Space Group ITA number\n%d\n# Lattice parameters\n" % self.ita) + + # Convert the cell into the a,b,c,alpha,beta,gamma format + cellbcs = Methods.cell2abc_alphabetagamma(self.unit_cell) + fp.write("%.8f %.8f %.8f %3d %3d %3d\n" % (cellbcs[0], cellbcs[1], cellbcs[2], + cellbcs[3], cellbcs[4], cellbcs[5])) + + # Get the independent atoms + if symmetry_file != "": + syms = [] + #TODO !!!!!! + syms = SYM.get_symmetries_from_ita(self.ita, True) + + removing_struct = self.copy() + + running = True + while running: + # Try to remove an atoms + total_removed = 0 + for i in range(removing_struct.N_atoms): + tmp_struct = removing_struct.copy() + + # Delete the atom + tmp_struct.N_atoms -= 1 + tmp_struct.atoms.pop(i) + np.delete(tmp_struct.coords, i, axis = 0) + + # Apply all the symmetries + for ind, sym in enumerate(syms): + tmp_struct.apply_symmetry(sym) + print("atom %d, sym %d - NEW %d / %d" % (i, ind, tmp_struct.N_atoms, removing_struct.N_atoms)) + + if tmp_struct.N_atoms == removing_struct.N_atoms: + total_removed += 1 + removing_struct = tmp_struct.copy() + + # If no atoms can be removed, then we obtained the minimal structure + if not total_removed: + running = False + + + # Write the atoms + fp.write("# Number of independent atoms\n%d\n" % removing_struct.N_atoms) + fp.write("# [atom type] [number] [WP] [x] [y] [z]\n") + + for i in range(removing_struct.N_atoms): + cvect = Methods.covariant_coordinates(self.unit_cell, removing_struct.coords[i,:]) + vect_str = " ".join(["%.8f" % item for item in cvect]) + fp.write("%2s %3d - %s\n" % (removing_struct.atoms[i], i+1, vect_str)) + + fp.close() + + def get_xcoords(self): + """ + Returns the crystalline coordinates + """ + + assert self.has_unit_cell + + xcoords = np.zeros(self.coords) + for i in range(self.N_atoms): + xcoords[i,:] = Methods.covariant_coordinates(self.unit_cell, self.coords[i,:]) + + return xcoords + def set_from_xcoords(self, xcoords): + """ + Set the cartesian coordinates from crystalline + """ + + assert self.has_unit_cell + + for i in range(self.N_atoms): + self.coords[i,:] = self.unit_cell.T.dot(xcoords[i,:]) + + + def save_scf(self, filename, alat = 1, avoid_header=False, crystal = False, get_text = False): + """ + This methods export the phase in the quantum espresso readable format. + Of course, only the data reguarding the unit cell and the atomic position will be written. + The rest of the file must be edited by the user to start a calculation. + + Parameters + ---------- + filename : string + The name of the file that you want to save. If None, no file is generated (in that case, use get_text to get the string of the scf file) + alat : float, optional + If different from 1, both the cell and the coordinates are saved in alat units. + It must be in Angstrom. + avoid_header : bool, optional + If true nor the cell neither the ATOMIC_POSITION header is printed. + Usefull for the sscha.x code. + crystal : bool, optional + If true, the atomic coordinates are saved in crystal components + get_text : bool + If true, the scf file is returned as pure text. + """ + + if alat <= 0: + raise ValueError("Error, alat must be positive [Angstrom]") + + data = [] + if self.has_unit_cell and not avoid_header: + unit_cell = np.copy(self.unit_cell) + if alat == 1: + data.append("CELL_PARAMETERS angstrom\n") + else: + data.append("CELL_PARAMETERS alat\n") + + unit_cell /= alat + + for i in range(3): + data.append("%.16f %.16f %.16f\n" % (unit_cell[i, 0], + unit_cell[i, 1], + unit_cell[i, 2])) + data.append("\n") + + + if not avoid_header: + if crystal: + unit_type = "crystal" + else: + if alat == 1: + unit_type = "angstrom" + else: + data.append("ATOMIC_POSITIONS alat\n") + + data.append("ATOMIC_POSITIONS {}\n".format(unit_type)) + for i in range(self.N_atoms): + if not crystal: + coords = np.copy(self.coords) + coords /= alat + else: + coords = Methods.covariant_coordinates(self.unit_cell, self.coords) + + data.append("%s %.16f %.16f %.16f\n" % (self.atoms[i], + coords[i, 0], + coords[i, 1], + coords[i, 2])) + + # Write + if filename is not None: + fdata = open(filename, "w") + fdata.writelines(data) + fdata.close() + + if get_text: + return "".join(data) + + + def fix_coords_in_unit_cell(self, delete_copies = True, debug = False): + """ + This method fix the coordinates of the structure inside + the unit cell. It works only if the structure has + predefined unit cell. + """ + + if not self.has_unit_cell: + raise ValueError("Error, try to fix the coordinates without the unit cell") + + if not debug: + self.coords = symph.fix_coords_in_unit_cell(self.coords, self.unit_cell) + + if debug: + c1 = symph.fix_coords_in_unit_cell(self.coords, self.unit_cell) + coords = self.coords.copy() + for i in range(self.N_atoms): + coords[i,:] = Methods.put_into_cell(self.unit_cell, self.coords[i,:]) + + check = np.max(np.abs(coords - c1)) < 1e-7 + + + if not check: + print("Error in the check coordinates") + print(self.unit_cell) + + for i in range(self.N_atoms): + print("Atom {}:".format(i)) + print("original: {}".format(self.coords[i, :])) + print("new method: {}".format(c1[i,:])) + print("old method: {}".format(coords[i,:])) + print() + + self.coords = c1 + #ase.visualize.view(self.get_ase_atoms()) + + raise ValueError("Error, the two methods to fix the coordinates in the unit cell give different results.") + + + # Delete duplicate atoms + if delete_copies: + self.delete_copies() + + def fix_wigner_seitz(self): + """ + Atoms will be replaced in the periodic images inside the wigner_seitz cell + """ + + assert self.has_unit_cell, "Error, the wigner_seitz is defined for periodic boundary conditions" + + for i in range(self.N_atoms): + new_r = Methods.get_closest_vector(self.unit_cell, self.coords[i,:]) + self.coords[i, :] = new_r + + def get_strct_conventional_cell(self): + """ + This methods, starting from the primitive cell, returns the same structure + in the conventional cell. It picks the angle that mostly differs from 90 deg, + and transfrom the axis of the cell accordingly to obtain a bigger cell, but similar + to an orthorombic one. + The atoms are then replicated and correctly placed inside the new cell. + + If the structure does not have a unit cell, the method will raise an error. + + NOTE: The new structure will be returned, but this will not be modified + + Returns + ------- + Structure.Structure() + The structure with the conventional cell + """ + + + if not self.has_unit_cell: + raise ValueError("Error, the given structure does not have a valid unit cell.") + + # Compute the three angles + angls = np.zeros(3) + for i in range(3): + nexti = (i+1)%3 + otheri = (i+2)%3 + angls[otheri] = np.arccos( np.dot(self.unit_cell[i,:], self.unit_cell[nexti,:]) / + (np.sqrt(np.dot(self.unit_cell[i,:], self.unit_cell[i,:])) * + np.sqrt(np.dot(self.unit_cell[nexti,:], self.unit_cell[nexti,:])))) * 180 / np.pi + + # Pick the angle that differ the most from 90 + otheri = np.argmax( np.abs( angls - 90)) + #print angls, otheri + + # Now select the two vectors between this angle + vec1 = self.unit_cell[(otheri + 1) % 3,:].copy() + vec2 = self.unit_cell[(otheri + 2) % 3,:].copy() + + # Get the new system + vec1_prime = vec1 + vec2 + vec2_prime = vec1 - vec2 + + # Get the new structure + s_new = self.generate_supercell( (2,2,2) ) + s_new.unit_cell = self.unit_cell.copy() + s_new.unit_cell[(otheri+1)%3,:] = vec1_prime + s_new.unit_cell[(otheri+2)%3,:] = vec2_prime + + s_new.fix_coords_in_unit_cell() + + return s_new + + def get_ase_atoms(self): + """ + This method returns the ase atoms structure, ready for computations. + + Results + ------- + - atoms : ase.Atoms() + The ase.Atoms class containing the self structure. + """ + + if not __ASE__: + print ("ASE library not found") + raise ImportError("Error, ASE library not found") + + # Get thee atom list + atm_list = [] + for i in range(self.N_atoms): + atm_list.append(ase.Atom(self.atoms[i], self.coords[i,:])) + + atm = ase.Atoms(atm_list) + + if self.has_unit_cell: + atm.set_cell(self.unit_cell) + atm.pbc[:] = True + + + return atm + + def get_phonopy_calculation(self, supercell = [1,1,1]): + """ + Convert the CellConstructor structure to a phonopy object + for the calculation of phonons using finite differences. + + Note: while phonopy allows for nonconventional supercells, + this method is only interfaced to create supercell calculations which are finite multiple + of the unit cell defined by the self structure. + + Parameters + ---------- + supercell : list of 3 int + The supercell (how many times the unit cell vector). + """ + + if not __PHONOPY__: + raise ValueError("Error, to run 'get_phonopy_calculation' you need to have Phonopy installed.") + + if not self.has_unit_cell: + raise ValueError("Error, to run 'get_phonopy_calculation' the system has to have a defined unit cell") + + if self.get_volume() < 1e-8: + raise ValueError("Error, this structure has singular unit cell; I cannot initialize a phonopy object.") + + if len(supercell) != 3: + raise ValueError("Error, the given supercell must be a 3 element vector ({} elements given)".format(self.supercell)) + + + scaled_position = Methods.covariant_coordinates(self.unit_cell, self.coords) + unitcell = phonopy.structure.atoms.PhonopyAtoms(symbols = self.atoms, cell = self.unit_cell, scaled_positions = scaled_position) + + return phonopy.Phonopy(unitcell, np.eye(3).dot(np.array(supercell))) + + + def get_ityp(self): + """ + GET THE TYPE ATOMS + ================== + + This is for fortran compatibility. + Get the ityp array for the structure. + Pass it + 1 to the fortran subroutine to match also the difference + between python and fortran indices + + Results + ------- + ityp : ndarray of int + The type of the atom in integer (starting from 0) + """ + + if not self.masses: + atm_species = list(set(self.atoms)) + else: + atm_species = list(self.masses) + + ityp = np.zeros(self.N_atoms, dtype = np.intc) + + for i in range(self.N_atoms): + # Rank the atom number + + ityp[i] = atm_species.index(self.atoms[i]) + + return ityp + + def get_itau(self, unit_cell_structure): + """ + GET ITAU + ======== + + This subroutine (called by a supercell structure), returns the array + of the corrispondence between its atoms and those in the unit cell.s + + NOTE: The ITAU is returned in Fortran indexing, subtract by 1 if you want to use it in python + + Parameters + ---------- + - unit_cell_structure : Structure() + The structure of the unit cell used to generate this supercell structure. + + Results + ------- + - itau : ndarray (size = nat_sc, type = int) + For each atom in the supercell contains the index of the corrisponding + atom in the unit_cell, starting from 1 to unit_cell_structure.N_atoms (included) + """ + + itau = np.zeros( self.N_atoms, dtype = np.intc) + + for i in range(self.N_atoms): + + v1 = Methods.put_into_cell(unit_cell_structure.unit_cell, self.coords[i,:]) + d = np.zeros(unit_cell_structure.N_atoms) + for j in range(unit_cell_structure.N_atoms): + d[j] = Methods.get_min_dist_into_cell(unit_cell_structure.unit_cell, v1, unit_cell_structure.coords[j,:]) + + itau[i] = np.argmin(d) + 1 + + return itau + + + def get_sublattice_vectors(self, unit_cell_structure): + """ + Get the lattice vectors that connects the atom of this supercell structure to those of + the unit_cell structure. + """ + + itau = self.get_itau(unit_cell_structure) - 1 + return self.coords[:,:] - unit_cell_structure.coords[itau[:], :] + + def generate_supercell(self, dim, itau = None, QE_convention = True, get_itau = False): + """ + This method generate a supercell of specified dimension, replicating the system + on the n-th neighbours unit cells. + + Parameters + ---------- + - dim : list, size(3), integer + A list that specifies the number of cells for each dimension. + - itau : ndarray of int, size(Natoms * supercell_size) + An array of integer. If it is of the correct shape and type it will be filled + with the correspondance of each new vector to the corresponding one in the unit cell + - QE_convention : bool, optional + If true (default) the quantum espresso set_tau subroutine is used to determine + the order of how the atoms in the supercell are generated + - get_itau : bool + If true also the itau order is returned in output (python convention). + + Results + ------- + - supercell : Structure + This structure is the supercell of the system. + - itau : ndarray + For each atom in the supercell, the index of the corresponding atom + in the primitive cell. Only if get_itau = True + """ + + + if len(dim) != 3: + raise ValueError("ERROR, dim must have 3 integers.") + + if not self.has_unit_cell: + raise ValueError("ERROR, the specified system has not the unit cell.") + + total_dim = np.prod(dim) + + new_N_atoms = self.N_atoms * total_dim + new_coords = np.zeros( (new_N_atoms, 3)) + atoms = [None] * new_N_atoms # Create an empty list for the atom's label + + + # Get the new data + + + # Check if itau is passed + if itau is not None: + try: + itau[:] = np.zeros(new_N_atoms, dtype = np.intc) + except: + raise ValueError("Error, itau passed to generate_supercell does not match the required shape\nRequired %d, passed %d"% (new_N_atoms, len(itau))) + + # Start the generation of the new supercell + if not QE_convention: + for i_z in range(dim[2]): + for i_y in range(dim[1]): + for i_x in range(dim[0]): + basis_index = self.N_atoms * (i_x + dim[0] * i_y + dim[0]*dim[1] * i_z) + for i_atm in range(self.N_atoms): + new_coords[basis_index + i_atm, :] = self.coords[i_atm, :] + \ + i_z * self.unit_cell[2, :] + \ + i_y * self.unit_cell[1, :] + \ + i_x * self.unit_cell[0, :] + atoms[i_atm + basis_index] = self.atoms[i_atm] + if itau is not None: + itau[i_atm + basis_index] = i_atm + + # Define the new structure + supercell = Structure() + supercell.coords = new_coords + supercell.N_atoms = new_N_atoms + supercell.atoms = atoms + supercell.masses = self.masses.copy() + + # Define the supercell + supercell.has_unit_cell = True + + for i in range(3): + supercell.unit_cell[i, :] = self.unit_cell[i,:] * dim[i] + + + if QE_convention: + # Prepare the variables + tau = np.array(self.coords.transpose(), dtype = np.float64, order = "F") + tau_sc = np.zeros((3, new_N_atoms), dtype = np.float64, order = "F") + ityp_sc = np.zeros( new_N_atoms, dtype = np.intc) + ityp = self.get_atomic_types() + + at_sc = np.array( supercell.unit_cell.transpose(), dtype = np.float64, order = "F") + at = np.array( self.unit_cell.transpose(), dtype = np.float64, order = "F") + + itau = np.zeros(new_N_atoms, dtype = np.intc) +# +# print "AT SC:", at_sc +# print "AT:", at +# print "TAU SC:", tau_sc +# print "TAU:", tau +# + # Fill the atom + symph.set_tau(at_sc, at, tau_sc, tau, ityp_sc, ityp, itau, new_N_atoms, self.N_atoms) + + + supercell.coords[:,:] = tau_sc.transpose() + itau -= 1 # Fortran To Python indexing + supercell.atoms = [self.atoms[x] for x in itau] + + + + if get_itau: + return supercell, itau + return supercell + + def reorder_atoms_supercell(self, reference_structure): + """ + ORDER THE ATOMS + =============== + + This subroutines order the atoms to match the same order as in the + generate_supercell method. + The self structure is supposed to be a structure that belongs to a supercell + of the given unit_cell, then it is reordered so that each atom in any different + supercell are consequent and the order of the supercell matches the one + created by generate supercell. The code will work even if the structures + do not match exactly the supercell generation. In this case, the closest + unit cell atom of the correct type is used as reference. + + TODO: THIS DOES NOT WORK!!!! + + Parameters + ---------- + - reference_structure : Structure() + The cell and coordinates that must be used as a reference + to reorder the atoms + + Results + ------- + - itau : ndarray of int + The shuffling array to order any array of this list + + """ + #raise ValueError("Subroutine still not working...") + + if not reference_structure.has_unit_cell: + raise ValueError("Error, the reference structure must have a unit cell") + + if not self.has_unit_cell: + raise ValueError("Error, the self structure must have a unit cell") + + unit_cell = reference_structure.unit_cell + reference_coords = reference_structure.coords + + # Get the supercell size + sx = self.unit_cell[0,:].dot(unit_cell[0,:]) / unit_cell[0,:].dot(unit_cell[0,:]) + sy = self.unit_cell[1,:].dot(unit_cell[1,:]) / unit_cell[1,:].dot(unit_cell[1,:]) + sz = self.unit_cell[2,:].dot(unit_cell[2,:]) / unit_cell[2,:].dot(unit_cell[2,:]) + + supercell_size = (int(sx + .5), int(sy + .5), int(sz + .5)) + + print ("SUPERCELL:", supercell_size) + + # Atoms in the unit cell + nat_uc = np.shape(reference_coords)[0] + + # Check if they match the given structure + if self.N_atoms % nat_uc != 0: + raise ValueError("Error, the number of atoms in this structure %d is not a multiple of %d (the reference structure)" % (self.N_atoms, nat_uc)) + + # The shuffling array + itau = np.arange(self.N_atoms) + + # Get cristal coordinates + for i in range(self.N_atoms): + cov = Methods.covariant_coordinates(unit_cell, self.coords[i,:]) + + # Identify the cell + i_x = int(cov[0] + .5) + i_y = int(cov[1] + .5) + i_z = int(cov[2] + .5) + + print (cov[0], cov[1], cov[2], i_x, i_y, i_z) + + # Get the index of the cell + basis_index = nat_uc * (i_x + supercell_size[0] * i_y + supercell_size[0] * supercell_size[1] * i_z) + + # Identify the atom + d = np.zeros(nat_uc, dtype = np.float32) + mask_good = np.zeros(nat_uc, dtype = bool) + for j in range(nat_uc): + if self.atoms[i] == reference_structure.atoms[j]: + mask_good[j] = True + d[j] = Methods.get_min_dist_into_cell(unit_cell, self.coords[i,:], reference_coords[j]) + + # Avoid to pick a wrong atom type + d[~mask_good] = np.max(d) + 1 + + # Avoid that two atoms point to the same position + process = True + while process: + + # Get the atom corresponding to the minimum distance + atm_index = np.argmin(d) + index = basis_index + atm_index + + #print "Chosen %d -> %d" % (i, index), "ITAU:", itau[:i] + + # Check if another atom already matched this one + if index in itau[:i]: + d[atm_index] = np.max(d) + 1 + else: + process = False + + + itau[i] = index + + # Now shuffle the current structure + self.coords = self.coords[itau, :] + + # Now shuffle the atom types + new_atoms = [] + for i in range(self.N_atoms): + new_atoms.append(self.atoms[itau[i]]) + self.atoms = new_atoms + + # Return the shuffling array + return itau + + + + def get_min_dist(self, index_1, index_2): + """ + This method returns the minimum distance between atom index 1 and atom index 2. + It uses the unit cell to correctly take into account the atoms at the edge of the unit cell. + + Parameters + ---------- + - index_1 : int + The index of the first atom in the structure + - index_2 : int + The index of the second atom in the structure + + Results + ------- + - min_dist : float + The minimum distance between the chosen atoms, eventually traslated by the unit cell. + """ + + vector1 = self.coords[index_1, :] + vector2 = self.coords[index_2, :] + + if not self.has_unit_cell: + return np.sqrt( np.sum( (vector1 - vector2)**2)) + + # Get the covariant components + cell = self.unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = cell[i,:].dot(cell[j, :]) + + imt = np.linalg.inv(metric_tensor) + + # Get contravariant components + contra_vect = np.zeros(3) + for i in range(3): + contra_vect[i] = vector1.dot(cell[i, :]) + + # Invert the metric tensor and obtain the covariant coordinates + covect1 = imt.dot(contra_vect) + + contra_vect = np.zeros(3) + for i in range(3): + contra_vect[i] = vector2.dot(cell[i, :]) + + # Invert the metric tensor and obtain the covariant coordinates + covect2 = imt.dot(contra_vect) + + covect_distance = covect1 - covect2 + + # Bring the distance as close as possible to zero + covect_distance -= (covect_distance + np.sign(covect_distance)*.5).astype(int) + + # Compute the distance using the metric tensor + return np.sqrt(covect_distance.dot(metric_tensor.dot(covect_distance))) + + + def get_brillouin_zone(self, ISO_MESH=10): # NOT WORKING ----- + """ + BRILLOUIN ZONE + ============== + + This function uses ase utilities to plot the Brillouin zone. + TODO: Z primitive cell must be perpendicular to the others (only few reticulus) + + NOT WORKING!!!! + + Parameters + ---------- + - ISO_MESH : int (default 100) + The number of points for the volume mesh (to the 3 power) + + Results + ------- + - BZone : array of 3D vectors + The points of the ISO_MESH inside the first brillouin zone + """ + + # Get the reciprocal lattice vectors + b_vectors = self.get_reciprocal_vectors() + + b_mod = np.sum( b_vectors**2, axis = 1) + + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = b_vectors[i,:].dot(b_vectors[j, :]) + invmt = np.linalg.inv(metric_tensor) + + # Uniformly fill the Reciprocal Unit Cell + spacing = np.linspace(-.5, .5, ISO_MESH) + + vectors = [] + + # Create all the surface in the contravariant coordinates + for x in spacing: + + mask1 = (spacing - x <= 0.5) & (spacing -x >= -.5) + mask2 = (spacing + x <= .5) & (spacing + x >= -.5) + + for y in spacing[mask1 & mask2]: + # if abs(y) != 0.5 and abs(x) != 0.5 and abs(y+x) != 0.5 and abs(y-x) != 0.5: + # continue + + for z in [-0.5, 0.5]: + contravect = np.array([b_mod[0] * x, b_mod[1] * y, b_mod[2] * z]) + covect = invmt.dot(contravect) + vectors.append(covect) + + # TODO NOT WORKING + pass + + return np.array(vectors) + + def GetBiatomicMolecules(self, atoms, distance, tollerance=0.01, return_indices = False): + """ + GET BIATOMIC MOLECULE + ===================== + + This function allows one to extract from a structure all the biatomic + molecules that contains the two atoms specified and that are at the distance + with a given tollerance. + This is very usefull to compute some particular average bond length. + + Parameters + ---------- + - atoms : list (char) (size = 2) + The atomic symbols of the molecule + - distance : float + The average distance between the two atom in the molecule + - tollerance : float, default 0.01 + The tollerance on the distance after which the two atoms are + no more consider inside the same molecule. + - return_indices : bool, default false + If true, per each molecule is returned also the list of the + original indices inside the structure. + + Results + ------- + - Molecules : list + List of molecules (Structure) that matches the input. + If none is found an empty list is returned + """ + # Check if the atoms is a 2 char list + if len(atoms) != 2: + raise ValueError("Error, the molecule must be biatomic") + + for a in atoms: + if not a in self.atoms: + raise ValueError("Error, the atom %s is not into this structure" % a) + + # Scroll all the atoms in the list that match the first type. + molecules = [] + original_indices = [] + for index1 in range(self.N_atoms): + atm1 = self.atoms[index1] + if atm1 != atoms[0]: + continue + + # Avoid double counting if the molecule is omonuclear + starting_index = 0 + if atoms[0] == atoms[1]: + starting_index = index1 + 1 + + for index2 in range(starting_index, self.N_atoms): + atm2 = self.atoms[index2] + if atm2 != atoms[1]: + continue + + # Check if the distances between the two atoms matches + d = self.get_min_dist(index1, index2) + if d > distance - tollerance and d < distance + tollerance: + # Create the structure of the molecule + mol = Structure() + mol.N_atoms = 2 + mol.atoms = atoms + mol.coords = np.zeros((2, 3)) + + # Translate the molecule in the middle of the cell + if self.has_unit_cell: + for i in range(3): + mol.coords[0,:] += self.unit_cell[i,:] / 2. + mol.coords[1,:] += self.unit_cell[i,:] / 2. + + mol.coords[1,:] += self.coords[index2,:] - self.coords[index1,:] + + # If the system has a unit cell, put the second atom inside the cell + if self.has_unit_cell: + mol.coords[1,:] = Methods.put_into_cell(self.unit_cell, mol.coords[1,:]) + + # Append the molecule to the structure + molecules.append(mol) + original_indices.append( (index1, index2) ) + + if return_indices: + return molecules, original_indices + + return molecules + + def get_displacement(self, target, dtype = np.float64): + """ + GET THE DISPLACEMENT STRUCTURE + ============================== + + This function will return an array of displacement respect to the target + of the current structure. Note that the two structures must be compatible. + + + NOTE: if any the self unit_cell will be considered, otherwise the target one. + no unit cell is used only if neither the self nor the target have one. + + Parameters + ---------- + target : Structure.Structure() + The reference atomic positions (also this is a structure) + dtype : type + The type to be cast the result. By default is the double precision + + Results + ------- + ndarray N_atoms x 3 + The displacements (same shape as self.coords) + """ + + # Check if the two structures are compatible + if self.N_atoms != target.N_atoms: + raise ValueError("Error, the target must share the same number of atoms") + + unit_cell = np.zeros((3,3)) + easy = False + if not self.has_unit_cell: + if not target.has_unit_cell: + easy = True + else: + unit_cell = target.unit_cell + else: + unit_cell = self.unit_cell + + disp = np.zeros(np.shape(self.coords)) + disp = np.float64(self.coords - target.coords) + if easy: + return disp + + # Check that the cell is good + for i in range(self.N_atoms): + # Add half of the the unit cell + for j in range(3): + disp[i,:] += unit_cell[j,:] * .5 + + disp[i,:] = Methods.put_into_cell(unit_cell, disp[i,:]) + + # Remove again the half cell + for j in range(3): + disp[i,:] -= unit_cell[j,:] * .5 + + return disp + + + def get_angle(self, index1, index2, index3, rad = False): + """ + GET ANGLE BETWEEN THREE ATOMS + ============================= + + This function evaluate the angle between three atoms located + in the structure at the correct indices. The unit cell is centered around + the second atom to compute correctly the structure. + + + Parameters + ---------- + indexI : int + Index of the Ith atom. (The angle is the one between 1-2-3) + rad : bool, optional + If true, the angle is returned in radiants (otherwise in degrees) + + Return + ------ + angle : float + Value of the angle in degrees (unles rad is specified) between the index1-index2-index3 + atoms of the structure. + + """ + + if index1 >= self.N_atoms or index2 >= self.N_atoms or index3 >= self.N_atoms: + raise ValueError("Error, the indices must be lower than the number of atoms.") + + + # Get the three vectors + v1 = self.coords[index1,:].copy() + v2 = self.coords[index2,:].copy() + v3 = self.coords[index3,:].copy() + + # center with respect of v2 + v1 -= v2 + v2 -= v2 + v3 -= v2 + + # Manipulate them if there is an unitcell + if self.has_unit_cell: + # Sum half of the cell vectors + for i in range(3): + v1 += self.unit_cell[i,:] * .5 + v2 += self.unit_cell[i,:] * .5 + v3 += self.unit_cell[i,:] * .5 + + # Put the vectors in the unit cell + v1 = Methods.put_into_cell(self.unit_cell, v1) + v2 = Methods.put_into_cell(self.unit_cell, v2) + v3 = Methods.put_into_cell(self.unit_cell, v3) + + # Center again around v2 + for i in range(3): + v1 -= self.unit_cell[i,:] * .5 + v2 -= self.unit_cell[i,:] * .5 + v3 -= self.unit_cell[i,:] * .5 + + # Now we can measure the angle + angle = np.arccos(np.dot(v1, v3) / np.sqrt(np.dot(v1, v1) * np.dot(v3, v3))) + + # Degree conversion + if not rad: + angle *= 180 / np.pi + + return angle + + + + def GetTriatomicMolecules(self, atoms, distance1, distance2, angle, thr_dist=0.01, thr_ang = 1, return_indices = False): + """ + GET TRIATOMIC MOLECULE + ===================== + + This function allows one to extract from a structure all the triatomic + molecules that contains the atoms specified and that are at the distance and angle + with a given tollerance. + This is very usefull to compute some particular average bond length. + + The two distances are between the first-second and second-third atom, while the angle + is between first-second-third atom. + + Be carefull if the atoms are equal and the distance1 and distance2 are very similar + the algorithm can find twice the same molecules. + + Parameters + ---------- + - atoms : list (char) (size = 3) + The atomic symbols of the molecule + - distance1 : float + The average distance between the first two atom in the molecule + - distance2 : float + The average distance between the last two atom in the molecule + - angle : float + Angle (in degree) between the central atom and the other two. + - thr_dist: float, default 0.01 + The tollerance on the distance after which the two atoms are + no more consider inside the same molecule. + - thr_angle: float, default 1 + Tollerance for the angle + - return_indices : bool, default false + If true, per each molecule is returned also the list of the + original indices inside the structure. + + Results + ------- + - Molecules : list + List of molecules (Structure) that matches the input. + If none is found an empty list is returned + """ + # Check if the atoms is a 3 char list + if len(atoms) != 3: + raise ValueError("Error, the molecule must be triatomic") + + for a in atoms: + if not a in self.atoms: + raise ValueError("Error, the atom %s is not into this structure" % a) + + # Scroll all the atoms in the list that match the first type. + molecules = [] + original_indices = [] + for index1 in range(self.N_atoms): + atm1 = self.atoms[index1] + if atm1 != atoms[0]: + continue + + # Avoid double counting if the molecule is omonuclear + starting_index = 0 + if atoms[0] == atoms[1]: + starting_index = index1 + 1 + + for index2 in range(starting_index, self.N_atoms): + atm2 = self.atoms[index2] + if atm2 != atoms[1]: + continue + + if index2 == index1: + continue + + # Check if the distances between the two atoms matches + d = self.get_min_dist(index1, index2) + #print "1) Selected %d %d => d = %.3f" % (index1, index2, d) + if not (d > distance1 - thr_dist and d < distance1 + thr_dist): + continue + + # Accepted the first two atoms + for index3 in range(0, self.N_atoms): + if index3 in [index1, index2]: + continue + + d = self.get_min_dist(index2, index3) + #print "2) Selected %d %d => d = %.3f" % (index2, index3, d) + + if not (d > distance2 - thr_dist and d < distance2 + thr_dist): + continue + + # Ok accepted for distance + # Check also the angle + ang = self.get_angle(index1, index2, index3) + print ("A> %d %d %d = %.3f" % (index1, index2, index3, ang)) + + if not (ang > angle - thr_ang and ang < angle + thr_ang): + continue + + + # Create the structure of the molecule + mol = Structure() + mol.N_atoms = 3 + mol.atoms = atoms + mol.coords = np.zeros((3, 3)) + mol.unit_cell = self.unit_cell + mol.has_unit_cell = True + + # Translate the molecule in the middle of the cell + if self.has_unit_cell: + for i in range(3): + mol.coords[0,:] += self.unit_cell[i,:] / 2. + mol.coords[1,:] += self.unit_cell[i,:] / 2. + mol.coords[2,:] += self.unit_cell[i,:] / 2. + + + mol.coords[0,:] += self.coords[index1,:] - self.coords[index2,:] + mol.coords[2,:] += self.coords[index3,:] - self.coords[index2,:] + + print ("1-Accepted:", mol.get_min_dist(0,1), mol.get_min_dist(1,2), mol.get_angle(0, 1, 2)) + + # If the system has a unit cell, put the second atom inside the cell + if self.has_unit_cell: + for k in range(3): + mol.coords[k,:] = Methods.put_into_cell(self.unit_cell, mol.coords[k,:]) + + print ("2-Accepted:", mol.get_min_dist(0,1), mol.get_min_dist(1,2), mol.get_angle(0, 1, 2)) + + # Append the molecule to the structure + molecules.append(mol) + original_indices.append( (index1, index2, index3) ) + + if return_indices: + return molecules, original_indices + + return molecules + + def generate_espresso_input(self, flags): + """ + GENERATE ESPRESSO INPUT + ======================= + + This subroutine will generate the input for a quantum espresso calculation + """ + pass + + def IsolateAtoms(self, *args, **kwargs): + warnings.warn("This function is deprecated, use isolate_atoms instead.") + return self.isolate_atoms(*args, **kwargs) + + def isolate_atoms(self, atoms_indices): + """ + This subroutine returns a Structure() with only the atoms indices identified + by the provided list. + + Parameters + ---------- + atoms_indices : list of int + List of the atoms that you want to isolate + + Returns + ------- + new_structure : Structure() + A structure with only the isolated atoms. + """ + + + new_struct = self.copy() + nat = len(atoms_indices) + new_struct.N_atoms = nat + + new_struct.coords = np.zeros( (nat, 3), dtype = np.float64) + new_struct.atoms = [None] * nat + + for i, x in enumerate(atoms_indices): + new_struct.coords[i,:] = self.coords[x,:] + new_struct.atoms[i] = self.atoms[x] + + return new_struct + + def get_inertia_tensor(self): + """ + GET INERTIA TENSOR + ==================== + + This method get the intertial tensor of the current structure. + Note periodic boundary conditions will be ingored, + so take care that the atoms are correctly centered. + + The units will be the units given for the mass dot the position^2 + + Results + ------- + I : ndarray ( size = (3,3), dtype = np.double) + The inertia tensor + """ + + # Extract the masses + m = self.get_masses_array() + + I = np.zeros( (3,3), dtype = np.double) + E = np.eye(3, dtype = np.double) + + # Get the center of mass + r_cm = np.einsum("a, ab->b", m, self.coords) / np.sum(m) + + # Get the inertia tensor + for i in range(self.N_atoms): + r = self.coords[i, :] - r_cm + + I += m[i] * (E * r.dot(r) - np.outer(r,r)) + + return I + + def get_classical_rotational_free_energy(self, temperature, unit_mass = "Ry"): + """ + ROTATIONAL FREE ENERGY + ====================== + + Get the classical free energy of a rigid rotor. + + Parameters + ---------- + temperature : float + Temperature in K + unit_mass : string + The unit of measurement of the masses. + It can be one of: + - "uma" : the atomic mass unit (1/12 of the C12 nucleus) + - "Ry" : the rydberg mass (twice electron mass) + - "Ha" : the hartree mass (electron mass) + Results + ------- + free_energy : float + The rotational free energy in eV + """ + + # Get the inertia tensor + It = self.get_inertia_tensor() + + # convert the mass + if unit_mass.lower() == "ry": + It *= MASS_RY_TO_UMA + elif unit_mass.lower() == "ha": + It /= ELECTRON_MASS_UMA + elif unit_mass.lower() == "uma": + pass + else: + ERROR_MSG = """ + Error, unkwown unit type: {} +""" + raise ValueError(ERROR_MSG.format(unit_mass)) + + + Idiag, dumb = np.linalg.eigh(It) + + kbT = temperature* K_B + Z = np.sqrt((2 * np.pi* kbT)**3 * np.prod(Idiag)) + free_energy = - kbT * np.log(Z) + #free_energy = 3 * kbT * np.log(2*kbT)/2 + kbT / 2 * np.sum(np.log(Idiag)) + + return free_energy + + + + +def get_structures_from_phonopy_supercells(ph_supercells): + """ + Get a list of CellConstructor structures starting from the phonopy supercells_with_displacements + object. + """ + + if not __PHONOPY__: + raise ImportError("Error, to execute this method you must have Phonopy installed.") + + if not isinstance(ph_supercells, list): + raise ValueError("Error ph_supercells must be a list.") + + if not isinstance(ph_supercells[0], phonopy.structure.atoms.PhonopyAtoms): + raise ValueError("Error, ph_supercells must be a list of PhonopyAtoms object.") + + n_tot = len(ph_supercells) + atms = ph_supercells[0].get_chemical_symbols() + nat = len(atms) + + s_basis = Structure(nat) + s_basis.atoms = atms + s_basis.has_unit_cell = True + s_basis.unit_cell = ph_supercells[0].get_cell() + + all_structures = [] + for i in range(n_tot): + s = s_basis.copy() + s.coords = ph_supercells[0].get_positions().copy() + + all_structures.append(s) + + return all_structures \ No newline at end of file diff --git a/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py b/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py new file mode 100644 index 00000000..6aba1fc7 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py @@ -0,0 +1,525 @@ +from ast import Delete +import cellconstructor as CC +import cellconstructor.Structure +import cellconstructor.Methods + +import subprocess + +import ase, ase.io +import ase.calculators.calculator + +import cellconstructor.Settings as Settings + +import cellconstructor.Units +import copy + +import scipy, scipy.optimize + +import numpy as np +import copy +import sys, os + + + + +class Calculator: + def __init__(self): + """ + CELLCONSTRUCTOR CALCULATOR + =========================== + + This is an alternative to ASE calculators, which often do not work. + It is explicitely done for cellconstructor and python-sscha + + """ + + self.label = "label" + self.directory = None + self.command = None + self.results = {} + self.structure = None + + def set_directory(self): + pass + + + +def get_energy_forces(calculator, structure): + """ + Accepts both an ase calculaotr and a calculator of CellConstructor + """ + + if isinstance(calculator, ase.calculators.calculator.Calculator): + atm = structure.get_ase_atoms() + atm.set_calculator(calculator) + energy = atm.get_total_energy() + if isinstance(energy, np.ndarray): + energy = energy[0] + return energy, atm.get_forces() + elif isinstance(calculator, Calculator): + calculator.calculate(structure) + return calculator.results["energy"], calculator.results["forces"] + else: + raise ValueError("Error, unknown calculator type") + +def get_results(calculator, structure, get_stress = True): + """ + Accepts both an ASE calculator and a Calculator from Cellconstructor + and computes all the implemented properties (energy, forces and stress tensor). + """ + + results = {} + if isinstance(calculator, ase.calculators.calculator.Calculator): + atm = structure.get_ase_atoms() + atm.set_calculator(calculator) + results["energy"] = atm.get_total_energy() + results["forces"] = atm.get_forces() + if get_stress: + results["stress"] = atm.get_stress(voigt = False) + elif isinstance(calculator, Calculator): + calculator.calculate(structure) + results = calculator.results + if get_stress: + results["stress"] = CC.Methods.transform_voigt(results["stress"], voigt_to_mat = True) + else: + raise ValueError("Error, unknown calculator type") + + return results + + +class FileIOCalculator(Calculator): + def __init__(self): + Calculator.__init__(self) + self.structure = None + self.output_file = "PREFIX.pwo" + + def write_input(self, structure): + if self.directory is None: + self.directory = os.path.abspath(".") + + if not os.path.isdir(self.directory): + os.makedirs(self.directory) + + # This is not thread safe, as writing the input override the structure of the shared calculator object + # Which is then overridden by the read_results + #self.structure = structure.copy() + + def calculate(self, structure): + self.write_input(structure) + self.execute() + self.read_results() + + def set_label(self, lbl): + self.label = lbl + + def set_directory(self, directory): + self.directory = directory + + # Produce the directory if it does not exists + if not os.path.exists(directory): + os.makedirs(directory) + + def execute(self): + #cmd = "cd {} && {} && cd ..".format(self.directory, self.command.replace("PREFIX", self.label)) + cmd = self.command.replace("PREFIX", os.path.join(os.path.abspath(self.directory),self.label)) + outputfname = self.output_file.replace("PREFIX", os.path.join(os.path.abspath(self.directory),self.label)) + + + new_env = {k: v for k, v in os.environ.items() if "MPI" not in k if "PMI" not in k} + sys.stdout.flush() + with open(os.path.join(self.directory, outputfname), "w") as foutput: + proc = subprocess.Popen(cmd, shell = True, env = new_env, cwd = self.directory, stdout = foutput) + sys.stdout.flush() + errorcode = proc.wait() + sys.stdout.flush() + + + #os.system(cmd) + + def read_results(self): + pass + + +class Espresso(FileIOCalculator): + def __init__(self, input_data = {}, pseudopotentials = {}, masses = None, command = "pw.x -i PREFIX.pwi", kpts = (1,1,1), koffset = (0,0,0)): + """ + ESPRESSO CALCULATOR + =================== + + parameters + ---------- + data_input : dict + Dictionary of the Quantum Espresso PW input namespace + pseudopotentials : dict + Dictionary of the file names of the pseudopotentials + masses : dict + Dictionary of the masses (in UMA) of the specified atomic species + kpts : list + A list of the k points grid to sample the space. + If the calculation is given at gamma, use the gamma string. + Note gamma is incompatible with a koffset + """ + FileIOCalculator.__init__(self) + + self.command = command + self.kpts = kpts + self.koffset = koffset + self.input_data = copy.deepcopy(input_data) # Copy to avoid double modification + self.pseudopotentials = pseudopotentials + self.output_file = "PREFIX.pwo" + if masses is None: + masses = {} + for atm in pseudopotentials: + masses[atm] = 1.000 + self.masses = masses + + assert len(list(self.pseudopotentials)) == len(list(self.masses)), "Error, pseudopotential and masses must match" + + def copy(self): + """ + Return an identical instance, without inhering the info of the calculation. + """ + new_class = Espresso(self.input_data, self.pseudopotentials, self.masses, self.command, self.kpts, self.koffset) + return new_class + + + def set_label(self, lbl, override_prefix = True, *args, **kwargs): + FileIOCalculator.set_label(self, lbl, *args, **kwargs) + + # Enforce the override of the prefix + if override_prefix: + if "control" in self.input_data: + self.input_data = copy.deepcopy(self.input_data) + self.input_data["control"].update({"prefix" : lbl}) + + def setup_from_ase(self, ase_calc): + """ + Copy the parameters from the ASE calculator + """ + + for kwarg in ase_calc.parameters: + self.__setattr__(kwarg, copy.deepcopy(ase_calc.parameters[kwarg])) + + self.set_label(ase_calc.label) + + #self.input_data = copy.deepcopy(ase_calc.parameters["input_data"]) + #self.kpts = ase_calc.parameters["kpts"] + #self.koffset = ase_calc.parameters["koffset"] + #self.pseudopotentials = copy.deepcopy(ase_calc.parameters["pseudopotentials"]) + + def write_input(self, structure): + FileIOCalculator.write_input(self, structure) + + typs = np.unique(structure.atoms) + + total_input = copy.deepcopy(self.input_data) + total_input["system"].update({"nat" : structure.N_atoms, "ntyp" : len(typs), "ibrav" : 0}) + #total_input["control"].update({"outdir" : self.directory, "prefix" : self.label}) + if not "prefix" in total_input["control"]: + total_input["control"].update({"prefix" : self.label}) + + scf_text = "".join(CC.Methods.write_namelist(total_input)) + + print("TOTAL INPUT:") + print(total_input) + scf_text += """ +ATOMIC_SPECIES +""" + for atm in typs: + scf_text += "{} {} {}\n".format(atm, self.masses[atm], self.pseudopotentials[atm]) + + if isinstance(self.kpts, str): + if self.kpts.lower() == 'gamma': + scf_text += ''' +K_POINTS gamma +''' + else: + raise ValueError('Error, kpts msut be either list or gamma, {} not recognized'.format(self.kpts)) + elif len(np.shape(self.kpts)) == 2: + nkpts, _ = np.shape(self.kpts) + scf_text += ''' +K_POINTS crystal +{} +'''.format(nkpts) + for i in range(nkpts): + scf_text += '{:.16f} {:.16f} {:.16f} 1\n'.format(*list(self.kpts[i, :])) + elif len(self.kpts) == 3: + scf_text += """ +K_POINTS automatic +{} {} {} {} {} {} +""".format(self.kpts[0], self.kpts[1], self.kpts[2], + self.koffset[0], self.koffset[1], self.koffset[2]) + + + scf_text += structure.save_scf(None, get_text = True) + + filename = os.path.join(self.directory, self.label + ".pwi") + + with open(filename, "w") as fp: + fp.write(scf_text) + + + def read_results(self, override_structure = True): + FileIOCalculator.read_results(self) + + + filename = os.path.join(self.directory, self.label + ".pwo") + + print('READING RESULTS FROM FILE ', filename) + + # Settings.all_print("reading {}".format(filename)) + #atm = ase.io.read(filename) + + energy = 0 + read_forces = False + counter = 0 + stress = np.zeros((3,3), dtype = np.double) + + read_stress = False + got_stress = False + read_structure = override_structure + read_coords = False + alat = CC.Units.BOHR_TO_ANGSTROM + + # If we read until the stress + # Everything went correctly, otherwise check for the JOB DONE + job_done = False + + if self.structure is None: + read_structure = True + else: + forces = np.zeros_like(self.structure.coords) + + with open(filename, "r") as fp: + for line in fp.readlines(): + line = line.strip() + data = line.split() + + # Avoid white lines + if not line: + continue + + # Check if the script exited correctly + if "JOB DONE" in line: + job_done = True + + if read_structure: + new_data = line.replace("=", " ").split() + if new_data[0] == "celldm(1)": + alat *= float(new_data[1]) + + if "number of atoms/cell" in line: + nat = int(data[-1]) + self.structure = CC.Structure.Structure(nat) + self.structure.has_unit_cell = True + self.structure.unit_cell = np.eye(3) + forces = np.zeros_like(self.structure.coords) + + if data[0] == "a(1)": + self.structure.unit_cell[0,:] = [float(x) * alat for x in data[3:-1]] + if data[0] == "a(2)": + self.structure.unit_cell[1,:] = [float(x) * alat for x in data[3:-1]] + if data[0] == "a(3)": + self.structure.unit_cell[2,:] = [float(x) * alat for x in data[3:-1]] + + if "Cartesian axes" in line: + read_coords = True + + + if read_coords: + # Improve the split of the line to avoid merging numbers + data = line.replace("-", " -").replace("(", "( ").split() + if len(data) == 10: + i_atm = int(data[0]) - 1 + self.structure.coords[i_atm, :] = [float(x) * alat for x in data[6:9]] + self.structure.atoms[i_atm] = data[1] + if i_atm == self.structure.N_atoms - 1: + read_coords = False + read_structure = False + continue + + + + if line[0] == "!": + energy = float(data[4]) + + if "Forces acting on atoms" in line: + read_forces = True + read_stress = False + continue + + if "total stress" in line: + read_stress = True + read_forces = False + counter = 0 + continue + + if read_forces and len(data) == 9: + if data[0] == "atom": + counter += 1 + + at_index = int(data[1]) - 1 + forces[at_index, :] = [float(x) for x in data[6:]] + + if counter >= self.structure.N_atoms: + read_forces = False + + if read_stress and len(data) == 6: + stress[counter, :] = [float(x) for x in data[:3]] + counter += 1 + if counter == 3: + got_stress = True + read_stress = False + + + # Convert to match ASE conventions + energy *= CC.Units.RY_TO_EV + forces *= CC.Units.RY_TO_EV / CC.Units.BOHR_TO_ANGSTROM + stress *= CC.Units.RY_PER_BOHR3_TO_EV_PER_A3 + stress = CC.Methods.transform_voigt(stress) # To be consistent with ASE, use voigt notation + + print('READING RESULTS : energy = {} | job done = {}'.format(energy, job_done)) + + # Everything went on correctly, update the results + if job_done or got_stress: + self.results = {"energy" : energy, "forces" : forces} + if got_stress: + # Use voit + self.results.update({"stress" : - stress}) + else: + self.results = None + + + + +# Here the methods to minimize the structure with a standard calculator +class Relax: + def __init__(self, structure, calculator, method = "BFGS", verbose = True, store_trajectory = True): + """ + Class that perform the structure relaxation. + + Parameters + ---------- + structure : CC.Structure.Structure() + The atomic structure + calculator : CC.calculators.Calculator() + The CellConstructor (or ASE) calculator. + method : string + The algorithm for the minimization. Default BFGS + verbose : bool + If true, prints the current total energy and forces + store_trajector : bool + If true, the trajectory of the minimization is saved in self.trajectory + """ + self.structure = structure + self.calculator = calculator + self.method = method + self.verbose = verbose + self.store_trajectory = store_trajectory + + self.trajectory = [] + + # Usefull variables to track the energy and add a callback + self.last_eval = None + self.last_energy = None + self.last_force = None + self.iterations = 1 + + + def static_relax(self, **kwargs): + """ + RELAX THE STRUCTURE + ------------------- + + Relax the structure keeping fixed the lattice parameters using a BFGS algorithm. + + Parameters + ---------- + **kwargs : + Any optional arguments of scipy.optimize.minimize to control + the minimization. + + Results + ------- + optimized_structure : CC.Structure.Structure() + The structure after the optimization + """ + + if "method" in kwargs: + self.method = kwargs["method"] + + + # Parse the function to match the scipy minimizer + self.last_eval = np.zeros(self.structure.coords.ravel().shape, dtype = np.double) + self.last_energy = 0 + self.last_force = np.zeros_like(self.last_eval) + + def func(x): + if np.linalg.norm(x - self.last_eval) < 1e-16: + return self.last_energy, self.last_force + + struct = self.structure.copy() + struct.coords[:,:] = x.reshape(struct.coords.shape) + + energy, forces = get_energy_forces(self.calculator, struct) + + self.last_eval[:] = x.copy() + self.last_energy = energy + self.last_force[:] = -forces.ravel().copy() + + + return energy, -forces.ravel() + + def callback(xk): + + if self.verbose: + energy, force = func(xk) + #print('it:', self.iterations) + #print('energy:', energy) + #print('force:', force) + print("{:5d}) {:16.8f} eV {:16.8f} eV/A".format(self.iterations, energy, np.linalg.norm(force))) + self.iterations += 1 + + if self.store_trajectory: + struc = self.structure.copy() + struc.coords[:,:] = xk.reshape(struc.coords.shape) + self.trajectory.append(struc) + + if self.verbose: + print("STATIC STRUCTURE RELAX") + print() + print("{:5s} {:16s} {:16s} ".format("ITERS", "ENERGY", "FORCE GRAD")) + print("--------------------------------------------------") + + + res = scipy.optimize.minimize(func, self.structure.coords.ravel(), method = self.method, jac = True, callback = callback, **kwargs) + + if self.verbose: + print() + + final_struct = self.structure.copy() + final_struct.coords[:,:] = res.x.reshape(final_struct.coords.shape) + self.structure = final_struct + + return final_struct + + + + + + + + + + + + + + + + + + + + + diff --git a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py new file mode 100644 index 00000000..01bd3a01 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py @@ -0,0 +1,3027 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 29 11:10:21 2017 + +@author: darth-vader +""" +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division + +import time +import os +import numpy as np + +import scipy +import scipy.linalg + +import cellconstructor.Methods as Methods +from cellconstructor.Units import * +import cellconstructor.Timer as Timer +from cellconstructor.Settings import ParallelPrint as print + +# Load the fortran symmetry QE module +import symph + +# Load the LinAlgebra module in C +from cc_linalg import GramSchmidt + +import warnings + + +__SPGLIB__ = True +try: + import spglib +except: + __SPGLIB__ = False + + + + +CURRENT_PATH = os.path.realpath(__file__) +CURRENT_DIR = os.path.dirname(CURRENT_PATH) +__EPSILON__ = 1e-5 + +class QE_Symmetry: + def __init__(self, structure, threshold = 1e-5): + """ + Quantum ESPRESSO Symmetry class + =============================== + + This class contains all the info about Quantum ESPRESSO symmetry data. + It is used to wrap symmetries into the quantum espresso fortran subroutines. + + Starting from a set of symmetry operation and the structure of the system, it + builds all the QE symmetry operations. + + NOTE: + Use always the provided methods to change the self variables, as they are + handled to properly cast the fortran types and array alignment. + + Parameters + ---------- + structure : CC.Structure.Structure() + The structure to which the symmetries refer to. + threshold : float + The threshold of the symmetry operation. + + """ + + if not structure.has_unit_cell: + raise ValueError("Error, symmetry operation can be initialize only if the structure has a unit cell") + + self.structure = structure + self.threshold = np.float64(threshold) + + # Setup the threshold + symph.symm_base.set_accep_threshold(self.threshold) + + nat = structure.N_atoms + + # Define the quantum espresso symmetry variables in optimized way to work with Fortran90 + self.QE_nat = np.intc( nat ) + self.QE_s = np.zeros( (3, 3, 48) , dtype = np.intc, order = "F") + self.QE_irt = np.zeros( (48, nat), dtype = np.intc, order = "F") + self.QE_invs = np.zeros( (48), dtype = np.intc, order = "F") + self.QE_rtau = np.zeros( (3, 48, nat), dtype = np.float64, order = "F") + self.QE_ft = np.zeros( (3, 48), dtype = np.float64, order = "F") + + + self.QE_minus_q = np.bool( False ) + self.QE_irotmq = np.intc(0) + self.QE_nsymq = np.intc( 0 ) + self.QE_nsym = np.intc(0) + + # Prepare the QE structure + self.QE_tau = np.zeros((3, nat), dtype = np.float64, order = "F") + self.QE_ityp = np.zeros(nat, dtype = np.intc) + + symbs = {} + counter = 1 + for i in range(nat): + # Rank the atom number + atm = structure.atoms[i] + if not atm in symbs.keys(): + symbs[atm] = counter + counter += 1 + + self.QE_ityp[i] = symbs[atm] + # Convert in bohr + for j in range(3): + self.QE_tau[j, i] = structure.coords[i, j] + + + self.QE_at = np.zeros( (3,3), dtype = np.float64, order = "F") + self.QE_bg = np.zeros( (3,3), dtype = np.float64, order = "F") + + bg = structure.get_reciprocal_vectors() + for i in range(3): + for j in range(3): + self.QE_at[i,j] = structure.unit_cell[j,i] + self.QE_bg[i,j] = bg[j,i] / (2* np.pi) + + # Here we define the quantities required to symmetrize the supercells + self.QE_at_sc = self.QE_at.copy() + self.QE_bg_sc = self.QE_bg.copy() + self.QE_translation_nr = 1 # The supercell total dimension (Nx * Ny * Nz) + self.QE_translations = [] # The translations in crystal axes + + # After the translation, which vector is transformed in which one? + # This info is stored here as ndarray( size = (N_atoms, N_trans), dtype = np.intc, order = "F") + self.QE_translations_irt = [] + + def ForceSymmetry(self, structure): + """ + FORCE SYMMETRY + ============== + + Force the symmetries found at a given threshold to + be satisfied also in a lower threshold. + + This use the irt trick + """ + if self.QE_nsymq == 0: + raise ValueError("Error, initialize the symmetries with SetupQPoint.") + + coords = np.zeros( (3, structure.N_atoms), order = "F", dtype = np.float64) + coords[:,:] = structure.coords.transpose() + + # Transform in crystal coordinates + symph.cryst_to_cart(coords, self.QE_bg, -1) + + new_coords = np.zeros( (3, structure.N_atoms), order = "F", dtype = np.float64) + for s_i in range(self.QE_nsymq): + for i in range(structure.N_atoms): + new_coords[:, self.QE_irt[s_i, i]-1 ] += self.QE_s[:,:,s_i].dot(coords[:,i]) + new_coords[:, self.QE_irt[s_i, i]-1 ] += self.QE_ft[:, s_i] + + new_coords /= self.QE_nsymq + + # Transform back into cartesian coordinates + symph.cryst_to_cart(new_coords, self.QE_at, 1) + + # Save in the structure + structure.coords[:,:] = new_coords.transpose() + + def PrintSymmetries(self): + """ + This method just prints the symmetries on stdout. + """ + + print() + print("Number of symmetries: {}".format(self.QE_nsym)) + syms = self.GetSymmetries() + for i in range(self.QE_nsym): + print(" Symmetry {}".format(i+1)) + for j in range(3): + print(" {:3.0f}{:3.0f}{:3.0f} | {:6.3f}".format(*syms[i][j,:])) + print() + + def GetUniqueRotations(self): + """ + This subroutine returns an alternative symmetries + that contains only unique rotations (without fractional translations). + This is usefull if the peculiar cell is a supercell + and the symmetrization was performed with SPGLIB + + Returns + ------- + QE_s : ndarray(size = (3,3,48), dtype = np.intc) + The symmetries + QE_invs : ndarray(size = 48, dtype = np.intc) + The index of the inverse symmetry + QE_nsym : int + The number of symmetries + """ + + QE_s = np.zeros( self.QE_s.shape, dtype = np.intc, order = "F") + QE_invs = np.zeros(self.QE_invs.shape, dtype = np.intc, order = "F") + QE_nsym = 0 + + + for i in range(self.QE_nsym): + # Check if the same rotation was already added + skip = False + for j in range(QE_nsym): + # Check if the rotation occurred + if (QE_s[:,:,j] == self.QE_s[:,:,i]).all(): + skip = True + break + + if not skip: + # We did not find another equal rotation + # Lets add this one + QE_s[:,:, QE_nsym] = self.QE_s[:,:,i] + QE_nsym += 1 + + # Get the inverse + QE_invs[:] = get_invs(QE_s, QE_nsym) + + return QE_s, QE_invs, QE_nsym + + + + + + + + def SetupQStar(self, q_tot, supergroup = False): + """ + DIVIDE THE Q POINTS IN STARS + ============================ + + This method divides the given q point list into the star. + Remember, you need to pass the whole number of q points + + Parameters + ---------- + q_tot : list + List of q vectors to be divided into stars + supergroup : bool + If true then assume we have initialized a supercell bigger + Results + ------- + q_stars : list of lists + The list of q_star (list of q point in the same star). + sort_mask : ndarray(size=len(q_tot), dtype = int) + a mask to sort the q points in order to match the + same order than the q_star + """ + + # Setup the symmetries + #self.SetupQPoint() + + # Lets copy the q list (we are going to pop items from it) + q_list = q_tot[:] + q_stars = [] + + count_qstar = 0 + count_q = 0 + q_indices = np.zeros( len(q_tot), dtype = int) + while len(q_list) > 0: + q = q_list[0] + # Get the star of the current q point + _q_ = np.array(q, dtype = np.float64) # Fortran explicit conversion + + nq_new, sxq, isq, imq = symph.star_q(_q_, self.QE_at, self.QE_bg, + self.QE_nsym, self.QE_s, self.QE_invs, 0) + + # print ("START WITH Q:", q) + # print ("FOUND STAR:") + # for jq in range(nq_new): + # print (sxq[:, jq]) + # print () + + # print ("TELL ME THE BG:") + # print (self.QE_bg.transpose()) + + # print("Manual star:") + # for k in range(self.QE_nsym): + # trial_q = q.dot(self.QE_s[:,:, k]) + # distance_q = Methods.get_min_dist_into_cell(self.QE_bg.T, trial_q, q) + # distance_mq = Methods.get_min_dist_into_cell(self.QE_bg.T, trial_q, -q) + # print("trial_q : {} | DQ: {:.4f} | DMQ: {:.4f}".format(trial_q, distance_q, distance_mq )) + + # Prepare the star + q_star = [sxq[:, k] for k in range(nq_new)] + + # If imq is not zero (we do not have -q in the star) then add the -q for each in the star + if imq == 0: + old_q_star = q_star[:] + min_dist = 1 + + for q in old_q_star: + q_star.append(-q) + + + + q_stars.append(q_star) + + # Pop out the q_star from the q_list + for jq, q_instar in enumerate(q_star): + # Look for the q point in the star and pop them + #print("q_instar:", q_instar) + q_dist = [Methods.get_min_dist_into_cell(self.QE_bg.transpose(), + np.array(q_instar), q_point) for q_point in q_list] + + pop_index = np.argmin(q_dist) + q_list.pop(pop_index) + + # Use the same trick to identify the q point + q_dist = [Methods.get_min_dist_into_cell(self.QE_bg.transpose(), + np.array(q_instar), q_point) for q_point in q_tot] + + q_index = np.argmin(q_dist) + #print (q_indices, count_q, q_index) + q_indices[count_q] = q_index + + count_q += 1 + + + return q_stars, q_indices + + + def ApplySymmetryToTensor3(self, v3, initialize_symmetries = True): + """ + SYMMETRIZE A RANK-3 TENSOR + ========================== + + This subroutines uses the current symmetries to symmetrize + a rank-3 tensor. + This tensor must be in the supercell space. + + The v3 argument will be overwritten. + + NOTE: The symmetries must be initialized in the supercell using spglib + + + Parameters + ---------- + v3 : ndarray( size=(3*nat, 3*nat, 3*nat), dtype = np.double, order = "F") + The 3-rank tensor to be symmetrized. + It will be overwritten with the new symmetric one. + It is suggested to specify the order of the array to "F", as this will prevent + the parser to copy the matrix when doing the symmetrization in Fortran. + initialize_symmetries : bool + If True the symmetries will be initialized using spglib. Otherwise + the already present symmetries will be use. Use it False at your own risk! + (It can crash with seg fault if symmetries are not properly initialized) + """ + if initialize_symmetries: + self.SetupFromSPGLIB() + + # Apply the permutation symmetry + symph.permute_v3(v3) + + # Apply the translational symmetries + symph.trans_v3(v3, self.QE_translations_irt) + + # Apply all the symmetries at gamma + symph.sym_v3(v3, self.QE_at, self.QE_s, self.QE_irt, self.QE_nsymq) + + def ApplySymmetryToEffCharge(self, eff_charges): + """ + SYMMETRIZE EFFECTIVE CHARGES + ============================ + + This subroutine applies the symmetries to the effective charges. + + As always, the eff_charges will be modified by this subroutine. + + Parameters + ---------- + - eff_charges : ndarray (size = (nat, 3, 3)) + The effective charges tensor. + The first dimension is the index of the atom in the primitive cell + the second index is the electric field. + The third index is the cartesian axis. + """ + + nat, cart1, cart2 = np.shape(eff_charges) + + assert cart1 == cart2 + assert cart1 == 3 + assert nat == self.QE_nat, "Error, the structure and effective charges are not compatible" + + + # Apply the sum rule + tot_sum = np.sum(eff_charges, axis = 0) + eff_charges -= np.tile(tot_sum, (nat, 1)).reshape((nat, 3,3 )) / nat + + new_eff_charges = np.zeros((nat, cart1, cart2), dtype = np.double) + + # Get the effective charges in crystal components + for i in range(nat): + eff_charges[i, :, :] = Methods.convert_matrix_cart_cryst(eff_charges[i, :, :], self.QE_at.T) + + # Apply translations + if self.QE_translation_nr > 1: + for i in range(self.QE_translation_nr): + irt = self.QE_translations_irt[:, i] - 1 + for j in range(nat): + new_mat = eff_charges[irt[j], :, :] + new_eff_charges[j, :, :] += new_mat + + eff_charges[:,:,:] = new_eff_charges / self.QE_translation_nr + new_eff_charges[:,:,:] = 0. + + # Apply rotations + for i in range(self.QE_nsym): + irt = self.QE_irt[i, :] - 1 + + for j in range(nat): + new_mat = self.QE_s[:,:, i].dot( eff_charges[irt[j], :, :].dot(self.QE_s[:,:,i].T)) + new_eff_charges[j, :, :] += new_mat + new_eff_charges /= self.QE_nsym + + # Convert back into cartesian + for i in range(nat): + eff_charges[i, :, :] = Methods.convert_matrix_cart_cryst(new_eff_charges[i, :, :], self.QE_at.T, True) + + def ApplySymmetryToRamanTensor(self, raman_tensor): + """ + SYMMETRIZE RAMAN TENSOR + ============================ + + This subroutine applies the symmetries to the raman tensor + + As always, the raman_tensor will be modified by this subroutine. + + Parameters + ---------- + - raman_tensor : ndarray (size = (3, 3, 3 * nat)) + The raman tensor. The first two indices indicate + the polarization of the incoming/outcoming field, while the last one + is the atomic/cartesian coordinate + """ + + pol1, pol2, at_cart = np.shape(raman_tensor) + + assert pol1 == pol2 + assert pol2 == 3 + assert at_cart == 3*self.QE_nat, "Error, the structure and effective charges are not compatible" + + # Apply the permutation on the electric fields + raman_tensor += np.einsum("abc->bac", raman_tensor) + raman_tensor /= 2 + + # Apply the sum rule + # The sum over all the atom for each cartesian coordinate should be zero. + rt_reshaped = raman_tensor.reshape((3,3,self.QE_nat, 3)) + + # Sum over all the atomic indices + tot_sum = np.sum(rt_reshaped, axis = 2) + + # Rebuild the shift to the tensor of the correct shape + shift = np.tile(tot_sum, (self.QE_nat, 1, 1, 1)) + + # Place the number of atoms at the correct position + # From the first to the third + shift = np.einsum("abcd->bcad", shift) + + # Now we apply the sum rule + rt_reshaped -= shift / self.QE_nat + + # Auxiliary variable + new_tensor = np.zeros(np.shape(rt_reshaped), dtype = np.double) + + # Get the raman tensor in crystal components + for i in range(self.QE_nat): + rt_reshaped[:,:, i, :] = Methods.convert_3tensor_to_cryst(rt_reshaped[:,:, i, :], self.QE_at.T) + + # Apply translations + if self.QE_translation_nr > 1: + for i in range(self.QE_translation_nr): + irt = self.QE_translations_irt[:, i] - 1 + for j in range(self.QE_nat): + new_mat = rt_reshaped[:,:, irt[j], :] + new_tensor[:,:,j,:] += new_mat[:,:,:] + + rt_reshaped = new_tensor / self.QE_translation_nr + new_tensor[:,:,:,:] = 0. + + # Apply rotations + for i in range(self.QE_nsym): + irt = self.QE_irt[i, :] - 1 + + for j in range(self.QE_nat): + # Apply the symmetry to the 3 order tensor + new_mat = np.einsum("ai, bj, ck, ijk -> abc", self.QE_s[:,:,i], self.QE_s[:,:,i], self.QE_s[:,:,i], rt_reshaped[:,:, irt[j], :]) + #new_mat = self.QE_s[:,:, i].dot( eff_charges[irt[j], :, :].dot(self.QE_s[:,:,i].T)) + new_tensor[:,:,j,:] += new_mat + + new_tensor /= self.QE_nsym + + # Convert back into cartesian + for i in range(self.QE_nat): + rt_reshaped[:, :, i, :] = Methods.convert_3tensor_to_cryst(new_tensor[:,:,i,:], self.QE_at.T, True) + + # Compress again the notation + raman_tensor[:,:,:] = rt_reshaped.reshape((3,3, 3*self.QE_nat)) + + + def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): + """ + SYMMETRIZE TWO PHONON EFFECTIVE CHARGES + ======================================= + + This subroutine applies simmetries to the two phonon + effective charges. + + Note, to symmetrize this tensor, symmetries must be imposed + on the supercell. + + Parameters + ---------- + dM_drdr : ndarray (size = (3 nat_sc, 3 nat_sc, 3)) + The derivative of effective charges. + The last index refers to electric field + apply_asr : bool + If True the sum rule is applied. + The sum rule is the 'custom' one where translations are projected + out from the space for each polarization components. + """ + + nat3, nat3_, cart = np.shape(dM_drdr) + + assert nat3 == nat3_, "Error on the shape of the argument" + assert nat3 == 3 * self.QE_nat, "Wrong number of atoms (Symmetries must be setup in the supercell)" + assert cart == 3 + + nat = int(nat3 / 3) + + # Apply hermitianity + #print("Original:") + #print(dM_drdr[:,:,0]) + + dM_drdr += np.einsum("abc->bac", dM_drdr) + dM_drdr /= 2 + + # Apply the Sum Rule + if apply_asr: + for pol in range(3): + CustomASR(dM_drdr[:,:,pol]) + + #print("After the sum rule:") + #print(dM_drdr[:,:,0]) + + # Convert in crystal coordinates + for i in range(nat): + for j in range(nat): + dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T) + + + #print("Crystal:") + #print(dM_drdr[:,:,0]) + + + # Apply translations + new_dM = np.zeros(np.shape(dM_drdr), dtype = np.double) + if self.QE_translation_nr > 1: + for i in range(self.QE_translation_nr): + irt = self.QE_translations_irt[:, i] - 1 + for jat in range(nat): + for kat in range(nat): + new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] + new_dM[3*jat: 3*jat+3, 3*kat:3*kat+3, :] += new_mat + + dM_drdr[:,:,:] = new_dM / self.QE_translation_nr + new_dM[:,:,:] = 0 + + + #print("After transl:") + #print(dM_drdr[:,:,0]) + + #self.PrintSymmetries() + + # Apply rotations + for i in range(self.QE_nsym): + irt = self.QE_irt[i, :] - 1 + + #print("") + #print("--------------------") + #print("symmetry: {:d}, irt: {}".format(i+1, irt +1)) + #prova = np.zeros(np.shape(new_dM)) + + for jat in range(nat): + for kat in range(nat): + new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] + # Apply the symmetries + + new_mat = np.einsum("ck, ijk -> ijc", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("bj, ijc -> ibc", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ai, ibc -> abc", self.QE_s[:,:,i], new_mat) + #prova[3*jat:3*jat+3, 3*kat:3*kat+3,:] = new_mat + new_dM[3*jat:3*jat+3, 3*kat:3*kat+3,:] += new_mat + + #print(np.einsum("abc->cab", prova)) + #print("--------------------") + dM_drdr[:,:,:] = new_dM / self.QE_nsym + + # Convert in crystal coordinates + for i in range(nat): + for j in range(nat): + dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T, True) + + + + def ApplySymmetryToSecondOrderRamanTensor(self, dalpha_drdr, apply_asr = True): + """ + SYMMETRIZE TWO PHONON EFFECTIVE CHARGES + ======================================= + + This subroutine applies simmetries to the second order Raman tensor. + + Note, to symmetrize this tensor, symmetries must be imposed on the supercell. + + Parameters + ---------- + dalpha_drdr : ndarray (size = (3, 3, 3 nat_sc, 3 nat_sc)) + The second derivative of polarizability. + apply_asr : bool + If True the sum rule is applied. + The sum rule is the 'custom' one where translations are projected + out from the space for each polarization components. + """ + # Check the shape of the tensor + E1, E2, nat3, nat3_ = np.shape(dalpha_drdr) + + assert nat3 == nat3_, "Error on the shape of the argument for the atomic indices" + assert nat3 == 3 * self.QE_nat, "Wrong number of atoms (Symmetries must be setup in the supercell)" + assert E1 == E2, "Error on the shape of the argument electric field" + assert E1 == 3, "The first two entries are assosciated with electric field" + + # Get the number of atoms in the supercell + nat = int(nat3 /3) + + # Apply hermitianity on the atomic indices + dalpha_drdr += np.einsum("abcd->abdc", dalpha_drdr) + dalpha_drdr /= 2 + + # Apply hermitianity on the electric field + dalpha_drdr += np.einsum("abcd->bacd", dalpha_drdr) + dalpha_drdr /= 2 + + # SUM RULE ranging on the electric field components + if apply_asr: + for pol1 in range(3): + for pol2 in range(3): + CustomASR(dalpha_drdr[pol1, pol2, :, :]) + + # CONVERT TO CRYSTAL COORDINATES + for i in range(nat): + for j in range(nat): + dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3], self.QE_at.T) + + + # Get a ZERO second order Raman tensor (AUXILIARY) + new_dalpha_drdr = np.zeros(np.shape(dalpha_drdr), dtype = np.double) + + # TRANSLATIONS + if self.QE_translation_nr > 1: + for i in range(self.QE_translation_nr): + # irt[at1] is the atom on which the translation i maps at1 + irt = self.QE_translations_irt[:, i] - 1 + for at1 in range(nat): + for at2 in range(nat): + # Get the part of the tensor that is equivalent by translations of atom at1 at2 + new_mat = dalpha_drdr[:, :, 3*irt[at1]: 3*irt[at1] + 3, 3 * irt[at2]: 3*irt[at2] + 3] + # Fill with the symmetric counterparts + new_dalpha_drdr[:, :, 3*at1: 3*at1+3, 3*at2:3*at2+3] += new_mat + + # OVERWRITE the second order Raman tensor + dalpha_drdr[:,:,:,:] = new_dalpha_drdr / self.QE_translation_nr + # SET TO ZERO THE AUXILIARY VARIABLE + new_dalpha_drdr[:,:,:,:] = 0 + + # # DEBUG VARIABLE + # debug = np.zeros(np.shape(new_dalpha_drdr)) + + # ROTATIONS + for i in range(self.QE_nsym): + irt = self.QE_irt[i, :] - 1 # the symmetry applied on irt[at] gives the atom at + + for at1 in range(nat): + for at2 in range(nat): + # Get the part of the tensor that is equivalent by rotations of atom at1 at2 + # This has shape = (3, 3, 3, 3) + new_mat = dalpha_drdr[:, :, 3*irt[at1] : 3*irt[at1] + 3, 3*irt[at2] : 3*irt[at2] + 3] + + # Apply the symmetries + new_mat = np.einsum("dl, ijkl -> ijkd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ck, ijkd -> ijcd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("bj, ijcd -> ibcd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ai, ibcd -> abcd", self.QE_s[:,:,i], new_mat) + + new_dalpha_drdr[:, :, 3*at1: 3*at1 + 3, 3*at2: 3*at2 + 3] += new_mat + + # # CONVERT IN CARTESIAN COORDINATES TO DEBUG + # for _i_ in range(nat): + # for _j_ in range(nat): + # debug[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3] = Methods.convert_4tensor_to_cryst(new_dalpha_drdr[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3],\ + # self.QE_at.T, cryst_to_cart = True) + # np.save('CC_new_{}'.format(i), debug) + + # OVERWRITE THE second order Raman tensor + dalpha_drdr[:,:,:,:] = new_dalpha_drdr /self.QE_nsym + + + # Convert BACK in crystal coordinates + for i in range(nat): + for j in range(nat): + dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j+3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j : 3*j + 3],\ + self.QE_at.T, cryst_to_cart = True) + + # # TO DEBUG + # np.save('CC_raman', dalpha_drdr) + + return + + + + + + def ApplySymmetryToTensor4(self, v4, initialize_symmetries = True): + """ + SYMMETRIZE A RANK-4 TENSOR + ========================== + + This subroutines uses the current symmetries to symmetrize + a rank-4 tensor. + This tensor must be in the supercell space. + + The v4 argument will be overwritten. + + NOTE: The symmetries must be initialized in the supercell using spglib + + + Parameters + ---------- + v4 : ndarray( size=(3*nat, 3*nat, 3*nat, 3*nat), dtype = np.double, order = "F") + The 4-rank tensor to be symmetrized. + It will be overwritten with the new symmetric one. + It is suggested to specify the order of the array to "F", as this will prevent + the parser to copy the matrix when doing the symmetrization in Fortran. + initialize_symmetries : bool + If True the symmetries will be initialized using spglib. Otherwise + the already present symmetries will be use. Use it False at your own risk! + (It can crash with seg fault if symmetries are not properly initialized) + """ + if initialize_symmetries: + self.SetupFromSPGLIB() + + # Apply the permutation symmetry + symph.permute_v4(v4) + + # Apply the translational symmetries + symph.trans_v4(v4, self.QE_translations_irt) + + # Apply all the symmetries at gamma + symph.sym_v4(v4, self.QE_at, self.QE_s, self.QE_irt, self.QE_nsymq) + + def ApplyQStar(self, fcq, q_point_group): + """ + APPLY THE Q STAR SYMMETRY + ========================= + + Given the fc matrix at each q in the star, it applies the symmetries in between them. + + Parameters + ---------- + - fcq : ndarray(nq, 3xnat, 3xnat) + The dynamical matrices for each q point in the star + - q_point_group : ndarray(nq, 3) + The q vectors that belongs to the same star + """ + + nq = np.shape(q_point_group)[0] + final_fc = np.zeros(np.shape(fcq), dtype = np.complex128) + + # Setup all the symmetries + self.SetupQPoint() + + new_dyn = np.zeros( (3 * self.QE_nat, 3*self.QE_nat), dtype = np.complex128, order = "F") + + dyn_star = np.zeros( (nq, 3, 3, self.QE_nat, self.QE_nat), dtype = np.complex128, order = "F") + + for i in range(nq): + # Get the q points order + nq_new, sxq, isq, imq = symph.star_q(q_point_group[i,:], self.QE_at, self.QE_bg, + self.QE_nsymq, self.QE_s, self.QE_invs, 0) + + + #print "Found nq:", nq_new + #print "IMQ?", imq + + # Check if the q star is correct + if nq_new != nq and imq != 0: + print ("Reciprocal lattice vectors:") + print (self.QE_bg.transpose() ) + print ("Passed q star:") + print (q_point_group) + print ("QE q star:") + print (sxq[:, :nq_new].transpose()) + raise ValueError("Error, the passed q star does not match the one computed by QE") +# +# # Print the star +# print "q point:", q_point_group[i,:] +# print "Point in the stars:", nq_new +# print "Star of q:" +# print sxq[:, :nq_new].transpose() +# +# print "NEW_DYN:", np.shape(new_dyn) +# print "AT:", np.shape(self.QE_at) +# print "BG:", np.shape(self.QE_bg) +# print "N SYM:", self.QE_nsymq +# print "S:", np.shape(self.QE_s) +# print "QE_INVS:", np.shape(self.QE_invs) +# print "IRT:", np.shape(self.QE_irt) +# print "RTAU:", np.shape(self.QE_rtau) +# print "NQ_NEW:", nq_new +# print "SXQ:", np.shape(sxq) +# print "ISQ:", np.shape(isq) +# print "IMQ:", imq +# print "NAT:", self.QE_nat + + new_dyn[:,:] = fcq[i,:,:] + #print "new dyn ready" + + # Get the new matrix + dyn_star = symph.q2qstar_out(new_dyn, self.QE_at, self.QE_bg, self.QE_nsymq, + self.QE_s, self.QE_invs, self.QE_irt, self.QE_rtau, + nq_new, sxq, isq, imq, nq, self.QE_nat) + #print "Fake" + + #print "XQ:", q_point_group[i, :], "NQ_NEW:", nq_new + + # Now to perform the match bring the star in the same BZ as the q point + # This facilitate the comparison between q points + current_q = q_point_group.copy() + #print "Fake2" +# for xq in range(nq): +# tmp = Methods.put_into_cell(self.QE_bg, sxq[:, xq]) +# sxq[:, xq] = tmp +# current_q[xq,:] = Methods.put_into_cell(self.QE_bg, current_q [xq,:]) +# + # Print the order of the q star + sorting_q = np.arange(nq) + for xq in range(nq): + count = 0 # Debug (avoid no or more than one identification) + for yq in range(nq): + real_y = yq + dot_f = 1 + if imq == 0 and yq >= nq_new: + real_y -= nq_new + dot_f = -1 + if Methods.get_min_dist_into_cell(self.QE_bg.transpose(), dot_f* sxq[:, real_y], current_q[xq,:]) < __EPSILON__: + sorting_q[xq] = yq + count += 1 + + if count != 1: + print ("Original star:") + print (q_point_group) + print ("Reshaped star:") + print (current_q) + print ("Reciprocal lattice vectors:") + print (self.QE_bg.transpose() ) + print ("STAR:") + print (sxq[:, :nq_new].transpose() ) + pta = (current_q[xq,:]) + print ("Distances of xq in the QE star:") + for yq in range(nq_new): + print ("%.4f %.4f %.4f => " % (sxq[0, yq], sxq[1, yq], sxq[2, yq]), Methods.get_min_dist_into_cell(self.QE_bg.transpose(), sxq[:, yq], current_q[xq,:])) + raise ValueError("Error, the vector (%.3f, %.3f, %.3f) has %d identification in the star" % (pta[0], pta[1], pta[2], + count)) + #print "Sorting array:" + #print sorting_q + + + # Copy the matrix in the new one + for xq in range(nq): + for xat in range(self.QE_nat): + for yat in range(self.QE_nat): + final_fc[xq, 3*xat: 3*xat + 3, 3*yat : 3*yat + 3] += dyn_star[sorting_q[xq], :,:, xat, yat] + + + # Now divide the matrix per the xq value + final_fc /= nq + + # Overwrite the matrix + fcq[:,:,:] = final_fc + + def ApplySymmetryToMatrix(self, matrix, err = None): + """ + Apply the symmetries to the 3x3 matrix. + It can be a stress tensor, a dielectric tensor and so on. + + Parameters + ---------- + matrix : a 3x3 matrix + The matrix to which you want to apply the symmetrization. + The matrix is overwritten with the output. + """ + + # Setup the symmetries in the Gamma point + #self.SetupQPoint() + + # Perform the symmetrization + mat_f = np.array(matrix, order = "F", dtype = np.float64) + + symph.symmatrix(mat_f, self.QE_s, self.QE_nsymq, self.QE_at, self.QE_bg) + + # To compute the error we count which element + # of the stress tensor are summed togheter to obtain any element. + # Then we propagate the error only on these. + if err is not None: + err_new = err.copy() + for i in range(3): + for j in range(3): + work = np.zeros( (3,3), dtype = np.float64, order = "F") + work[i,j] = np.float64(1) + + # Apply the symmetry + symph.symmatrix(work, self.QE_s, self.QE_nsymq, self.QE_at, self.QE_bg) + mask = (np.abs(work) > __EPSILON__) + naverage = np.sum( mask.astype(int)) + + if naverage == 0: + err_new[i,j] = 0 + else: + err_new[i,j] = np.sqrt(np.sum( err[mask]**2)) / naverage + err[:,:] = err_new + matrix[:,:] = mat_f + + + + def SymmetrizeFCQ(self, fcq, q_stars, verbose = False, asr = "simple"): + """ + Use the current structure to impose symmetries on a complete dynamical matrix + in q space. Also the simple sum rule at Gamma is imposed + + Parameters + ---------- + - fcq : ndarray(nq, 3xnat, 3xnat) + The q space force constant matrix to be symmetrized (it will be overwritten) + - q_stars : list of list of q points + The list of q points divided by stars, the fcq must follow the order + of the q points in the q_stars array + """ + + nqirr = len(q_stars) + nq = np.sum([len(x) for x in q_stars]) + + # Get the q_points vector + q_points = np.zeros( (nq, 3), dtype = np.float64) + sigma = 0 + for i in range(nqirr): + for q_vec in q_stars[i]: + q_points[sigma, :] = q_vec + sigma += 1 + + if nq != np.shape(fcq)[0]: + raise ValueError("Error, the force constant number of q point %d does not match with the %d given q_points" % (np.shape(fcq)[0], nq)) + + + for iq in range(nq): + # Prepare the symmetrization + if verbose: + print ("Symmetries in q = ", q_points[iq, :]) + t1 = time.time() + self.SetupQPoint(q_points[iq,:], verbose) + t2 = time.time() + if verbose: + print (" [SYMMETRIZEFCQ] Time to setup the q point %d" % iq, t2-t1, "s") + + # Proceed with the sum rule if we are at Gamma + + if asr == "simple" or asr == "custom": + if np.sqrt(np.sum(q_points[iq,:]**2)) < __EPSILON__: + if verbose: + print ("q_point:", q_points[iq,:]) + print ("Applying sum rule") + self.ImposeSumRule(fcq[iq,:,:], asr) + elif asr == "crystal": + self.ImposeSumRule(fcq[iq, :,:], asr = asr) + elif asr == "no": + pass + else: + raise ValueError("Error, only 'simple', 'crystal', 'custom' or 'no' asr are supported, given %s" % asr) + + t1 = time.time() + if verbose: + print (" [SYMMETRIZEFCQ] Time to apply the sum rule:", t1-t2, "s") + + # # Symmetrize the matrix + if verbose: + old_fcq = fcq[iq, :,:].copy() + w_old = np.linalg.eigvals(fcq[iq, :, :]) + print ("FREQ BEFORE SYM:", w_old ) + self.SymmetrizeDynQ(fcq[iq, :,:], q_points[iq,:]) + t2 = time.time() + if verbose: + print (" [SYMMETRIZEFCQ] Time to symmetrize the %d dynamical matrix:" % iq, t2 -t1, "s" ) + print (" [SYMMETRIZEFCQ] Difference before the symmetrization:", np.sqrt(np.sum(np.abs(old_fcq - fcq[iq, :,:])**2))) + w_new = np.linalg.eigvals(fcq[iq, :, :]) + print ("FREQ AFTER SYM:", w_new) + + # For each star perform the symmetrization over that star + q0_index = 0 + for i in range(nqirr): + q_len = len(q_stars[i]) + t1 = time.time() + if verbose: + print ("Applying the q star symmetrization on:") + print (np.array(q_stars[i])) + self.ApplyQStar(fcq[q0_index : q0_index + q_len, :,:], np.array(q_stars[i])) + t2 = time.time() + if verbose: + print (" [SYMMETRIZEFCQ] Time to apply the star q_irr = %d:" % i, t2 - t1, "s") + q0_index += q_len + + + def ChangeThreshold(self, threshold): + """ + Change the symmetry threshold sensibility + """ + self.threshold = np.float64(threshold) + symph.symm_base.set_accep_threshold(self.threshold) + + + def ImposeSumRule(self, force_constant, asr = "simple", axis = 1, zeu = None): + """ + QE SUM RULE + =========== + + This subroutine imposes on the given force constant matrix the acustic sum rule + + Parameters + ---------- + force_constnat : 3xnat , 3xnat + The force constant matrix, it is overwritten with the new one + after the sum rule has been applied. + asr : string, optional, default = 'custom' + One of 'custom', 'simple', 'crystal', 'one-dim' or 'zero-dim'. For a detailed + explanation look at the Quantum ESPRESSO documentation. + The custom one, default, is implemented in python as CustomASR. + No ASR is imposed on the effective charges in this case. + axis : int, optional + If asr = 'one-dim' you must set the rotational axis: 1 for x, 2 for + y and 3 for z. Ohterwise it is unused. + zeu : ndarray (N_atoms, 3, 3), optional + If different from None, it is the effective charge array. + As the force_constant, it is updated. + + """ + + QE_fc = np.zeros( (3, 3, self.QE_nat, self.QE_nat), order ="F", dtype = np.complex128) + + # Fill the effective charges if required + if zeu is not None: + # Convert in the correct indexing and use the fortran order + f_zeu = np.einsum("ijk -> kji", zeu, order = "F", dtype = np.float64) + else: + f_zeu = np.zeros( (3, 3, self.QE_nat), order = "F", dtype = np.float64) + + # Prepare the force constant + if asr != "custom": + for na in range(self.QE_nat): + for nb in range(self.QE_nat): + QE_fc[:, :, na, nb] = force_constant[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] + # +# print "ASR:", asr +# print "AXIS:", axis +# print "NAT:", self.QE_nat +# print "TAU SHAPE:", np.shape(self.QE_tau) +# print "QE_FC SHAPE:", np.shape(self.QE_fc) + + + symph.set_asr(asr, axis, self.QE_tau, QE_fc, f_zeu) + + # Copy the new value on output + for na in range(self.QE_nat): + if zeu is not None: + zeu[na, :,:] = f_zeu[:,:, na] + + for nb in range(self.QE_nat): + force_constant[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] = QE_fc[:,:, na, nb] + else: + CustomASR(force_constant) + + + + + def SetupQPoint(self, q_point = np.zeros(3), verbose = False): + """ + Get symmetries of the small group of q + + Setup the symmetries in the small group of Q. + + Parameters + ---------- + q_point : ndarray + The q vector in reciprocal space (NOT in crystal axes) + verbose : bool + If true the number of symmetries found for the bravais lattice, + the crystal and the small group of q are written in stdout + """ + # Convert the q point in Fortran + if len(q_point) != 3: + raise ValueError("Error, the q point must be a 3d vector") + + aq = np.zeros(3, dtype = np.float64) + aq[:] = Methods.covariant_coordinates(self.QE_bg.transpose(), q_point) + + # Setup the bravais lattice + symph.symm_base.set_at_bg(self.QE_at, self.QE_bg) + + # Prepare the symmetries + symph.symm_base.set_sym_bl() + + if verbose: + print ("Symmetries of the bravais lattice:", symph.symm_base.nrot) + + + # Now copy all the work initialized on the symmetries inside python + self.QE_s = np.copy(symph.symm_base.s) + self.QE_ft = np.copy(symph.symm_base.ft) + self.QE_nsym = symph.symm_base.nrot + + # Prepare a dummy variable for magnetic spin + m_loc = np.zeros( (3, self.QE_nat), dtype = np.float64, order = "F") + + # Find the symmetries of the crystal + #print "TAU:", np.shape(self.QE_tau) + symph.symm_base.find_sym(self.QE_tau, self.QE_ityp, 6, 6, 6, False, m_loc) + #print "IRT NOW:", np.shape(symph.symm_base.irt) + + if verbose: + print ("Symmetries of the crystal:", symph.symm_base.nsym) + + + + # Now copy all the work initialized on the symmetries inside python + self.QE_s = np.copy(symph.symm_base.s) + self.QE_ft = np.copy(symph.symm_base.ft) + + + # Prepare the symmetries of the small group of q + syms = np.zeros( (48), dtype = np.intc) + + # Initialize to true the symmetry of the crystal + syms[:symph.symm_base.nsym] = np.intc(1) + + self.QE_minus_q = symph.symm_base.smallg_q(aq, 0, syms) + self.QE_nsymq = symph.symm_base.copy_sym(symph.symm_base.nsym, syms) + self.QE_nsym = symph.symm_base.nsym + + + # Recompute the inverses + symph.symm_base.inverse_s() + + if verbose: + print ("Symmetries of the small group of q:", self.QE_nsymq) + + # Assign symmetries + self.QE_s = np.copy(symph.symm_base.s) + self.QE_invs = np.copy(symph.symm_base.invs) + self.QE_ft = np.copy(symph.symm_base.ft) + self.QE_irt = np.copy(symph.symm_base.irt) + + #print np.shape(self.QE_irt) + + # Compute the additional shift caused by fractional translations + self.QE_rtau = symph.sgam_ph_new(self.QE_at, self.QE_bg, symph.symm_base.nsym, self.QE_s, + self.QE_irt, self.QE_tau, self.QE_nat) + + lgamma = 0 + if np.sqrt(np.sum(q_point**2)) > 0.0001: + lgamma = 1 + +# self.QE_irotmq = symph.set_irotmq(q_point, self.QE_s, self.QE_nsymq, +# self.QE_nsym, self.QE_minus_q, +# self.QE_bg, self.QE_at, lgamma) + # If minus q check which is the symmetry +# + #syms = self.GetSymmetries() + self.QE_irotmq = 0 + if self.QE_minus_q: + # Fix in the Same BZ + #aq = aq - np.floor(aq) + + + #print "VECTOR AQ:", aq + + # Get the first symmetry: + for k in range(self.QE_nsym): + # Skip the identity + #if k == 0: + # continue + + # Position feels the symmetries with S (fortran S is transposed) + # While q vector feels the symmetries with S^t (so no .T required for fortran matrix) + new_q = self.QE_s[:,:, k].dot(aq) + # Compare new_q with aq + dmin = Methods.get_min_dist_into_cell(np.eye(3), -new_q, aq) + #print "Applying %d sym we transform " % (k+1), aq, "into", new_q, "dmin:", dmin + #print "Vector in cart: ", q_point, "We used symmetry:" + #print self.QE_s[:, :, k] + #print "" + #dmin = np.sqrt(np.sum( ((new_q + aq) % 1)**2)) +# +# print "Symmetry number ", k+1 +# print sym[:, :3] +# print "q cryst:", aq +# print "new_q_cryst:", new_q +# + #print "SYM NUMBER %d, NEWQ:" % (k+1), new_q + #print "Distance:", dmin + if dmin < __EPSILON__: + #print "CORRECT FOR IROTMQ" + self.QE_irotmq = k + 1 + break + if self.QE_irotmq == 0: + print ("Error, the fortran code tells me there is S so that Sq = -q + G") + print ("But I did not find such a symmetry!") + raise ValueError("Error in the symmetrization. See stdout") + + def SetupFromSPGLIB(self): + """ + USE SPGLIB TO SETUP THE SYMMETRIZATION + ====================================== + + This function uses spglib to find symmetries, recognize the supercell + and setup all the variables to perform the symmetrization inside the supercell. + + NOTE: If spglib cannot be imported, an ImportError will be raised + """ + if not __SPGLIB__: + raise ImportError("Error, this function works only if spglib is available") + + # Get the symmetries + spg_syms = spglib.get_symmetry(self.structure.get_ase_atoms(), symprec = self.threshold) + symmetries = GetSymmetriesFromSPGLIB(spg_syms, regolarize= False) + + trans_irt = 0 + self.QE_s[:,:,:] = 0 + + + # Check how many point group symmetries do we have + n_syms = 0 + for i, sym in enumerate(symmetries): + # Extract the rotation and the fractional translation + rot = sym[:,:3] + + # Check if the rotation is equal to the first one + if np.sum( (rot - symmetries[0][:,:3])**2 ) < 0.1 and n_syms == 0 and i > 0: + # We got all the rotations + n_syms = i + break + + # Extract the point group + if n_syms == 0: + self.QE_s[:,:, i] = rot.T + + # Get the IRT (Atoms mapping using symmetries) + irt = GetIRT(self.structure, sym) + self.QE_irt[i, :] = irt + 1 #Py to Fort + + + if n_syms == 0: + n_syms = len(symmetries) + + # From the point group symmetries, get the supercell + n_supercell = len(symmetries) // n_syms + self.QE_translation_nr = n_supercell + self.QE_nsymq = n_syms + self.QE_nsym = n_syms + + self.QE_translations_irt = np.zeros( (self.structure.N_atoms, n_supercell), dtype = np.intc, order = "F") + self.QE_translations = np.zeros( (3, n_supercell), dtype = np.double, order = "F") + + # Now extract the translations + for i in range(n_supercell): + sym = symmetries[i * n_syms] + # Check if the symmetries are correctly setup + + I = np.eye(3) + ERROR_MSG=""" + Error, symmetries are not correctly ordered. + They must always start with the identity. + + N_syms = {}; N = {}; SYM = {} + """.format(n_syms,i*n_syms, sym) + assert np.sum( (I - sym[:,:3])**2) < 0.5, ERROR_MSG + + # Get the irt for the translation (and the translation) + irt = GetIRT(self.structure, sym) + self.QE_translations_irt[:, i] = irt + 1 + self.QE_translations[:, i] = sym[:,3] + + # For each symmetry operation, assign the inverse + self.QE_invs[:] = get_invs(self.QE_s, self.QE_nsym) + + + + def ApplyTranslationsToVector(self, vector): + """ + This subroutine applies the translations to the given vector. + To be used only if the structure is a supercell structure + and the symmetries have been initialized with SPGLIB + + Parameters + ---------- + vector : size (nat, 3) + A vector that must be symmetrized. It will be overwritten. + """ + + nat = self.QE_nat + + assert vector.shape[0] == nat + assert vector.shape[1] == 3 + + # Ignore if no translations are presents + if self.QE_translation_nr <= 1: + return + + sum_all = np.zeros((nat, 3), dtype = type(vector[0,0])) + + for i in range(self.QE_translation_nr): + n_supercell = np.shape(self.QE_translations_irt)[1] + + sum_all += vector[self.QE_translations_irt[:, i] - 1, :] + sum_all /= self.QE_translation_nr + vector[:,:] = sum_all + + + + + def InitFromSymmetries(self, symmetries, q_point = np.array([0,0,0])): + """ + This function initialize the QE symmetries from the symmetries expressed in the + Cellconstructor format, i.e. a list of numpy array 3x4 where the last column is + the fractional translation. + + TODO: add the q_point preparation by limitng the symmetries only to + those that satisfies the specified q_point + """ + + nsym = len(symmetries) + + self.QE_nsymq = np.intc(nsym) + self.QE_nsym = self.QE_nsymq + + + for i, sym in enumerate(symmetries): + self.QE_s[:,:, i] = np.transpose(sym[:, :3]) + + # Get the atoms correspondence + eq_atoms = GetIRT(self.structure, sym) + + self.QE_irt[i, :] = eq_atoms + 1 + + # Get the inverse symmetry + inv_sym = np.linalg.inv(sym[:, :3]) + for k, other_sym in enumerate(symmetries): + if np.sum( (inv_sym - other_sym[:, :3])**2) < __EPSILON__: + break + + self.QE_invs[i] = k + 1 + + # Setup the position after the symmetry application + for k in range(self.QE_nat): + self.QE_rtau[:, i, k] = self.structure.coords[eq_atoms[k], :].astype(np.float64) + + + # Get the reciprocal lattice vectors + b_vectors = self.structure.get_reciprocal_vectors() + + # Get the minus_q operation + self.QE_minusq = False + + # NOTE: HERE THERE COULD BE A BUG + + # q != -q + # Get the q vectors in crystal coordinates + q = Methods.covariant_coordinates(b_vectors, q_point) + for k, sym in enumerate(self.QE_s): + new_q = self.QE_s[:,:, k].dot(q) + if np.sum( (Methods.put_into_cell(b_vectors, -q_point) - new_q)**2) < __EPSILON__: + self.QE_minus_q = True + self.QE_irotmq = k + 1 + break + + def GetSymmetries(self, get_irt=False): + """ + GET SYMMETRIES FROM QE + ====================== + + This method returns the symmetries in the CellConstructor format from + the ones elaborated here. + + + Parameters + ---------- + get_irt : bool + If true (default false) also the irt are returned. + They are the corrispondance between atoms for each symmetry operation. + Results + ------- + list : + List of 3x4 ndarray representing all the symmetry operations + irt : ndarray(size=(nsym, nat), dtype = int), optional + Returned only if get_irt = True. + It is the corrispondance between atoms after the symmetry operation is applied. + irt[x, y] is the atom mapped into y by the x symmetry. + """ + + syms = [] + for i in range(self.QE_nsym): + s_rot = np.zeros( (3, 4)) + s_rot[:, :3] = np.transpose(self.QE_s[:, :, i]) + s_rot[:, 3] = self.QE_ft[:, i] + + syms.append(s_rot) + + if not get_irt: + return syms + return syms, self.QE_irt[:self.QE_nsym, :].copy() - 1 + + + + + def SymmetrizeVector(self, vector): + """ + SYMMETRIZE A VECTOR + =================== + + This is the easier symmetrization of a generic vector. + Note, fractional translation and generic translations are not imposed. + This is because this simmetrization acts on displacements and forces. + + Parameters + ---------- + vector : ndarray(natoms, 3) + This is the vector to be symmetrized, it will be overwritten + with the symmetrized version + """ + + # Apply Translations if any + self.ApplyTranslationsToVector(vector) + + # Prepare the real vector + tmp_vector = np.zeros( (3, self.QE_nat), dtype = np.float64, order = "F") + + for i in range(self.QE_nat): + tmp_vector[0, i] = vector[i,0] + tmp_vector[1, i] = vector[i,1] + tmp_vector[2,i] = vector[i,2] + + symph.symvector(self.QE_nsymq, self.QE_irt, self.QE_s, self.QE_at, self.QE_bg, + tmp_vector, self.QE_nat) + + + for i in range(self.QE_nat): + vector[i, :] = tmp_vector[:,i] + + + def SymmetrizeDynQ(self, dyn_matrix, q_point): + """ + DYNAMICAL MATRIX SYMMETRIZATION + =============================== + + Use the Quantum ESPRESSO fortran code to symmetrize the dynamical matrix + at the given q point. + + NOTE: the symmetries must be already initialized. + + Parameters + ---------- + dyn_matrix : ndarray (3nat x 3nat) + The dynamical matrix associated to the specific q point (cartesian coordinates) + q_point : ndarray 3 + The q point related to the dyn_matrix. + + The input dynamical matrix will be modified by the current code. + """ + + # TODO: implement hermitianity to speedup the conversion + + #Prepare the array to be passed to the fortran code + QE_dyn = np.zeros( (3, 3, self.QE_nat, self.QE_nat), dtype = np.complex128, order = "F") + + # Get the crystal coordinates for the matrix + for na in range(self.QE_nat): + for nb in range(self.QE_nat): + fc = dyn_matrix[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] + QE_dyn[:, :, na, nb] = Methods.convert_matrix_cart_cryst(fc, self.structure.unit_cell, False) + + # Prepare the xq variable + #xq = np.ones(3, dtype = np.float64) + xq = np.array(q_point, dtype = np.float64) + # print "XQ:", xq + # print "XQ_CRYST:", Methods.covariant_coordinates(self.QE_bg.T, xq) + # print "NSYMQ:", self.QE_nsymq, "NSYM:", self.QE_nsym + # print "QE SYM:" + # print np.einsum("abc->cba", self.QE_s[:, :, :self.QE_nsymq]) + # print "Other syms:" + # print np.einsum("abc->cba", self.QE_s[:, :, self.QE_nsymq: self.QE_nsym]) + # print "QE INVS:" + # print self.QE_invs[:self.QE_nsymq] + # #print "QE RTAU:" + # #print np.einsum("abc->bca", self.QE_rtau[:, :self.QE_nsymq, :]) + # print "IROTMQ:", self.QE_irotmq + # print "MINUS Q:", self.QE_minus_q + # print "IRT:" + # print self.QE_irt[:self.QE_nsymq, :] + # print "NAT:", self.QE_nat + + # Inibhit minus q + #self.QE_minus_q = 0 + + + # USE THE QE library to perform the symmetrization + symph.symdynph_gq_new( xq, QE_dyn, self.QE_s, self.QE_invs, self.QE_rtau, + self.QE_irt, self.QE_irotmq, self.QE_minus_q, self.QE_nsymq, self.QE_nat) + + # Return to cartesian coordinates + for na in range(self.QE_nat): + for nb in range(self.QE_nat): + fc = QE_dyn[:, :, na, nb] + dyn_matrix[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] = Methods.convert_matrix_cart_cryst(fc, self.structure.unit_cell, True) + + def GetQStar(self, q_vector): + """ + GET THE Q STAR + ============== + + Given a vector in q space, get the whole star. + We use the quantum espresso subrouitine. + + Parameters + ---------- + q_vector : ndarray(size= 3, dtype = np.float64) + The q vector + + Results + ------- + q_star : ndarray(size = (nq_star, 3), dtype = np.float64) + The complete q star + """ + self.SetupQPoint() + nq_new, sxq, isq, imq = symph.star_q(q_vector, self.QE_at, self.QE_bg, + self.QE_nsymq, self.QE_s, self.QE_invs, 0) + + #print ("STAR IMQ:", imq) + if imq != 0: + total_star = np.zeros( (nq_new, 3), dtype = np.float64) + else: + total_star = np.zeros( (2*nq_new, 3), dtype = np.float64) + + total_star[:nq_new, :] = sxq[:, :nq_new].transpose() + + if imq == 0: + total_star[nq_new:, :] = -sxq[:, :nq_new].transpose() + + return total_star + + def SelectIrreducibleQ(self, q_vectors): + """ + GET ONLY THE IRREDUCIBLE Q POINTS + ================================= + + This methods selects only the irreducible q points + given a list of total q points for the structure. + + Parameters + ---------- + q_vectors : list of q points + The list of q points to be polished fromt he irreducible + + Results + ------- + q_irr : list of q points + The q_vectors without the copies by symmetry of the dynamical matrix. + """ + + qs = np.array(q_vectors) + nq = np.shape(qs)[0] + + q_irr = [qs[x, :].copy() for x in range(nq)] + for i in range(nq): + if i >= len(q_irr): + break + + q_stars = self.GetQStar(q_irr[i]) + n_star = np.shape(q_stars)[0] + + # Look if the list contains point in the star + for j in range(n_star): + q_in_star = q_stars[j,:] + # Go reverse, in this way if we pop an element we do not have to worry about indices + for k in range(len(q_irr)-1, i, -1): + if Methods.get_min_dist_into_cell(self.QE_bg.transpose(), q_in_star, q_irr[k]) < __EPSILON__: + q_irr.pop(k) # Delete the k element + + return q_irr + + def GetQIrr(self, supercell): + """ + GET THE LIST OF IRREDUCIBLE Q POINTS + ==================================== + + This method returns a list of irreducible q points given the supercell size. + + Parameters + ---------- + supercell : (X, Y, Z) where XYZ are int + The supercell size along each unit cell vector. + + Returns + ------- + q_irr_list : list of q vectors + The list of irreducible q points in the brilluin zone. + """ + + # Get all the q points + q_points = GetQGrid(self.QE_at.T, supercell) + + # Delete the irreducible ones + q_irr = self.SelectIrreducibleQ(q_points) + + return q_irr + + def ApplySymmetriesToV2(self, v2, apply_translations = True): + """ + APPLY THE SYMMETRIES TO A 2-RANK TENSOR + ======================================= + + This subroutines applies the symmetries to a 2-rank + tensor. Usefull to work with supercells. + + Parameters + ---------- + v2 : ndarray (size = (3*nat, 3*nat), dtype = np.double) + The 2-rank tensor to be symmetrized. + It is directly modified + apply_translation : bool + If false pure translations are neglected. + """ + + # Apply the Permutation symmetry + v2[:,:] = 0.5 * (v2 + v2.T) + + # First lets recall that the fortran subroutines + # Takes the input as (3,3,nat,nat) + new_v2 = np.zeros( (3,3, self.QE_nat, self.QE_nat), dtype = np.double, order ="F") + for i in range(self.QE_nat): + for j in range(self.QE_nat): + new_v2[:, :, i, j] = v2[3*i : 3*(i+1), 3*j : 3*(j+1)] + + # Apply the translations + if apply_translations: + # Check that the translations have been setted up + assert len(np.shape(self.QE_translations_irt)) == 2, "Error, symmetries not setted up to work in the supercell" + symph.trans_v2(new_v2, self.QE_translations_irt) + + # Apply the symmetrization + symph.sym_v2(new_v2, self.QE_at, self.QE_bg, self.QE_s, self.QE_irt, self.QE_nsym, self.QE_nat) + + # Return back + for i in range(self.QE_nat): + for j in range(self.QE_nat): + v2[3*i : 3*(i+1), 3*j : 3*(j+1)] = new_v2[:, :, i, j] + + + +def get_symmetries_from_ita(ita, red=False): + """ + This function returns a matrix containing the symmetries from the given ITA code of the Group. + The corresponding ITA/group label can be found on the Bilbao Crystallographic Server. + + Parameters + ---------- + - ita : int + The ITA code that identifies the group symmetry. + - red : bool (default = False) + If red is True then load the symmetries only in the smallest unit cell (orthorombic) + Results + ------- + - symmetries : list + A list of 3 rows x 4 columns matrices (ndarray), containing the symmetry operations + of the chosen group. + """ + + if ita <= 0: + raise ValueError("Error, ITA group %d is not valid." % ita) + + filename="%s/SymData/%d.dat" % (CURRENT_DIR, ita) + if red: + filename="%s/SymData/%d_red.dat" % (CURRENT_DIR, ita) + + + if not os.path.exists(filename): + print ("Error, ITA group not yet implemented.") + print ("You can download the symmetries for this group from the Bilbao Crystallographic Server") + print ("And just add the %d.dat file into the SymData folder of the current program." % ita) + print ("It should take less than five minutes.") + + raise ValueError("Error, ITA group %d not yet implemented. Check stdout on how to solve this problem." % ita) + + fp = open(filename, "r") + + # Get the number of symemtries + n_sym = int(fp.readline().strip()) + fp.close() + + symdata = np.loadtxt(filename, skiprows = 1) + symmetries = [] + + for i in range(n_sym): + symmetries.append(symdata[3*i:3*(i+1), :]) + + return symmetries + + +def GetSymmetriesFromSPGLIB(spglib_sym, regolarize = False): + """ + CONVERT THE SYMMETRIES + ====================== + + This module comvert the symmetry fynction from the spglib format. + + + Parameters + ---------- + spglib_sym : dict + Result of spglib.get_symmetry( ... ) function + regolarize : bool, optional + If True it rewrites the translation to be exact. Usefull if you want to + constrain the symmetry exactly + + Returns + ------- + symmetries : list + A list of 4x3 matrices containing the symmetry operation + """ + + # Check if the type is correct + if not "translations" in spglib_sym: + raise ValueError("Error, your symmetry dict has no 'translations' key.") + + if not "rotations" in spglib_sym: + raise ValueError("Error, your symmetry dict has no 'rotations' key.") + + # Get the number of symmetries + out_sym = [] + n_sym = np.shape(spglib_sym["translations"])[0] + + translations = spglib_sym["translations"] + rotations = spglib_sym["rotations"] + + for i in range(n_sym): + # Create the symmetry + sym = np.zeros((3,4)) + sym[:,:3] = rotations[i, :, :] + sym[:, 3] = translations[i,:] + + # Edit the translation + if regolarize: + sym[:, 3] *= 2 + sym[:, 3] = np.floor(sym[:, 3] + .5) + sym[:, 3] *= .5 + sym[:, 3] = sym[:,3] % 1 + + out_sym.append(sym) + + return out_sym + +def CustomASR(fc_matrix): + """ + APPLY THE SUM RULE + ================== + + This function applies a particular sum rule. It projects out the translations + exactly. + + Parameters + ---------- + fc_matrix : ndarray(3nat x 3nat) + The force constant matrix. The sum rule is applied on that. + """ + + shape = np.shape(fc_matrix) + if shape[0] != shape[1]: + raise ValueError("Error, the provided matrix is not square: (%d, %d)" % (shape[0], shape[1])) + + nat = np.shape(fc_matrix)[0] // 3 + if nat*3 != shape[0]: + raise ValueError("Error, the matrix must have a dimension divisible by 3: %d" % shape[0]) + + + dtype = type(fc_matrix[0,0]) + + trans = np.eye(3*nat, dtype = dtype) + for i in range(3): + v1 = np.zeros(nat*3, dtype = dtype) + v1[3*np.arange(nat) + i] = 1 + v1 /= np.sqrt(v1.dot(v1)) + + trans -= np.outer(v1, v1) + + #print trans + + fc_matrix[:,:] = trans.dot(fc_matrix.dot(trans)) + + +def ExcludeRotations(fc_matrix, structure): + """ + APPLY THE ROTATION SUM RULE + =========================== + + We exclude the rotations from the force constant matrix. + + Parameters + ---------- + fc_matrix : ndarray(3*nat, 3*nat) + The force constant matrix + structure : Structure() + The structure that is identified by the force constant matrix + + """ + + nat = structure.N_atoms + dtype = type(fc_matrix[0,0]) + + # Get the center of the structure + r_cm = np.sum(structure.coords, axis = 0) / nat + r = structure.coords - r_cm + + v_rots = np.zeros((3, 3*nat), dtype = dtype) + projector = np.eye(3*nat, dtype = dtype) + counter = 0 + for i in range(3): + for j in range(i+1,3): + v = np.zeros(3*nat, dtype = dtype) + v_i = r[:, j] + v_j = -r[:, i] + + v[3*np.arange(nat) + i] = v_i + v[3*np.arange(nat) + j] = v_j + + + # orthonormalize + for k in range(counter): + v -= v_rots[k, :].dot(v) * v_rots[k, :] + + # Normalize + norm = np.sqrt(v.dot(v)) + v /= norm + + v_rots[counter, :] = v + projector -= np.outer(v,v) + counter += 1 + + + + fc_matrix[:,:] = projector.dot(fc_matrix.dot(projector)) + + +def GetIRT(structure, symmetry, timer = Timer.Timer(), debug = False): + """ + GET IRT + ======= + + Get the irt array. It is the array of the atom index that the symmetry operation + swaps. + + the y-th element of the array (irt[y]) is the index of the original structure, while + y is the index of the equivalent atom after the symmetry is applied. + + Parameters + ---------- + structure: Structure.Structure() + The unit cell structure + symmetry: list of 3x4 matrices + symmetries with frac translations + timer : Timer class + The functions will be timed using the timer object. + + """ + + + new_struct = structure.copy() + if timer is None: + new_struct.fix_coords_in_unit_cell(delete_copies = False, debug = debug) + else: + timer.execute_timed_function(new_struct.fix_coords_in_unit_cell, delete_copies = False, debug = debug) + n_struct_2 = new_struct.copy() + + if timer is None: + new_struct.apply_symmetry(symmetry, True) + irt = np.array(new_struct.get_equivalent_atoms(n_struct_2), dtype =np.intc) + else: + timer.execute_timed_function(new_struct.apply_symmetry, symmetry, True, timer = timer) + irt = np.array( timer.execute_timed_function(new_struct.get_equivalent_atoms, n_struct_2), dtype =np.intc) + + return irt + +def ApplySymmetryToVector(symmetry, vector, unit_cell, irt): + """ + APPLY SYMMETRY + ============== + + Apply the symmetry to the given vector of displacements. + Translations are neglected. + + .. math:: + + \\vec {v'}[irt] = S \\vec v + + + Parameters + ---------- + symmetry: ndarray(size = (3,4)) + The symmetry operation (crystalline coordinates) + vector: ndarray(size = (nat, 3)) + The vector to which apply the symmetry. + In cartesian coordinates + unit_cell : ndarray( size = (3,3)) + The unit cell in which the structure is defined + irt : ndarray(nat, dtype = int) + The index of how the symmetry exchanges the atom. + + """ + + # Get the vector in crystalline coordinate + nat, dumb = np.shape(vector) + work = np.zeros( (nat, 3)) + sym = symmetry[:, :3] + + for i in range(nat): + # Pass to crystalline coordinates + v1 = Methods.covariant_coordinates(unit_cell, vector[i, :]) + # Apply the symmetry + w1 = sym.dot(v1) + # Return in cartesian coordinates + work[irt[i], :] = np.einsum("ab,a", unit_cell, w1) + + return work + +def ApplySymmetriesToVector(symmetries, vector, unit_cell, irts): + """ + APPLY SYMMETRY + ============== + + Apply the symmetry to the given vector of displacements. + Translations are neglected. + + .. math:: + + \\vec {v'}[irt] = S \\vec v + + + Parameters + ---------- + symmetries: list of ndarray(size = (3,4)) + The symmetries operation (crystalline coordinates) + vector: ndarray(size = (nat, 3)) + The vector to which apply the symmetry. + In cartesian coordinates + unit_cell : ndarray( size = (3,3)) + The unit cell in which the structure is defined + irts : list of ndarray(nat, dtype = int) + The index of how the symmetry exchanges the atom. + + """ + + # Get the vector in crystalline coordinate + nat, dumb = np.shape(vector) + n_sym = len(symmetries) + + assert n_sym == len(irts) + + work = np.zeros( (n_sym, nat, 3), dtype = np.double, order = "C") + + # Pass to crystalline coordinates + v1 = Methods.covariant_coordinates(unit_cell, vector) + + # Apply the symmetry + for j, symmetry in enumerate(symmetries): + sym = symmetry[:, :3] + w1 = sym.dot(v1.T).T + + # Return in cartesian coordinates + work[j, irts[j][:], :] = w1.dot(unit_cell)# unit_cell.T.dot(w1) #np.einsum("ab,a", unit_cell, w1) + + return work + + +def PrepareISOTROPYFindSymInput(structure, path_to_file = "findsym.in", + title = "Prepared with Cellconstructor", + latticeTolerance = 1e-5, atomicPositionTolerance = 0.001): + """ + Prepare a FIND SYM input file + ============================= + + This method can be used to prepare a suitable input file for the ISOTROPY findsym program. + + Parameters + ---------- + path_to_file : string + A valid path to write the findsym input. + title : string, optional + The title of the job + """ + + lines = GetISOTROPYFindSymInput(structure, title, latticeTolerance, atomicPositionTolerance) + + fp = open(path_to_file, "w") + fp.writelines(lines) + fp.close() + + +def GetISOTROPYFindSymInput(structure, title = "Prepared with Cellconstructor", + latticeTolerance = 1e-5, atomicPositionTolerance = 0.001): + """ + As the method PrepareISOTROPYFindSymInput, but the input is returned as a list of string (lines). + + """ + # Check if the structure has a unit cell + if not structure.has_unit_cell: + raise ValueError("Error, the given structure has not a valid unit cell.") + + # Prepare the standard input + lines = [] + lines.append("!useKeyWords\n") + lines.append("!title\n") + lines.append(title + "\n") + lines.append("!latticeTolerance\n") + lines.append("%.8f\n" % latticeTolerance) + lines.append("!atomicPositionTolerance\n") + lines.append("%.8f\n" % atomicPositionTolerance) + lines.append("!latticeBasisVectors\n") + for i in range(3): + lines.append("%16.8f %16.8f %16.8f\n" % (structure.unit_cell[i, 0], + structure.unit_cell[i, 1], + structure.unit_cell[i, 2])) + + lines.append("!atomCount\n") + lines.append("%d\n" % structure.N_atoms) + lines.append("!atomType\n") + lines.append(" ".join(structure.atoms) + "\n") + lines.append("!atomPosition\n") + for i in range(structure.N_atoms): + # Get the crystal coordinate + new_vect = Methods.covariant_coordinates(structure.unit_cell, structure.coords[i, :]) + lines.append("%16.8f %16.8f %16.8f\n" % (new_vect[0], + new_vect[1], + new_vect[2])) + + return lines + + +def GetQGrid(unit_cell, supercell_size, enforce_gamma_first = True): + """ + GET THE Q GRID + ============== + + This method gives back a list of q points given the + reciprocal lattice vectors and the supercell size. + + Parameters + ---------- + unit_cell : ndarray(size=(3,3), dtype = np.float64) + The unit cell, rows are the vectors + supercell_size : ndarray(size=3, dtype = int) + The dimension of the supercell along each unit cell vector. + enforce_gamma_first : bool + If true, the Gamma point is the first one of the list. + + Returns + ------- + q_list : list + The list of q points, of type ndarray(size = 3, dtype = np.float64) + + """ + bg = Methods.get_reciprocal_vectors(unit_cell) + + n_vects = int(np.prod(supercell_size)) + q_final = np.zeros((3, n_vects), dtype = np.double, order = "F") + q_final[:,:] = symph.get_q_grid(bg.T, supercell_size, n_vects) + + # Get the list of the closest vectors + q_list = [Methods.get_closest_vector(bg, q_final[:, i]) for i in range(n_vects)] + + # Setup Gamma as the first vector + if enforce_gamma_first: + for i, q in enumerate(q_list): + if np.abs(np.sum(q)) < __EPSILON__: + tmp = q_list[0].copy() + q_list[0] = q.copy() + q_list[i] = tmp + break + + + return q_list + +def GetQGrid_old(unit_cell, supercell_size): + """ + GET THE Q GRID + ============== + + This method gives back a list of q points given the + reciprocal lattice vectors and the supercell size. + + Parameters + ---------- + unit_cell : ndarray(size=(3,3), dtype = np.float64) + The unit cell, rows are the vectors + supercell_size : ndarray(size=3, dtype = int) + The dimension of the supercell along each unit cell vector. + + Returns + ------- + q_list : list + The list of q points, of type ndarray(size = 3, dtype = np.float64) + + """ + + q_list = [] + # Get the recirpocal lattice vectors + bg = Methods.get_reciprocal_vectors(unit_cell) + + # Get the supercell + supercell = np.tile(supercell_size, (3, 1)).transpose() * unit_cell + + # Get the lattice vectors of the supercell + bg_s = Methods.get_reciprocal_vectors(supercell) + + #print "SUPERCELL:", supercell_size + + for ix in range(supercell_size[0]): + for iy in range(supercell_size[1]): + for iz in range(supercell_size[2]): + n_s = np.array( [ix, iy, iz], dtype = np.float64) + q_vect = n_s.dot(bg_s) + #q_vect = Methods.get_closest_vector(bg, q_vect) + + # Check if q is in the listcount = 0 + count = 0 + for q in q_list: + if Methods.get_min_dist_into_cell(bg, -q_vect, q) < __EPSILON__: + count += 1 + break + if count > 0: + continue + + # Add the q point + q_list.append(q_vect) + + # Check if -q and q are different + if Methods.get_min_dist_into_cell(bg, -q_vect, q_vect) > __EPSILON__: + q_list.append(-q_vect) + + + + return q_list + + + +def CheckSupercellQ(unit_cell, supercell_size, q_list): + """ + CHECK THE Q POINTS + ================== + + This subroutine checks that the given q points of a dynamical matrix + matches the desidered supercell. + It is usefull to spot bugs like the wrong definitions of alat units, + or error not spotted just by the number of q points (confusion between 1,2,2 or 2,1,2 supercell). + + Parameters + ---------- + unit_cell : ndarray(size=(3,3), dtype = np.float64) + The unit cell, rows are the vectors + supercell_size : ndarray(size=3, dtype = int) + The dimension of the supercell along each unit cell vector. + q_list : list of vectors + The total q point list. + Returns + ------- + is_ok : bool + True => No error + False => Error + """ + # Get the q point list for the given supercell + correct_q = GetQGrid(unit_cell, supercell_size) + + # Get the reciprocal lattice vectors + bg = Methods.get_reciprocal_vectors(unit_cell) + + # Check if the vectors are equivalent or not + for iq, q in enumerate(q_list): + for jq, qnew in enumerate(correct_q): + if Methods.get_min_dist_into_cell(bg, q, qnew) < __EPSILON__: + correct_q.pop(jq) + break + + if len(correct_q) > 0: + print ("[CHECK SUPERCELL]") + print (" MISSING Q ARE ") + print ("\n".join([" q =%16.8f%16.8f%16.8f " % (q[0], q[1], q[2]) for q in correct_q])) + return False + return True + +def GetNewQFromUnitCell(old_cell, new_cell, old_qs): + """ + GET NEW Q POINTS AFTER A CELL STRAIN + ==================================== + + This method returns the new q points after the unit cell is changed. + Remember, when changing the cell to mantain the same kind (cubic, orthorombic, hexagonal...) + otherwise the star identification will fail. + + The q point are passed (and returned) in cartesian coordinates. + + Parameters + ---------- + structure : Structure.Structure() + The structure to be changed (with the old unit celll) + new_cell : ndarray(size=(3,3), dtype = np.float64) + The new unit cell. + old_qs : list of ndarray(size=3, dtype = np.float64) + The list of q points to be converted + + Returns + ------- + new_qs : list of ndarray(size=3, dtype = np.float64) + The list of the new q points adapted in the new cell. + """ + + bg = Methods.get_reciprocal_vectors(old_cell) #/ (2 * np.pi) + new_bg = Methods.get_reciprocal_vectors(new_cell)# / (2 * np.pi) + + new_qs = [] + for iq, q in enumerate(old_qs): + # Get the q point in crystal coordinates + new_qprime = Methods.covariant_coordinates(bg, q) + + # Convert the crystal coordinates in the new reciprocal lattice vectors + new_q = np.einsum("ji, j", new_bg, new_qprime) + new_qs.append(new_q) + + return new_qs + +def GetSupercellFromQlist(q_list, unit_cell): + """ + GET THE SUPERCELL FROM THE LIST OF Q POINTS + =========================================== + + This method returns the supercell size from the list of q points + and the unit cell of the structure. + + Parameters + ---------- + q_list : list + List of the q points in cartesian coordinates + unit_cell : ndarray(3,3) + Unit cell of the structure (rows are the unit cell vectors) + + Results + ------- + supercell_size : list of 3 integers + The supercell dimension along each unit cell vector. + """ + + # Get the bravais lattice + bg = Methods.get_reciprocal_vectors(unit_cell) + + # Convert the q points in crystalline units + supercell = [1,1,1] + + for q in q_list: + qprime = Methods.covariant_coordinates(bg, q) + qprime -= np.floor(qprime) + qprime[np.abs(qprime) < __EPSILON__] = 1 + + rmax = 1/np.abs(qprime) + for j in range(3): + if supercell[j] < int(rmax[j] + .5): + supercell[j] = int(rmax[j] + .5) + + return supercell + + +# def GetSymmetriesOnModes(symmetries, structure, pol_vects): +# """ +# GET SYMMETRIES ON MODES +# ======================= + +# This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them +# through any symmetry operation. + +# Parameters +# ---------- +# symmetries : list +# The list of 3x4 matrices representing the symmetries. +# structure : Structure.Structure() +# The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one +# in each other. +# pol_vects : ndarray(size = (n_dim, n_modes)) +# The array of the polarization vectors (must be real) + + +# Results +# ------- +# pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) +# The symmetry operation between the modes. This allow to identify which mode +# will be degenerate, and which will not interact. +# """ + +# # Get the vector of the displacement in the polarization +# m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() +# disp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) + +# n_dim, n_modes = np.shape(pol_vects) + +# n_sym = len(symmetries) +# nat = structure.N_atoms + +# # For each symmetry operation apply the +# pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) +# for i, sym_mat in enumerate(symmetries): +# irt = GetIRT(structure, sym_mat) + +# for j in range(n_modes): +# # Apply the i-th symmetry to the j-th mode +# new_vector = ApplySymmetryToVector(sym_mat, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irt).ravel() +# new_coords = Methods.covariant_coordinates(disp_v, new_vector) +# pol_symmetries[i, j, :] = new_coords + +# return pol_symmetries + + +def _GetSymmetriesOnModes(symmetries, structure, pol_vects): + """ + GET SYMMETRIES ON MODES + ======================= + + This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them + through any symmetry operation. + + Parameters + ---------- + symmetries : list + The list of 3x4 matrices representing the symmetries. + structure : Structure.Structure() + The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one + in each other. + pol_vects : ndarray(size = (n_dim, n_modes)) + The array of the polarization vectors (must be real) + + + Results + ------- + pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) + The symmetry operation between the modes. This allow to identify which mode + will be degenerate, and which will not interact. + """ + + # Get the vector of the displacement in the polarization + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + disp_v = np.einsum("im,i->mi", pol_vects, 1 / np.sqrt(m)) + underdisp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) + + n_dim, n_modes = np.shape(pol_vects) + + n_sym = len(symmetries) + nat = structure.N_atoms + + # For each symmetry operation apply the + pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) + for i, sym_mat in enumerate(symmetries): + irt = GetIRT(structure, sym_mat) + + for j in range(n_modes): + # Apply the i-th symmetry to the j-th mode + new_vector = ApplySymmetryToVector(sym_mat, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irt).ravel() + pol_symmetries[i, :, j] = underdisp_v.dot(new_vector.ravel()) + + return pol_symmetries + +def GetSymmetriesOnModes(symmetries, structure, pol_vects, irts = [], timer = None, debug = False): + """ + GET SYMMETRIES ON MODES + ======================= + + This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them + through any symmetry operation. + + Parameters + ---------- + symmetries : list + The list of 3x4 matrices representing the symmetries. + structure : Structure.Structure() + The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one + in each other. + pol_vects : ndarray(size = (n_dim, n_modes)) + The array of the polarization vectors (must be real) + irts : list + The list of equivalent atoms for each symmetry + + + Results + ------- + pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) + The symmetry operation between the modes. This allow to identify which mode + will be degenerate, and which will not interact. + """ + + # Get the vector of the displacement in the polarization + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + disp_v = np.einsum("im,i->mi", pol_vects, 1 / np.sqrt(m)) + underdisp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) + + n_dim, n_modes = np.shape(pol_vects) + + n_sym = len(symmetries) + nat = structure.N_atoms + + # For each symmetry operation apply the + pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) + + # Get the irt for all the symmetries (if needed) + if len(irts) == 0: + for i, sym_mat in enumerate(symmetries): + irts.append(GetIRT(structure, sym_mat, timer, debug = debug)) + + + + for j in range(n_modes): + # Apply the i-th symmetry to the j-th mode + t1 = time.time() + new_vectors = ApplySymmetriesToVector( symmetries, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irts).reshape((n_sym, 3 * nat)) + t2 = time.time() + + if timer is not None: + timer.add_timer(ApplySymmetriesToVector.__name__, t2-t1) + pol_symmetries[:, :, j] = underdisp_v.dot(new_vectors.T).T + + return pol_symmetries + + +def GetSymmetriesOnModesDeg(symmetries, structure, pol_vects, w_freq, timer = None, debug = False): + """ + GET SYMMETRIES ON MODES + ======================= + + This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them + through any symmetry operation. + Differently from the previous subroutine GetSymmetriesOnModes, + which returns a tensor of the size (n_sym, n_modes, n_modes), this subroutine returns a list of lenght n_deg as + [(n_sym, ni, ni)] + where n_sym is the number of symmetries, n_deg the number of different non-degenerate modes, + and ni is the dimension of the degeneracy of the i-th group of modes. + This allows for a much lower memory consumption for symmetries + + Parameters + ---------- + symmetries : list + The list of 3x4 matrices representing the symmetries. + structure : Structure.Structure() + The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one + in each other. + pol_vects : ndarray(size = (n_dim, n_modes)) + The array of the polarization vectors (must be real) + + + Results + ------- + pol_symmetries : list + List of all the symmetries expressed in blocks. + pol_symmetries[a][k, x, y] = block of degenerate modes a, symmetry id k, modes x and y of the block + basis : list + basis[a] is the id of the modes inside the block a (the one corresponding to x, y indices) + """ + + + Ns = len(symmetries) + + # Now we can pull out the translations + pols = pol_vects + w = w_freq + #trans_mask = Methods.get_translations(pol_vects, structure.get_masses_array()) + + # Exclude degeneracies + #w = w_freq[~trans_mask] + #pols = pol_vects[:, ~trans_mask] + + + # Get the degeneracy + n_modes = len(w) + N_deg = np.ones(len(w), dtype = np.intc) + n_blocks = min(len(w), 1) # Counter of the different non-degenerate modes + start_deg = -1 + deg_space = [ [x] for x in range(n_modes)] + final_space = [] + + threshold = 1e-8 + + # Compute irts once for all + irts = [] + for i, sym_mat in enumerate(symmetries): + irts.append(GetIRT(structure, sym_mat, timer, debug = debug)) + + for i in range(1, len(w)): + if np.abs(w[i-1] - w[i]) < threshold : + N_deg[i] = N_deg[i-1] + 1 + + if start_deg == -1: + start_deg = i - 1 + + for j in range(start_deg, i): + N_deg[j] = N_deg[i] + deg_space[j].append(i) + deg_space[i].append(j) + + else: + start_deg = -1 + n_blocks += 1 + deg_space[i-1].sort() + final_space.append(deg_space[i-1]) + + deg_space[-1].sort() + final_space.append(deg_space[-1]) + + assert len(final_space) == n_blocks + + + # Now compute the symmetries only in the correct blocks + i_mode = 0 + result_list = [] + for i in range(n_blocks): # TODO ADD MPI PARALLELIZATION + mode_mask = np.zeros(n_modes, dtype = bool) + + for k in final_space[i]: + mode_mask[k] = True + + + + #assert np.sum(mode_mask.astype(int)) == N_deg[i_mode], "Error, something went wrong while computing the degeneracies." + + select_pols = pols[:, mode_mask] + pol_syms = GetSymmetriesOnModes(symmetries, structure, select_pols, irts, timer, debug) + + i_mode += len(deg_space[i_mode]) + + result_list.append(pol_syms) + + return result_list, final_space + + + + +def get_degeneracies(w): + """ + GET THE SUBSPACES OF DEGENERACIES + ================================= + + From the given frequencies, for each mode returns a list of the indices of the modes of degeneracies. + + Parameters + ---------- + w : ndarray(n_modes) + Frequencies + + Results + ------- + deg_list : list of lists + A list that contains, for each mode, the list of the modes (indices) that are degenerate with the latter one + """ + + + n_modes = len(w) + + ret_list = [] + for i in range(n_modes): + deg_list = np.arange(n_modes)[np.abs(w - w[i]) < 1e-8] + ret_list.append(deg_list) + return ret_list + +def get_diagonal_symmetry_polarization_vectors(pol_sc, w, pol_symmetries): + """ + GET THE POLARIZATION VECTORS THAT DIAGONALIZES THE SYMMETRIES + ============================================================= + + This function is very usefull to have a complex basis in which the application of symmetries + is trivial. + + In this basis, each symmetry is diagonal. + Indeed this forces the polarization vectors to be complex in the most general case. + + NOTE: To be tested, do not use for production run + It seems to be impossible to correctly decompose simmetries when we have multiple rotations. + + If the symmetries are not unitary, an exception will be raised. + + Parameters + ---------- + pol_sc : ndarray(3*nat, n_modes) + The polarizaiton vectors in the supercell (obtained by DiagonalizeSupercell of the Phonon class) + w : ndarray(n_modes) + The frequency for each polarization vectors + pol_symmetries : ndarray(N_sym, n_modes, n_modes) + The Symmetry operator that acts on the polarization vector + + + Results + ------- + pol_vects : ndarray(3*nat, n_modes) + The new (complex) polarization vectors that diagonalizes all the symmetries. + syms_values : ndarray(n_modes, n_sym) + The (complex) unitary eigenvalues of each symmetry operation along the given mode. + """ + raise NotImplementedError("Error, this subroutine has not been implemented.") + + # First we must get the degeneracies + deg_list = get_degeneracies(w) + + # Now perform the diagonalization on each degeneracies + final_vectors = np.zeros( pol_sc.shape, dtype = np.complex128) + final_vectors[:,:] = pol_sc.copy() + + n_modes = len(w) + n_syms = pol_symmetries.shape[0] + skip_list = [] + + syms_values = np.zeros((n_modes, n_syms), dtype = np.complex128) + + print("All modes:") + for i in range(n_modes): + print("Mode {} = {} cm-1 => ".format(i, w[i] * RY_TO_CM), deg_list[i]) + + print() + for i in range(n_modes): + if i in skip_list: + continue + + # If we have no degeneracies, we can ignore it + if len(deg_list[i]) == 1: + continue + + partial_modes = np.zeros((len(deg_list[i]), len(deg_list[i])), dtype = np.complex128) + partial_modes[:,:] = np.eye(len(deg_list[i])) # identity matrix + + mask_final = np.array([x in deg_list[i] for x in range(n_modes)]) + + # If we have degeneracies, lets diagonalize all the symmetries + for i_sym in range(n_syms): + skip_j = [] + diagonalized = False + np.savetxt("sym_{}.dat".format(i_sym), pol_symmetries[i_sym, :,:]) + + + # Get the symmetry matrix in the mode space (this could generate a problem with masses) + ps = pol_symmetries[i_sym, :, :] + sym_mat_origin = ps[np.outer(mask_final, mask_final)].reshape((len(deg_list[i]), len(deg_list[i]))) + + for j_mode in deg_list[i]: + if j_mode in skip_j: + continue + + # Get the modes that can be still degenerate by symmetries + mode_dna = syms_values[j_mode, : i_sym] + + # Avoid a bad error if i_sym = 0 + if len(mode_dna) > 0: + mode_space = [x for x in deg_list[i] if np.max(np.abs(syms_values[x, :i_sym] - mode_dna)) < 1e-3] + else: + mode_space = [x for x in deg_list[i]] + + # The mask for the whole symmetry and the partial_modes + mask_all = np.array([x in mode_space for x in np.arange(n_modes)]) + mask_partial_mode = np.array([x in mode_space for x in deg_list[i]]) + n_deg_new = np.sum(mask_all.astype(int)) + + if len(mode_space) == 1: + continue + + p_modes_new = partial_modes[:, mask_partial_mode] + + + print() + print("SYMMETRY_INDEX:", i_sym) + print("SHAPE sym_mat_origin:", sym_mat_origin.shape) + print("MODES: {} | DEG: {}".format(mode_space, deg_list[i])) + print("SHAPE P_MODES_NEW:", p_modes_new.shape) + sym_mat = np.conj(p_modes_new.T).dot(sym_mat_origin.dot(p_modes_new)) + + # Decompose in upper triangular (assures that eigenvectors are orthogonal) + s_eigvals_mat, s_eigvects = scipy.linalg.schur(sym_mat, output = "complex") + s_eigvals = np.diag(s_eigvals_mat) + + # Check if the s_eigvals confirm the unitary of sym_mat + # TODO: Check if some mass must be accounted or not... + print("SYM_MAT") + print(sym_mat) + print("Eigvals:") + print(s_eigvals) + print("Eigval_mat:") + print(s_eigvals_mat) + print("Eigvects:") + print(s_eigvects) + assert np.max(np.abs(np.abs(s_eigvals) - 1)) < 1e-5, "Error, it seems that the {}-th matrix is not a rotation.".format(i_sym).format(sym_mat) + + # Update the polarization vectors to account this diagonalization + partial_modes[:, mask_partial_mode] = p_modes_new.dot(s_eigvects) + + # Add the symmetry character on the new eigen modes + for k_i, k in enumerate(mode_space): + syms_values[k, i_sym] = s_eigvals[k_i] + + # Now add the modes analyzed up to know to the skip + for x in mode_space: + skip_j.append(x) + + diagonalized = True + + + # Now we diagonalized the space + # Apply the symmetries if we did not perform the diagonalization + if not diagonalized: + # Get the symmetrized matrix in the partial mode list: + sym_mat = np.conj(partial_modes.T).dot(sym_mat_origin.dot(partial_modes)) + + # Check that it is diagonal + s_eigvals = np.diag(sym_mat) + disp = sym_mat - np.diag( s_eigvals) + if np.max(np.abs(disp)) > 1e-4: + print("Matrix {}:".format(i_sym)) + print(sym_mat) + raise ValueError("Error, I expect the symmetry {} to be diagonal".format(i_sym)) + + syms_values[k, i_sym] = s_eigvals[k_i] + + # Add the symmetry character on the new eigen modes + for k_i, k in enumerate(deg_list[i]): + syms_values[k, i_sym] = s_eigvals[k_i] + + + # Now we solved our polarization vectors, add them to the final ones + final_vectors[:, mask_final] = pol_sc[:, mask_final].dot(partial_modes) + + # Do not further process the modes we used in this iteration + for mode in deg_list[i]: + skip_list.append(mode) + + + return final_vectors, syms_values + + + + + +def GetQForEachMode(pols_sc, unit_cell_structure, supercell_structure, \ + supercell_size, crystal = True): + """ + GET THE Q VECTOR + ================ + + For each polarization mode in the supercell computes the + corresponding q vector. + + Indeed the polarization vector will be a have components both at q and at -q. + + If a polarization vector mixes two q an error will be raised. + + NOTE: use DiagonalizeSupercell of Phonons to avoid mixing q. + + + Parameters + ---------- + pols_sc : ndarray ( size = (3*nat_sc, n_modes), dtype = np.float64) + The polarization vector of the supercell (real) + unit_cell_structure : Structure() + The structure in the unit cell + supercell_structure: Structure() + The structure in the super cell + supercell_size : list of 3 int + The supercell + crystal : bool + If True, q points are returned in cristal coordinates. + + + Results + ------- + q_list : ndarray(size = (n_modes, 3), dtype = np.float, order = "C") + The list of q points associated with each polarization mode. + If crystal is true, they will be in crystal coordinates. + """ + + # Check the supercell + n_cell = np.prod(supercell_size) + + nat = unit_cell_structure.N_atoms + nat_sc = np.shape(pols_sc)[0] / 3 + n_modes = np.shape(pols_sc)[1] + + ERR_MSG = """ + Error, the supercell {} is not commensurate with the polarization vector given. + nat = {}, nat_sc = {} + """ + assert n_cell * nat == nat_sc, ERR_MSG.format(supercell_size, nat, nat_sc) + assert nat_sc == supercell_structure.N_atoms + + # Get the reciprocal lattice + bg = Methods.get_reciprocal_vectors(unit_cell_structure.unit_cell) / (2 * np.pi) + + # Get the possible Q list + q_grid = GetQGrid(unit_cell_structure.unit_cell, supercell_size) + + # Allocate the output variable + q_list = np.zeros( (n_modes, 3), dtype = np.double, order = "C") + + # Get the correspondance between the unit cell and the super cell atoms + itau = supercell_structure.get_itau(unit_cell_structure) - 1 #Fort2Py + + # Get the translational vectors + R_vects = np.zeros( (nat_sc, 3), dtype = np.double) + for i in range(nat_sc): + R_vects[i, :] = unit_cell_structure.coords[itau[i],:] - supercell_structure.coords[i,:] + + R_vects = R_vects.ravel() + __thr__ = 1e-6 + + for imu in range(n_modes): + pol_v = pols_sc[:, imu] + + nq = 0 + for q in q_grid: + q_vec = np.tile(q, nat_sc) + q_cos = np.cos(2*np.pi * q_vec * R_vects) + q_cos /= np.sqrt(q_cos.dot(q_cos)) + q_sin = np.sin(2*np.pi * q_vec * R_vects) + q_sin /= np.sqrt(q_cos.dot(q_cos)) + + cos_proj = q_cos.dot(pol_v) + sin_proj = q_sin.dot(pol_v) + # Wrong, this select only a translational mode + + if np.abs(cos_proj**2 + sin_proj**2 -1) < __thr__: + new_q = q + if crystal: + new_q = Methods.covariant_coordinates(bg, q) + q_list[imu, :] = new_q + break + elif cos_proj**2 + sin_proj**2 > __thr__: + print (q_cos) + ERROR_MSG = """ + Error, mixing between two |q|. + Please provide polarization vectors that are well defined in |q|. + This can be reached using the subroutine Phonons.Phonons.DiagonalizeSupercell. + q = {} + i_mode = {} + + cos_proj = {} | sin_proj = {} + """ + raise ValueError(ERROR_MSG.format(q, imu, cos_proj, sin_proj)) + else: + nq += 1 + + + # If we are here not q has been found + if nq == len(q_grid): + ERROR_MSG = """ + Error, the polarization vector {} cannot be identified! + No q found in this supercell! + """ + raise ValueError(ERROR_MSG.format(imu)) + + + return q_list + + +def ApplyTranslationsToSupercell(fc_matrix, super_cell_structure, supercell): + """ + Impose the translational symmetry directly on the supercell + matrix. + + Parameters + ---------- + - fc_matrix : ndarray(size=(3*natsc, 3*natsc)) + The matrix in the supercell. In output will be + modified + - super_cell_structure : Structure() + The structure of the super cell + - supercell : (nx,ny,nz) + The dimension of the supercell. + """ + + natsc = super_cell_structure.N_atoms + + # Check the consistency of the passed options + natsc3, _ = np.shape(fc_matrix) + assert natsc == int(natsc3 / 3), "Error, wrong number of atoms in the supercell structure" + assert natsc3 == _, "Error, the matrix passed has a wrong shape" + assert natsc % np.prod(supercell) == 0, "Error, the given supercell is impossible with the number of atoms" + + # Fill the auxiliary matrix + new_v2 = np.zeros( (3,3, natsc, natsc), dtype = np.double, order ="F") + for i in range(natsc): + for j in range(natsc): + new_v2[:, :, i, j] = fc_matrix[3*i : 3*(i+1), 3*j : 3*(j+1)] + + + # The number of translations + n_trans = np.prod(supercell) + trans_irt = np.zeros((natsc, n_trans), dtype = np.double, order = "F") + + # Setup the translational symmetries + for nx in range(supercell[0]): + for ny in range(supercell[1]): + for nz in range(supercell[2]): + # Build the translational symmetry + symmat = np.zeros((3,4)) + symmat[:3,:3] = np.eye(3) + symmat[:, 3] = np.array([nx, ny, nz], dtype = float) / np.array(supercell) + + + nindex = supercell[2] * supercell[1] *nx + nindex += supercell[2] * ny + nindex += nz + + # Get the IRT for this symmetry operation in the supercell + trans_irt[:, nindex] = GetIRT(super_cell_structure, symmat) + 1 + + + + + # Apply the translations + symph.trans_v2(new_v2, trans_irt) + + # Return back to the fc_matrix + for i in range(natsc): + for j in range(natsc): + fc_matrix[3*i : 3*(i+1), 3*j : 3*(j+1)] = new_v2[:, :, i, j] + + + +def get_invs(QE_s, QE_nsym): + """ + GET INVERSION SYMMETRY + ====================== + + For each symmetry operation, get an index that its inverse + Note, the array must be in Fortran indexing (starts from 1) + + Parameters + ---------- + QE_s : ndarray(size = (3,3,48), dtype = np.intc) + The symmetries + QE_nsym : int + The number of symmetries + + Results + ------- + QE_invs : ndarray(size = 48, dtype = np.intc) + The index of the inverse symmetry. + In fortran indexing (1 => index 0) + """ + QE_invs = np.zeros(48, dtype = np.intc) + for i in range(QE_nsym): + found = False + for j in range(QE_nsym): + if (QE_s[:,:,i].dot(QE_s[:,:,j]) == QE_s[:,:,0]).all(): + QE_invs[i] = j + 1 # Fortran index + found = True + + if not found: + warnings.warn("This is not a group, some features like Q star division may fail.") + + return QE_invs + + +def GetSymmetryMatrix(sym, structure, crystal = False): + """ + GET THE SYMMETRY MATRIX + ======================= + + This subroutine converts the 3x4 symmetry matrix to a 3N x 3N matrix. + It also transform the symmetry to be used directly in cartesian space. + However, take care, it could be a very big matrix, so it is preverred to work with the small matrix, + and maybe use a fortran wrapper if you want speed. + + NOTE: The passe structure must already satisfy the symmetry + + Parameters + ---------- + sym : ndarray(size = (3, 4)) + The symmetry and translations + structure : CC.Structure.Structure() + The structure on which the symmetry is applied (The structure must satisfy the symmetry already) + crystal : bool + If true, the symmetry is returned in crystal coordinate (default false) + + Results + ------- + sym_mat : ndarray(size = (3*structure.N_atoms, 3*structure.N_atoms)) + """ + + # Get the IRT array + irt = GetIRT(structure, sym) + + nat = structure.N_atoms + sym_mat = np.zeros((3 * nat, 3*nat), dtype = np.double) + + # Comvert the symmetry matrix in cartesian + if not crystal: + sym_cryst = Methods.convert_matrix_cart_cryst2(sym[:,:3], structure.unit_cell, cryst_to_cart = True) + else: + sym_cryst = sym[:,:3] + + # Correctly fill the atomic position of sym_mat + for i in range(nat): + i_irt = irt[i] + sym_mat[3 * i_irt : 3*i_irt+3, 3*i : 3*i+ 3] = sym_cryst + + return sym_mat \ No newline at end of file diff --git a/cellconstructor/Methods.py b/cellconstructor/Methods.py index 56c90824..cfb6b047 100644 --- a/cellconstructor/Methods.py +++ b/cellconstructor/Methods.py @@ -1365,6 +1365,38 @@ def convert_3tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): comp_matrix = np.linalg.inv(comp_matrix) return np.einsum("ia, jb, kc, ijk -> abc", comp_matrix, comp_matrix, comp_matrix, tensor) + + +def convert_4tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): + """ + Convert to crystal coordinates + ============================== + + This subroutine converts the 4 rank tensor to crystal coordinates and vice versa. + + Paramters + --------- + tensor : ndarray( size = (3,3,3,3)) + The 4rank tensor to be converted + unit_cell : ndarray + The unit cell of the structure + cryst_to_cart : bool + If true, reverse convert crystal to cartesian. + """ + + # Get the metric tensor from the unit_cell + metric_tensor = np.zeros((3,3)) + for i in range(0, 3): + for j in range(i, 3): + metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) + + comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) + if not cryst_to_cart: + comp_matrix = np.linalg.inv(comp_matrix) + + return np.einsum("ia, jb, kc, ld, ijkl -> abcd", comp_matrix, comp_matrix, comp_matrix, comp_matrix, tensor) + + def convert_fc(fc_matrix, unit_cell, cryst_to_cart = False): """ diff --git a/cellconstructor/symmetries.py b/cellconstructor/symmetries.py index 920352bf..01bd3a01 100644 --- a/cellconstructor/symmetries.py +++ b/cellconstructor/symmetries.py @@ -430,7 +430,7 @@ def ApplySymmetryToRamanTensor(self, raman_tensor): Parameters ---------- - - raman_tensor : ndarray (size = (3, 3, 3*nat)) + - raman_tensor : ndarray (size = (3, 3, 3 * nat)) The raman tensor. The first two indices indicate the polarization of the incoming/outcoming field, while the last one is the atomic/cartesian coordinate @@ -462,6 +462,8 @@ def ApplySymmetryToRamanTensor(self, raman_tensor): # Now we apply the sum rule rt_reshaped -= shift / self.QE_nat + + # Auxiliary variable new_tensor = np.zeros(np.shape(rt_reshaped), dtype = np.double) # Get the raman tensor in crystal components @@ -474,7 +476,7 @@ def ApplySymmetryToRamanTensor(self, raman_tensor): irt = self.QE_translations_irt[:, i] - 1 for j in range(self.QE_nat): new_mat = rt_reshaped[:,:, irt[j], :] - new_tensor += new_mat + new_tensor[:,:,j,:] += new_mat[:,:,:] rt_reshaped = new_tensor / self.QE_translation_nr new_tensor[:,:,:,:] = 0. @@ -485,7 +487,7 @@ def ApplySymmetryToRamanTensor(self, raman_tensor): for j in range(self.QE_nat): # Apply the symmetry to the 3 order tensor - new_mat = np.einsum("ai, bj, ck, ijk", self.QE_s[:,:,i], self.QE_s[:,:,i], self.QE_s[:,:,i], rt_reshaped[:,:, irt[j], :]) + new_mat = np.einsum("ai, bj, ck, ijk -> abc", self.QE_s[:,:,i], self.QE_s[:,:,i], self.QE_s[:,:,i], rt_reshaped[:,:, irt[j], :]) #new_mat = self.QE_s[:,:, i].dot( eff_charges[irt[j], :, :].dot(self.QE_s[:,:,i].T)) new_tensor[:,:,j,:] += new_mat @@ -512,8 +514,9 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): Parameters ---------- - dM_drdr : ndarray (size = (3 nat_sc, 3nat_sc, 3)) + dM_drdr : ndarray (size = (3 nat_sc, 3 nat_sc, 3)) The derivative of effective charges. + The last index refers to electric field apply_asr : bool If True the sum rule is applied. The sum rule is the 'custom' one where translations are projected @@ -579,7 +582,6 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): #print("") #print("--------------------") #print("symmetry: {:d}, irt: {}".format(i+1, irt +1)) - #prova = np.zeros(np.shape(new_dM)) for jat in range(nat): @@ -587,9 +589,9 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] # Apply the symmetries - new_mat = np.einsum("ck, ijk->ijc", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("bj, ijc->ibc", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("ai, ibc->abc", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ck, ijk -> ijc", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("bj, ijc -> ibc", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ai, ibc -> abc", self.QE_s[:,:,i], new_mat) #prova[3*jat:3*jat+3, 3*kat:3*kat+3,:] = new_mat new_dM[3*jat:3*jat+3, 3*kat:3*kat+3,:] += new_mat @@ -597,8 +599,6 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): #print("--------------------") dM_drdr[:,:,:] = new_dM / self.QE_nsym - - # Convert in crystal coordinates for i in range(nat): for j in range(nat): @@ -606,6 +606,122 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): + def ApplySymmetryToSecondOrderRamanTensor(self, dalpha_drdr, apply_asr = True): + """ + SYMMETRIZE TWO PHONON EFFECTIVE CHARGES + ======================================= + + This subroutine applies simmetries to the second order Raman tensor. + + Note, to symmetrize this tensor, symmetries must be imposed on the supercell. + + Parameters + ---------- + dalpha_drdr : ndarray (size = (3, 3, 3 nat_sc, 3 nat_sc)) + The second derivative of polarizability. + apply_asr : bool + If True the sum rule is applied. + The sum rule is the 'custom' one where translations are projected + out from the space for each polarization components. + """ + # Check the shape of the tensor + E1, E2, nat3, nat3_ = np.shape(dalpha_drdr) + + assert nat3 == nat3_, "Error on the shape of the argument for the atomic indices" + assert nat3 == 3 * self.QE_nat, "Wrong number of atoms (Symmetries must be setup in the supercell)" + assert E1 == E2, "Error on the shape of the argument electric field" + assert E1 == 3, "The first two entries are assosciated with electric field" + + # Get the number of atoms in the supercell + nat = int(nat3 /3) + + # Apply hermitianity on the atomic indices + dalpha_drdr += np.einsum("abcd->abdc", dalpha_drdr) + dalpha_drdr /= 2 + + # Apply hermitianity on the electric field + dalpha_drdr += np.einsum("abcd->bacd", dalpha_drdr) + dalpha_drdr /= 2 + + # SUM RULE ranging on the electric field components + if apply_asr: + for pol1 in range(3): + for pol2 in range(3): + CustomASR(dalpha_drdr[pol1, pol2, :, :]) + + # CONVERT TO CRYSTAL COORDINATES + for i in range(nat): + for j in range(nat): + dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3], self.QE_at.T) + + + # Get a ZERO second order Raman tensor (AUXILIARY) + new_dalpha_drdr = np.zeros(np.shape(dalpha_drdr), dtype = np.double) + + # TRANSLATIONS + if self.QE_translation_nr > 1: + for i in range(self.QE_translation_nr): + # irt[at1] is the atom on which the translation i maps at1 + irt = self.QE_translations_irt[:, i] - 1 + for at1 in range(nat): + for at2 in range(nat): + # Get the part of the tensor that is equivalent by translations of atom at1 at2 + new_mat = dalpha_drdr[:, :, 3*irt[at1]: 3*irt[at1] + 3, 3 * irt[at2]: 3*irt[at2] + 3] + # Fill with the symmetric counterparts + new_dalpha_drdr[:, :, 3*at1: 3*at1+3, 3*at2:3*at2+3] += new_mat + + # OVERWRITE the second order Raman tensor + dalpha_drdr[:,:,:,:] = new_dalpha_drdr / self.QE_translation_nr + # SET TO ZERO THE AUXILIARY VARIABLE + new_dalpha_drdr[:,:,:,:] = 0 + + # # DEBUG VARIABLE + # debug = np.zeros(np.shape(new_dalpha_drdr)) + + # ROTATIONS + for i in range(self.QE_nsym): + irt = self.QE_irt[i, :] - 1 # the symmetry applied on irt[at] gives the atom at + + for at1 in range(nat): + for at2 in range(nat): + # Get the part of the tensor that is equivalent by rotations of atom at1 at2 + # This has shape = (3, 3, 3, 3) + new_mat = dalpha_drdr[:, :, 3*irt[at1] : 3*irt[at1] + 3, 3*irt[at2] : 3*irt[at2] + 3] + + # Apply the symmetries + new_mat = np.einsum("dl, ijkl -> ijkd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ck, ijkd -> ijcd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("bj, ijcd -> ibcd", self.QE_s[:,:,i], new_mat) + new_mat = np.einsum("ai, ibcd -> abcd", self.QE_s[:,:,i], new_mat) + + new_dalpha_drdr[:, :, 3*at1: 3*at1 + 3, 3*at2: 3*at2 + 3] += new_mat + + # # CONVERT IN CARTESIAN COORDINATES TO DEBUG + # for _i_ in range(nat): + # for _j_ in range(nat): + # debug[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3] = Methods.convert_4tensor_to_cryst(new_dalpha_drdr[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3],\ + # self.QE_at.T, cryst_to_cart = True) + # np.save('CC_new_{}'.format(i), debug) + + # OVERWRITE THE second order Raman tensor + dalpha_drdr[:,:,:,:] = new_dalpha_drdr /self.QE_nsym + + + # Convert BACK in crystal coordinates + for i in range(nat): + for j in range(nat): + dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j+3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j : 3*j + 3],\ + self.QE_at.T, cryst_to_cart = True) + + # # TO DEBUG + # np.save('CC_raman', dalpha_drdr) + + return + + + + + def ApplySymmetryToTensor4(self, v4, initialize_symmetries = True): """ SYMMETRIZE A RANK-4 TENSOR diff --git a/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt b/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt new file mode 100644 index 00000000..fa322882 --- /dev/null +++ b/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt @@ -0,0 +1,3 @@ +In this example one set of displacement generated by one particular dynamical +matrix is changed into another one dynamical matrix with a different unit +cell. diff --git a/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py b/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py new file mode 100644 index 00000000..547f2b72 --- /dev/null +++ b/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py @@ -0,0 +1,51 @@ +from __future__ import print_function +from __future__ import division + + +import cellconstructor as CC +import cellconstructor.Phonons +import cellconstructor.symmetries + +import spglib +import sys, os + +import numpy as np + +import ase +from ase.visualize import view + +import pytest + +@pytest.mark.skip(reason="Function not implemented") +def test_diag_symmetries(): + total_path = os.path.dirname(os.path.abspath(__file__)) + os.chdir(total_path) + + # Diagonalize the dynamical matrix in the supercell + dyn = CC.Phonons.Phonons("../TestDiagonalizeSupercell/prova", 4) + w, p = dyn.DiagonalizeSupercell() + + view(dyn.structure.get_ase_atoms()) + + # Get the symmetries + supercell_s = dyn.structure.generate_supercell(dyn.GetSupercell()) + spglib_syms = spglib.get_symmetry(dyn.structure.get_ase_atoms()) + syms = CC.symmetries.GetSymmetriesFromSPGLIB(spglib_syms) + + # Get the symmetries on the polarization vectors + pols_syms = CC.symmetries.GetSymmetriesOnModes(syms, supercell_s, p) + + # Now complete the diagonalization of the polarization vectors + # To fully exploit symmetries + new_pols, syms_character = CC.symmetries.get_diagonal_symmetry_polarization_vectors(p, w, pols_syms) + + # TODO: Test if these new polarization vectors really rebuild the dynamical matrix + + # write the symmetry character + n_modes, n_syms = syms_character.shape + + for i in range(n_modes): + print("Mode {} | ".format(i), np.angle(syms_character[i,:], deg = True)) + +if __name__ == "__main__": + test_diag_symmetries() diff --git a/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py b/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py new file mode 100644 index 00000000..4168a806 --- /dev/null +++ b/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +import numpy as np + +import cellconstructor as CC +import cellconstructor.Phonons + + +import sys, os + +def test_supercell_fourier(): + + total_path = os.path.dirname(os.path.abspath(__file__)) + os.chdir(total_path) + + + SUPER_DYN = "../TestPhononSupercell/dynmat" + NQIRR = 8 + SUPERCELL = (3, 3, 2) + + + dyn = CC.Phonons.Phonons(SUPER_DYN, NQIRR) + + + fc = dyn.GetRealSpaceFC(SUPERCELL) + fc_new = fc.copy() + + + print("Real space:") + print(fc[:6, :6]) + + print("First one:") + print(dyn.dynmats[0]) + + + print ("Distances") + super_structure = dyn.structure.generate_supercell(SUPERCELL) + m =super_structure.get_masses_array() + nq = np.prod(SUPERCELL) + nat_sc = dyn.structure.N_atoms *nq + + _m_ = np.zeros(3*nat_sc) + for i in range(nat_sc): + _m_[3 * i : 3*i + 3] = m[i] + + m_mat = np.outer(1 / np.sqrt(_m_), 1 / np.sqrt(_m_)) + + fc *= m_mat + + w_tot = np.sqrt(np.abs(np.real(np.linalg.eigvals(fc)))) + w_tot.sort() + + w_old = np.zeros(len(w_tot)) + + for i in range(nq): + w,p = dyn.DyagDinQ(i) + w_old[ i * len(w) : (i+1) * len(w)] = w + + w_old.sort() + print ("Freq:") + print ("\n".join ( [" %.5f vs %.5f" % (w_tot[i] * CC.Phonons.RY_TO_CM, w_old[i] * CC.Phonons.RY_TO_CM) for i in range (len(w_tot))])) + + + # Try to revert the code + + dynmats_new = CC.Phonons.GetDynQFromFCSupercell(fc_new, np.array(dyn.q_tot), dyn.structure, super_structure) + d2 = CC.Phonons.GetDynQFromFCSupercell_parallel(fc_new, np.array(dyn.q_tot), dyn.structure, super_structure) + + + dyn_sc_new = CC.Phonons.GetSupercellFCFromDyn(dynmats_new, np.array(dyn.q_tot), dyn.structure, super_structure) + dyn_sc_new2 = CC.Phonons.GetSupercellFCFromDyn(d2, np.array(dyn.q_tot), dyn.structure, super_structure) + + dist1 = np.max(np.abs(dyn_sc_new - fc_new)) + dist2 = np.max(np.abs(dyn_sc_new2 - fc_new)) + print ("Distance reverted:", dist1) + print ("Distance reverted:", dist2) + + assert dist1 < 1e-10, 'Error in the fourier transform' + assert dist2 < 1e-10, 'Error in the parallel fourier transform' + + #print "\n".join ( ["RATIO: %.5f " % (w_tot[i] / w_old[i] ) for i in range (len(w_tot))]) + + +if __name__ == "__main__": + test_supercell_fourier() diff --git a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py new file mode 100644 index 00000000..5661192e --- /dev/null +++ b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py @@ -0,0 +1,58 @@ +import sys, os +import cellconstructor as CC +import cellconstructor.Phonons +import pytest + +import numpy as np + +try: + __SPGLIB__ = True + import spglib +except: + __SPGLIB__ = False + +def test_spglib_symmetrization(): + total_path = os.path.dirname(os.path.abspath(__file__)) + os.chdir(total_path) + + # Skip the test if spglib is not installed + if not __SPGLIB__: + pytest.skip("This test requires SPGLIB installed") + + + # Load the dyn + dyn = CC.Phonons.Phonons("SnTe_sscha", 3) + + # Symmetrize with quantum espresso + dyn.Symmetrize() + + w, pols = dyn.DiagonalizeSupercell() + + # Check identity on the polarization vectors + identity = np.einsum("ai, bi", pols, pols) + I = np.eye(identity.shape[0]) + assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with QE symmetrization failed" + + # Generate the supercell and symmetrize with spglib + new_dyn = dyn.GenerateSupercellDyn(dyn.GetSupercell()) + new_dyn.Symmetrize(use_spglib = True) + w2, pols = new_dyn.DiagonalizeSupercell() + + identity = np.einsum("ai, bi", pols, pols) + I = np.eye(identity.shape[0]) + assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with SPGLIB symmetrization (supercell) failed" + + + # Symmetrize with spglib (nothing should happen) + dyn.Symmetrize(use_spglib = True) + + w3, pols = dyn.DiagonalizeSupercell() + + # Check identity on the polarization vectors + identity = np.einsum("ai, bi", pols, pols) + I = np.eye(identity.shape[0]) + assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with SPGLIB symmetrization (unit_cell) failed" + + +if __name__ == "__main__": + test_spglib_symmetrization() diff --git a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py new file mode 100644 index 00000000..5bcc2671 --- /dev/null +++ b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +import numpy as np + +import cellconstructor as CC +import cellconstructor.Phonons +import cellconstructor.symmetries + +import sys, os +import pytest + +@pytest.mark.parametrize("FILDYN, NQIRR", [("Sym.dyn.", 3), ("skydyn_", 4)]) +def test_symmetries_supercell(FILDYN, NQIRR): + + total_path = os.path.dirname(os.path.abspath(__file__)) + os.chdir(total_path) + + + dynmat = CC.Phonons.Phonons(FILDYN, NQIRR) + SUPERCELL = dynmat.GetSupercell() + + # Compute the frequencies + supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) + w1, pols = supercell_dyn.DyagDinQ(0) + + # Show the modes for each q point + for i,q in enumerate(dynmat.q_tot): + print ("Dyagonalizing:", q) + w, p = dynmat.DyagDinQ(i) + print (" ".join(["%.4f cm-1 " % (x * CC.Phonons.RY_TO_CM) for x in w])) + + #dynmat.Symmetrize() + # # Test the symmetrization + qe_sym = CC.symmetries.QE_Symmetry(dynmat.structure) + + fc_dynmat_start = np.array(dynmat.dynmats) + + + after_sym = fc_dynmat_start.copy() + qe_sym.SymmetrizeFCQ(after_sym, np.array(dynmat.q_stars), verbose = True) + for i,q in enumerate(dynmat.q_tot): + dynmat.dynmats[i] = after_sym[i,:,:] + + # Show the modes for each q point + for i,q in enumerate(dynmat.q_tot): + print ("After Dyagonalizing:", q) + w, p = dynmat.DyagDinQ(i) + print (" ".join(["%.4f cm-1 " % (x * CC.Phonons.RY_TO_CM) for x in w])) + + # Print the difference between before and after the symmetrization + print () + print ("Difference of the symmetrization:") + print (np.sqrt( np.sum( (after_sym - fc_dynmat_start)**2 ) / np.sum(after_sym*fc_dynmat_start))) + + # print "" + + # Now lets try to randomize the matrix + #new_random = np.random.uniform( size = np.shape(fc_dynmat_start)) + 1j*np.random.uniform( size = np.shape(fc_dynmat_start)) + + # print "Saving a not symmetrized random matrix to Random.dyn.IQ, where IQ is the q index" + # # Lets save the new matrix in QE format + # for i, q in enumerate(dynmat.q_tot): + # dynmat.dynmats[i] = new_random[i, :, :] + # dynmat.save_qe("Random.dyn.") + + # # Lets constrain the symmetries + # # We use asr = crystal to force the existence of the acustic modes in Gamma + # qe_sym.SymmetrizeFCQ(new_random, np.array(dynmat.q_stars), asr = "no") + + # # Lets save the new matrix in QE format + # for i, q in enumerate(dynmat.q_tot): + # dynmat.dynmats[i] = new_random[i, :, :] + + # print "Saving a symmetrized random matrix to Sym.dyn.IQ, where IQ is the q index" + # dynmat.save_qe("Sym.dyn.") + # print "" + + # Compute the frequencies + supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) + w, pols = supercell_dyn.DyagDinQ(0) + # Get the translations + t = CC.Methods.get_translations(pols, supercell_dyn.structure.get_masses_array()) + + dynmat.Symmetrize() + # Compute the frequencies + supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) + w3, pols = supercell_dyn.DyagDinQ(0) + # Get the translations + t = CC.Methods.get_translations(pols, supercell_dyn.structure.get_masses_array()) + + + # Make the assert test + for i, _w_ in enumerate(w): + w2 = w3[i] + + assert np.abs(_w_ - w2) < 1e-8 + + # print "Frequencies:" + # print "\n".join(["%.4f cm-1 | %.4f cm-1 | %.4f cm-1 T: %d" % (w1[i]*CC.Phonons.RY_TO_CM, w[i]*CC.Phonons.RY_TO_CM, w3[i]*CC.Phonons.RY_TO_CM, t[i]) for i in range(len(w))]) + # print "" + # print "Done." + + +if __name__ == "__main__": + test_symmetries_supercell("Sym.dyn.", 3) diff --git a/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py b/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py new file mode 100644 index 00000000..fd836b85 --- /dev/null +++ b/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py @@ -0,0 +1,71 @@ +import cellconstructor as CC +import cellconstructor.Phonons +import cellconstructor.ForceTensor + +import ase, ase.dft.kpoints +import sys, os + +import numpy as np +import matplotlib.pyplot as plt + +# The dynamical matrix +PATH_TO_DYN="../QuasiHarmonicApproximation/V804/dynmat" +NQIRR = 8 + +# Load the dynamical matrix +dyn = CC.Phonons.Phonons(PATH_TO_DYN, NQIRR) + +# Optionally you can display the BZ +# With the standard paths +# to choose the path +# Just uncomment the following three lines +""" SHOW THE BRILLUIN ZONE +ase_atoms = dyn.structure.get_ase_atoms() +lattice = ase_atoms.cell.get_bravais_lattice() +lattice.plot_bz(show = True) +""" + +# Select the path +PATH = "GYTAZG" +N_POINTS = 1000 + + + +# -------- HERE THE CORE SCRIPT ------------ +band_path = ase.dft.kpoints.bandpath(PATH, + dyn.structure.unit_cell, + N_POINTS) + +# Get the q points of the path +q_path = band_path.cartesian_kpts() + +# Get the values of x axis for plotting the band path +x_axis, xticks, xlabels = band_path.get_linear_kpoint_axis() + + +# Perform the interpolation +frequencies = CC.ForceTensor.get_phonons_in_qpath(dyn, q_path) + +# ============= PLOT THE FIGURE ================= +fig = plt.figure(dpi = 200) +ax = plt.gca() + +# Plot all the modes +for i in range(frequencies.shape[-1]): + ax.plot(x_axis, frequencies[:,i]) + +# Plot vertical lines for each high symmetry points +for x in xticks: + ax.axvline(x, 0, 1, color = "k", lw = 0.4) + +# Set the x labels to the high symmetry points +ax.set_xticks(xticks) +ax.set_xticklabels(xlabels) + +ax.set_ylabel("Energy [cm-1]") +ax.set_xlabel("q path") + +fig.tight_layout() +fig.savefig("dispersion.png") +fig.savefig("dispersion.eps") +plt.show() diff --git a/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py b/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py new file mode 100644 index 00000000..30ee69ca --- /dev/null +++ b/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +import cellconstructor as CC +import cellconstructor.Manipulate +import cellconstructor.Phonons +import numpy as np +import matplotlib.pyplot as plt + + + +""" +This example file provide the quasi harmonic approximation to compute the pressure +by interpolating two different dynamical matrix on a supercell 3x3x2 of common ice at +different volumes. +""" + +RyToEv=13.605698 +Ev_AngToGPa=160.21766208 + +# Import the two phonons +ph1 = CC.Phonons.Phonons("V804/dynmat", nqirr = 8) +ph2 = CC.Phonons.Phonons("V907/dynmat", nqirr = 8) + + +# Interpolate the dynamical matrices between the two volumes +N_points = 100 # How many points to interpolate +N_T = 100 +T = np.linspace(0, 300, N_T) +free_energy = CC.Manipulate.QHA_FreeEnergy(ph1, ph2, T, N_points) + + +# Get the volumes (The determinant of the unit cell vectors) +V0 = np.linalg.det(ph1.structure.unit_cell) +V1 = np.linalg.det(ph2.structure.unit_cell) + +print ("The two volumes are:", V0, "Angstrom^3 and", V1, "Angstrom^3") + +# Take the derivative and compute the pressure [Ry/angstrom^3] +pressure = np.diff(free_energy, axis = 0) / ((V0 - V1)/(N_points - 1)) +pressure *= RyToEv*Ev_AngToGPa * 10 # kbar + + +# Plot the free energy +plt.figure() +plt.imshow(free_energy, aspect = "auto") +plt.colorbar() + + +# Plot a single graf of the pressure +plt.figure() +plt.title("QHA Pressure contribution") +plt.plot(T, pressure[0,:]) +plt.xlabel("T [K]") +plt.ylabel("P [kbar]") +plt.show() diff --git a/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn b/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn new file mode 100644 index 00000000..376330ed --- /dev/null +++ b/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn @@ -0,0 +1,1077 @@ +Dynamical matrix file +default + 2 12 0 8.5037676 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 +Basis vectors + 0.973217108 0.000000000 0.000000000 + 0.486608554 0.845319362 0.000000000 + 0.000000000 0.000000000 1.590192051 + 1 'H ' 918.836054935965 + 2 'O ' 14582.1964298742 + 1 2 0.4866085540 0.2817095890 0.0949782740 + 2 1 0.4866085540 0.2787508620 0.3180055250 + 3 1 0.4866085540 0.0689293120 0.0280554270 + 4 2 0.9732171080 0.5643454260 1.4868195660 + 5 1 1.1518511060 0.4545173740 1.5628857730 + 6 1 0.7945831110 0.4545173740 1.5628857730 + 7 2 0.9732171080 0.5636097740 0.8900742990 + 8 1 0.9732171080 0.5665685010 1.1131015510 + 9 1 0.9732171080 0.7763900520 0.8231514520 + 10 2 0.4866085540 0.2809739370 0.6917235400 + 11 1 0.3079745570 0.3908019890 0.7677897470 + 12 1 0.6652425520 0.3908019890 0.7677897470 + + Dynamical Matrix in cartesian axes + + q = ( 0.000000000 0.000000000 0.000000000 ) + + 1 1 + 0.11584946 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.82182268 0.00000000 0.16886655 0.00000000 + 0.00000000 0.00000000 0.16886655 0.00000000 0.95237180 0.00000000 + 1 2 + -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.10095398 0.00000000 -0.01416349 0.00000000 + 0.00000000 0.00000000 0.05891446 0.00000000 -0.69413923 0.00000000 + 1 3 + -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.62884967 0.00000000 -0.11827583 0.00000000 + 0.00000000 0.00000000 -0.19762180 0.00000000 -0.14963933 0.00000000 + 1 4 + -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.08360949 0.00000000 0.00422445 0.00000000 + 0.00000000 0.00000000 0.00103466 0.00000000 0.00373450 0.00000000 + 1 5 + 0.01925038 0.00000000 -0.03312385 0.00000000 0.01956453 0.00000000 + -0.03167955 0.00000000 -0.00978269 0.00000000 -0.01830764 0.00000000 + 0.00867241 0.00000000 -0.01293408 0.00000000 -0.02439769 0.00000000 + 1 6 + 0.01925038 0.00000000 0.03312385 0.00000000 -0.01956453 0.00000000 + 0.03167955 0.00000000 -0.00978269 0.00000000 -0.01830764 0.00000000 + -0.00867241 0.00000000 -0.01293408 0.00000000 -0.02439769 0.00000000 + 1 7 + 0.00130736 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00178111 0.00000000 -0.00131520 0.00000000 + 0.00000000 0.00000000 0.00131520 0.00000000 -0.01847965 0.00000000 + 1 8 + -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00205108 0.00000000 0.00204797 0.00000000 + 0.00000000 0.00000000 -0.00116139 0.00000000 0.02213726 0.00000000 + 1 9 + -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00154776 0.00000000 -0.00040028 0.00000000 + 0.00000000 0.00000000 -0.01119291 0.00000000 0.00468808 0.00000000 + 1 10 + 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.01210823 0.00000000 -0.00907041 0.00000000 + 0.00000000 0.00000000 -0.00740392 0.00000000 -0.07314055 0.00000000 + 1 11 + -0.00104804 0.00000000 0.00045496 0.00000000 -0.00261184 0.00000000 + -0.00025909 0.00000000 0.00043365 0.00000000 0.00236601 0.00000000 + -0.01275350 0.00000000 0.00656946 0.00000000 0.00053849 0.00000000 + 1 12 + -0.00104804 0.00000000 -0.00045496 0.00000000 0.00261184 0.00000000 + 0.00025909 0.00000000 0.00043365 0.00000000 0.00236601 0.00000000 + 0.01275350 0.00000000 0.00656946 0.00000000 0.00053849 0.00000000 + 2 1 + -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.10095398 0.00000000 0.05891446 0.00000000 + 0.00000000 0.00000000 -0.01416349 0.00000000 -0.69413923 0.00000000 + 2 2 + 0.06838210 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.11880890 0.00000000 0.00471634 0.00000000 + 0.00000000 0.00000000 0.00471634 0.00000000 0.69459118 0.00000000 + 2 3 + -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00704542 0.00000000 -0.07432155 0.00000000 + 0.00000000 0.00000000 -0.00889208 0.00000000 -0.02118800 0.00000000 + 2 4 + -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00784790 0.00000000 0.00095621 0.00000000 + 0.00000000 0.00000000 -0.00218866 0.00000000 0.02355706 0.00000000 + 2 5 + 0.00449813 0.00000000 -0.00402947 0.00000000 -0.00825895 0.00000000 + -0.00587673 0.00000000 0.00151429 0.00000000 0.00440330 0.00000000 + -0.01487396 0.00000000 0.00942971 0.00000000 -0.00713054 0.00000000 + 2 6 + 0.00449813 0.00000000 0.00402947 0.00000000 0.00825895 0.00000000 + 0.00587673 0.00000000 0.00151429 0.00000000 0.00440330 0.00000000 + 0.01487396 0.00000000 0.00942971 0.00000000 -0.00713054 0.00000000 + 2 7 + -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00205108 0.00000000 0.00116139 0.00000000 + 0.00000000 0.00000000 -0.00204797 0.00000000 0.02213726 0.00000000 + 2 8 + 0.00053534 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00061854 0.00000000 -0.00124916 0.00000000 + 0.00000000 0.00000000 0.00124916 0.00000000 -0.02752885 0.00000000 + 2 9 + -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00214572 0.00000000 0.00215791 0.00000000 + 0.00000000 0.00000000 0.01655552 0.00000000 -0.00464964 0.00000000 + 2 10 + -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.01939748 0.00000000 0.01170030 0.00000000 + 0.00000000 0.00000000 0.00298573 0.00000000 0.02668255 0.00000000 + 2 11 + 0.00260018 0.00000000 -0.00158705 0.00000000 0.01000915 0.00000000 + -0.00030567 0.00000000 -0.00069963 0.00000000 -0.00642102 0.00000000 + 0.01778799 0.00000000 -0.00853623 0.00000000 -0.00260117 0.00000000 + 2 12 + 0.00260018 0.00000000 0.00158705 0.00000000 -0.01000915 0.00000000 + 0.00030567 0.00000000 -0.00069963 0.00000000 -0.00642102 0.00000000 + -0.01778799 0.00000000 -0.00853623 0.00000000 -0.00260117 0.00000000 + 3 1 + -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.62884967 0.00000000 -0.19762180 0.00000000 + 0.00000000 0.00000000 -0.11827583 0.00000000 -0.14963933 0.00000000 + 3 2 + -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00704542 0.00000000 -0.00889208 0.00000000 + 0.00000000 0.00000000 -0.07432155 0.00000000 -0.02118800 0.00000000 + 3 3 + 0.06917921 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.61325234 0.00000000 0.16954008 0.00000000 + 0.00000000 0.00000000 0.16954008 0.00000000 0.17821400 0.00000000 + 3 4 + -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.04589381 0.00000000 0.00752648 0.00000000 + 0.00000000 0.00000000 0.01499647 0.00000000 -0.02510867 0.00000000 + 3 5 + 0.00753674 0.00000000 0.01530146 0.00000000 0.01171428 0.00000000 + 0.03191275 0.00000000 -0.01897960 0.00000000 0.01331570 0.00000000 + 0.01870442 0.00000000 0.00338128 0.00000000 0.01172827 0.00000000 + 3 6 + 0.00753674 0.00000000 -0.01530146 0.00000000 -0.01171428 0.00000000 + -0.03191275 0.00000000 -0.01897960 0.00000000 0.01331570 0.00000000 + -0.01870442 0.00000000 0.00338128 0.00000000 0.01172827 0.00000000 + 3 7 + -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00154776 0.00000000 0.01119291 0.00000000 + 0.00000000 0.00000000 0.00040028 0.00000000 0.00468808 0.00000000 + 3 8 + -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00214572 0.00000000 -0.01655552 0.00000000 + 0.00000000 0.00000000 -0.00215791 0.00000000 -0.00464964 0.00000000 + 3 9 + 0.00051766 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00395634 0.00000000 -0.00121369 0.00000000 + 0.00000000 0.00000000 0.00121369 0.00000000 -0.00338285 0.00000000 + 3 10 + 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00051211 0.00000000 0.01287658 0.00000000 + 0.00000000 0.00000000 0.00231581 0.00000000 0.00110165 0.00000000 + 3 11 + 0.00016512 0.00000000 0.00030688 0.00000000 -0.00037550 0.00000000 + 0.00326324 0.00000000 -0.00171497 0.00000000 -0.00174310 0.00000000 + 0.00144169 0.00000000 -0.00023793 0.00000000 -0.00174915 0.00000000 + 3 12 + 0.00016512 0.00000000 -0.00030688 0.00000000 0.00037550 0.00000000 + -0.00326324 0.00000000 -0.00171497 0.00000000 -0.00174310 0.00000000 + -0.00144169 0.00000000 -0.00023793 0.00000000 -0.00174915 0.00000000 + 4 1 + -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.08360949 0.00000000 0.00103466 0.00000000 + 0.00000000 0.00000000 0.00422445 0.00000000 0.00373450 0.00000000 + 4 2 + -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00784790 0.00000000 -0.00218866 0.00000000 + 0.00000000 0.00000000 0.00095621 0.00000000 0.02355706 0.00000000 + 4 3 + -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.04589381 0.00000000 0.01499647 0.00000000 + 0.00000000 0.00000000 0.00752648 0.00000000 -0.02510867 0.00000000 + 4 4 + 1.04989861 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.51330794 0.00000000 -0.27558434 0.00000000 + 0.00000000 0.00000000 -0.27558434 0.00000000 0.32457497 0.00000000 + 4 5 + -0.45402227 0.00000000 0.25561456 0.00000000 -0.17805745 0.00000000 + 0.19887622 0.00000000 -0.23012753 0.00000000 0.12943346 0.00000000 + -0.14741975 0.00000000 0.13438072 0.00000000 -0.13746891 0.00000000 + 4 6 + -0.45402227 0.00000000 -0.25561456 0.00000000 0.17805745 0.00000000 + -0.19887622 0.00000000 -0.23012753 0.00000000 0.12943346 0.00000000 + 0.14741975 0.00000000 0.13438072 0.00000000 -0.13746891 0.00000000 + 4 7 + 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.01210823 0.00000000 0.00740392 0.00000000 + 0.00000000 0.00000000 0.00907041 0.00000000 -0.07314055 0.00000000 + 4 8 + -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.01939748 0.00000000 -0.00298573 0.00000000 + 0.00000000 0.00000000 -0.01170030 0.00000000 0.02668255 0.00000000 + 4 9 + 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00051211 0.00000000 -0.00231581 0.00000000 + 0.00000000 0.00000000 -0.01287658 0.00000000 0.00110165 0.00000000 + 4 10 + 0.00173537 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00140161 0.00000000 -0.00013023 0.00000000 + 0.00000000 0.00000000 0.00013023 0.00000000 -0.01764532 0.00000000 + 4 11 + -0.00060478 0.00000000 0.00030420 0.00000000 -0.00062368 0.00000000 + 0.00040537 0.00000000 -0.00052025 0.00000000 0.00046462 0.00000000 + -0.00882148 0.00000000 0.00475129 0.00000000 0.00512915 0.00000000 + 4 12 + -0.00060478 0.00000000 -0.00030420 0.00000000 0.00062368 0.00000000 + -0.00040537 0.00000000 -0.00052025 0.00000000 0.00046462 0.00000000 + 0.00882148 0.00000000 0.00475129 0.00000000 0.00512915 0.00000000 + 5 1 + 0.01925038 0.00000000 -0.03167955 0.00000000 0.00867241 0.00000000 + -0.03312385 0.00000000 -0.00978269 0.00000000 -0.01293408 0.00000000 + 0.01956453 0.00000000 -0.01830764 0.00000000 -0.02439769 0.00000000 + 5 2 + 0.00449813 0.00000000 -0.00587673 0.00000000 -0.01487396 0.00000000 + -0.00402947 0.00000000 0.00151429 0.00000000 0.00942971 0.00000000 + -0.00825895 0.00000000 0.00440330 0.00000000 -0.00713054 0.00000000 + 5 3 + 0.00753674 0.00000000 0.03191275 0.00000000 0.01870442 0.00000000 + 0.01530146 0.00000000 -0.01897960 0.00000000 0.00338128 0.00000000 + 0.01171428 0.00000000 0.01331570 0.00000000 0.01172827 0.00000000 + 5 4 + -0.45402227 0.00000000 0.19887622 0.00000000 -0.14741975 0.00000000 + 0.25561456 0.00000000 -0.23012753 0.00000000 0.13438072 0.00000000 + -0.17805745 0.00000000 0.12943346 0.00000000 -0.13746891 0.00000000 + 5 5 + 0.48758632 0.00000000 -0.21339648 0.00000000 0.14826407 0.00000000 + -0.21339648 0.00000000 0.22722893 0.00000000 -0.10952791 0.00000000 + 0.14826407 0.00000000 -0.10952791 0.00000000 0.14583933 0.00000000 + 5 6 + -0.06619473 0.00000000 0.01984028 0.00000000 -0.01338595 0.00000000 + -0.01984028 0.00000000 0.03056873 0.00000000 -0.02341442 0.00000000 + 0.01338595 0.00000000 -0.02341442 0.00000000 0.01544505 0.00000000 + 5 7 + -0.00104804 0.00000000 -0.00025909 0.00000000 0.01275350 0.00000000 + 0.00045496 0.00000000 0.00043365 0.00000000 -0.00656946 0.00000000 + 0.00261184 0.00000000 -0.00236601 0.00000000 0.00053849 0.00000000 + 5 8 + 0.00260018 0.00000000 -0.00030567 0.00000000 -0.01778799 0.00000000 + -0.00158705 0.00000000 -0.00069963 0.00000000 0.00853623 0.00000000 + -0.01000915 0.00000000 0.00642102 0.00000000 -0.00260117 0.00000000 + 5 9 + 0.00016512 0.00000000 0.00326324 0.00000000 -0.00144169 0.00000000 + 0.00030688 0.00000000 -0.00171497 0.00000000 0.00023793 0.00000000 + 0.00037550 0.00000000 0.00174310 0.00000000 -0.00174915 0.00000000 + 5 10 + -0.00060478 0.00000000 0.00040537 0.00000000 0.00882148 0.00000000 + 0.00030420 0.00000000 -0.00052025 0.00000000 -0.00475129 0.00000000 + 0.00062368 0.00000000 -0.00046462 0.00000000 0.00512915 0.00000000 + 5 11 + 0.00310282 0.00000000 -0.00138901 0.00000000 -0.00104543 0.00000000 + -0.00138901 0.00000000 0.00116852 0.00000000 0.00036956 0.00000000 + 0.00104543 0.00000000 -0.00036956 0.00000000 -0.00337297 0.00000000 + 5 12 + -0.00287051 0.00000000 -0.00138915 0.00000000 -0.00126441 0.00000000 + 0.00138915 0.00000000 0.00091142 0.00000000 0.00086395 0.00000000 + -0.00126441 0.00000000 -0.00086395 0.00000000 -0.00197263 0.00000000 + 6 1 + 0.01925038 0.00000000 0.03167955 0.00000000 -0.00867241 0.00000000 + 0.03312385 0.00000000 -0.00978269 0.00000000 -0.01293408 0.00000000 + -0.01956453 0.00000000 -0.01830764 0.00000000 -0.02439769 0.00000000 + 6 2 + 0.00449813 0.00000000 0.00587673 0.00000000 0.01487396 0.00000000 + 0.00402947 0.00000000 0.00151429 0.00000000 0.00942971 0.00000000 + 0.00825895 0.00000000 0.00440330 0.00000000 -0.00713054 0.00000000 + 6 3 + 0.00753674 0.00000000 -0.03191275 0.00000000 -0.01870442 0.00000000 + -0.01530146 0.00000000 -0.01897960 0.00000000 0.00338128 0.00000000 + -0.01171428 0.00000000 0.01331570 0.00000000 0.01172827 0.00000000 + 6 4 + -0.45402227 0.00000000 -0.19887622 0.00000000 0.14741975 0.00000000 + -0.25561456 0.00000000 -0.23012753 0.00000000 0.13438072 0.00000000 + 0.17805745 0.00000000 0.12943346 0.00000000 -0.13746891 0.00000000 + 6 5 + -0.06619473 0.00000000 -0.01984028 0.00000000 0.01338595 0.00000000 + 0.01984028 0.00000000 0.03056873 0.00000000 -0.02341442 0.00000000 + -0.01338595 0.00000000 -0.02341442 0.00000000 0.01544505 0.00000000 + 6 6 + 0.48758632 0.00000000 0.21339648 0.00000000 -0.14826407 0.00000000 + 0.21339648 0.00000000 0.22722893 0.00000000 -0.10952791 0.00000000 + -0.14826407 0.00000000 -0.10952791 0.00000000 0.14583933 0.00000000 + 6 7 + -0.00104804 0.00000000 0.00025909 0.00000000 -0.01275350 0.00000000 + -0.00045496 0.00000000 0.00043365 0.00000000 -0.00656946 0.00000000 + -0.00261184 0.00000000 -0.00236601 0.00000000 0.00053849 0.00000000 + 6 8 + 0.00260018 0.00000000 0.00030567 0.00000000 0.01778799 0.00000000 + 0.00158705 0.00000000 -0.00069963 0.00000000 0.00853623 0.00000000 + 0.01000915 0.00000000 0.00642102 0.00000000 -0.00260117 0.00000000 + 6 9 + 0.00016512 0.00000000 -0.00326324 0.00000000 0.00144169 0.00000000 + -0.00030688 0.00000000 -0.00171497 0.00000000 0.00023793 0.00000000 + -0.00037550 0.00000000 0.00174310 0.00000000 -0.00174915 0.00000000 + 6 10 + -0.00060478 0.00000000 -0.00040537 0.00000000 -0.00882148 0.00000000 + -0.00030420 0.00000000 -0.00052025 0.00000000 -0.00475129 0.00000000 + -0.00062368 0.00000000 -0.00046462 0.00000000 0.00512915 0.00000000 + 6 11 + -0.00287051 0.00000000 0.00138915 0.00000000 0.00126441 0.00000000 + -0.00138915 0.00000000 0.00091142 0.00000000 0.00086395 0.00000000 + 0.00126441 0.00000000 -0.00086395 0.00000000 -0.00197263 0.00000000 + 6 12 + 0.00310282 0.00000000 0.00138901 0.00000000 0.00104543 0.00000000 + 0.00138901 0.00000000 0.00116852 0.00000000 0.00036956 0.00000000 + -0.00104543 0.00000000 -0.00036956 0.00000000 -0.00337297 0.00000000 + 7 1 + 0.00130736 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00178111 0.00000000 0.00131520 0.00000000 + 0.00000000 0.00000000 -0.00131520 0.00000000 -0.01847965 0.00000000 + 7 2 + -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00205108 0.00000000 -0.00204797 0.00000000 + 0.00000000 0.00000000 0.00116139 0.00000000 0.02213726 0.00000000 + 7 3 + -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00154776 0.00000000 0.00040028 0.00000000 + 0.00000000 0.00000000 0.01119291 0.00000000 0.00468808 0.00000000 + 7 4 + 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.01210823 0.00000000 0.00907041 0.00000000 + 0.00000000 0.00000000 0.00740392 0.00000000 -0.07314055 0.00000000 + 7 5 + -0.00104804 0.00000000 0.00045496 0.00000000 0.00261184 0.00000000 + -0.00025909 0.00000000 0.00043365 0.00000000 -0.00236601 0.00000000 + 0.01275350 0.00000000 -0.00656946 0.00000000 0.00053849 0.00000000 + 7 6 + -0.00104804 0.00000000 -0.00045496 0.00000000 -0.00261184 0.00000000 + 0.00025909 0.00000000 0.00043365 0.00000000 -0.00236601 0.00000000 + -0.01275350 0.00000000 -0.00656946 0.00000000 0.00053849 0.00000000 + 7 7 + 0.11584946 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.82182268 0.00000000 -0.16886655 0.00000000 + 0.00000000 0.00000000 -0.16886655 0.00000000 0.95237180 0.00000000 + 7 8 + -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.10095398 0.00000000 0.01416349 0.00000000 + 0.00000000 0.00000000 -0.05891446 0.00000000 -0.69413923 0.00000000 + 7 9 + -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.62884967 0.00000000 0.11827583 0.00000000 + 0.00000000 0.00000000 0.19762180 0.00000000 -0.14963933 0.00000000 + 7 10 + -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.08360949 0.00000000 -0.00422445 0.00000000 + 0.00000000 0.00000000 -0.00103466 0.00000000 0.00373450 0.00000000 + 7 11 + 0.01925038 0.00000000 -0.03312385 0.00000000 -0.01956453 0.00000000 + -0.03167955 0.00000000 -0.00978269 0.00000000 0.01830764 0.00000000 + -0.00867241 0.00000000 0.01293408 0.00000000 -0.02439769 0.00000000 + 7 12 + 0.01925038 0.00000000 0.03312385 0.00000000 0.01956453 0.00000000 + 0.03167955 0.00000000 -0.00978269 0.00000000 0.01830764 0.00000000 + 0.00867241 0.00000000 0.01293408 0.00000000 -0.02439769 0.00000000 + 8 1 + -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00205108 0.00000000 -0.00116139 0.00000000 + 0.00000000 0.00000000 0.00204797 0.00000000 0.02213726 0.00000000 + 8 2 + 0.00053534 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00061854 0.00000000 0.00124916 0.00000000 + 0.00000000 0.00000000 -0.00124916 0.00000000 -0.02752885 0.00000000 + 8 3 + -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00214572 0.00000000 -0.00215791 0.00000000 + 0.00000000 0.00000000 -0.01655552 0.00000000 -0.00464964 0.00000000 + 8 4 + -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.01939748 0.00000000 -0.01170030 0.00000000 + 0.00000000 0.00000000 -0.00298573 0.00000000 0.02668255 0.00000000 + 8 5 + 0.00260018 0.00000000 -0.00158705 0.00000000 -0.01000915 0.00000000 + -0.00030567 0.00000000 -0.00069963 0.00000000 0.00642102 0.00000000 + -0.01778799 0.00000000 0.00853623 0.00000000 -0.00260117 0.00000000 + 8 6 + 0.00260018 0.00000000 0.00158705 0.00000000 0.01000915 0.00000000 + 0.00030567 0.00000000 -0.00069963 0.00000000 0.00642102 0.00000000 + 0.01778799 0.00000000 0.00853623 0.00000000 -0.00260117 0.00000000 + 8 7 + -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.10095398 0.00000000 -0.05891446 0.00000000 + 0.00000000 0.00000000 0.01416349 0.00000000 -0.69413923 0.00000000 + 8 8 + 0.06838210 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.11880890 0.00000000 -0.00471634 0.00000000 + 0.00000000 0.00000000 -0.00471634 0.00000000 0.69459118 0.00000000 + 8 9 + -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00704542 0.00000000 0.07432155 0.00000000 + 0.00000000 0.00000000 0.00889208 0.00000000 -0.02118800 0.00000000 + 8 10 + -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00784790 0.00000000 -0.00095621 0.00000000 + 0.00000000 0.00000000 0.00218866 0.00000000 0.02355706 0.00000000 + 8 11 + 0.00449813 0.00000000 -0.00402947 0.00000000 0.00825895 0.00000000 + -0.00587673 0.00000000 0.00151429 0.00000000 -0.00440330 0.00000000 + 0.01487396 0.00000000 -0.00942971 0.00000000 -0.00713054 0.00000000 + 8 12 + 0.00449813 0.00000000 0.00402947 0.00000000 -0.00825895 0.00000000 + 0.00587673 0.00000000 0.00151429 0.00000000 -0.00440330 0.00000000 + -0.01487396 0.00000000 -0.00942971 0.00000000 -0.00713054 0.00000000 + 9 1 + -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00154776 0.00000000 -0.01119291 0.00000000 + 0.00000000 0.00000000 -0.00040028 0.00000000 0.00468808 0.00000000 + 9 2 + -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00214572 0.00000000 0.01655552 0.00000000 + 0.00000000 0.00000000 0.00215791 0.00000000 -0.00464964 0.00000000 + 9 3 + 0.00051766 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00395634 0.00000000 0.00121369 0.00000000 + 0.00000000 0.00000000 -0.00121369 0.00000000 -0.00338285 0.00000000 + 9 4 + 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00051211 0.00000000 -0.01287658 0.00000000 + 0.00000000 0.00000000 -0.00231581 0.00000000 0.00110165 0.00000000 + 9 5 + 0.00016512 0.00000000 0.00030688 0.00000000 0.00037550 0.00000000 + 0.00326324 0.00000000 -0.00171497 0.00000000 0.00174310 0.00000000 + -0.00144169 0.00000000 0.00023793 0.00000000 -0.00174915 0.00000000 + 9 6 + 0.00016512 0.00000000 -0.00030688 0.00000000 -0.00037550 0.00000000 + -0.00326324 0.00000000 -0.00171497 0.00000000 0.00174310 0.00000000 + 0.00144169 0.00000000 0.00023793 0.00000000 -0.00174915 0.00000000 + 9 7 + -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.62884967 0.00000000 0.19762180 0.00000000 + 0.00000000 0.00000000 0.11827583 0.00000000 -0.14963933 0.00000000 + 9 8 + -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00704542 0.00000000 0.00889208 0.00000000 + 0.00000000 0.00000000 0.07432155 0.00000000 -0.02118800 0.00000000 + 9 9 + 0.06917921 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.61325234 0.00000000 -0.16954008 0.00000000 + 0.00000000 0.00000000 -0.16954008 0.00000000 0.17821400 0.00000000 + 9 10 + -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.04589381 0.00000000 -0.00752648 0.00000000 + 0.00000000 0.00000000 -0.01499647 0.00000000 -0.02510867 0.00000000 + 9 11 + 0.00753674 0.00000000 0.01530146 0.00000000 -0.01171428 0.00000000 + 0.03191275 0.00000000 -0.01897960 0.00000000 -0.01331570 0.00000000 + -0.01870442 0.00000000 -0.00338128 0.00000000 0.01172827 0.00000000 + 9 12 + 0.00753674 0.00000000 -0.01530146 0.00000000 0.01171428 0.00000000 + -0.03191275 0.00000000 -0.01897960 0.00000000 -0.01331570 0.00000000 + 0.01870442 0.00000000 -0.00338128 0.00000000 0.01172827 0.00000000 + 10 1 + 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.01210823 0.00000000 -0.00740392 0.00000000 + 0.00000000 0.00000000 -0.00907041 0.00000000 -0.07314055 0.00000000 + 10 2 + -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.01939748 0.00000000 0.00298573 0.00000000 + 0.00000000 0.00000000 0.01170030 0.00000000 0.02668255 0.00000000 + 10 3 + 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00051211 0.00000000 0.00231581 0.00000000 + 0.00000000 0.00000000 0.01287658 0.00000000 0.00110165 0.00000000 + 10 4 + 0.00173537 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.00140161 0.00000000 0.00013023 0.00000000 + 0.00000000 0.00000000 -0.00013023 0.00000000 -0.01764532 0.00000000 + 10 5 + -0.00060478 0.00000000 0.00030420 0.00000000 0.00062368 0.00000000 + 0.00040537 0.00000000 -0.00052025 0.00000000 -0.00046462 0.00000000 + 0.00882148 0.00000000 -0.00475129 0.00000000 0.00512915 0.00000000 + 10 6 + -0.00060478 0.00000000 -0.00030420 0.00000000 -0.00062368 0.00000000 + -0.00040537 0.00000000 -0.00052025 0.00000000 -0.00046462 0.00000000 + -0.00882148 0.00000000 -0.00475129 0.00000000 0.00512915 0.00000000 + 10 7 + -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.08360949 0.00000000 -0.00103466 0.00000000 + 0.00000000 0.00000000 -0.00422445 0.00000000 0.00373450 0.00000000 + 10 8 + -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 -0.00784790 0.00000000 0.00218866 0.00000000 + 0.00000000 0.00000000 -0.00095621 0.00000000 0.02355706 0.00000000 + 10 9 + -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.04589381 0.00000000 -0.01499647 0.00000000 + 0.00000000 0.00000000 -0.00752648 0.00000000 -0.02510867 0.00000000 + 10 10 + 1.04989861 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 + 0.00000000 0.00000000 0.51330794 0.00000000 0.27558434 0.00000000 + 0.00000000 0.00000000 0.27558434 0.00000000 0.32457497 0.00000000 + 10 11 + -0.45402227 0.00000000 0.25561456 0.00000000 0.17805745 0.00000000 + 0.19887622 0.00000000 -0.23012753 0.00000000 -0.12943346 0.00000000 + 0.14741975 0.00000000 -0.13438072 0.00000000 -0.13746891 0.00000000 + 10 12 + -0.45402227 0.00000000 -0.25561456 0.00000000 -0.17805745 0.00000000 + -0.19887622 0.00000000 -0.23012753 0.00000000 -0.12943346 0.00000000 + -0.14741975 0.00000000 -0.13438072 0.00000000 -0.13746891 0.00000000 + 11 1 + -0.00104804 0.00000000 -0.00025909 0.00000000 -0.01275350 0.00000000 + 0.00045496 0.00000000 0.00043365 0.00000000 0.00656946 0.00000000 + -0.00261184 0.00000000 0.00236601 0.00000000 0.00053849 0.00000000 + 11 2 + 0.00260018 0.00000000 -0.00030567 0.00000000 0.01778799 0.00000000 + -0.00158705 0.00000000 -0.00069963 0.00000000 -0.00853623 0.00000000 + 0.01000915 0.00000000 -0.00642102 0.00000000 -0.00260117 0.00000000 + 11 3 + 0.00016512 0.00000000 0.00326324 0.00000000 0.00144169 0.00000000 + 0.00030688 0.00000000 -0.00171497 0.00000000 -0.00023793 0.00000000 + -0.00037550 0.00000000 -0.00174310 0.00000000 -0.00174915 0.00000000 + 11 4 + -0.00060478 0.00000000 0.00040537 0.00000000 -0.00882148 0.00000000 + 0.00030420 0.00000000 -0.00052025 0.00000000 0.00475129 0.00000000 + -0.00062368 0.00000000 0.00046462 0.00000000 0.00512915 0.00000000 + 11 5 + 0.00310282 0.00000000 -0.00138901 0.00000000 0.00104543 0.00000000 + -0.00138901 0.00000000 0.00116852 0.00000000 -0.00036956 0.00000000 + -0.00104543 0.00000000 0.00036956 0.00000000 -0.00337297 0.00000000 + 11 6 + -0.00287051 0.00000000 -0.00138915 0.00000000 0.00126441 0.00000000 + 0.00138915 0.00000000 0.00091142 0.00000000 -0.00086395 0.00000000 + 0.00126441 0.00000000 0.00086395 0.00000000 -0.00197263 0.00000000 + 11 7 + 0.01925038 0.00000000 -0.03167955 0.00000000 -0.00867241 0.00000000 + -0.03312385 0.00000000 -0.00978269 0.00000000 0.01293408 0.00000000 + -0.01956453 0.00000000 0.01830764 0.00000000 -0.02439769 0.00000000 + 11 8 + 0.00449813 0.00000000 -0.00587673 0.00000000 0.01487396 0.00000000 + -0.00402947 0.00000000 0.00151429 0.00000000 -0.00942971 0.00000000 + 0.00825895 0.00000000 -0.00440330 0.00000000 -0.00713054 0.00000000 + 11 9 + 0.00753674 0.00000000 0.03191275 0.00000000 -0.01870442 0.00000000 + 0.01530146 0.00000000 -0.01897960 0.00000000 -0.00338128 0.00000000 + -0.01171428 0.00000000 -0.01331570 0.00000000 0.01172827 0.00000000 + 11 10 + -0.45402227 0.00000000 0.19887622 0.00000000 0.14741975 0.00000000 + 0.25561456 0.00000000 -0.23012753 0.00000000 -0.13438072 0.00000000 + 0.17805745 0.00000000 -0.12943346 0.00000000 -0.13746891 0.00000000 + 11 11 + 0.48758632 0.00000000 -0.21339648 0.00000000 -0.14826407 0.00000000 + -0.21339648 0.00000000 0.22722893 0.00000000 0.10952791 0.00000000 + -0.14826407 0.00000000 0.10952791 0.00000000 0.14583933 0.00000000 + 11 12 + -0.06619473 0.00000000 0.01984028 0.00000000 0.01338595 0.00000000 + -0.01984028 0.00000000 0.03056873 0.00000000 0.02341442 0.00000000 + -0.01338595 0.00000000 0.02341442 0.00000000 0.01544505 0.00000000 + 12 1 + -0.00104804 0.00000000 0.00025909 0.00000000 0.01275350 0.00000000 + -0.00045496 0.00000000 0.00043365 0.00000000 0.00656946 0.00000000 + 0.00261184 0.00000000 0.00236601 0.00000000 0.00053849 0.00000000 + 12 2 + 0.00260018 0.00000000 0.00030567 0.00000000 -0.01778799 0.00000000 + 0.00158705 0.00000000 -0.00069963 0.00000000 -0.00853623 0.00000000 + -0.01000915 0.00000000 -0.00642102 0.00000000 -0.00260117 0.00000000 + 12 3 + 0.00016512 0.00000000 -0.00326324 0.00000000 -0.00144169 0.00000000 + -0.00030688 0.00000000 -0.00171497 0.00000000 -0.00023793 0.00000000 + 0.00037550 0.00000000 -0.00174310 0.00000000 -0.00174915 0.00000000 + 12 4 + -0.00060478 0.00000000 -0.00040537 0.00000000 0.00882148 0.00000000 + -0.00030420 0.00000000 -0.00052025 0.00000000 0.00475129 0.00000000 + 0.00062368 0.00000000 0.00046462 0.00000000 0.00512915 0.00000000 + 12 5 + -0.00287051 0.00000000 0.00138915 0.00000000 -0.00126441 0.00000000 + -0.00138915 0.00000000 0.00091142 0.00000000 -0.00086395 0.00000000 + -0.00126441 0.00000000 0.00086395 0.00000000 -0.00197263 0.00000000 + 12 6 + 0.00310282 0.00000000 0.00138901 0.00000000 -0.00104543 0.00000000 + 0.00138901 0.00000000 0.00116852 0.00000000 -0.00036956 0.00000000 + 0.00104543 0.00000000 0.00036956 0.00000000 -0.00337297 0.00000000 + 12 7 + 0.01925038 0.00000000 0.03167955 0.00000000 0.00867241 0.00000000 + 0.03312385 0.00000000 -0.00978269 0.00000000 0.01293408 0.00000000 + 0.01956453 0.00000000 0.01830764 0.00000000 -0.02439769 0.00000000 + 12 8 + 0.00449813 0.00000000 0.00587673 0.00000000 -0.01487396 0.00000000 + 0.00402947 0.00000000 0.00151429 0.00000000 -0.00942971 0.00000000 + -0.00825895 0.00000000 -0.00440330 0.00000000 -0.00713054 0.00000000 + 12 9 + 0.00753674 0.00000000 -0.03191275 0.00000000 0.01870442 0.00000000 + -0.01530146 0.00000000 -0.01897960 0.00000000 -0.00338128 0.00000000 + 0.01171428 0.00000000 -0.01331570 0.00000000 0.01172827 0.00000000 + 12 10 + -0.45402227 0.00000000 -0.19887622 0.00000000 -0.14741975 0.00000000 + -0.25561456 0.00000000 -0.23012753 0.00000000 -0.13438072 0.00000000 + -0.17805745 0.00000000 -0.12943346 0.00000000 -0.13746891 0.00000000 + 12 11 + -0.06619473 0.00000000 -0.01984028 0.00000000 -0.01338595 0.00000000 + 0.01984028 0.00000000 0.03056873 0.00000000 0.02341442 0.00000000 + 0.01338595 0.00000000 0.02341442 0.00000000 0.01544505 0.00000000 + 12 12 + 0.48758632 0.00000000 0.21339648 0.00000000 0.14826407 0.00000000 + 0.21339648 0.00000000 0.22722893 0.00000000 0.10952791 0.00000000 + 0.14826407 0.00000000 0.10952791 0.00000000 0.14583933 0.00000000 + + Diagonalizing the dynamical matrix + + q = ( 0.000000000 0.000000000 0.000000000 ) + + ************************************************************************** + freq ( 1) = -0.614260 [THz] = -20.489523 [cm-1] + ( 0.000000 0.000000 -0.001962 0.000000 0.287945 0.000000 ) + ( 0.000000 0.000000 -0.001071 0.000000 0.287824 0.000000 ) + ( 0.000000 0.000000 -0.002314 0.000000 0.288648 0.000000 ) + ( 0.000000 0.000000 -0.001362 0.000000 0.289789 0.000000 ) + ( 0.000052 0.000000 -0.001855 0.000000 0.288903 0.000000 ) + ( -0.000052 0.000000 -0.001855 0.000000 0.288903 0.000000 ) + ( -0.000000 0.000000 0.001962 0.000000 0.287945 0.000000 ) + ( 0.000000 0.000000 0.001071 0.000000 0.287824 0.000000 ) + ( -0.000000 0.000000 0.002314 0.000000 0.288648 0.000000 ) + ( -0.000000 0.000000 0.001362 0.000000 0.289789 0.000000 ) + ( -0.000052 0.000000 0.001855 0.000000 0.288903 0.000000 ) + ( 0.000052 0.000000 0.001855 0.000000 0.288903 0.000000 ) + freq ( 2) = 0.121946 [THz] = 4.067685 [cm-1] + ( -0.000000 0.000000 0.288712 0.000000 -0.000052 0.000000 ) + ( -0.000000 0.000000 0.288716 0.000000 -0.000053 0.000000 ) + ( -0.000000 0.000000 0.288709 0.000000 -0.000032 0.000000 ) + ( -0.000000 0.000000 0.288630 0.000000 -0.000040 0.000000 ) + ( 0.000004 0.000000 0.288642 0.000000 -0.000041 0.000000 ) + ( -0.000004 0.000000 0.288642 0.000000 -0.000041 0.000000 ) + ( -0.000000 0.000000 0.288712 0.000000 0.000052 0.000000 ) + ( -0.000000 0.000000 0.288716 0.000000 0.000053 0.000000 ) + ( -0.000000 0.000000 0.288709 0.000000 0.000032 0.000000 ) + ( -0.000000 0.000000 0.288630 0.000000 0.000040 0.000000 ) + ( 0.000004 0.000000 0.288642 0.000000 0.000041 0.000000 ) + ( -0.000004 0.000000 0.288642 0.000000 0.000041 0.000000 ) + freq ( 3) = 0.207164 [THz] = 6.910251 [cm-1] + ( 0.288150 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288520 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288500 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.289016 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288932 0.000000 -0.000168 0.000000 0.000128 0.000000 ) + ( 0.288932 0.000000 0.000168 0.000000 -0.000128 0.000000 ) + ( 0.288150 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288520 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288500 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.289016 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.288932 0.000000 -0.000168 0.000000 -0.000128 0.000000 ) + ( 0.288932 0.000000 0.000168 0.000000 0.000128 0.000000 ) + freq ( 4) = 1.824548 [THz] = 60.860378 [cm-1] + ( 0.306135 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.092112 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.314874 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.292992 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.319679 0.000000 -0.000398 0.000000 -0.064873 0.000000 ) + ( 0.319679 0.000000 0.000398 0.000000 0.064873 0.000000 ) + ( -0.306135 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.092112 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.314874 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.292992 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.319679 0.000000 0.000398 0.000000 -0.064873 0.000000 ) + ( -0.319679 0.000000 -0.000398 0.000000 0.064873 0.000000 ) + freq ( 5) = 1.877116 [THz] = 62.613851 [cm-1] + ( 0.000000 0.000000 0.294936 0.000000 0.006583 0.000000 ) + ( 0.000000 0.000000 0.139856 0.000000 0.006868 0.000000 ) + ( 0.000000 0.000000 0.322994 0.000000 -0.085038 0.000000 ) + ( 0.000000 0.000000 0.288436 0.000000 -0.002559 0.000000 ) + ( -0.001014 0.000000 0.312868 0.000000 0.037616 0.000000 ) + ( 0.001014 0.000000 0.312868 0.000000 0.037616 0.000000 ) + ( -0.000000 0.000000 -0.294936 0.000000 0.006583 0.000000 ) + ( -0.000000 0.000000 -0.139856 0.000000 0.006868 0.000000 ) + ( -0.000000 0.000000 -0.322994 0.000000 -0.085038 0.000000 ) + ( -0.000000 0.000000 -0.288436 0.000000 -0.002559 0.000000 ) + ( 0.001014 0.000000 -0.312868 0.000000 0.037616 0.000000 ) + ( -0.001014 0.000000 -0.312868 0.000000 0.037616 0.000000 ) + freq ( 6) = 5.924224 [THz] = 197.610826 [cm-1] + ( 0.000000 0.000000 0.041694 0.000000 0.357517 0.000000 ) + ( 0.000000 0.000000 -0.043262 0.000000 0.358199 0.000000 ) + ( -0.000000 0.000000 0.114156 0.000000 0.223962 0.000000 ) + ( -0.000000 0.000000 -0.052961 0.000000 -0.351392 0.000000 ) + ( -0.037498 0.000000 0.054002 0.000000 -0.145170 0.000000 ) + ( 0.037498 0.000000 0.054002 0.000000 -0.145170 0.000000 ) + ( -0.000000 0.000000 0.041694 0.000000 -0.357517 0.000000 ) + ( -0.000000 0.000000 -0.043262 0.000000 -0.358199 0.000000 ) + ( 0.000000 0.000000 0.114156 0.000000 -0.223962 0.000000 ) + ( 0.000000 0.000000 -0.052961 0.000000 0.351392 0.000000 ) + ( -0.037498 0.000000 0.054002 0.000000 0.145170 0.000000 ) + ( 0.037498 0.000000 0.054002 0.000000 0.145170 0.000000 ) + freq ( 7) = 8.051612 [THz] = 268.572872 [cm-1] + ( 0.320549 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.336665 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.081599 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.312957 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.265320 0.000000 0.110710 0.000000 -0.083672 0.000000 ) + ( -0.265320 0.000000 -0.110710 0.000000 0.083672 0.000000 ) + ( -0.320549 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.336665 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.081599 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.312957 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.265320 0.000000 -0.110710 0.000000 -0.083672 0.000000 ) + ( 0.265320 0.000000 0.110710 0.000000 0.083672 0.000000 ) + freq ( 8) = 8.339785 [THz] = 278.185291 [cm-1] + ( 0.000000 0.000000 0.199694 0.000000 0.247330 0.000000 ) + ( 0.000000 0.000000 0.224913 0.000000 0.293869 0.000000 ) + ( 0.000000 0.000000 0.254527 0.000000 0.229680 0.000000 ) + ( -0.000000 0.000000 -0.221032 0.000000 -0.269203 0.000000 ) + ( 0.002130 0.000000 -0.080706 0.000000 -0.071091 0.000000 ) + ( -0.002130 0.000000 -0.080706 0.000000 -0.071091 0.000000 ) + ( -0.000000 0.000000 -0.199694 0.000000 0.247330 0.000000 ) + ( -0.000000 0.000000 -0.224913 0.000000 0.293869 0.000000 ) + ( 0.000000 0.000000 -0.254527 0.000000 0.229680 0.000000 ) + ( 0.000000 0.000000 0.221032 0.000000 -0.269203 0.000000 ) + ( -0.002130 0.000000 0.080706 0.000000 -0.071091 0.000000 ) + ( 0.002130 0.000000 0.080706 0.000000 -0.071091 0.000000 ) + freq ( 9) = 8.479599 [THz] = 282.848972 [cm-1] + ( -0.373863 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.094230 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.095277 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.347179 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.297421 0.000000 -0.122424 0.000000 0.086147 0.000000 ) + ( 0.297421 0.000000 0.122424 0.000000 -0.086147 0.000000 ) + ( -0.373863 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.094230 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.095277 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.347179 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.297421 0.000000 -0.122424 0.000000 -0.086147 0.000000 ) + ( 0.297421 0.000000 0.122424 0.000000 0.086147 0.000000 ) + freq ( 10) = 8.797510 [THz] = 293.453339 [cm-1] + ( 0.000000 0.000000 0.008630 0.000000 -0.283918 0.000000 ) + ( -0.000000 0.000000 0.007935 0.000000 -0.295395 0.000000 ) + ( -0.000000 0.000000 0.004103 0.000000 -0.272828 0.000000 ) + ( -0.000000 0.000000 -0.009736 0.000000 -0.301222 0.000000 ) + ( 0.000432 0.000000 0.001913 0.000000 -0.288709 0.000000 ) + ( -0.000432 0.000000 0.001913 0.000000 -0.288709 0.000000 ) + ( 0.000000 0.000000 0.008630 0.000000 0.283918 0.000000 ) + ( 0.000000 0.000000 0.007935 0.000000 0.295395 0.000000 ) + ( 0.000000 0.000000 0.004103 0.000000 0.272828 0.000000 ) + ( -0.000000 0.000000 -0.009736 0.000000 0.301222 0.000000 ) + ( 0.000432 0.000000 0.001913 0.000000 0.288709 0.000000 ) + ( -0.000432 0.000000 0.001913 0.000000 0.288709 0.000000 ) + freq ( 11) = 11.512386 [THz] = 384.011863 [cm-1] + ( 0.000000 0.000000 -0.331565 0.000000 0.035201 0.000000 ) + ( 0.000000 0.000000 -0.184395 0.000000 0.032016 0.000000 ) + ( 0.000000 0.000000 -0.344120 0.000000 0.041810 0.000000 ) + ( 0.000000 0.000000 0.337392 0.000000 -0.046409 0.000000 ) + ( -0.033447 0.000000 0.218817 0.000000 -0.099111 0.000000 ) + ( 0.033447 0.000000 0.218817 0.000000 -0.099111 0.000000 ) + ( -0.000000 0.000000 -0.331565 0.000000 -0.035201 0.000000 ) + ( -0.000000 0.000000 -0.184395 0.000000 -0.032016 0.000000 ) + ( 0.000000 0.000000 -0.344120 0.000000 -0.041810 0.000000 ) + ( 0.000000 0.000000 0.337392 0.000000 0.046409 0.000000 ) + ( -0.033447 0.000000 0.218817 0.000000 0.099111 0.000000 ) + ( 0.033447 0.000000 0.218817 0.000000 0.099111 0.000000 ) + freq ( 12) = 11.626042 [THz] = 387.803025 [cm-1] + ( -0.000000 0.000000 -0.239601 0.000000 0.203299 0.000000 ) + ( -0.000000 0.000000 -0.314439 0.000000 0.198897 0.000000 ) + ( -0.000000 0.000000 -0.184409 0.000000 0.059195 0.000000 ) + ( -0.000000 0.000000 0.237635 0.000000 -0.199016 0.000000 ) + ( -0.035952 0.000000 0.197060 0.000000 -0.156509 0.000000 ) + ( 0.035952 0.000000 0.197060 0.000000 -0.156509 0.000000 ) + ( 0.000000 0.000000 0.239601 0.000000 0.203299 0.000000 ) + ( 0.000000 0.000000 0.314439 0.000000 0.198897 0.000000 ) + ( -0.000000 0.000000 0.184409 0.000000 0.059195 0.000000 ) + ( -0.000000 0.000000 -0.237635 0.000000 -0.199016 0.000000 ) + ( 0.035952 0.000000 -0.197060 0.000000 -0.156509 0.000000 ) + ( -0.035952 0.000000 -0.197060 0.000000 -0.156509 0.000000 ) + freq ( 13) = 19.794106 [THz] = 660.260289 [cm-1] + ( -0.007247 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.206154 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.277056 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.008984 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.255470 0.000000 -0.308826 0.000000 0.172235 0.000000 ) + ( -0.255470 0.000000 0.308826 0.000000 -0.172235 0.000000 ) + ( -0.007247 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.206154 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.277056 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.008984 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.255470 0.000000 -0.308826 0.000000 -0.172235 0.000000 ) + ( -0.255470 0.000000 0.308826 0.000000 0.172235 0.000000 ) + freq ( 14) = 19.863612 [THz] = 662.578779 [cm-1] + ( 0.000000 0.000000 -0.005249 0.000000 -0.008036 0.000000 ) + ( -0.000000 0.000000 0.436905 0.000000 -0.003041 0.000000 ) + ( -0.000000 0.000000 -0.136053 0.000000 0.413368 0.000000 ) + ( -0.000000 0.000000 0.004052 0.000000 0.008511 0.000000 ) + ( -0.001488 0.000000 -0.140962 0.000000 -0.199744 0.000000 ) + ( 0.001488 0.000000 -0.140962 0.000000 -0.199744 0.000000 ) + ( 0.000000 0.000000 -0.005249 0.000000 0.008036 0.000000 ) + ( -0.000000 0.000000 0.436905 0.000000 0.003041 0.000000 ) + ( -0.000000 0.000000 -0.136053 0.000000 -0.413368 0.000000 ) + ( -0.000000 0.000000 0.004052 0.000000 -0.008511 0.000000 ) + ( -0.001488 0.000000 -0.140962 0.000000 0.199744 0.000000 ) + ( 0.001488 0.000000 -0.140962 0.000000 0.199744 0.000000 ) + freq ( 15) = 20.013897 [THz] = 667.591740 [cm-1] + ( 0.004130 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.005379 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.389775 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.008727 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.219327 0.000000 0.354127 0.000000 -0.021596 0.000000 ) + ( 0.219327 0.000000 -0.354127 0.000000 0.021596 0.000000 ) + ( -0.004130 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.005379 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.389775 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.008727 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.219327 0.000000 -0.354127 0.000000 -0.021596 0.000000 ) + ( -0.219327 0.000000 0.354127 0.000000 0.021596 0.000000 ) + freq ( 16) = 20.476336 [THz] = 683.017052 [cm-1] + ( 0.000566 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.375790 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.329108 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.000788 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.025139 0.000000 -0.171084 0.000000 -0.308760 0.000000 ) + ( 0.025139 0.000000 0.171084 0.000000 0.308760 0.000000 ) + ( 0.000566 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.375790 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.329108 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.000788 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.025139 0.000000 -0.171084 0.000000 0.308760 0.000000 ) + ( 0.025139 0.000000 0.171084 0.000000 -0.308760 0.000000 ) + freq ( 17) = 22.845039 [THz] = 762.028462 [cm-1] + ( -0.000000 0.000000 0.006467 0.000000 0.013313 0.000000 ) + ( -0.000000 0.000000 -0.442128 0.000000 0.004637 0.000000 ) + ( 0.000000 0.000000 0.151214 0.000000 -0.449915 0.000000 ) + ( -0.000000 0.000000 -0.030040 0.000000 -0.006016 0.000000 ) + ( 0.010010 0.000000 0.108562 0.000000 0.164747 0.000000 ) + ( -0.010010 0.000000 0.108562 0.000000 0.164747 0.000000 ) + ( 0.000000 0.000000 -0.006467 0.000000 0.013313 0.000000 ) + ( -0.000000 0.000000 0.442128 0.000000 0.004637 0.000000 ) + ( 0.000000 0.000000 -0.151214 0.000000 -0.449915 0.000000 ) + ( 0.000000 0.000000 0.030040 0.000000 -0.006016 0.000000 ) + ( -0.010010 0.000000 -0.108562 0.000000 0.164747 0.000000 ) + ( 0.010010 0.000000 -0.108562 0.000000 0.164747 0.000000 ) + freq ( 18) = 25.006256 [THz] = 834.118899 [cm-1] + ( 0.030077 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.071820 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.282270 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.023857 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.167018 0.000000 -0.023716 0.000000 0.422361 0.000000 ) + ( -0.167018 0.000000 0.023716 0.000000 -0.422361 0.000000 ) + ( -0.030077 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.071820 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.282270 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.023857 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.167018 0.000000 0.023716 0.000000 0.422361 0.000000 ) + ( 0.167018 0.000000 -0.023716 0.000000 -0.422361 0.000000 ) + freq ( 19) = 28.752969 [THz] = 959.095799 [cm-1] + ( 0.027583 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.700479 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.005033 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.017776 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.005173 0.000000 -0.005029 0.000000 -0.063731 0.000000 ) + ( 0.005173 0.000000 0.005029 0.000000 0.063731 0.000000 ) + ( -0.027583 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.700479 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.005033 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.017776 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.005173 0.000000 0.005029 0.000000 -0.063731 0.000000 ) + ( -0.005173 0.000000 -0.005029 0.000000 0.063731 0.000000 ) + freq ( 20) = 29.478312 [THz] = 983.290632 [cm-1] + ( 0.000000 0.000000 0.024280 0.000000 0.021249 0.000000 ) + ( -0.000000 0.000000 -0.239107 0.000000 0.024256 0.000000 ) + ( -0.000000 0.000000 0.080795 0.000000 -0.143353 0.000000 ) + ( 0.000000 0.000000 0.015097 0.000000 0.033496 0.000000 ) + ( 0.005035 0.000000 -0.256342 0.000000 -0.375032 0.000000 ) + ( -0.005035 0.000000 -0.256342 0.000000 -0.375032 0.000000 ) + ( 0.000000 0.000000 -0.024280 0.000000 0.021249 0.000000 ) + ( 0.000000 0.000000 0.239107 0.000000 0.024256 0.000000 ) + ( 0.000000 0.000000 -0.080795 0.000000 -0.143353 0.000000 ) + ( 0.000000 0.000000 -0.015097 0.000000 0.033496 0.000000 ) + ( -0.005035 0.000000 0.256342 0.000000 -0.375032 0.000000 ) + ( 0.005035 0.000000 0.256342 0.000000 -0.375032 0.000000 ) + freq ( 21) = 31.171406 [THz] = 1039.766193 [cm-1] + ( 0.040180 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.447581 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.422309 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.029792 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.120295 0.000000 -0.167304 0.000000 0.130193 0.000000 ) + ( -0.120295 0.000000 0.167304 0.000000 -0.130193 0.000000 ) + ( 0.040180 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.447581 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.422309 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.029792 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.120295 0.000000 -0.167304 0.000000 -0.130193 0.000000 ) + ( -0.120295 0.000000 0.167304 0.000000 0.130193 0.000000 ) + freq ( 22) = 32.440719 [THz] = 1082.105905 [cm-1] + ( -0.000000 0.000000 -0.016741 0.000000 -0.022554 0.000000 ) + ( 0.000000 0.000000 0.257880 0.000000 -0.017510 0.000000 ) + ( 0.000000 0.000000 -0.100301 0.000000 0.211066 0.000000 ) + ( -0.000000 0.000000 -0.025213 0.000000 -0.031270 0.000000 ) + ( 0.004500 0.000000 0.254123 0.000000 0.351413 0.000000 ) + ( -0.004500 0.000000 0.254123 0.000000 0.351413 0.000000 ) + ( -0.000000 0.000000 -0.016741 0.000000 0.022554 0.000000 ) + ( 0.000000 0.000000 0.257880 0.000000 0.017510 0.000000 ) + ( 0.000000 0.000000 -0.100301 0.000000 -0.211066 0.000000 ) + ( -0.000000 0.000000 -0.025213 0.000000 0.031270 0.000000 ) + ( 0.004500 0.000000 0.254123 0.000000 -0.351413 0.000000 ) + ( -0.004500 0.000000 0.254123 0.000000 -0.351413 0.000000 ) + freq ( 23) = 34.006409 [THz] = 1134.331691 [cm-1] + ( 0.018708 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.044791 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.509721 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.014744 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.049373 0.000000 -0.262416 0.000000 -0.217958 0.000000 ) + ( -0.049373 0.000000 0.262416 0.000000 0.217958 0.000000 ) + ( -0.018708 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.044791 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.509721 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.014744 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.049373 0.000000 0.262416 0.000000 -0.217958 0.000000 ) + ( 0.049373 0.000000 -0.262416 0.000000 0.217958 0.000000 ) + freq ( 24) = 35.944576 [THz] = 1198.981999 [cm-1] + ( 0.001245 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.339074 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( -0.366312 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.000993 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.004144 0.000000 -0.207683 0.000000 -0.286830 0.000000 ) + ( -0.004144 0.000000 0.207683 0.000000 0.286830 0.000000 ) + ( 0.001245 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.339074 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.366312 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.000993 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.004144 0.000000 -0.207683 0.000000 0.286830 0.000000 ) + ( -0.004144 0.000000 0.207683 0.000000 -0.286830 0.000000 ) + freq ( 25) = 47.986716 [THz] = 1600.664538 [cm-1] + ( -0.000000 0.000000 0.032186 0.000000 -0.023659 0.000000 ) + ( 0.000000 0.000000 -0.433639 0.000000 -0.017552 0.000000 ) + ( 0.000000 0.000000 -0.118475 0.000000 0.398902 0.000000 ) + ( -0.000000 0.000000 0.014019 0.000000 -0.018448 0.000000 ) + ( -0.162216 0.000000 -0.145860 0.000000 0.143490 0.000000 ) + ( 0.162216 0.000000 -0.145860 0.000000 0.143490 0.000000 ) + ( 0.000000 0.000000 -0.032186 0.000000 -0.023659 0.000000 ) + ( 0.000000 0.000000 0.433639 0.000000 -0.017552 0.000000 ) + ( -0.000000 0.000000 0.118475 0.000000 0.398902 0.000000 ) + ( 0.000000 0.000000 -0.014019 0.000000 -0.018448 0.000000 ) + ( 0.162216 0.000000 0.145860 0.000000 0.143490 0.000000 ) + ( -0.162216 0.000000 0.145860 0.000000 0.143490 0.000000 ) + freq ( 26) = 48.024405 [THz] = 1601.921727 [cm-1] + ( 0.000000 0.000000 0.028648 0.000000 -0.022735 0.000000 ) + ( -0.000000 0.000000 -0.354703 0.000000 -0.007596 0.000000 ) + ( 0.000000 0.000000 -0.104624 0.000000 0.355033 0.000000 ) + ( -0.000000 0.000000 0.026969 0.000000 -0.019256 0.000000 ) + ( -0.212732 0.000000 -0.211664 0.000000 0.165204 0.000000 ) + ( 0.212732 0.000000 -0.211664 0.000000 0.165204 0.000000 ) + ( -0.000000 0.000000 0.028648 0.000000 0.022735 0.000000 ) + ( -0.000000 0.000000 -0.354703 0.000000 0.007596 0.000000 ) + ( 0.000000 0.000000 -0.104624 0.000000 -0.355033 0.000000 ) + ( -0.000000 0.000000 0.026969 0.000000 0.019256 0.000000 ) + ( -0.212732 0.000000 -0.211664 0.000000 -0.165204 0.000000 ) + ( 0.212732 0.000000 -0.211664 0.000000 -0.165204 0.000000 ) + freq ( 27) = 49.710608 [THz] = 1658.167412 [cm-1] + ( 0.000000 0.000000 0.013399 0.000000 -0.011080 0.000000 ) + ( 0.000000 0.000000 -0.242439 0.000000 -0.001003 0.000000 ) + ( -0.000000 0.000000 -0.088878 0.000000 0.260755 0.000000 ) + ( 0.000000 0.000000 -0.032010 0.000000 0.016930 0.000000 ) + ( 0.253139 0.000000 0.294426 0.000000 -0.176330 0.000000 ) + ( -0.253139 0.000000 0.294426 0.000000 -0.176330 0.000000 ) + ( 0.000000 0.000000 -0.013399 0.000000 -0.011080 0.000000 ) + ( 0.000000 0.000000 0.242439 0.000000 -0.001003 0.000000 ) + ( -0.000000 0.000000 0.088878 0.000000 0.260755 0.000000 ) + ( 0.000000 0.000000 0.032010 0.000000 0.016930 0.000000 ) + ( -0.253139 0.000000 -0.294426 0.000000 -0.176330 0.000000 ) + ( 0.253139 0.000000 -0.294426 0.000000 -0.176330 0.000000 ) + freq ( 28) = 50.923523 [THz] = 1698.625902 [cm-1] + ( -0.000000 0.000000 -0.019459 0.000000 0.013454 0.000000 ) + ( 0.000000 0.000000 0.340859 0.000000 0.008310 0.000000 ) + ( 0.000000 0.000000 0.106566 0.000000 -0.327959 0.000000 ) + ( -0.000000 0.000000 0.021667 0.000000 -0.015472 0.000000 ) + ( -0.213842 0.000000 -0.241226 0.000000 0.166923 0.000000 ) + ( 0.213842 0.000000 -0.241226 0.000000 0.166923 0.000000 ) + ( 0.000000 0.000000 -0.019459 0.000000 -0.013454 0.000000 ) + ( -0.000000 0.000000 0.340859 0.000000 -0.008310 0.000000 ) + ( -0.000000 0.000000 0.106566 0.000000 0.327959 0.000000 ) + ( 0.000000 0.000000 0.021667 0.000000 0.015472 0.000000 ) + ( -0.213842 0.000000 -0.241226 0.000000 -0.166923 0.000000 ) + ( 0.213842 0.000000 -0.241226 0.000000 -0.166923 0.000000 ) + freq ( 29) = 87.364360 [THz] = 2914.161372 [cm-1] + ( 0.000000 0.000000 -0.020894 0.000000 0.014786 0.000000 ) + ( -0.000000 0.000000 -0.001054 0.000000 -0.351005 0.000000 ) + ( -0.000000 0.000000 0.336773 0.000000 0.115155 0.000000 ) + ( -0.000000 0.000000 -0.020865 0.000000 0.014522 0.000000 ) + ( -0.289234 0.000000 0.166149 0.000000 -0.114655 0.000000 ) + ( 0.289234 0.000000 0.166149 0.000000 -0.114655 0.000000 ) + ( -0.000000 0.000000 0.020894 0.000000 0.014786 0.000000 ) + ( -0.000000 0.000000 0.001054 0.000000 -0.351005 0.000000 ) + ( 0.000000 0.000000 -0.336773 0.000000 0.115155 0.000000 ) + ( 0.000000 0.000000 0.020865 0.000000 0.014522 0.000000 ) + ( 0.289234 0.000000 -0.166149 0.000000 -0.114655 0.000000 ) + ( -0.289234 0.000000 -0.166149 0.000000 -0.114655 0.000000 ) + freq ( 30) = 88.257758 [THz] = 2943.961933 [cm-1] + ( -0.000000 0.000000 0.033364 0.000000 0.006633 0.000000 ) + ( -0.000000 0.000000 0.007841 0.000000 0.097679 0.000000 ) + ( 0.000000 0.000000 -0.512347 0.000000 -0.170686 0.000000 ) + ( 0.000000 0.000000 0.017267 0.000000 -0.014497 0.000000 ) + ( 0.258021 0.000000 -0.149506 0.000000 0.098094 0.000000 ) + ( -0.258021 0.000000 -0.149506 0.000000 0.098094 0.000000 ) + ( 0.000000 0.000000 0.033364 0.000000 -0.006633 0.000000 ) + ( 0.000000 0.000000 0.007841 0.000000 -0.097679 0.000000 ) + ( -0.000000 0.000000 -0.512347 0.000000 0.170686 0.000000 ) + ( 0.000000 0.000000 0.017267 0.000000 0.014497 0.000000 ) + ( 0.258021 0.000000 -0.149506 0.000000 -0.098094 0.000000 ) + ( -0.258021 0.000000 -0.149506 0.000000 -0.098094 0.000000 ) + freq ( 31) = 90.484705 [THz] = 3018.244885 [cm-1] + ( -0.003948 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( -0.003248 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.006936 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.054959 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.399881 0.000000 0.244655 0.000000 -0.169387 0.000000 ) + ( -0.399881 0.000000 -0.244655 0.000000 0.169387 0.000000 ) + ( 0.003948 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.003248 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.006936 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.054959 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.399881 0.000000 -0.244655 0.000000 -0.169387 0.000000 ) + ( 0.399881 0.000000 0.244655 0.000000 0.169387 0.000000 ) + freq ( 32) = 90.508657 [THz] = 3019.043844 [cm-1] + ( 0.003767 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) + ( 0.006547 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.007269 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( -0.055049 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.400025 0.000000 -0.244548 0.000000 0.169134 0.000000 ) + ( 0.400025 0.000000 0.244548 0.000000 -0.169134 0.000000 ) + ( 0.003767 0.000000 0.000000 0.000000 -0.000000 0.000000 ) + ( 0.006547 0.000000 0.000000 0.000000 0.000000 0.000000 ) + ( 0.007269 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( -0.055049 0.000000 -0.000000 0.000000 0.000000 0.000000 ) + ( 0.400025 0.000000 -0.244548 0.000000 -0.169134 0.000000 ) + ( 0.400025 0.000000 0.244548 0.000000 0.169134 0.000000 ) + freq ( 33) = 90.554693 [THz] = 3020.579432 [cm-1] + ( -0.000000 0.000000 -0.032049 0.000000 -0.044754 0.000000 ) + ( -0.000000 0.000000 -0.010764 0.000000 0.500218 0.000000 ) + ( 0.000000 0.000000 0.473441 0.000000 0.149585 0.000000 ) + ( 0.000000 0.000000 0.002255 0.000000 0.003024 0.000000 ) + ( 0.001300 0.000000 0.003032 0.000000 0.006238 0.000000 ) + ( -0.001300 0.000000 0.003032 0.000000 0.006238 0.000000 ) + ( -0.000000 0.000000 0.032049 0.000000 -0.044754 0.000000 ) + ( -0.000000 0.000000 0.010764 0.000000 0.500218 0.000000 ) + ( -0.000000 0.000000 -0.473441 0.000000 0.149585 0.000000 ) + ( 0.000000 0.000000 -0.002255 0.000000 0.003024 0.000000 ) + ( -0.001300 0.000000 -0.003032 0.000000 0.006238 0.000000 ) + ( 0.001300 0.000000 -0.003032 0.000000 0.006238 0.000000 ) + freq ( 34) = 94.958978 [THz] = 3167.490553 [cm-1] + ( -0.000000 0.000000 -0.003622 0.000000 0.040164 0.000000 ) + ( 0.000000 0.000000 -0.006978 0.000000 -0.662605 0.000000 ) + ( -0.000000 0.000000 0.070340 0.000000 0.041780 0.000000 ) + ( -0.000000 0.000000 0.009235 0.000000 -0.006539 0.000000 ) + ( 0.131157 0.000000 -0.076226 0.000000 0.056714 0.000000 ) + ( -0.131157 0.000000 -0.076226 0.000000 0.056714 0.000000 ) + ( 0.000000 0.000000 -0.003622 0.000000 -0.040164 0.000000 ) + ( 0.000000 0.000000 -0.006978 0.000000 0.662605 0.000000 ) + ( -0.000000 0.000000 0.070340 0.000000 -0.041780 0.000000 ) + ( -0.000000 0.000000 0.009235 0.000000 0.006539 0.000000 ) + ( 0.131157 0.000000 -0.076226 0.000000 -0.056714 0.000000 ) + ( -0.131157 0.000000 -0.076226 0.000000 -0.056714 0.000000 ) + freq ( 35) = 97.425645 [THz] = 3249.769715 [cm-1] + ( 0.000000 0.000000 0.019594 0.000000 -0.014154 0.000000 ) + ( -0.000000 0.000000 0.014700 0.000000 0.352428 0.000000 ) + ( -0.000000 0.000000 -0.330607 0.000000 -0.123437 0.000000 ) + ( 0.000000 0.000000 -0.019903 0.000000 0.013902 0.000000 ) + ( -0.293537 0.000000 0.161593 0.000000 -0.112505 0.000000 ) + ( 0.293537 0.000000 0.161593 0.000000 -0.112505 0.000000 ) + ( 0.000000 0.000000 -0.019594 0.000000 -0.014154 0.000000 ) + ( -0.000000 0.000000 -0.014700 0.000000 0.352428 0.000000 ) + ( -0.000000 0.000000 0.330607 0.000000 -0.123437 0.000000 ) + ( -0.000000 0.000000 0.019903 0.000000 0.013902 0.000000 ) + ( 0.293537 0.000000 -0.161593 0.000000 -0.112505 0.000000 ) + ( -0.293537 0.000000 -0.161593 0.000000 -0.112505 0.000000 ) + freq ( 36) = 97.513320 [THz] = 3252.694225 [cm-1] + ( -0.000000 0.000000 0.025803 0.000000 0.023309 0.000000 ) + ( 0.000000 0.000000 0.009229 0.000000 -0.215156 0.000000 ) + ( -0.000000 0.000000 -0.417799 0.000000 -0.143843 0.000000 ) + ( 0.000000 0.000000 -0.020440 0.000000 0.014898 0.000000 ) + ( -0.297024 0.000000 0.161735 0.000000 -0.117821 0.000000 ) + ( 0.297024 0.000000 0.161735 0.000000 -0.117821 0.000000 ) + ( 0.000000 0.000000 0.025803 0.000000 -0.023309 0.000000 ) + ( 0.000000 0.000000 0.009229 0.000000 0.215156 0.000000 ) + ( -0.000000 0.000000 -0.417799 0.000000 0.143843 0.000000 ) + ( -0.000000 0.000000 -0.020440 0.000000 -0.014898 0.000000 ) + ( -0.297024 0.000000 0.161735 0.000000 0.117821 0.000000 ) + ( 0.297024 0.000000 0.161735 0.000000 0.117821 0.000000 ) + ************************************************************************** diff --git a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py new file mode 100644 index 00000000..90ce41d2 --- /dev/null +++ b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py @@ -0,0 +1,52 @@ +from __future__ import print_function + +# Import numpy +import numpy as np + +# Import cellconstructor +import cellconstructor as CC +import cellconstructor.Structure +import cellconstructor.Phonons +import cellconstructor.symmetries + +# Define a rocksalt structure +bcc = CC.Structure.Structure(2) +bcc.coords[1,:] = 5.6402 * np.array([.5, .5, .5]) # Shift the second atom in the center +bcc.atoms = ["Na", "Cl"] +bcc.unit_cell = np.eye(3) * 5.6402 # A cubic cell of 5.64 A edge +bcc.has_unit_cell = True # Setup periodic boundary conditions + +# Setup the mass on the two atoms (Ry units) +bcc.masses = {"Na": 20953.89349715178, + "Cl": 302313.43272048925} + + + +# Lets generate the random dynamical matrix +dynamical_matrix = CC.Phonons.Phonons(bcc) +dynamical_matrix.dynmats[0] = np.random.uniform(size = (3 * bcc.N_atoms, + 3* bcc.N_atoms)) + +# Force the random matrix to be hermitian (so we can diagonalize it) +dynamical_matrix.dynmats[0] += dynamical_matrix.dynmats[0].T + +# Lets compute the phonon frequencies without symmetries +w, pols = dynamical_matrix.DiagonalizeSupercell() + +# Print on the screen the random frequencies +print("Non symmetric frequencies:") +print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM)for i,w in enumerate(w)])) + +# Symmetrize the dynamical matrix +dynamical_matrix.Symmetrize() # Use QE to symmetrize + +# Recompute the frequencies and print them in output +w, pols = dynamical_matrix.DiagonalizeSupercell() +print() +print("frequencies after the symmetrization:") +print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM) for i,w in enumerate(w)])) + + + + + diff --git a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py new file mode 100644 index 00000000..54eff828 --- /dev/null +++ b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py @@ -0,0 +1,70 @@ +from __future__ import print_function + +# Import numpy +import numpy as np + +# Import cellconstructor +import cellconstructor as CC +import cellconstructor.Structure +import cellconstructor.Phonons +import cellconstructor.symmetries + +# Define a rocksalt structure +bcc = CC.Structure.Structure(2) +bcc.coords[1,:] = 5.6402 * np.array([.5, .5, .5]) # Shift the second atom in the center +bcc.atoms = ["Na", "Cl"] +bcc.unit_cell = np.eye(3) * 5.6402 # A cubic cell of 5.64 A edge +bcc.has_unit_cell = True # Setup periodic boundary conditions + +# Setup the mass on the two atoms (Ry units) +bcc.masses = {"Na": 20953.89349715178, + "Cl": 302313.43272048925} + + + +# Lets generate the random dynamical matrix +dynamical_matrix = CC.Phonons.Phonons(bcc) +dynamical_matrix.dynmats[0] = np.random.uniform(size = (3 * bcc.N_atoms, + 3* bcc.N_atoms)) + +# Force the random matrix to be hermitian (so we can diagonalize it) +dynamical_matrix.dynmats[0] += dynamical_matrix.dynmats[0].T + +# Lets compute the phonon frequencies without symmetries +w, pols = dynamical_matrix.DiagonalizeSupercell() + +# Print on the screen the random frequencies +print("Non symmetric frequencies:") +print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM)for i,w in enumerate(w)])) + +# Initialize the symmetry class +syms = CC.symmetries.QE_Symmetry(bcc) +syms.SetupFromSPGLIB() # Setup the espresso symmetries on spglib + +# Generate the real space dynamical matrix +superdyn = dynamical_matrix.GenerateSupercellDyn(dynamical_matrix.GetSupercell()) +# Apply the symmetries to the real space matrix +CC.symmetries.CustomASR(superdyn.dynmats[0]) +syms.ApplySymmetriesToV2(superdyn.dynmats[0]) +# Get back the dyanmical matrix in q space +dynq = CC.Phonons.GetDynQFromFCSupercell(superdyn.dynmats[0], + np.array(dynamical_matrix.q_tot), + dynamical_matrix.structure, + superdyn.structure) + +# Copy each q point of the symmetrized dynamical matrix into +# the original one +for i in range(len(dynamical_matrix.q_tot)): + dynamical_matrix.dynmats[i] = dynq[i,:,:] + + +# Recompute the frequencies and print them in output +w, pols = dynamical_matrix.DiagonalizeSupercell() +print() +print("frequencies after the symmetrization:") +print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM) for i,w in enumerate(w)])) + + + + + From f524dc2208210740fdaaf167b678c4cdde1b558a Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 18 Jan 2023 18:08:50 +0100 Subject: [PATCH 02/18] Symmetrizations for second order eff charges and Raman tensor --- .../.ipynb_checkpoints/trntnsc-checkpoint.f90 | 70 + .../ForceTensor-checkpoint.py | 3078 +++++++++++++++++ .../.ipynb_checkpoints/Phonons-checkpoint.py | 6 +- .../.ipynb_checkpoints/Settings-checkpoint.py | 346 ++ .../.ipynb_checkpoints/Units-checkpoint.py | 27 + .../symmetries-checkpoint.py | 41 +- cellconstructor/Phonons.py | 6 +- cellconstructor/symmetries.py | 41 +- 8 files changed, 3555 insertions(+), 60 deletions(-) create mode 100644 FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 create mode 100644 cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Units-checkpoint.py diff --git a/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 b/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 new file mode 100644 index 00000000..ca99e559 --- /dev/null +++ b/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 @@ -0,0 +1,70 @@ +! +! Copyright (C) 2001 PWSCF group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +! +!----------------------------------------------------------------------- +subroutine trntnsc (phi, at, bg, iflg) + !----------------------------------------------------------------------- + ! + ! trasforms a COMPLEX tensor (like the dynamical matrix) + ! from crystal to cartesian axis (iflg >= 1) or viceversa (iflg <= -1) + ! + implicit none + + integer :: iflg + ! input: gives the versus of the trans. + + double complex :: phi (3, 3) + ! inp/out: the matrix to transform + + double precision :: at (3, 3), bg (3, 3) + ! input: the direct lattice vectors + ! input: the reciprocal lattice + + integer :: i, j, k, l + ! + ! counters on polarizations + ! / + !/ + + + double complex :: wrk (3, 3) + ! a working array + if (iflg.gt.0) then + ! + ! forward transformation (crystal to cartesian axis) + ! + + call zcopy (9, phi, 1, wrk, 1) + do i = 1, 3 + do j = 1, 3 + phi (i, j) = (0.d0, 0.d0) + do k = 1, 3 + do l = 1, 3 + phi (i, j) = phi (i, j) + wrk (k, l) * bg (i, k) * bg (j, l) + enddo + enddo + enddo + enddo + else + ! + ! backward transformation (cartesian to crystal axis) + ! + do i = 1, 3 + do j = 1, 3 + wrk (i, j) = (0.d0, 0.d0) + do k = 1, 3 + do l = 1, 3 + wrk (i, j) = wrk (i, j) + phi (k, l) * at (k, i) * at (l, j) + enddo + enddo + enddo + enddo + call zcopy (9, wrk, 1, phi, 1) + endif + return +end subroutine trntnsc diff --git a/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py b/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py new file mode 100644 index 00000000..d9b9995a --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py @@ -0,0 +1,3078 @@ +from __future__ import print_function +from __future__ import division + +# Import for python2/3 compatibility +import cellconstructor.Phonons as Phonons +import cellconstructor.Methods as Methods +import cellconstructor.symmetries as symmetries +import cellconstructor.Units as Units + +import cellconstructor.Settings as Settings +from cellconstructor.Settings import ParallelPrint as print + + +import numpy as np +import scipy, scipy.signal, scipy.interpolate + +import symph +import time +import itertools +import thirdorder +import secondorder + +""" +In this module we create a tensor class. +This is used to store high order tensors defined +in periodic bondary systems, interpolate and multiply between +them +""" + + +class GenericTensor: + """ + This is the generic tensor class. + Do not use this when programming. + This should be used only to define new tensors. + """ + + def __init__(self, unitcell_structure, supercell_structure, supercell_size): + """ + Define the tensors + """ + self.supercell_size = supercell_size + self.itau = supercell_structure.get_itau(unitcell_structure) - 1 + + self.tau=unitcell_structure.coords + + self.nat = unitcell_structure.N_atoms + + self.unitcell_structure = unitcell_structure + self.supercell_structure = supercell_structure + + self.verbose = True + + + +class Tensor2(GenericTensor): + """ + This class defines the 2rank tensors, like force constant matrices. + """ + + def __init__(self, unitcell_structure, supercell_structure, supercell_size): + GenericTensor.__init__(self, unitcell_structure, supercell_structure, supercell_size) + + self.n_R = np.prod(np.prod(supercell_size)) + self.x_r_vector2 = np.zeros((3, self.n_R), dtype = np.intc, order = "F") + self.r_vector2 = np.zeros((3, self.n_R), dtype = np.double, order = "F") + self.tensor = np.zeros((self.n_R, 3*self.nat, 3*self.nat), dtype = np.double) + self.n_sup = np.prod(supercell_size) + + # Prepare the initialization of the effective charges + self.effective_charges = None + self.dielectric_tensor = None + + # Prepare the values ready to be used inside quantum espresso Fortran subroutines + self.QE_tau = None + self.QE_omega = None + self.QE_zeu = None + self.QE_bg = None + + # NOTE: this QE_alat is not the unit of measure like in QE subroutines, + # But rather the dimension of the first unit-cell vector in Bohr. + # It is used for computing the ideal integration size in rgd_blk from symph + self.QE_alat = None + + def SetupFromPhonons(self, phonons): + """ + SETUP FROM PHONONS + ================== + + Setup the 2rank tensor from a phonons class + + Parameters + ---------- + - phonons : Phonons.Phonons() + The dynamical matrix from which you want to setup the tensor + """ + + # Check if the supercell is correct + ERR = """ +Error, the supercell of the phonon object is {}. + it must match with the supercell defined for the Tensor2: {} +""".format(phonons.GetSupercell(), self.supercell_size) + #print(ERR) + + assert np.all([self.supercell_size[i] == phonons.GetSupercell()[i] for i in range(3)]), ERR + + # Check that no 1 atom with 1 q point + if np.prod(phonons.GetSupercell()) == 1 and phonons.structure.N_atoms == 1: + ERR = """ +Error, cannot initialize a tensor from a structure with 1 atom with only Gamma + check if you imported the dynamical matrix with the correct nqirr. + +""" + raise ValueError(ERR) + current_dyn = phonons.Copy() + + # Check if the dynamical matrix has the effective charges + if phonons.effective_charges is not None: + time1 = time.time() + self.effective_charges = current_dyn.effective_charges.copy() + assert current_dyn.dielectric_tensor is not None, "Error, effective charges provided, but not the dielectric tensor." + + self.dielectric_tensor = current_dyn.dielectric_tensor.copy() + + self.QE_alat = phonons.alat * Units.A_TO_BOHR + + # Prepare the coordinates in Bohr for usage in QE subroutines + self.QE_tau = np.zeros((3, self.nat), dtype = np.double, order = "F") + self.QE_tau[:,:] = self.tau.T * Units.A_TO_BOHR / self.QE_alat + self.QE_zeu = np.zeros((3,3,self.nat), dtype = np.double, order = "F") + self.QE_zeu[:,:,:] = np.einsum("sij->ijs", self.effective_charges) # Swap axis (we hope they are good) + self.QE_bg = np.zeros((3,3), dtype = np.double, order = "F") + bg = self.unitcell_structure.get_reciprocal_vectors() + self.QE_bg[:,:] = bg.T * self.QE_alat / (2*np.pi * Units.A_TO_BOHR) + self.QE_omega = self.unitcell_structure.get_volume() * Units.A_TO_BOHR**3 + + # The typical distance in the cell + #self.QE_alat = np.sqrt(np.sum(self.unitcell_structure.unit_cell[0, :]**2)) + #self.QE_alat = Units.A_TO_BOHR + + # Subtract the long range interaction for any value of gamma. + dynq = np.zeros((3, 3, self.nat, self.nat), dtype = np.complex128, order = "F") + for iq, q in enumerate(current_dyn.q_tot): + + t1 = time.time() + # Fill the temporany dynamical matrix in the correct fortran subroutine + for i in range(self.nat): + for j in range(self.nat): + dynq[:,:, i, j] = current_dyn.dynmats[iq][3*i: 3*i+3, 3*j : 3*j+3] + t3 = time.time() + + # Lets go in QE units + QE_q = q * self.QE_alat / Units.A_TO_BOHR + + # Remove the long range interaction from the dynamical matrix + symph.rgd_blk(0, 0, 0, dynq, QE_q, self.QE_tau, self.dielectric_tensor, self.QE_zeu, self.QE_bg, self.QE_omega, self.QE_alat, 0, -1.0, self.nat) + + # Copy it back into the current_dynamical matrix + for i in range(self.nat): + for j in range(self.nat): + current_dyn.dynmats[iq][3*i: 3*i+3, 3*j: 3*j+3] = dynq[:,:, i, j] + + # Impose hermitianity + current_dyn.dynmats[iq][:,:] = 0.5 * (current_dyn.dynmats[iq] + np.conj(current_dyn.dynmats[iq].T)) + + t2 = time.time() + if self.verbose: + print("Time for the step {} / {}: {} s".format(iq+1, len(current_dyn.q_tot), t2 - t1)) + print("(The preparation of the dynq: {} s)".format(t3 - t1)) + print("NAT:", self.nat) + + + + time2 = time.time() + + if self.verbose: + print("Time to prepare the effective charges: {} s".format(time2 - time1)) + + # Get the dynamical matrix in the supercell + time3 = time.time() + + # Apply the acoustic sum rule (could be spoiled by the effective charges) + #iq_gamma = np.argmin(np.sum(np.array(current_dyn.q_tot)**2, axis = 1)) + #symmetries.CustomASR(current_dyn.dynmats[0]) + #current_dyn.Symmetrize() + + + if self.verbose: + print("Generating Real space force constant matrix...") + + # TODO: we could use the fft to speedup this + # fc_q = dyn.GetMatrixFFT() + # fc_real_space = np.conj(np.fft.fftn(np.conj(fc_q), axes = (0,1,2))) / np.prod(current_dyn.GetSupercell()) + # fc_real_space is already in tensor form (first three indices the R_2 components, or maybe -R_2) in crystalline coordinates) + # It must be just rearranged in the correct tensor + super_dyn = current_dyn.GenerateSupercellDyn(phonons.GetSupercell(), img_thr =1e-6) + time4 = time.time() + + if self.verbose: + print("Time to generate the real space force constant matrix: {} s".format(time4 - time3)) + print("TODO: the last time could be speedup with the FFT algorithm.") + + # Setup from the supercell dynamical matrix + self.SetupFromTensor(super_dyn.dynmats[0]) + + + def SetupFromTensor(self, tensor): + """ + SETUP FROM A TENSOR + =================== + + This module setup the tensor from a 3*natsc x 3*natsc matrix. + You should also pass the structure in the supercell to infer + which atom correspond to which one. + + NOTE: The first nat atoms of the superstructure must be a unit cell. + + Parameters + ---------- + - tensor : ndarray( size=(3*nat_sc, 3*nat_sc)) + The matrix to be converted in this Tensor2. + + """ + + nat = self.nat + nat_sc = self.supercell_structure.N_atoms + + # Check if the passed structure is a good superstructure + assert nat_sc % nat == 0, "Error, the given superstructure has a wrong number of atoms" + + natsc3, natsc3_ = np.shape(tensor) + + assert nat_sc == natsc3/3, "Error, the given tensor and superstructure are not compatible" + + assert natsc3 == natsc3_, "Error, the given tensor must be a square matrix" + + nat = self.unitcell_structure.N_atoms + nat_sc = nat * self.n_R + + for i_x in range(self.supercell_size[0]): + for i_y in range(self.supercell_size [1]): + for i_z in range(self.supercell_size[2]): + i_block = self.supercell_size[1] * self.supercell_size[2] * i_x + i_block += self.supercell_size[2] * i_y + i_block += i_z + self.x_r_vector2[:, i_block] = np.array([i_x, i_y, i_z]) + self.r_vector2[:, i_block] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, i_block]) + + for na1 in range(nat): + for na2 in range(nat): + # Get the atom in the supercell corresponding to the one in the unit cell + na2_vect = self.unitcell_structure.coords[na2, :] + self.r_vector2[:, i_block] + nat2_sc = np.argmin( [np.sum( (self.supercell_structure.coords[k, :] - na2_vect)**2) for k in range(nat_sc)]) + + self.tensor[i_block, 3*na1:3*na1+3, 3*na2: 3*na2+3] = tensor[3*na1 : 3*na1+3, 3*nat2_sc : 3*nat2_sc + 3] + + + def SetupFromFile(self, fname,file_format='Phonopy'): + """ + Setup the second order force constant form 2nd order tensor written in a file + + Warning about the D3Q format: + Coerently with the indexing of the 3rd order FCs, in the D3Q format with FC(s,t,R) + we refer to the FC between atoms (s,0) and (t,R). This is different from the format + used in the original d3q code by Lorenzo Paulatto, where the FC(s,t,R) refers to + (s,R) and (t,0), or ,equivalently, (s,0) and (t,-R) + + + Parameters + ---------- + fname : string + The file name + file_format : string + The format of the file + """ + + + if Settings.am_i_the_master(): + + if file_format == 'Phonopy': + + print(" ") + print(" FC2 Phonopy reading format: TODO" ) + print(" ") + exit() + + elif file_format == 'D3Q': + + print(" ") + print(" Reading FC2 from "+fname) + print(" (D3Q format) " ) + print(" ") + + first_nR_read = True + with open(fname, "r") as f: + # ============== Skip header, if present === + if len(f.readline().split()) ==4: + f.seek(0) + else: + f.seek(0) + while True: + if len(f.readline().split()) ==1: + break + f.readline() + # =========================================== + for nat1 in range(self.nat): + for nat2 in range(self.nat): + for alpha in range(3): + for beta in range(3): + [alpha_read,beta_read, + nat1_read, nat2_read]=[int(l)-1 for l in f.readline().split()] + + assert ([nat1,nat2,alpha,beta]==[nat1_read,nat2_read, + alpha_read,beta_read]) + + + nR_read=int(f.readline().split()[0]) + + if (first_nR_read) : + self.n_R=nR_read + self.tensor=np.zeros((self.n_R,3*self.nat,3*self.nat),dtype=np.float64) + self.x_r_vector2=np.zeros((3,self.n_R),dtype=np.int16) + first_nR_read = False + else : + assert ( nR_read == self.n_R ), " Format unknown - different blocks size " + + for iR in range(self.n_R): + + res=[l for l in f.readline().split()] + + [self.x_r_vector2[0, iR], + self.x_r_vector2[1, iR], + self.x_r_vector2[2, iR]]=[int(l) for l in res[:-1]] + + self.tensor[iR, 3*nat1 + alpha, 3*nat2 + beta]=np.double(res[-1]) + + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + + # Broadcast + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + + + def Center(self, nneigh=None, Far=2,tol=1.0e-5): + """ + CENTERING + ========= + + This subroutine will center the second order force constant. + This means that for each atomic indices in the tensor, it will be identified by the lowest + distance between the replica of the atoms. Moreover, in case of existance of other elements + not included in the original supercell with the same distance, the tensor will be equally subdivided between equivalent of atoms. + + This function should be called before performing the Fourier interpolation. + + + Optional Parameters + -------------------- + - nneigh [default= None]: integer + if different from None, it sets a maximum distance allowed to consider + equivalent atoms in the centering. + + nneigh > 0 + + for each atom the maximum distance is: + the average between the nneigh-th and the nneigh+1-th neighbor distance + (if, for the considered atom, the supercell is big enough to find up to + the nneigh+1-th neighbor distance, otherwise it is the maxmimum distance +10%) + + nneigh = 0 + + the maximum distance is the same for all the atoms, equal to + maximum of the minimum distance between equivalent atoms (+ 10%) + + nneigh < 0 + + the maximum distance is the same for all the atoms, equal to + maximum of the |nneigh|-th neighbor distances found for all the atoms + (if, for the considered atom, the supercell is big enough to find up to + the |nneigh|+1-th neighbor distance, otherwise it is the maxmimum distance +10%) + + - Far [default= 1]: integer + + In the centering, supercell equivalent atoms are considered within + -Far,+Far multiples of the super-lattice vectors + """ + # Check if the phonons is initialized TODO + #if np.max(np.abs(self.x_r_vector2)) == 0: + # raise ValueError("Error, Tensor object not initialized!") + + if Settings.am_i_the_master(): + + + t1 = time.time() + + if self.verbose: + print(" ") + print(" ======================= Centering 2nd FCs ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + # by default no maximum distances + dist_range=np.ones((self.nat))*np.infty + # + if nneigh!=None: + # ======================================================================= + uc_struc = self.unitcell_structure + sc_struc = self.supercell_structure + + # lattice vectors in Angstrom in columns + uc_lat = uc_struc.unit_cell.T + sc_lat = sc_struc.unit_cell.T + + # atomic positions in Angstrom in columns + uc_tau = uc_struc.coords.T + sc_tau = sc_struc.coords.T + + uc_nat = uc_tau.shape[1] + sc_nat = sc_tau.shape[1] + + # == calc dmin ================================================== + # calculate the distances between the atoms in the supercell replicas + + # array square distances between i-th and the j-th + # equivalent atoms of the supercell + d2s=np.empty(( (2*Far+1)**3 , sc_nat, sc_nat )) + + rvec_i=sc_tau + for j, (Ls,Lt,Lu) in enumerate( + itertools.product(range(-Far,Far+1),range(-Far,Far+1),range(-Far,Far+1))): + + rvec_j = sc_tau+np.dot(sc_lat,np.array([[Ls,Lt,Lu]]).T) + + d2s[j,:,:]=scipy.spatial.distance.cdist(rvec_i.T,rvec_j.T,"sqeuclidean") + + # minimum of the square distances + d2min=d2s.min(axis=0) + # minimum distance between two atoms in the supercell, + # considering also equivalent supercell images + dmin=np.sqrt(d2min) + # + if nneigh == 0: + dist_range=np.ones((self.nat))*np.amax(dmin)*1.1 + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to ") + print(" the max of the minimum distances between ") + print(" supercell equivalent atoms (+10%): {:8.5f} A".format(dist_range[0])) + print(" ") + + # to include all the equivalent atoms having the smallest distance + # between them. It does not correspond to taking dist_range=+infity + # because, with the centering, smallest triangles with equivalent points can be obtained + # by considering atoms not at the minimum distance between them: + # with dist_range=+infity these triangles are included, with dist_range=np.amax(dmin) + # they are not included (here I add the 10%) + + else: + #== max dist of the n-th neighbors ============================ + # For all the uc_nat atoms of unit cell (due to the lattice translation symmetry, + # I can limit the analysis to the atoms of a unit cell - not the supercell) + # I look for the nneigh-th neighbor distance and build the array dist_range + # + nn=np.abs(nneigh) + warned = False + for i in range(uc_nat): # for each unit cell atom + ds=dmin[i,:].tolist() # list of distances from other atoms + ds.sort() # list of distances from the i-th atom in increasing order + u=[] # same list, without repetitions + for j in ds: + for k in u: + if np.allclose(k,j): + break + else: + u.append(j) + # the list u of increasing distances for i-th atom has been completed + # try do consider the average of the nneigh-th and nneigh+1-th distance to nth_len + # if it fails, because there are not enough atoms to reach the n-th neighbors, + # then consider the maximum distance found (augmented of 10%) + try: + dist_range[i]=(.5*(u[nn]+u[nn+1])) + except IndexError: + if not warned: + print(" Warning: supercell too small to find {}-th neighbors for all the atoms ".format(nn+1)) + warned = True + dist_range[i]=1.1*max(u) + # + if nneigh < 0 : + # + dist_range=np.ones((self.nat))*np.amax(dist_range) + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to {:8.5f} A".format(dist_range[0])) + print(" ") + # + else : + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to:".format(dist_range[0])) + print(" ") + for i in range(self.nat): + print("{:8.5f} A for atom {}".format(dist_range[i],i+1)) + print(" ") + # + #============================================================== + # look for the minimum supercell + # + xRmin=np.min(self.x_r_vector2,1) + xRmax=np.max(self.x_r_vector2,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + + tensor= np.transpose(self.tensor,axes=[1,2,0]) + self.n_sup = np.prod(xRlen) + + alat=self.unitcell_structure.unit_cell + + weight,xR2 = secondorder.second_order_centering.analysis(Far,tol,dist_range,xRlen, + self.x_r_vector2, + alat, + self.tau, tensor,self.nat,self.n_R) + + + mask= weight >0 + + mask=np.repeat(mask[np.newaxis,...], (2*Far+1)*(2*Far+1), axis=0) + + xR2_reshaped=xR2[:,mask] + + xR2_unique = np.unique(xR2_reshaped,axis=1) + nblocks_old=self.n_R + # + self.n_R=xR2_unique.shape[1] + self.x_r_vector2 = xR2_unique + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + + centered=secondorder.second_order_centering.center(tensor,weight, + self.x_r_vector2,xR2, + Far,self.nat, + self.n_R,nblocks_old) + t2 = time.time() + + self.tensor = np.transpose(centered, axes=[2,0,1]) + + if self.verbose: + print(" Time elapsed for computing the centering: {} s".format( t2 - t1)) + print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + print(" ") + print(" ====================================================================") + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + + + + def Apply_ASR(self,PBC=False,power=0,maxiter=1000,threshold=1.0e-12): + """ + Apply_ASR + ========= + + This subroutine apply the ASR to the second order force constants iteratively. + For each iteration, the ASR is imposed on the second index + (any of the two indeces would be equivalent, apart from the subtleties that + would require the implementation for the first index, due to the representation chosen), + and, subsequently, the symmetry by permutation of the two indeces is imposed. + + + Optional Parameters + -------------------- + - PBC [default=False]: logical + + If True, periodic boundary conditions are considered. This is necessary, for example, + to apply this routine to non-centered tensors. + + - power [default=0]: float >= 0 + + The way the ASR is imposed on the second index: + phi(i,j,a,b)=phi(i,j,a,b)-[\sum_b phi(i,j,a,b)]* |phi(i,j,a,b)|^pow / [sum_b |phi(i,j,a,b)|^pow] + + - maxiter [default= 1000]: integer >= 0 + + n>0 Maximum number of iteration to reach the convergence. + n=0 Endless iteration + + If a file STOP is found, the iteration stops. + + - threshold [default= 1.0e-12]: float > 0 + + Threshold for the convergence. The convergence is on two values: the value of sum on the third index and the + variation of the phi after the imposition of the permutation symmetry (both divided by sum |phi|) + """ + + + if Settings.am_i_the_master(): + + + t1 = time.time() + + if self.verbose: + print(" ") + print(" ======================= ASR ========================== ") + print(" ") + + + + xRmin=np.min(self.x_r_vector2,1) + xRmax=np.max(self.x_r_vector2,1) + SClat=xRmax-xRmin+np.ones((3,),dtype=int) + + + tensor=np.transpose(self.tensor,axes=[1,2,0]) + + tensor_out=secondorder.second_order_asr.impose_asr(tensor, + self.x_r_vector2, + power,SClat, + PBC,threshold, + maxiter,self.verbose, + self.nat,self.n_R) + + self.tensor=np.transpose(tensor_out,axes=[2,0,1]) + + t2 = time.time() + + if self.verbose: + print(" ") + print(" Time elapsed for imposing ASR: {} s".format( t2 - t1)) + print(" ") + print(" ============================================================ ") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + + + + def WriteOnFile(self, fname,file_format='Phonopy'): + """ + WRITE ON FILE + ============= + + Save the tensor on a file. + This is usefull if you want to check if everything is working as you expect. + + The file format is the same as phono3py or D3Q + In the first line there is the total number of blocks. + Then for each block there is the lattice vector followed by the atomic index in the unit cell. + Then there is the tensor for each cartesian coordinate + + As follows: + + N_blocks + Block_index + R_x R_y R_z + at_1 at_2 + coord_1 coord_2 tensor_value + coord_1 coord_2 tensor_value + ... + + Warning about the D3Q format: + Coerently with the indexing of the 3rd order FCs, in the D3Q format with FC(s,t,R) + we refer to the FC between atoms (s,0) and (t,R). This is different from the format + used in the original d3q code by Lorenzo Paulatto, where the FC(s,t,R) refers to + (s,R) and (t,0), or ,equivalently, (s,0) and (t,-R) + + + Parameters + ---------- + fname : string + Path to the file on which you want to save the tensor. + """ + + if file_format == 'Phonopy': + + print(" ") + print(" Writing FC2 on "+fname ) + print(" (Phonopy format) ") + print(" ") + + with open(fname, "w") as f: + # Write the total number of blocks + f.write("{:>5}\n".format(self.n_R * self.nat**2)) + + i_block = 1 + for r_block in range(self.n_R): + for nat1 in range(self.nat): + for nat2 in range(self.nat): + # Write the info on the current block + f.write("{:d}\n".format(i_block)) + f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2[:, r_block]))) + f.write("{:>6d} {:>6d}\n".format(nat1 + 1, nat2+1)) + i_block += 1 + + # Write the tensor for the block + # For each cartesian coordinate + for x in range(3): + for y in range(3): + f.write("{:>2d} {:>2d} {:>20.10e}\n".format(x+1, y+1, self.tensor[r_block, 3*nat1 + x, 3*nat2 + y])) + + + elif file_format == 'D3Q': + + print(" ") + print(" Writing FC2 on "+fname ) + print(" (D3Q format) ") + print(" ") + + with open(fname, "w") as f: + #TODO Print header... + + for nat1 in range(self.nat): + for nat2 in range(self.nat): + for alpha in range(3): + for beta in range(3): + f.write("{:>6d} {:>6d} {:>6d} {:>6d} \n".format(alpha+1,beta+1,nat1+1, nat2+1)) + f.write("{:>5}\n".format(self.n_R)) + for r_block in range(self.n_R): + f.write("{:>6d} {:>6d} {:>6d} {:16.8e}\n".format(self.x_r_vector2[0, r_block],self.x_r_vector2[1, r_block],self.x_r_vector2[2, r_block], self.tensor[r_block, 3*nat1 + alpha, 3*nat2 + beta])) + + def Interpolate(self, q2, asr = False, verbose = False, asr_range = None, q_direct = None, lo_to_splitting = True): + """ + Perform the Fourier interpolation to obtain the force constant matrix at a given q + This subroutine automatically performs the ASR + + Parameters + ---------- + q2 : ndarray(size = 3) + The q vector in 2pi/A + asr : bool + If true, apply the acousitc sum rule + asr_range : float, optional + If it is given, then use a gaussian as a activation function + for the asr, with asr_range equal to sigma. + Otherwise, a sin(Nq)/(Nsin(q)) will be used, equal to apply the sum rule on a + grid. + verbose : bool + Print some debugging info + q_direct : ndarray(dtype = 3) + If q2 is gamma and effective charges are present, this vector is used + to pick the direction of the nonanalitical correction to apply. + If it is not initialized, a random versor will be chosen. + If it is the 0 vector, the nonanalitic correction at gamma will be avoided. + lo_to_splitting : bool + If True and the point is gamma, add the nonanalytic correction in a direction + given by q_direct + + Results + ------- + phi2 : ndarray(size = (3*nat, 3*nat), dtype = np.complex128) + The second order force constant at q2. Atomic indices runs over the unit cell + """ + + final_fc = np.zeros((3*self.nat, 3*self.nat), dtype = np.complex128) + + # Perform the fourier transform of the short range real space tensor. + for i in range(self.n_R): + arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i])) + phase = np.exp(np.complex128(-1j) * arg) + final_fc += phase * self.tensor[i, :, :] + + # If effective charges are present, then add the nonanalitic part + if self.effective_charges is not None: + dynq = np.zeros((3,3,self.nat, self.nat), dtype = np.complex, order = "F") + for i in range(self.nat): + for j in range(self.nat): + dynq[:,:, i, j] = final_fc[3*i : 3*i+3, 3*j:3*j+3] + + # Add the nonanalitic part back + QE_q = -q2 * self.QE_alat / Units.A_TO_BOHR + symph.rgd_blk(0, 0, 0, dynq, QE_q, self.QE_tau, self.dielectric_tensor, self.QE_zeu, self.QE_bg, self.QE_omega, self.QE_alat, 0, +1.0, self.nat) + + # Check if the vector is gamma + if np.max(np.abs(q2)) < 1e-12: + q_vect = np.zeros(3, dtype = np.double) + compute_nonanal = lo_to_splitting + if q_direct is not None: + # the - to take into account the difference between QE convension and our + if np.linalg.norm(q_direct) < 1e-8: + compute_nonanal = False + else: + q_vect[:] = -q_direct / np.sqrt(q_direct.dot(q_direct)) + else: + q_vect[:] = np.random.normal(size = 3) + q_vect /= np.sqrt(q_vect.dot(q_vect)) + + # Apply the nonanal contribution at gamma + if compute_nonanal: + QE_itau = np.arange(self.nat) + 1 + symph.nonanal(QE_itau, self.dielectric_tensor, q_vect, self.QE_zeu, self.QE_omega, dynq, self.nat, self.nat) + + # Copy in the final fc the result + for i in range(self.nat): + for j in range(self.nat): + final_fc[3*i : 3*i+3, 3*j:3*j+3] = dynq[:,:, i, j] + + # Apply the acoustic sum rule + if asr: + # Get the reciprocal lattice vectors + bg = Methods.get_reciprocal_vectors(self.unitcell_structure.unit_cell) / (2*np.pi) + nat = self.unitcell_structure.N_atoms + + # Create the ASR projector + Q_proj = np.zeros((3*nat, 3*nat), dtype = np.double) + for i in range(3): + v1 = np.zeros(nat*3, dtype = np.double) + v1[3*np.arange(nat) + i] = 1 + v1 /= np.sqrt(v1.dot(v1)) + + Q_proj += np.outer(v1,v1) + + # Lets pick the minimum and maximum lattice vectors + xRmin = np.min(self.x_r_vector2, axis = 1) + xRmax = np.max(self.x_r_vector2, axis = 1) + + # Now we obtain the total cell size that contains the ASR + N_i = xRmax - xRmin + np.ones((3,), dtype = np.intc) + + if verbose: + print("Supercell WS:", N_i) + + N_i = np.array([2*x + 1 for x in self.supercell_size], dtype = np.intc) + + # We check if they are even, in that case we add 1 + # f(q) is real only if we sum on odd cells + for ik in range(3): + if (N_i[ik] % 2 == 0): + N_i[ik] += 1 + + if verbose: + print("Supercell all odd:", N_i) + + + # We compute the f(q) function + at = self.unitcell_structure.unit_cell + + __tol__ = 1e-8 + f_q2i = np.ones(3, dtype = np.double) + + # We use mask to avoid division by 0, + # As we know that the 0/0 limit is 1 in this case + if asr_range is None: + mask2 = np.abs(np.sin(at.dot(q2) * np.pi)) > __tol__ + f_q2i[mask2] = np.sin(N_i[mask2] * at[mask2,:].dot(q2) * np.pi) / (N_i[mask2] * np.sin(at[mask2,:].dot(q2) * np.pi)) + f_q2 = np.prod(f_q2i) + else: + closest_q = Methods.get_closest_vector(bg * 2 * np.pi, q2) + f_q2 = np.exp( - np.linalg.norm(closest_q)**2 / (2 * asr_range**2)) + + if verbose: + print("The fq:") + print("{:16.8f} {:16.8f}".format(f_q2, f_q2)) + print("q1 = ", Methods.covariant_coordinates(bg * 2 * np.pi, -q2)) + print("q2 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q2)) + + # Now we can impose the acustic sum rule + final_fc -= np.einsum("ai, bi-> ab", final_fc, Q_proj) * f_q2 + final_fc -= np.einsum("ib, ai-> ab", final_fc, Q_proj) * f_q2 + + + return final_fc + + + + + # def GenerateSupercellTensor(self, supercell): + # """ + # GENERATE SUPERCELL TENSOR + # ========================= + + # This function returns a tensor defined in the supercell + # filling to zero all the elemets that have a minimum distance + # greater than the one defined in the current tensor. + # This is the key to interpolate. + + # The supercell atoms are defined using the generate_supercell + # method from the self.structure, so that is the link + # between indices of the returned tensor and atoms in the supercell. + + # Parameters + # ---------- + # - supercell : (nx, ny, nz) + # The dimension of the supercell in which + # you want to compute the supercell tensor + + # Results + # ------- + # - tensor : ndarray(size = ( 3*natsc, 3*natsc)) + # A tensor defined in the given supercell. + # """ + + # # TODO: ADD THE MULTIPLICITY COUNT ON THE SUPERCELL + + # super_structure, itau = self.structure.generate_supercell(supercell, get_itau = True) + + # nat_sc = super_structure.N_atoms + # new_tensor = np.zeros((3 * nat_sc, 3*nat_sc), dtype = np.double) + + # print("Unit cell coordinates:") + # print("\n".join(["{:3d}) {}".format(i, self.structure.coords[i, :]) for i in range(self.structure.N_atoms)])) + # print("Supercell coordinates:") + # print("\n".join(["{:3d}) {}".format(i, super_structure.coords[i, :]) for i in range(super_structure.N_atoms)])) + + + + # nat, nat_sc_old, _ = np.shape(self.r_vectors) + + # for i in range(nat_sc): + # i_cell = itau[i] + # for j in range(nat_sc): + + # r_vector = super_structure.coords[i,:] - super_structure.coords[j,:] + # r_vector = Methods.get_closest_vector(super_structure.unit_cell, r_vector) + + # # Now average all the values that + # # share the same r vector + # #dist_v = self.r_vectors[i_cell, :,:] - np.tile(new_r_vector, (nat_sc_old, 1)) + # #mask = [Methods.get_min_dist_into_cell(super_structure.unit_cell, dist_v[k, :], np.zeros(3)) < 1e-5 for k in range(nat_sc_old)] + # #mask = np.array(mask) + + # mask = Methods.get_equivalent_vectors(super_structure.unit_cell, self.r_vectors[i_cell, :, :], r_vector) + + # if i == 4 and j == 11: + # print("i = {}, j = {}".format(i, j)) + # print("r vector = {}".format(r_vector)) + # print("mask = {}".format(mask)) + + # # Apply the tensor + # n_elements1 = np.sum(mask.astype(int)) + # n_elements2 = 0 + + # # if n_elements1 == 0: + # # print("ZERO:") + # # print("itau[{}] = {}".format(i, i_cell)) + # # print("r to find:", new_r_vector) + # # print("r vectors:") + # # for k in range(nat_sc_old): + # # print("{}) {:12.6f} {:12.6f} {:12.6f}".format(k+1, *list(self.r_vectors[i_cell, k, :]))) + # # print() + # if n_elements1 > 0: + # #print("Apply element {} {} | n = {}".format(i, j, n_elements1)) + # tens = np.sum(self.tensor[i_cell, mask, :, :], axis = 0) / n_elements1 + # #print(tens) + # new_tensor[3*i: 3*i+3, 3*j:3*j+3] = tens + + # # NOTE: Here maybe a problem arising from the + # # double transpose inside the same unit cell + # # If the share a -1 with the vector then we found the transposed element + # if n_elements1 == 0: + # #dist_v2 = self.r_vectors[i_cell, :,:] + np.tile(new_r_vector, (nat_sc_old, 1)) + # mask2 = Methods.get_equivalent_vectors(super_structure.unit_cell, self.r_vectors[i_cell, :, :], -r_vector) + # #mask2 = [Methods.get_min_dist_into_cell(super_structure.unit_cell, dist_v2[k, :], np.zeros(3)) < 1e-5 for k in range(nat_sc_old)] + # #mask2 = np.array(mask2) + # n_elements2 = np.sum(mask2.astype(int)) + # if n_elements2 > 0: + # tens = np.sum(self.tensor[i_cell, mask, :, :], axis = 0) / n_elements2 + # new_tensor[3*j:3*j+3, 3*i:3*i+3] = tens + + + # #print("Elements {}, {} | r_vector = {} | n1 = {} | n2 = {}".format(i+1, j+1, r_vector, n_elements1, n_elements2)) + + # return new_tensor + + def GeneratePhonons(self, supercell, asr = False, lo_to_splitting = False): + """ + GENERATE PHONONS + ================ + + Interpolate the Tensor2 into a supercell and then + transform back into the dynamical matrix with the correct q. + + It might be that the new dynamical matrix should be symmetrized. + + NOTE: The Interpolate method uses a different convension of the Fourier Transform. + For this reason, this method returns the Complex Cojugate of the matrices interpolated at the q points. + This has been fixed, by manually computing the complex conjugate before the return + + + Parameters + ---------- + - supercell : (nx, ny, nz) + The supercell of the dynamical matrix + - asr : bool + If true, the ASR is imposed during the interpolation. + This is the best way to correct the modes even close to gamma + - lo_to_splitting : bool + If true, the phonons at gamma will have the LO-TO splitting + from a random direction. + Note, this will break symmetrization. + + Results + ------- + - dynmat : Phonons.Phonons() + The dynamical matrix interpolated into the new supercell. + It is defined in the unit cell. + """ + + # Prepare the phonons for this supercell + dynmat = Phonons.Phonons(self.unitcell_structure) + + # Prepare the q_points + dynmat.q_tot = symmetries.GetQGrid(self.unitcell_structure.unit_cell, supercell) + q_vectors = [x.copy() for x in dynmat.q_tot] + dynmat.q_stars = [q_vectors] + dynmat.dynmats = [] + + # Interpolate over the q points + for i, q_vector in enumerate(q_vectors): + q_direction = None + if lo_to_splitting: + q_direction = np.random.normal(size = 3) + q_direction /= np.linalg.norm(q_direction) + + dynq = self.Interpolate(-q_vector, asr = asr, q_direct= q_direction, lo_to_splitting=lo_to_splitting) + dynmat.dynmats.append(dynq) + + # Adjust the q star according to symmetries + dynmat.AdjustQStar() + + return dynmat + + + + def GetRDecay(self): + """ + Get a plot of the R decay. + + For each element of the block, plots the maximum intensity in the distance between the data + """ + + r_total = [] + max_intensity = [] + + for i_R in range(self.n_R): + # Get the distance for each atomic couple in the block + for at_1 in range(self.nat): + for at_2 in range(self.nat): + r_dist = self.r_vector2[:, i_R] + self.tau[at_2,:] - self.tau[at_1, :] + + tensor = self.tensor[i_R, 3*at_1 : 3*at_1 + 3, 3*at_2: 3*at_2 + 3] + intensity = np.sqrt(np.trace(tensor.dot(tensor.T))) + + # Skip zero values + if intensity < 1e-10: + continue + + r_mod = np.sqrt(r_dist.dot(r_dist)) + + if len(r_total) == 0: + r_total.append(r_mod) + max_intensity.append(intensity) + continue + + # Check if another vector with the same distance has already been found + distances = np.abs(r_mod - np.array(r_total)) + + if np.min(distances) < 1e-7: + # Compute the tensor intensity + + index = np.argmin(distances) + if max_intensity[index] < intensity: + max_intensity[index] = intensity + else: + r_total.append(r_mod) + max_intensity.append(intensity) + + + r_total = np.array(r_total) + max_intensity = np.array(max_intensity) + + # Return the value sorted by R distance + sort_mask = np.argsort(r_total) + return r_total[sort_mask], max_intensity[sort_mask] + + + # def ApplyKaiserWindow(self, rmax, beta=14, N_sampling = 1000): + # """ + # Apply a Kaiser-Bessel window to the signal. + # This is the best tool to perform the interpolation. + + # Each element of the tensor is multiplied by the kaiser + # function with the given parameters. + # The kaiser function is computed on the corresponding value of distance + + # Parameters + # ---------- + # - rmax : float + # The maximum distance on which the window is defined. + # All that is outside rmax is setted to 0 + # - beta : float + # The shape of the Kaiser window. + # For beta = 0 the window is a rectangular function, + # for beta = 14 it resample a gaussian. The higher beta, the + # narrower the window. + # - N_sampling : int + # The sampling of the kaiser window. + # """ + + # kaiser_data = scipy.signal.kaiser(N_sampling, beta) + + # # Build the kaiser function + # r_value = np.linspace(-rmax, rmax, N_sampling) + # kaiser_function = scipy.interpolate.interp1d(r_value, kaiser_data, bounds_error=False, fill_value= 0) + + # # Build the kaiser window + # kaiser_window = kaiser_function(self.distances) + + # nat, nat_sc = np.shape(self.distances) + + # # Apply the kaiser window on the tensor + # for i in range(nat): + # for j in range(nat_sc): + # self.tensor[i, j, :, :] *= kaiser_window[i,j] + + +# Third order force constant tensor +class Tensor3(): + """ + This class defines the 3rank tensors, like 3rd force constants. + """ + + def __init__(self, unitcell_structure, supercell_structure, supercell_size): + #GenericTensor.__init__(self, *args, **kwargs) + + n_sup = np.prod(supercell_size) + nat = unitcell_structure.N_atoms + + nat_sc= n_sup * nat + + self.n_sup = n_sup + self.n_R = n_sup**2 + n_R = self.n_R + self.nat = nat + self.tensor = np.zeros( (n_R, 3*nat, 3*nat, 3*nat), dtype = np.double) + + self.supercell_size = supercell_size + + + # Cartesian lattice vectors + self.r_vector2 = np.zeros((3, n_R), dtype = np.double, order = "F") + self.r_vector3 = np.zeros((3, n_R), dtype = np.double, order = "F") + + # Crystalline lattice vectors + self.x_r_vector2 = np.zeros((3, n_R), dtype = np.intc, order = "F") + self.x_r_vector3 = np.zeros((3, n_R), dtype = np.intc, order = "F") + + self.itau = supercell_structure.get_itau(unitcell_structure) - 1 + + self.tau=unitcell_structure.coords + + + self.unitcell_structure = unitcell_structure + self.supercell_structure = supercell_structure + + self.verbose = True + + def SetupFromTensor(self, tensor=None): + """ + Setup the third order force constant form 3rd order tensor defined in the supercell + + + Parameters + ---------- + unitcell_structure : Structure() + The structure in the unit cell + supercell_structure : Structure() + The supercell structure on which the tensor has been computed + supercell_size : truple + The number of supercell along each lattice vector + tensor : ndarray(size =(3*nat_sc, 3*nat_sc, 3*nat_sc, dtype = np.double) + The third order tensor + """ + + + n_sup = np.prod(self.supercell_size) + nat = self.unitcell_structure.N_atoms + supercell_size = self.supercell_size + + + nat_sc= n_sup * nat + n_R = self.n_R + + supercell_structure = self.supercell_structure + unitcell_structure = self.unitcell_structure + + + + for index_cell2 in range(n_sup): + n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=[0,0,0], + v_len=supercell_size) + for index_cell3 in range(n_sup): + n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=[0,0,0], + v_len=supercell_size) + # + total_index_cell = index_cell3 + n_sup * index_cell2 + # + self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) + self.r_vector2[:, total_index_cell] = unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:,total_index_cell]) + self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 + self.r_vector3[:, total_index_cell] = unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) + + for na1 in range(nat): + # + for na2 in range(nat): + # Get the atom in the supercell corresponding to the one in the unit cell + na2_vect = unitcell_structure.coords[na2, :] + self.r_vector2[:, total_index_cell] + nat2_sc = np.argmin( [np.sum( (supercell_structure.coords[k, :] - na2_vect)**2) for k in range(nat_sc)]) + # + for na3 in range(nat): + # Get the atom in the supercell corresponding to the one in the unit cell + na3_vect = unitcell_structure.coords[na3, :] + self.r_vector3[:, total_index_cell] + nat3_sc = np.argmin( [np.sum( (supercell_structure.coords[k, :] - na3_vect)**2) for k in range(nat_sc)]) + # + self.tensor[total_index_cell, + 3*na1 : 3*na1+3, + 3*na2 : 3*na2+3, + 3*na3 : 3*na3+3] = tensor[3*na1 : 3*na1 +3, + 3*nat2_sc : 3*nat2_sc + 3, + 3*nat3_sc : 3*nat3_sc + 3] + + def SetupFromFile(self, fname,file_format='Phonopy'): + """ + Setup the third order force constant form 3rd order tensor written in a file + + + Parameters + ---------- + fname : string + The file name + file_format : string + The format of the file + """ + if Settings.am_i_the_master(): + + if file_format == 'Phonopy': + + print(" ") + print(" Reading FC3 from " + fname) + print(" (Phonopy format) " ) + print(" ") + + f = open(fname, "r") + lines = [l.strip() for l in f.readlines()] + f.close() + + n_blocks = int(lines[0]) + self.n_R = n_blocks/self.nat**3 + + + id_block = 0 + total_lat_vec = 0 + lat_vect_2 = 0 + lat_vect_3 = 0 + nat1 = 0 + nat2 = 0 + nat3 = 0 + reading_lat2 = False + reading_lat3 = False + reading_atoms = False + for i, line in enumerate(lines): + if i == 0: + continue + + data = line.split() + if len(data) == 0: + continue + + if len(data) == 1: + id_block = int(data[0]) - 1 + total_lat_vec = id_block // self.nat**3 + nat_id = id_block % self.nat**3 + reading_lat2 = True + continue + + if reading_lat2: + self.r_vector2[:, total_lat_vec] = [float(x) for x in data] + self.x_r_vector2[:, total_lat_vec] = Methods.covariant_coordinates(self.unitcell_structure.unit_cell, self.r_vector2[:, total_lat_vec]) + reading_lat2 = False + reading_lat3 = True + elif reading_lat3: + self.r_vector3[:, total_lat_vec] = [float(x) for x in data] + self.x_r_vector3[:, total_lat_vec] = Methods.covariant_coordinates(self.unitcell_structure.unit_cell, self.r_vector3[:, total_lat_vec]) + reading_lat3 = False + reading_atoms = True + elif reading_atoms: + nat1, nat2, nat3 = [int(x) - 1 for x in data] + reading_atoms = False + print("Reading the vectors: ", self.r_vector2[:, total_lat_vec], self.r_vector3[:, total_lat_vec], total_lat_vec) + + if len(data) == 4: + xx, yy, zz = [int(x)-1 for x in data[:3]] + + self.tensor[total_lat_vec, 3*nat1+xx, 3*nat2+yy, 3*nat3+zz] = np.double(data[-1]) + + elif file_format == 'D3Q': + + print(" ") + print(" Reading FC3 from "+ fname ) + print(" (D3Q format) " ) + print(" ") + + first_nR_read = True + with open(fname, "r") as f: + # ============ Skip the header, if present ==================== + if len(f.readline().split()) ==6: + f.seek(0) + else: + f.seek(0) + while True: + if len(f.readline().split()) ==1: + break + f.readline() + # ============================================================= + for nat1 in range(self.nat): + for nat2 in range(self.nat): + for nat3 in range(self.nat): + for alpha in range(3): + for beta in range(3): + for gamma in range(3): + [alpha_read,beta_read, + gamma_read,nat1_read, + nat2_read, nat3_read]=[int(l)-1 for l in f.readline().split()] + + assert ([nat1,nat2,nat3,alpha,beta,gamma]==[nat1_read,nat2_read,nat3_read, + alpha_read,beta_read,gamma_read]) + + nR_read=int(f.readline().split()[0]) + + if (first_nR_read) : + self.n_R=nR_read + self.tensor=np.zeros((self.n_R,3*self.nat,3*self.nat,3*self.nat),dtype=np.float64) + self.x_r_vector2=np.zeros((3,self.n_R),dtype=np.int16) + self.x_r_vector3=np.zeros((3,self.n_R),dtype=np.int16) + first_nR_read = False + else : + assert ( nR_read == self.n_R ), " Format unknown - different blocks size " + + for iR in range(self.n_R): + + res=[l for l in f.readline().split()] + + [self.x_r_vector2[0, iR], + self.x_r_vector2[1, iR], + self.x_r_vector2[2, iR], + self.x_r_vector3[0, iR], + self.x_r_vector3[1, iR], + self.x_r_vector3[2, iR]]=[int(l) for l in res[:6]] + + self.tensor[iR, 3*nat1 + alpha, 3*nat2 + beta, 3*nat3 + gamma]=np.double(res[6]) + + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) + + # Broadcast + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + + + def WriteOnFile(self,fname,file_format='Phonopy'): + """ + WRITE ON FILE + ============= + + Save the tensor on a file. + + The file format is the same as phono3py or D3Q + + Parameters + ---------- + fname : string + Path to the file in which you want to save the real space force constant tensor. + file_format: string + It could be either 'phonopy' or 'd3q' (not case sensitive) + 'd3q' is the file format used in the thermal.x espresso package, while phonopy is the one + used in phono3py. + """ + + if file_format.lower() == 'phonopy': + + print(" ") + print(" Writing FC3 on "+ fname) + print(" (Phonopy format) " ) + print(" ") + + with open(fname, "w") as f: + + f.write("{:>5}\n".format(self.n_R * self.nat**3)) + + + i_block = 1 + for r_block in range(self.n_R): + for nat1 in range(self.nat): + for nat2 in range(self.nat): + for nat3 in range(self.nat): + f.write("{:d}\n".format(i_block)) + f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2[:, r_block]))) + f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector3[:, r_block]))) + f.write("{:>6d} {:>6d} {:>6d}\n".format(nat1+1, nat2+1, nat3+1)) + i_block += 1 + # + for xyz in range(27): + z = xyz % 3 + y = (xyz %9)//3 + x = xyz // 9 + f.write("{:>2d} {:>2d} {:>2d} {:>20.10e}\n".format(x+1,y+1,z+1, self.tensor[r_block, 3*nat1 + x, 3*nat2 + y, 3*nat3 + z])) + + + elif file_format.upper() == 'D3Q': + + print(" ") + print(" Writing FC3 on "+ fname) + print(" (D3Q format) " ) + print(" ") + + with open(fname, "w") as f: + #TODD Print header... + + for nat1 in range(self.nat): + for nat2 in range(self.nat): + for nat3 in range(self.nat): + for alpha in range(3): + for beta in range(3): + for gamma in range(3): + f.write("{:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:>6d}\n".format(alpha+1,beta+1,gamma+1,nat1+1, nat2+1, nat3+1)) + f.write("{:>5}\n".format(self.n_R)) + for r_block in range(self.n_R): + f.write("{:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:16.8e}\n".format(self.x_r_vector2[0, r_block],self.x_r_vector2[1, r_block],self.x_r_vector2[2, r_block],self.x_r_vector3[0, r_block],self.x_r_vector3[1, r_block],self.x_r_vector3[2, r_block], self.tensor[r_block, 3*nat1 + alpha, 3*nat2 + beta, 3*nat3 + gamma])) + + + def Center(self, nneigh=None, Far=2,tol=1.0e-5): + """ + CENTERING + ========= + + This subroutine will center the third order force constant. + This means that for each atomic indices in the tensor, it will be identified by the lowest + perimeter between the replica of the atoms. Moreover, in case of existance of other elements + not included in the original supercell with the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + This function should be called before performing the Fourier interpolation. + + + Optional Parameters + -------------------- + - nneigh [default= None]: integer + if different from None, it sets a maximum distance allowed to consider + equivalent atoms in the centering. + + nneigh > 0 + + for each atom the maximum distance is: + the average between the nneigh-th and the nneigh+1-th neighbor distance + (if, for the considered atom, the supercell is big enough to find up to + the nneigh+1-th neighbor distance, otherwise it is the maxmimum distance +10%) + + nneigh = 0 + + the maximum distance is the same for all the atoms, equal to + maximum of the minimum distance between equivalent atoms (+ 10%) + + nneigh < 0 + + the maximum distance is the same for all the atoms, equal to + maximum of the |nneigh|-th neighbor distances found for all the atoms + (if, for the considered atom, the supercell is big enough to find up to + the |nneigh|+1-th neighbor distance, otherwise it is the maxmimum distance +10%) + + - Far [default= 1]: integer + + In the centering, supercell equivalent atoms are considered within + -Far,+Far multiples of the super-lattice vectors + """ + + + + + if Settings.am_i_the_master(): + + + t1 = time.time() + + if self.verbose: + print(" ") + print(" ======================= Centering 3rd FCs ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + # by default no maximum distances + dist_range=np.ones((self.nat))*np.infty + # + if nneigh!=None: + # ======================================================================= + uc_struc = self.unitcell_structure + sc_struc = self.supercell_structure + + # lattice vectors in Angstrom in columns + uc_lat = uc_struc.unit_cell.T + sc_lat = sc_struc.unit_cell.T + + # atomic positions in Angstrom in columns + uc_tau = uc_struc.coords.T + sc_tau = sc_struc.coords.T + + uc_nat = uc_tau.shape[1] + sc_nat = sc_tau.shape[1] + + # == calc dmin ================================================== + # calculate the distances between the atoms in the supercell replicas + + # array square distances between i-th and the j-th + # equivalent atoms of the supercell + d2s=np.empty(( (2*Far+1)**3 , sc_nat, sc_nat )) + + rvec_i=sc_tau + for j, (Ls,Lt,Lu) in enumerate( + itertools.product(range(-Far,Far+1),range(-Far,Far+1),range(-Far,Far+1))): + + rvec_j = sc_tau+np.dot(sc_lat,np.array([[Ls,Lt,Lu]]).T) + + d2s[j,:,:]=scipy.spatial.distance.cdist(rvec_i.T,rvec_j.T,"sqeuclidean") + + # minimum of the square distances + d2min=d2s.min(axis=0) + # minimum distance between two atoms in the supercell, + # considering also equivalent supercell images + dmin=np.sqrt(d2min) + # + if nneigh == 0: + dist_range=np.ones((self.nat))*np.amax(dmin)*1.1 + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to ") + print(" the max of the minimum distances between ") + print(" supercell equivalent atoms (+10%): {:8.5f} A".format(dist_range[0])) + print(" ") + + # to include all the equivalent atoms having the smallest distance + # between them. It does not correspond to taking dist_range=+infity + # because, with the centering, smallest triangles with equivalent points can be obtained + # by considering atoms not at the minimum distance between them: + # with dist_range=+infity these triangles are included, with dist_range=np.amax(dmin) + # they are not included (here I add the 10%) + + else: + #== max dist of the n-th neighbors ============================ + # For all the uc_nat atoms of unit cell (due to the lattice translation symmetry, + # I can limit the analysis to the atoms of a unit cell - not the supercell) + # I look for the nneigh-th neighbor distance and build the array dist_range + # + nn=np.abs(nneigh) + warned = False + for i in range(uc_nat): # for each unit cell atom + ds=dmin[i,:].tolist() # list of distances from other atoms + ds.sort() # list of distances from the i-th atom in increasing order + u=[] # same list, without repetitions + for j in ds: + for k in u: + if np.allclose(k,j): + break + else: + u.append(j) + # the list u of increasing distances for i-th atom has been completed + # try do consider the average of the nneigh-th and nneigh+1-th distance to nth_len + # if it fails, because there are not enough atoms to reach the n-th neighbors, + # then consider the maximum distance found (augmented of 10%) + try: + dist_range[i]=(.5*(u[nn]+u[nn+1])) + except IndexError: + if not warned: + print(" Warning: supercell too small to find {}-th neighbors for all the atoms ".format(nn+1)) + warned = True + dist_range[i]=1.1*max(u) + # + if nneigh < 0 : + # + dist_range=np.ones((self.nat))*np.amax(dist_range) + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to {:8.5f} A".format(dist_range[0])) + print(" ") + # + else : + if self.verbose: + print(" ") + print(" Maximum distance allowed set equal to:".format(dist_range[0])) + print(" ") + for i in range(self.nat): + print("{:8.5f} A for atom {}".format(dist_range[i],i+1)) + print(" ") + # + #============================================================== + # look for the minimum supercell + # + xRmin=np.min(self.x_r_vector3,1) + xRmax=np.max(self.x_r_vector3,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + + tensor= np.transpose(self.tensor,axes=[1,2,3,0]) + self.n_sup = np.prod(xRlen) + + # to the Fortran routine the xR3 (faster than xR2) goes on the rightmost place + # which is the place after the reshape in python + + alat=self.unitcell_structure.unit_cell + + weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far,tol,dist_range,xRlen, + self.x_r_vector2,self.x_r_vector3, + alat, + self.tau, tensor,self.nat,self.n_R) + + + mask= weight >0 + mask=np.repeat(mask[np.newaxis,...], (2*Far+1)*(2*Far+1)*(2*Far+1), axis=0) + + xR2_reshaped=xR2[:,mask] + xR3_reshaped=xR3[:,mask] + xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) + nblocks_old=self.n_R + # + self.n_R=xR23.shape[1] + self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) + + centered=thirdorder.third_order_centering.center(tensor,weight, + self.x_r_vector2,xR2, + self.x_r_vector3,xR3, + Far,self.nat, + self.n_R,nblocks_old) + t2 = time.time() + + self.tensor = np.transpose(centered, axes=[3,0,1,2]) + + if self.verbose: + print(" Time elapsed for computing the centering: {} s".format( t2 - t1)) + print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + print(" ") + print(" ====================================================================") + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + + + def Apply_ASR(self,PBC=False,power=0,maxiter=1000,threshold=1.0e-12): + """ + Apply_ASR + ========= + + This subroutine apply the ASR to the third order force constants iteratively. + For each iteration, the ASR is imposed on the third index + (any of the three indeces would be equivalent, apart from the subtleties that + would require the implementation for the first index, due to the representation chosen), + and, subsequently, the symmetry by permutation of the three indeces is imposed. + + + Optional Parameters + -------------------- + - PBC [default=False]: logical + + If True, periodic boundary conditions are considered. This is necessary, for example, + to apply this routine to non-centered tensors. + + - power [default=0]: float >= 0 + + The way the ASR is imposed on the third index: + phi(i,j,k,abc)=phi(i,j,k,a,b,c)-[\sum_c phi(i,j,k,a,b,c)]* |phi(i,j,k,a,b,c)|^pow / [sum_c |phi(i,j,k,a,b,c)|^pow] + + - maxiter [default= 1000]: integer >= 0 + + n>0 Maximum number of iteration to reach the convergence. + n=0 Endless iteration + + If a file STOP is found, the iteration stops. + + - threshold [default= 1.0e-12]: float > 0 + + Threshold for the convergence. The convergence is on two values: the value of sum on the third index and the + variation of the phi after the imposition of the permutation symmetry (both divided by sum |phi|) + """ + + + if Settings.am_i_the_master(): + + + t1 = time.time() + + if self.verbose: + print(" ") + print(" ======================= ASR ========================== ") + print(" ") + + + xR23 = np.vstack((self.x_r_vector2,self.x_r_vector3)) + xR2list=np.unique(self.x_r_vector2,axis=1) + totnum_R2=xR2list.shape[1] + + xRmin=np.min(self.x_r_vector3,1) + xRmax=np.max(self.x_r_vector3,1) + SClat=xRmax-xRmin+np.ones((3,),dtype=int) + + + tensor=np.transpose(self.tensor,axes=[1,2,3,0]) + + tensor_out=thirdorder.third_order_asr.impose_asr(tensor,xR23, + self.x_r_vector2,xR2list, + power,SClat, + PBC,threshold, + maxiter,self.verbose, + totnum_R2,self.nat,self.n_R) + + self.tensor=np.transpose(tensor_out,axes=[3,0,1,2]) + + t2 = time.time() + + if self.verbose: + print(" ") + print(" Time elapsed for imposing ASR: {} s".format( t2 - t1)) + print(" ") + print(" ============================================================ ") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + + + +#============================================================================================================ + + + def Interpolate(self, q2, q3, asr = True, verbose = False): + """ + Interpolate the third order to the q2 and q3 points + + Parameters + ---------- + q2, q3 : ndarray(3) + The q points + asr : bool + If true, the Acoustic sum rule is applied directly in q space + verbose : bool + If true print debugging info + + Results + ------- + Phi3 : ndarray(size = (3*nat, 3*nat, 3*nat)) + The third order force constant in the defined q points. + atomic indices runs over the unit cell + """ + + final_fc = np.zeros((3*self.nat, 3*self.nat, 3*self.nat), + dtype = np.complex128) + for i in range(self.n_R): + arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i]) + + q3.dot(self.r_vector3[:, i])) + phase = np.exp(np.complex128(-1j) * arg) + final_fc += phase * self.tensor[i, :, :, :] + + # Apply the acoustic sum rule if necessary + if asr: + # Get the reciprocal lattice vectors + bg = Methods.get_reciprocal_vectors(self.unitcell_structure.unit_cell) / (2* np.pi) + + nat = self.unitcell_structure.N_atoms + + # Create the projector on the orthonormal space to the ASR + Q_proj = np.zeros((3*nat, 3*nat), dtype = np.double) + for i in range(3): + v1 = np.zeros(nat*3, dtype = np.double) + v1[3*np.arange(nat) + i] = 1 + v1 /= np.sqrt(v1.dot(v1)) + + Q_proj += np.outer(v1, v1) + + # Get the N_i in the centered cell + # First we get the list of vectors in crystal coordinates + xR_list=np.unique(self.x_r_vector3, axis = 1) + + # We pick the minimum and maximum values of the lattice vectors + # in crystal coordinates + xRmin=np.min(xR_list, axis = 1) + xRmax=np.max(xR_list, axis = 1) + + # Now we can obtain the dimension of the cell along each direction. + N_i = xRmax-xRmin+np.ones((3,),dtype=int) + + if verbose: + print("Centered supercell: ", N_i) + + # We check if they are even, in that case we add 1 + # f(q) is real only if we sum on odd cells + for ik in range(3): + if (N_i[ik] % 2 == 0): + N_i[ik] += 1 + + if verbose: + print("Supercell all odd:", N_i) + + + # We compute the f(q) function + at = self.unitcell_structure.unit_cell + + __tol__ = 1e-8 + f_q3i = np.ones(3, dtype = np.double) + f_q2i = np.ones(3, dtype = np.double) + f_q1i = np.ones(3, dtype = np.double) + + # We use mask to avoid division by 0, + # As we know that the 0/0 limit is 1 in this case + + mask3 = np.abs(np.sin(at.dot(q3) * np.pi)) > __tol__ + f_q3i[mask3] = np.sin(N_i[mask3] * at[mask3,:].dot(q3) * np.pi) / (N_i[mask3] * np.sin(at[mask3,:].dot(q3) * np.pi)) + f_q3 = np.prod(f_q3i) + + mask2 = np.abs(np.sin(at.dot(q2) * np.pi)) > __tol__ + f_q2i[mask2] = np.sin(N_i[mask2] * at[mask2,:].dot(q2) * np.pi) / (N_i[mask2] * np.sin(at[mask2,:].dot(q2) * np.pi)) + f_q2 = np.prod(f_q2i) + + q1 = -q2 - q3 + mask1 = np.abs(np.sin(at.dot(q1) * np.pi)) > __tol__ + f_q1i[mask1] = np.sin(N_i[mask1] * at[mask1,:].dot(q1) * np.pi) / (N_i[mask1] * np.sin(at[mask1,:].dot(q1) * np.pi)) + f_q1 = np.prod(f_q1i) + + if verbose: + print("The fq factors:") + print("{:16.8f} {:16.8f} {:16.8f}".format(f_q1, f_q2, f_q3)) + print("q1 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q1)) + print("q2 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q2)) + print("q3 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q3)) + + # Now we can impose the acustic sum rule + final_fc -= np.einsum("abi, ci-> abc", final_fc, Q_proj) * f_q3 + final_fc -= np.einsum("aic, bi-> abc", final_fc, Q_proj) * f_q2 + final_fc -= np.einsum("ibc, ai-> abc", final_fc, Q_proj) * f_q1 + + + + + return final_fc + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# ============================================================================================================================================ +# Tentative Sparse ========================================================================================================================== +# ============================================================================================================================================ + + + + + def WriteOnFile_sparse(self, filename): + """ + """ + + + with open(filename, "w") as f: + #TODD Print header... + f.write("{:>5}\n".format(self.n_R_sparse)) + + + for i_block in range(self.n_R_sparse): + f.write("{:d}\n".format(i_block+1)) + f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2_sparse[:, i_block]))) + f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector3_sparse[:, i_block]))) + + nat=self.atom_sparse[:,i_block] + f.write("{:>6d} {:>6d} {:>6d}\n".format(nat[0]+1,nat[1]+1,nat[2]+1)) + + # + for xyz in range(27): + z = xyz % 3 + y = (xyz %9)//3 + x = xyz // 9 + f.write("{:>2d} {:>2d} {:>2d} {:>20.10e}\n".format(x+1,y+1,z+1, self.tensor[self.r_blocks_sparse_list[i_block], 3*nat[0] + x, 3*nat[1] + y, 3*nat[2] + z])) + + def Center_sparse(self, Far=1,tol=1.0e-5): + """ + CENTERING + ========= + + This subrouine will center the third order force constant inside the Wigner-Seitz supercell. + This means that for each atomic indices in the tensor, it will be identified by the lowest + perimiter between the replica of the atoms. + Moreover, in case of existance of other elements not included in the original supercell with + the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + This function should be called before performing the Fourier interpolation. + """ + + t1 = time.time() + + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Centering ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + n_sup = np.prod(self.supercell_size) + tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, + 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + alat=self.unitcell_structure.unit_cell + + weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, + nq0,nq1,nq2,tol, + self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) + + + xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) + + self.n_R=xR23.shape[1] + self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) + + centered,self.n_R_sparse,x_r_vector2_sparse,x_r_vector3_sparse,atom_sparse,r_blocks_sparse_list=thirdorder.third_order_centering.center_sparse(tensor_reshaped,weight, + self.x_r_vector2,xR2,self.x_r_vector3,xR3, + Far,self.nat,n_sup,self.n_R) + + + + self.x_r_vector2_sparse=x_r_vector2_sparse[:,0:self.n_R_sparse] + self.x_r_vector3_sparse=x_r_vector3_sparse[:,0:self.n_R_sparse] + self.atom_sparse=atom_sparse[:,0:self.n_R_sparse] + self.r_blocks_sparse_list=r_blocks_sparse_list[0:self.n_R_sparse] + self.r_vector2_sparse=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2_sparse) + self.r_vector3_sparse=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3_sparse) + + t2 = time.time() + + self.tensor = np.transpose(centered, axes=[3,0,1,2]) + + + if self.verbose: + print("Time elapsed for computing the centering: {} s".format( t2 - t1)) + #print("Memory required for the not centered tensor {} Gb".format(self.tensor.nbytes / 1024.**3)) + print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + #print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + # + self.x_r_vector2_sparse = Settings.broadcast(self.x_r_vector2_sparse) + self.x_r_vector3_sparse = Settings.broadcast(self.x_r_vector3_sparse) + self.r_vector2_sparse = Settings.broadcast(self.r_vector2_sparse, enforce_double=True) + self.r_vector3_sparse = Settings.broadcast(self.r_vector3_sparse, enforce_double=True) + self.n_R_sparse = Settings.broadcast(self.n_R_sparse) + self.atom_sparse = Settings.broadcast(self.atom_sparse) + self.r_blocks_sparse_list = Settings.broadcast(self.r_blocks_sparse_list) + + + + + + + +# ============================================================================================================================================ +# Slower/To check/Old ======================================================================================================================= +# ============================================================================================================================================ + + + def ApplySumRule_old(self) : + + t1 = time.time() + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Imposing ASR ==========================") + print(" ") + + + + #tensor_new = np.transpose(self.tensor.reshape((n_sup_WS, n_sup_WS, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + + + + + #xR2_list, xR2_block_index, xR2_num_tot=np.unique(self.x_r_vector2, axis = 1, return_index = True, return_counts=True ) + #xR3_list, xR3_block_index, xR3_num_tot=np.unique(self.x_r_vector3, axis = 1, return_index = True, return_counts=True ) + + xR_list=np.unique(self.x_r_vector3, axis = 1) + totnum_R=xR_list.shape[1] + + xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) + totnum_Rdiff=xRdiff_list.shape[1] + + + #xRmin=np.min(xR_list,1) + #xRmax=np.max(xR_list,1) + #xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + #n_sup=np.prod(xRlen)**2 + + + #xR2_block_index=[ xR2_num_tot[i] for i in xR3_block_index ] + + #print(np.sum(xR2_list-xR3_list)) + #print(np.sum(xR2_block_index-xR3_block_index)) + #print(np.sum(xR2_num_tot-xR3_num_tot)) + + tensor_new = np.transpose(self.tensor,axes=[3,2,1,0]) + phi_asr=thirdorder.third_order_asr.impose_asr(tensor_new,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) + + #phi_asr=thirdorder.third_order_asr.impose_asr(tensor_trnsp,xRlen,n_sup,xR_list,xRdiff_list, + #self.x_r_vector2,self.x_r_vector3, + #totnum_Rdiff, + #self.n_R,totnum_R,self.nat) + + + + + self.tensor=np.transpose(phi_asr, axes=[3,2,1,0]) + + + ## DEBUG + #tensor_block_nonzero = np.sum(self.tensor**2, axis = (1,2,3)) > 1e-8 + #nn_R = np.sum(tensor_block_nonzero.astype(int)) + #print(nn_R) + ## + + + + t2 = time.time() + if self.verbose: + print(" Time elapsed for imposing the ASR: {} s".format( t2 - t1)) + print(" ") + print(" ============================================================") + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + + + + def Center_fast(self, Far=1,tol=1.0e-5): + """ + CENTERING + ========= + + This subrouine will center the third order force constant inside the Wigner-Seitz supercell. + This means that for each atomic indices in the tensor, it will be identified by the lowest + perimiter between the replica of the atoms. + Moreover, in case of existance of other elements not included in the original supercell with + the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + This function should be called before performing the Fourier interpolation. + + Faster but more memory demanding + """ + + t1 = time.time() + + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Centering ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + n_sup = np.prod(self.supercell_size) + tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + alat=self.unitcell_structure.unit_cell + + lat_min_prev=np.array([-Far*nq0,-Far*nq1,-Far*nq2]) + lat_max_prev=np.array([nq0-1+Far*nq0,nq1-1+Far*nq1,nq2-1+Far*nq2]) + lat_len_prev=lat_max_prev-lat_min_prev+np.ones(3,dtype=int) + n_sup_WS_prev=np.prod(lat_len_prev,dtype=int) + + centered_tmp, lat_min_new, lat_max_new =thirdorder.third_order_centering.pre_center(Far, + nq0,nq1,nq2,tol, + self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) + + # + # Reassignement + # + lat_len=lat_max_new-lat_min_new+np.ones(3,dtype=int) + n_sup_WS=np.prod(lat_len,dtype=int) + + + self.n_R=n_sup_WS**2 + + centered,self.x_r_vector2,self.x_r_vector3,self.r_vector2,self.r_vector3= \ + thirdorder.third_order_centering.assign(alat,lat_min_prev,lat_max_prev,centered_tmp,lat_min_new, + lat_max_new,n_sup_WS,self.nat,n_sup_WS_prev) + + + + + t2 = time.time() + + centered = np.transpose(centered, axes=[3,0,1,2]) + + + # Select the element different from zero + tensor_block_nonzero = np.sum(centered**2, axis = (1,2,3)) > 1e-8 + + self.tensor = centered[tensor_block_nonzero, :, :, :] + self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] + self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] + self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] + self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] + self.n_R = np.sum(tensor_block_nonzero.astype(int)) + + if self.verbose: + print("Time elapsed for computing the centering: {} s".format( t2 - t1)) + print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + + + def Center_fast_noreduce(self, Far=1,tol=1.0e-5): + """ + CENTERING + ========= + + This subrouine will center the third order force constant inside the Wigner-Seitz supercell. + This means that for each atomic indices in the tensor, it will be identified by the lowest + perimiter between the replica of the atoms. + Moreover, in case of existance of other elements not included in the original supercell with + the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + This function should be called before performing the Fourier interpolation. + + Faster but more memory demanding + """ + + t1 = time.time() + + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Centering ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + n_sup = np.prod(self.supercell_size) + tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + alat=self.unitcell_structure.unit_cell + + lat_min_prev=np.array([-Far*nq0,-Far*nq1,-Far*nq2]) + lat_max_prev=np.array([nq0-1+Far*nq0,nq1-1+Far*nq1,nq2-1+Far*nq2]) + lat_len_prev=lat_max_prev-lat_min_prev+np.ones(3,dtype=int) + n_sup_WS_prev=np.prod(lat_len_prev,dtype=int) + + centered_tmp, lat_min_new, lat_max_new =thirdorder.third_order_centering.pre_center(Far, + nq0,nq1,nq2,tol, + self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) + + # + # Reassignement + # + lat_len=lat_max_new-lat_min_new+np.ones(3,dtype=int) + n_sup_WS=np.prod(lat_len,dtype=int) + + + self.n_R=n_sup_WS**2 + + centered,self.x_r_vector2,self.x_r_vector3,self.r_vector2,self.r_vector3= \ + thirdorder.third_order_centering.assign(alat,lat_min_prev,lat_max_prev,centered_tmp,lat_min_new, + lat_max_new,n_sup_WS,self.nat,n_sup_WS_prev) + + + + + t2 = time.time() + + centered = np.transpose(centered, axes=[3,0,1,2]) + + + + # Select the element different from zero + tensor_block_nonzero = np.sum(centered**2, axis = (1,2,3)) > -10 + + self.tensor = centered[tensor_block_nonzero, :, :, :] + self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] + self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] + self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] + self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] + self.n_R = np.sum(tensor_block_nonzero.astype(int)) + print(self.n_R) + if self.verbose: + print("Time elapsed for computing the centering: {} s".format( t2 - t1)) + print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + + + #self.tensor = Settings.broadcast(self.tensor) + #self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + #self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + #self.r_vector2 = Settings.broadcast(self.r_vector2) + #self.r_vector3 = Settings.broadcast(self.r_vector3) + #self.n_R = Settings.broadcast(self.n_R) + + + + def Interpolate_fort(self, q2, q3): # OK but slower + """ + Interpolate the third order to the q2 and q3 points + + Parameters + ---------- + q2, q3 : ndarray(3) + The q points + + Results + ------- + Phi3 : ndarray(size = (3*nat, 3*nat, 3*nat)) + The third order force constant in the defined q points. + atomic indices runs over the unit cell + """ + tensor_new = np.zeros( shape = (3*self.nat, 3*self.nat, 3*self.nat, self.n_R), order = "F", dtype = np.complex128) + tensor_new[:,:,:,:] = np.transpose(self.tensor,axes=[3,2,1,0]) + interpolated_fc_tmp=thirdorder.third_order_interpol.interpol(tensor_new, + self.r_vector2,self.r_vector3, + q2,q3,self.n_R,self.nat) + + interpolated_fc=np.transpose(interpolated_fc_tmp,axes=[2,1,0]) + #final_fc = np.zeros((3*self.nat, 3*self.nat, 3*self.nat), + #dtype = np.complex128) + #for i in range(self.n_R): + #arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i]) + + #q3.dot(self.r_vector3[:, i])) + + #phase = np.complex128(np.exp(1j * arg)) + + #final_fc += phase * self.tensor[i, :, :, :] + return interpolated_fc + + + # def Center_py(self,Far=1,tol=1.0e-5): + # """ + # CENTERING + # ========= + + # This is the python routine to center the third order force constant inside the Wigner-Seitz supercell. + # This means that for each atomic indices in the tensor, it will be identified by the lowest + # perimiter between the replica of the atoms. + # Moreover, in case of existance of other elements not included in the original supercell with + # the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + # This function should be called before performing the Fourier interpolation. + # """ + + # if self.verbose: + # print(" ") + # print(" === Centering === ") + # print(" ") + + # # The supercell total size + # nq0=self.supercell_size[0] + # nq1=self.supercell_size[1] + # nq2=self.supercell_size[2] + + # # We prepare the tensor for the Wigner-Seitz cell (twice as big for any direction) + # WS_nsup = 2**3* np.prod(self.supercell_size) + # WS_n_R = (WS_nsup)**2 + + # # Allocate the vector in the WS cell + # # TODO: this could be memory expensive + # # we could replace this tensor with an equivalent object + # # that stores only the blocks that are actually written + # WS_r_vector2 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") + # WS_r_vector3 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") + # WS_xr_vector2 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") + # WS_xr_vector3 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") + + # # Allocate the tensor in the WS cell + # WS_tensor = np.zeros((WS_n_R, 3*self.nat, 3*self.nat, 3*self.nat), dtype = np.double) + + # # Here we prepare the vectors + # # Iterating for all the possible values of R2 and R3 in the cell that encloses the Wigner-Seitz one + # t1 = time.time() + # for i, (a2,b2,c2) in enumerate(itertools.product(range(-nq0, nq0), range(-nq1, nq1), range(-nq2, nq2))): + # for j, (a3,b3,c3) in enumerate(itertools.product(range(-nq0, nq0), range(-nq1, nq1), range(-nq2, nq2))): + + # # Enclose in one index i and j + # total_index = i * WS_nsup + j + + # # Get the crystal lattice + # WS_xr_vector2[:, total_index] = (a2,b2,c2) + # WS_xr_vector3[:, total_index] = (a3,b3,c3) + + + # # Convert all the vectors in cartesian coordinates + # WS_r_vector2[:,:] = self.unitcell_structure.unit_cell.T.dot(WS_xr_vector2) + # WS_r_vector3[:,:] = self.unitcell_structure.unit_cell.T.dot(WS_xr_vector3) + + # # print("WS vectors:") + # # print(WS_r_vector2.T) + + + + # t2 = time.time() + # if (self.verbose): + # print("Time elapsed to prepare vectors in the WS cell: {} s".format(t2-t1)) + + # # Here we create the lattice images + # # And we save the important data + + # # Allocate the distance between the superlattice vectors for each replica + # tot_replicas = (2*Far + 1)**3 + # total_size = tot_replicas**2 + # dR_12 = np.zeros( (total_size, 3)) + # dR_23 = np.zeros( (total_size, 3)) + # dR_13 = np.zeros( (total_size, 3)) + # # Allocate the perimeter of the superlattice for each replica + # PP = np.zeros(total_size) + # # Alloca the the vector of the superlattice in crystalline units + # V2_cryst = np.zeros((total_size,3)) + # V3_cryst = np.zeros((total_size,3)) + + # # Lets cycle over the replica (the first index is always in the unit cell) + # # To store the variables that will be used to compute the perimeters of + # # all the replica + # t1 = time.time() + # for i, (a2,b2,c2) in enumerate(itertools.product(range(-Far, Far+1), range(-1,Far+1),range(-Far, Far+1))): + # xR_2 = np.array((a2, b2, c2)) + # R_2 = xR_2.dot(self.supercell_structure.unit_cell) + # for j, (a3, b3, c3) in enumerate(itertools.product(range(-1, Far+1), range(-1,Far+1),range(-Far, Far+1))): + # xR_3 = np.array((a3, b3, c3)) + # R_3 = xR_3.dot(self.supercell_structure.unit_cell) + + # # Prepare an index that runs over both i and j + # total_index = tot_replicas*i + j + # #print(total_index, i, j) + + # # Store the replica vector in crystal coordinates + # V2_cryst[total_index, :] = np.array((a2,b2,c2)) * np.array(self.supercell_size) + # V3_cryst[total_index, :] = np.array((a3,b3,c3)) * np.array(self.supercell_size) + + # # Compute the distances between the replica of the indices + # dR_12[total_index, :] = xR_2 + # dR_13[total_index, :] = xR_3 + # dR_23[total_index, :] = xR_3 - xR_2 + + # # Store the perimeter of this replica triplet + # PP[total_index] = R_2.dot(R_2) + # PP[total_index]+= R_3.dot(R_3) + # PP[total_index]+= np.sum((R_3 - R_2)**2) + + # #print("R2:", R_2, "R3:", R_3, "PP:", PP[total_index]) + # t2 = time.time() + + # if self.verbose: + # print("Time elapsed to prepare the perimeter in the replicas: {} s".format(t2 - t1)) + + + # # Now we cycle over all the blocks and the atoms + # # For each triplet of atom in a block, we compute the perimeter of the all possible replica + # # Get the metric tensor of the supercell + # G = np.einsum("ab, cb->ac", self.supercell_structure.unit_cell, self.supercell_structure.unit_cell) + + # # We cycle over atoms and blocks + # for iR in range(self.n_R): + # for at1, at2, at3 in itertools.product(range(self.nat), range(self.nat), range(self.nat)): + # # Get the positions of the atoms + # r1 = self.tau[at1,:] + # r2 = self.r_vector2[:, iR] + self.tau[at2,:] + # r3 = self.r_vector3[:, iR] + self.tau[at3,:] + + # # Lets compute the perimeter without the replicas + # pp = np.sum((r1-r2)**2) + # pp+= np.sum((r2-r3)**2) + # pp+= np.sum((r1-r3)**2) + + # # Get the crystalline vectors (in the supercell) + # x1 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r1) + # x2 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r2) + # x3 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r3) + + # # Now we compute the quantities that do not depend on the lattice replica + # # As the current perimeter and the gg vector + # G_12 = 2*G.dot(x2-x1) + # G_23 = 2*G.dot(x3-x2) + # G_13 = 2*G.dot(x3-x1) + + # # Now we can compute the perimeters of all the replica + # # all toghether + # P = PP[:] + pp + # P[:] += dR_12.dot(G_12) + # P[:] += dR_23.dot(G_23) + # P[:] += dR_13.dot(G_13) + + # # if self.tensor[iR, 3*at1, 3*at2, 3*at3] > 0: + # # #print("all the perimeters:") + # # #print(P) + # # print("The minimum:", np.min(P)) + # # index = np.argmin(P) + # # print("R2 = ", self.r_vector2[:, iR], "R3 = ", self.r_vector3[:,iR]) + # # print("The replica perimeter:", PP[index]) + # # print("The standard perimeter:", pp) + # # print("The the cross values:") + # # print(dR_12.dot(G_12)[index], dR_13.dot(G_13)[index], dR_23.dot(G_23)[index]) + # # print("The replica vectors are:", "R2:", V2_cryst[index,:], "R3:", V3_cryst[index,:]) + + # # Now P is filled with the perimeters of all the replica + # # We can easily find the minimum + # P_min = np.min(P) + + # # We can find how many they are and a mask on their positions + # min_P_mask = (np.abs(P_min - P) < 1e-6).astype(bool) + + # # The number of minimium perimeters + # n_P = np.sum(min_P_mask.astype(int)) + + # # Get the replica vector for the minimum perimeters + # v2_shift = V2_cryst[min_P_mask, :] + # v3_shift = V3_cryst[min_P_mask, :] + + # # Now we can compute the crystalline coordinates of the lattice in the WS cell + # r2_cryst = np.tile(self.x_r_vector2[:, iR], (n_P, 1)) + v2_shift + # r3_cryst = np.tile(self.x_r_vector3[:, iR], (n_P, 1)) + v3_shift + # verb = False + + + # # Get the block indices in the WS cell + # WS_i_R = get_ws_block_index(self.supercell_size, r2_cryst, r3_cryst, verbose = verb) + + # # Now we fill all the element of the WS tensor + # # with the current tensor, dividing by the number of elemets + # new_elemet = np.tile(self.tensor[iR, 3*at1:3*at1+3, 3*at2:3*at2+3, 3*at3:3*at3+3], (n_P, 1,1,1)) + # new_elemet /= n_P + # WS_tensor[WS_i_R, 3*at1: 3*at1+3, 3*at2:3*at2+3, 3*at3:3*at3+3] = new_elemet + + + # t2 = time.time() + + # if self.verbose: + # print("Time elapsed for computing the cenetering: {} s".format( t2 - t1)) + + # # We can update the current tensor + # self.tensor = WS_tensor + # self.r_vector2 = WS_r_vector2 + # self.r_vector3 = WS_r_vector3 + # self.x_r_vector2 = WS_xr_vector2 + # self.x_r_vector3 = WS_xr_vector3 + # self.n_R = WS_n_R + + + def ApplySumRule_py(self): + r""" + Enforce the sum rule by projecting the third order force constant + in the sum rule space defined by + + .. math:: + + P = ( 1 - Q ) + + Q_{st}^{\alpha\beta} = 1 / nat_{sc} \left( \sum_x \delta_{x\alpha}\delta{x\beta}\right) + """ + + # Apply the sum rule on the last index + + n_sup_WS = int(np.sqrt(self.n_R)) + + for I_r2 in range(n_sup_WS): + # Here we want to sum over r3 (indices that goes from total_index to total_index + n_sup_WS + total_index = n_sup_WS * I_r2 + + for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): + for gamma in range(3): + # gamma mask runs over the third atom index + gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() + delta = np.sum(self.tensor[total_index : total_index + n_sup_WS, s, t, gamma_mask]) / (self.nat * self.n_R) + self.tensor[total_index : total_index + n_sup_WS, s, t, gamma_mask] -= delta + + + # Now apply the sum rule on the second index + for I_r3 in range(n_sup_WS): + total_index_mask = (np.arange(self.n_R) % n_sup_WS) == I_r3 + + for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): + for gamma in range(3): + gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() + #print("BLOCK MASK:",total_index_mask) + #print("GAMMA MASK:", gamma_mask) + #print("S:",s , "T:", t) + #print(np.shape(total_index_mask), np.shape(gamma_mask)) + #print(np.shape(self.tensor)) + #print (self.tensor[total_index_mask, s, 0, t]) + delta = 0 + for r in range(self.nat): + delta += np.sum(self.tensor[total_index_mask, s, 3*r + gamma, t]) / (self.nat * self.n_R) + + for r in range(self.nat): + self.tensor[total_index_mask, s, 3*r + gamma, t] -= delta + + + # Now apply the sum rule on the second index + for total_index in range(n_sup_WS**2): + r2 = self.r_vector2[:, total_index] + r3 = self.r_vector3[:, total_index] + + for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): + for gamma in range(3): + gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() + + delta = 0 + for i_Rt in range(n_sup_WS): + Rt = self.r_vector2[:, i_Rt * n_sup_WS] + Rnew = r3 - r2 + Rt + Rnew = np.tile(Rnew, (n_sup_WS,1)).T + + distances = np.sum(np.abs( Rnew - self.r_vector3[:, :n_sup_WS]), axis = 0) + good_vector = np.argmin(distances) + if distances[good_vector] < 1e-6: + + vect_index = i_Rt * n_sup_WS + good_vector + delta += np.sum(self.tensor[vect_index, gamma_mask, s, t]) / (self.nat * self.n_R) + + self.tensor[total_index, gamma_mask, s, t] -= delta + + def get_ws_block_index(supercell_size, cryst_vectors2, cryst_vectors3, verbose = False): + """ + Get the block in the supercell that contains the WS cell, + given the crystalline positions of vectors, returns the indices of the block. + + The block identifies the supercell of the three atoms as + iR <=> (0, R_2, R_3) + + This subroutines takes R_2 and R_3 in crystalline components, and computes the iR. + Assuming that the Wigner-Seitz cell is initialized correctly + + Parameters + ---------- + supercell_size : ndarray(size = (3), dtype = int) + The size of the original supercell (not the Wigner-Seitz) + cryst_vectors2 : ndarray(size= (N_vectors, 3), dtype = int) + The crystalline coordinates of the second vector. + It could also be a single vector. + cryst_vectors3 : ndarray(size= (N_vectors, 3), dtype = int) + The crystalline coordinates of the third vector. + It could also be a single vector. + + Results + ------- + block_id : ndarray(size = N_vectors, dtype = int) + The index of the block for each crystalline vector. + """ + + # Get the composed index in the iteration of the two vectors + # Rescale the vectors + new_v2 = cryst_vectors2.copy() + new_v2 += np.array(supercell_size) + + new_v3 = cryst_vectors3.copy() + new_v3 += np.array(supercell_size) + + # Get the total dimension of the block for each vector in the WS cell + WS_sup = np.prod(supercell_size) * 8 + + ws_z = 2 * supercell_size[2] + ws_y = 2 * supercell_size[1] + + # Transform the two vectors in the indices of the iterator + i2 = new_v2[:, 2] + new_v2[:,1] * ws_z + i2 += new_v2[:,0] * ws_z * ws_y + + i3 = new_v3[:, 2] + new_v3[:,1] * ws_z + i3 += new_v3[:,0] * ws_z * ws_y + + # Collect everything in the block index + WS_i_R = i2 * WS_sup + i3 + + # Convert in integer for indexing + WS_i_R = WS_i_R.astype(int) + + if verbose: + print("All values of i2:") + print(i2) + print("All values of i3:") + print(i3) + print("The block value:", WS_i_R) + + # Check if the block indices are correct + assert (WS_i_R >= 0).all() + assert (WS_i_R < WS_sup**2).all() + + return WS_i_R + + def Center_old(self, Far=1,tol=1.0e-5): + """ + CENTERING + ========= + + This subrouine will center the third order force constant inside the Wigner-Seitz supercell. + This means that for each atomic indices in the tensor, it will be identified by the lowest + perimiter between the replica of the atoms. + Moreover, in case of existance of other elements not included in the original supercell with + the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. + + This function should be called before performing the Fourier interpolation. + """ + + t1 = time.time() + + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Centering ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + n_sup = np.prod(self.supercell_size) + tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, + 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + alat=self.unitcell_structure.unit_cell + + weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, + nq0,nq1,nq2,tol, + self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) + + + xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) + + self.n_R=xR23.shape[1] + self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) + + centered=thirdorder.third_order_centering.center(tensor_reshaped,weight, + self.x_r_vector2,xR2,self.x_r_vector3,xR3, + Far,self.nat,n_sup,self.n_R) + + + + t2 = time.time() + + self.tensor = np.transpose(centered, axes=[3,0,1,2]) + + if self.verbose: + print("Time elapsed for computing the centering: {} s".format( t2 - t1)) + #print("Memory required for the not centered tensor {} Gb".format(self.tensor.nbytes / 1024.**3)) + print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + #print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + + def ApplySumRule_old(self) : + + t1 = time.time() + + if Settings.am_i_the_master(): + + if self.verbose: + print(" ") + print(" ======================= Imposing ASR ==========================") + print(" ") + + + + xR_list=np.unique(self.x_r_vector3, axis = 1) + totnum_R=xR_list.shape[1] + + xRmin=np.min(xR_list,1) + xRmax=np.max(xR_list,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + n_Rnew=np.prod(xRlen)**2 + + if n_Rnew != self.n_R: + if self.verbose: + print("*********") + print("Enlarging") + print("*********") + # reassignement + xr2_old=self.x_r_vector2 + xr3_old=self.x_r_vector3 + # + self.x_r_vector2=np.zeros((3,n_Rnew),dtype=np.int) + self.x_r_vector3=np.zeros((3,n_Rnew),dtype=np.int) + self.r_vector2=np.zeros((3,n_Rnew)) + self.r_vector3=np.zeros((3,n_Rnew)) + for index_cell2 in range(np.prod(xRlen)): + n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=xRmin,v_len=xRlen) + for index_cell3 in range(np.prod(xRlen)): + n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=xRmin,v_len=xRlen) + # + total_index_cell = index_cell3 + np.prod(xRlen) * index_cell2 + # + self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) + self.r_vector2[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, total_index_cell]) + self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 + self.r_vector3[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) + ### + tensor_trnsp = np.transpose(self.tensor,axes=[3,2,1,0]) + tensor=thirdorder.third_order_asr.enlarge(tensor_trnsp, + self.x_r_vector2,self.x_r_vector3, + xr2_old,xr3_old, + self.nat,n_Rnew,self.n_R) + + xR_list=np.unique(self.x_r_vector3, axis = 1) + totnum_R=xR_list.shape[1] + + xRmin=np.min(xR_list,1) + xRmax=np.max(xR_list,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + self.n_R=np.prod(xRlen)**2 + + + xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) + totnum_Rdiff=xRdiff_list.shape[1] + + + #phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) + phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xRlen,xR_list,xRdiff_list, + self.x_r_vector2,self.x_r_vector3, + totnum_Rdiff,self.n_R,totnum_R,self.nat) + + phi_asr=np.transpose(phi_asr, axes=[3,2,1,0]) + + # Select the element different from zero ######################## + tensor_block_nonzero = np.sum(phi_asr**2, axis = (1,2,3)) > 1e-8 + + self.tensor = phi_asr[tensor_block_nonzero, :, :, :] + self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] + self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] + self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] + self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] + self.n_R = np.sum(tensor_block_nonzero.astype(int)) + t2 = time.time() + if self.verbose: + print("Time elapsed for imposing the ASR: {} s".format( t2 - t1)) + print("Memory required for the ASR centered tensor: {} Gb".format(phi_asr.nbytes / 1024.**3)) + print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + + + + def Center_and_ApplySumRule_old(self,Far=1,tol=1.0e-5) : + + t1 = time.time() + + if Settings.am_i_the_master(): + + + if self.verbose: + print(" ") + print(" ======================= Centering ==========================") + print(" ") + + # The supercell total size + # + nq0=self.supercell_size[0] + nq1=self.supercell_size[1] + nq2=self.supercell_size[2] + + n_sup = np.prod(self.supercell_size) + tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, + 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) + alat=self.unitcell_structure.unit_cell + + weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, + nq0,nq1,nq2,tol, + self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) + + + xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) + xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) + + self.n_R=xR23.shape[1] + self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) + self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) + self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) + + centered=thirdorder.third_order_centering.center(tensor_reshaped,weight, + self.x_r_vector2,xR2,self.x_r_vector3,xR3, + Far,self.nat,n_sup,self.n_R) + + t2 = time.time() + + self.tensor = np.transpose(centered, axes=[3,0,1,2]) + + if self.verbose: + print("Time elapsed for computing the centering: {} s".format( t2 - t1)) + print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) + + + if self.verbose: + print(" ") + print(" ======================= Imposing ASR ==========================") + print(" ") + + + + xR_list=np.unique(self.x_r_vector3, axis = 1) + totnum_R=xR_list.shape[1] + + xRmin=np.min(xR_list,1) + xRmax=np.max(xR_list,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + n_Rnew=np.prod(xRlen)**2 + + if n_Rnew != self.n_R: + if self.verbose: + print("*********") + print("Enlarging") + print("*********") + # reassignement + xr2_old=self.x_r_vector2 + xr3_old=self.x_r_vector3 + # + self.x_r_vector2=np.zeros((3,n_Rnew),dtype=int) + self.x_r_vector3=np.zeros((3,n_Rnew),dtype=int) + self.r_vector2=np.zeros((3,n_Rnew)) + self.r_vector3=np.zeros((3,n_Rnew)) + for index_cell2 in range(np.prod(xRlen)): + n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=xRmin,v_len=xRlen) + for index_cell3 in range(np.prod(xRlen)): + n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=xRmin,v_len=xRlen) + # + total_index_cell = index_cell3 + np.prod(xRlen) * index_cell2 + # + self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) + self.r_vector2[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, total_index_cell]) + self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 + self.r_vector3[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) + ### + tensor_trnsp = np.transpose(self.tensor,axes=[3,2,1,0]) + tensor=thirdorder.third_order_asr.enlarge(tensor_trnsp, + self.x_r_vector2,self.x_r_vector3, + xr2_old,xr3_old, + self.nat,n_Rnew,self.n_R) + + xR_list=np.unique(self.x_r_vector3, axis = 1) + totnum_R=xR_list.shape[1] + + xRmin=np.min(xR_list,1) + xRmax=np.max(xR_list,1) + xRlen=xRmax-xRmin+np.ones((3,),dtype=int) + self.n_R=np.prod(xRlen)**2 + + + xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) + totnum_Rdiff=xRdiff_list.shape[1] + + + #phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) + phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xRlen,xR_list,xRdiff_list, + self.x_r_vector2,self.x_r_vector3, + totnum_Rdiff,self.n_R,totnum_R,self.nat) + + phi_asr=np.transpose(phi_asr, axes=[3,2,1,0]) + + # Select the element different from zero + tensor_block_nonzero = np.sum(phi_asr**2, axis = (1,2,3)) > 1e-8 + + self.tensor = phi_asr[tensor_block_nonzero, :, :, :] + self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] + self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] + self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] + self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] + self.n_R = np.sum(tensor_block_nonzero.astype(int)) + + t3 = time.time() + if self.verbose: + print(" Time elapsed for imposing the ASR: {} s".format( t3 - t2)) + print(" Memory required for the ASR centered tensor: {} Gb".format(phi_asr.nbytes / 1024.**3)) + print(" Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) + print(" ") + print(" ============================================================") + + self.tensor = Settings.broadcast(self.tensor, enforce_double=True) + self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) + self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) + self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) + self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) + self.n_R = Settings.broadcast(self.n_R) + self.n_sup = Settings.broadcast(self.n_sup) + + + + +# Plot the phonons in the given k-path +def get_phonons_in_qpath(dynamical_matrix, q_path, center_args = {}): + """ + GET PHONONS IN K-PATH + ===================== + + Compute the phonons in the given q pat. + + Parameters + ---------- + dynamical_matrix : Phonons.Phonons + The dynamical matrix (with effective charges) + q_path : list of ndarray(size of 3) + List of q points in 2pi/A units. + center_args : dict + Dictionary of parameters to be passed to the Center function of + Tensor2 + + Results + ------- + frequencies : ndarray(size= (Nq, Nmodes)) + The frequencies of the dynamical matrix along the given path. + The result is given in cm-1 + + + Examples + -------- + + To get a valid path specifying the special points of the Brilluin zone + you can exploit the ASE library: + + .. code:: + + # Load the dynamical matrix + dyn = CC.Phonons.Phonons("mydyn", nqirr = 6) + + # Get the q-path from high-symmetry points + path = ase.dft.kpoints.bandpath("GKWXULG", # The q-path + dyn.structure.unit_cell, + npoints = 1000) + qpath = path.cartesian_kpts() + + # Compute the frequencies in the q path + ws = CC.ForceTensor.get_phonons_in_qpath(dyn, qpath) + + + # Plot the results (with matplotlib.pyplot as plt) + xaxis, xticks, xlabels = path.get_linear_kpoint_axis() + for i in range(dyn.structure.N_atoms * 3): + plt.plot(x_axis, ws[:, i]) + + plt.gca().set_xticks(xticks) + plt.gca().set_xticklabels(xlabels) + """ + + + # Generate the tensor from the dynamical matrix + t2 = Tensor2(dynamical_matrix.structure, + dynamical_matrix.structure.generate_supercell(dynamical_matrix.GetSupercell()), + dynamical_matrix.GetSupercell()) + + t2.SetupFromPhonons(dynamical_matrix) + t2.Center(**center_args) + t2.Apply_ASR() + + n_k, _ = q_path.shape + + n_modes = 3 * dynamical_matrix.structure.N_atoms + ws = np.zeros((n_k, n_modes), dtype = np.double) + m = dynamical_matrix.structure.get_masses_array() + m = np.tile(m, (3,1)).T.ravel() + _mm_ = np.sqrt(np.outer(m, m)) + + + # Interpolate the result + for i in range(n_k): + dynq = t2.Interpolate(-q_path[i,:]) + fc = dynq / _mm_ + + # Diagonalize + all_freqs = np.linalg.eigvalsh(fc) + ws[i, :] = np.sqrt(np.abs(all_freqs)) * Units.RY_TO_CM * np.sign(all_freqs) + + return ws diff --git a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py index 5844307a..096fb3d5 100644 --- a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py +++ b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py @@ -4223,8 +4223,10 @@ def InterpolateDynFC(starting_fc, coarse_grid, unit_cell_structure, super_cell_s natsc = np.shape(starting_fc)[0] // 3 nat = natsc // supercell_size - #print "nat:", nat - #print "natsc:", natsc + print('shape',starting_fc.shape) + print('sc',natsc) + print('uc',nat) + # Get the force constant in an appropriate supercell diff --git a/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py new file mode 100644 index 00000000..5d3f8aee --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py @@ -0,0 +1,346 @@ +from __future__ import print_function +""" +This file keeps in mind common settings that needs to be initialized once. +""" +import numpy as np + +# The parallelization setup +__PARALLEL_TYPE__ = "serial" +try: + import mpi4py + import mpi4py.MPI + __PARALLEL_TYPE__ = "mpi4py" +except: + try: + __PARALLEL_TYPE__ = "serial" + import multiprocessing as mp + except: + __PARALLEL_TYPE__ = "serial" + + + +__SUPPORTED_LIBS__ = ["mp", "serial", "mpi4py"] +__MPI_LIBRARIES__ = ["mpi4py"] +__NPROC__ = 1 + +def ParallelPrint(*args, **kwargs): + """ + Print only if I am the master + """ + if am_i_the_master(): + print(*args, **kwargs) + +def all_print(*args, **kwargs): + """ + Print for all the processors + """ + print("[RANK {}] ".format(get_rank()), end = "") + print(*args, **kwargs) + + +def am_i_the_master(): + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + rank = comm.Get_rank() + if rank == 0: + return True + return False + else: + return True + +def get_rank(): + """ + Get the rank of the process + """ + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + return comm.Get_rank() + elif __PARALLEL_TYPE__ == "serial": + return 0 + else: + raise NotImplementedError("Error, I do not know what is the rank with the {} parallelization".format(__PARALLEL_TYPE__)) + +def broadcast(list_of_values, enforce_double = False, other_type = None): + """ + Broadcast the list to all the processors from the master. + It returns a list equal for all the processors from the master. + + If you are broadcasting a numpy array, use enforce_double. If the array is not a C double + type, specify the other_type (must be an MPI type). + + NOTE: Now enforce_double is just a dumb variable, as it is always overridded. + It seems that Bcast does not work as expected + """ + + # STRONG OVERRIDE OVER ENFORCE DOUBLE THAT IS NOT WORKING + enforce_double = False + + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + if comm.Get_size() == 1: + return list_of_values + + if not enforce_double: + return comm.bcast(list_of_values, root = 0) + else: + total_shape = list_of_values.shape + mpitype = mpi4py.MPI.DOUBLE + if other_type is not None: + mpitype = other_type + new_data = list_of_values.ravel() + comm.Bcast([new_data, np.prod(total_shape), mpitype], root = 0) + return new_data.reshape(total_shape) + elif __PARALLEL_TYPE__ == "serial": + return list_of_values + else: + raise NotImplementedError("broadcast not implemented for {} parallelization.".format(__PARALLEL_TYPE__)) + + +def barrier(): + """ + This function force the MPI processes to sync: + All the processes arrived at this function are stopped until all the others call the same method. + """ + + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + comm.barrier() + +def SetupParallel(n_processors=1): + """ + SETUP THE MODULE FOR PARALLEL EXECUTION + ======================================= + + This method initialize the parallelization of the module. + For serial execution use n_processors = 1. + Note that this kind of parallelization is implemented in single nodes. + + Parameters + ---------- + n_processors : int + The number of processors to be used for the heavy parallel + executions. If negative or zero, the system tries to determine + automatically the number of aveabile. + It is useless if the parallelization is done with MPI + """ + + global __NPROC__ + global __PARALLEL_TYPE__ + + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + __NPROC__ = comm.Get_size() + if __NPROC__ == 1: + __PARALLEL_TYPE__ = "serial" + + __NPROC__ = n_processors + if n_processors > 1 and __PARALLEL_TYPE__ == "serial": + raise ValueError("Error, trying to setup a parallel computation with a 'serial' library") + if n_processors < 1: + raise ValueError("Error, the number of processors must be 1 or higher") + +def GetNProc(): + """ + GET THE PROCESSORS FOR THE PARALLEL COMPUTATION + =============================================== + + This method returns the total number of processors currently setted up + for a parallel execution. If a serial algorithm is chosen, then 1 is returned. + You can modify it using the SetupParallel method. + """ + + if __PARALLEL_TYPE__ == "mpi4py": + return mpi4py.MPI.COMM_WORLD.Get_size() + + return __NPROC__ + +def GoParallel(function, list_of_inputs, reduce_op = None): + """ + GO PARALLEL + =========== + + Perform a parallel evaluation of the provided function with the spawned + list of inputs, and returns a list of output + + Parameters + ---------- + function : pointer to function + The function to be executed in parallel + list_of_inputs : list + A list containing the inputs to be passed to the function. + reduce_op : string + If a reduction must be performed on output, specify the operator, + accepted are "+", "*". For now this is implemented only with MPI + + """ + if not __PARALLEL_TYPE__ in __SUPPORTED_LIBS__: + raise ValueError("Error, wrong parallelization type: %s\nSupported types: %s" % (__PARALLEL_TYPE__, " ".join(__SUPPORTED_LIBS__))) + + + if __PARALLEL_TYPE__ in __MPI_LIBRARIES__ or __PARALLEL_TYPE__ == "serial": + if not reduce_op is None: + if not reduce_op in ["*", "+"]: + raise NotImplementedError("Error, reduction '{}' not implemented.".format(reduce_op)) + + # Here we create the poll manually + n_proc = GetNProc() + rank = get_rank() + + # broadcast the values + list_of_inputs = broadcast(list_of_inputs) + + # Prepare the work for the current processor + # TODO: Use a generator + computing_list = [] + for i in range(rank, len(list_of_inputs), n_proc): + computing_list.append(list_of_inputs[i]) + + #print("Rank {} is computing {} elements".format(rank, len(computing_list))) + #all_print("Computing:", computing_list) + + + # Perform the reduction + if reduce_op == "+": + result = 0 + for x in computing_list: + result += function(x) + + elif reduce_op == "*": + result = 1 + for x in computing_list: + result *= function(x) + + # If a reduction must be done, return + if not reduce_op is None: + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + results = comm.allgather(result) + elif __PARALLEL_TYPE__ == "serial": + return result + else: + raise NotImplementedError("Error, not implemented {}".format(__PARALLEL_TYPE__)) + + + #np.savetxt("result_{}.dat".format(rank), result) + result = results[0] + # Perform the last reduction + if reduce_op == "+": + for i in range(1,len(results)): + result+= results[i] + elif reduce_op == "*": + for i in range(1,len(results)): + result*= results[i] + + #np.savetxt("result_{}_total.dat".format(rank), result) + + return result + else: + raise NotImplementedError("Error, for now parallelization with MPI implemented only with reduction") + else: + raise NotImplementedError("Something went wrong: {}".format(__PARALLEL_TYPE__)) + + #elif __PARALLEL_TYPE__ == "mp": + #p = mp.Pool(__NPROC__) + #return p.map(function, list_of_inputs) + #elif __PARALLEL_TYPE__ == "serial": + #return map(function, list_of_inputs) + + +def GoParallelTuple(function, list_of_inputs, reduce_op = None): + """ + GO PARALLEL TUPLE + ================== + + Perform a parallel evaluation of the provided function with the spawned + list of inputs, and returns a list of output. It works well if function returns more than one result + + Parameters + ---------- + function : pointer to function + The function to be executed in parallel. It must return a tuple, and each element of the tuple must be defined + list_of_inputs : list + A list containing the inputs to be passed to the function. + reduce_op : string + If a reduction must be performed on output, specify the operator, + accepted are "+", "*". For now this is implemented only with MPI + + """ + if not __PARALLEL_TYPE__ in __SUPPORTED_LIBS__: + raise ValueError("Error, wrong parallelization type: %s\nSupported types: %s" % (__PARALLEL_TYPE__, " ".join(__SUPPORTED_LIBS__))) + + + if __PARALLEL_TYPE__ in __MPI_LIBRARIES__ or __PARALLEL_TYPE__ == "serial": + if not reduce_op is None: + if not reduce_op in ["*", "+"]: + raise NotImplementedError("Error, reduction '{}' not implemented.".format(reduce_op)) + + # Here we create the poll manually + n_proc = GetNProc() + rank = get_rank() + + # broadcast the values + list_of_inputs = broadcast(list_of_inputs) + + # Prepare the work for the current processor + # TODO: Use a generator + computing_list = [] + for i in range(rank, len(list_of_inputs), n_proc): + computing_list.append(list_of_inputs[i]) + + #print("Rank {} is computing {} elements".format(rank, len(computing_list))) + + # Work! TODO: THIS IS VERY MEMORY HEAVY + results = [function(x) for x in computing_list] + + + # Perform the reduction + if reduce_op == "+": + result = list(results[0]) + for i in range(1,len(results)): + for j in range(len(results[i])): + result[j] += results[i][j] + + + if reduce_op == "*": + result = list(results[0]) + for i in range(1,len(results)): + for j in range(len(results[i])): + result[j] *= results[i][j] + + + # If a reduction must be done, return + if not reduce_op is None: + if __PARALLEL_TYPE__ == "mpi4py": + comm = mpi4py.MPI.COMM_WORLD + results = [] + for i in range(len(result)): + results.append(comm.allgather(result[i])) + + elif __PARALLEL_TYPE__ == "serial": + return result + else: + raise NotImplementedError("Error, not implemented {}".format(__PARALLEL_TYPE__)) + + + result = results[0] + for j in range(len(results)): + # Perform the last reduction + if reduce_op == "+": + for i in range(1,len(results[j])): + result[j]+= results[j][i] + elif reduce_op == "*": + for i in range(1,len(results[j])): + result[j]*= results[j][i] + + return result + else: + raise NotImplementedError("Error, for now parallelization with MPI implemented only with reduction") + else: + raise NotImplementedError("Something went wrong: {}".format(__PARALLEL_TYPE__)) + + #elif __PARALLEL_TYPE__ == "mp": + #p = mp.Pool(__NPROC__) + #return p.map(function, list_of_inputs) + #elif __PARALLEL_TYPE__ == "serial": + #return map(function, list_of_inputs) + diff --git a/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py new file mode 100644 index 00000000..91d2a766 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py @@ -0,0 +1,27 @@ +from __future__ import print_function +from __future__ import division + + +import numpy as np + +__INFO__ = """ +Here all the conversion between common units are stored. + +The default units are Angstrom, eV, and the atomic unit mass (1/12 of C12 nucleus) + +However, phonons are saved in Ry/bohr^2 (to mantain the quantum-espresso compatibility) +therefore some unsefull conversion with Ry and Ha atomic units are provided. +""" + +A_TO_BOHR = np.float64(1.889725989) +BOHR_TO_ANGSTROM = 1 / A_TO_BOHR +RY_TO_CM = np.float64(109737.36034769034314)#109691.40235 +ELECTRON_MASS_UMA = np.float64(1822.8885468045) +MASS_RY_TO_UMA = 2 / ELECTRON_MASS_UMA +HBAR = np.float64(0.06465415105180661) +K_B = np.float64(8.617330337217213e-05) +RY_TO_EV = np.float64(13.605693012183622) +RY_PER_BOHR3_TO_GPA = 14710.513242194795 +RY_PER_BOHR3_TO_EV_PER_A3 = 91.81576765360094 +RY_TO_KELVIN = RY_TO_EV / K_B +GPA_TO_EV_PER_A3 = RY_PER_BOHR3_TO_EV_PER_A3 / RY_PER_BOHR3_TO_GPA diff --git a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py index 01bd3a01..83001fc8 100644 --- a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py +++ b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py @@ -530,12 +530,8 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): assert cart == 3 nat = int(nat3 / 3) - - # Apply hermitianity - #print("Original:") - #print(dM_drdr[:,:,0]) - dM_drdr += np.einsum("abc->bac", dM_drdr) + dM_drdr += np.einsum("abc -> bac", dM_drdr) dM_drdr /= 2 # Apply the Sum Rule @@ -543,19 +539,12 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): for pol in range(3): CustomASR(dM_drdr[:,:,pol]) - #print("After the sum rule:") - #print(dM_drdr[:,:,0]) - - # Convert in crystal coordinates + # Convert in crystal coordinates MAKE SURE THAT THIS IS CONVERTED BACK IN CARTESIAN COORDINATES for i in range(nat): for j in range(nat): dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T) - #print("Crystal:") - #print(dM_drdr[:,:,0]) - - # Apply translations new_dM = np.zeros(np.shape(dM_drdr), dtype = np.double) if self.QE_translation_nr > 1: @@ -568,22 +557,13 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): dM_drdr[:,:,:] = new_dM / self.QE_translation_nr new_dM[:,:,:] = 0 - - #print("After transl:") - #print(dM_drdr[:,:,0]) - - #self.PrintSymmetries() + # # DEBUG VARIABLE + # debug = np.zeros(np.shape(new_dM)) # Apply rotations for i in range(self.QE_nsym): irt = self.QE_irt[i, :] - 1 - - #print("") - #print("--------------------") - #print("symmetry: {:d}, irt: {}".format(i+1, irt +1)) - #prova = np.zeros(np.shape(new_dM)) - for jat in range(nat): for kat in range(nat): new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] @@ -592,14 +572,19 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): new_mat = np.einsum("ck, ijk -> ijc", self.QE_s[:,:,i], new_mat) new_mat = np.einsum("bj, ijc -> ibc", self.QE_s[:,:,i], new_mat) new_mat = np.einsum("ai, ibc -> abc", self.QE_s[:,:,i], new_mat) - #prova[3*jat:3*jat+3, 3*kat:3*kat+3,:] = new_mat new_dM[3*jat:3*jat+3, 3*kat:3*kat+3,:] += new_mat - #print(np.einsum("abc->cab", prova)) - #print("--------------------") dM_drdr[:,:,:] = new_dM / self.QE_nsym + + # # CONVERT IN CARTESIAN COORDINATES TO DEBUG + # for _i_ in range(nat): + # for _j_ in range(nat): + # debug[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :] = Methods.convert_3tensor_to_cryst(new_dM[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :],\ + # self.QE_at.T, cryst_to_cart = True) + # np.save('CC_new_{}'.format(i), debug) + - # Convert in crystal coordinates + # Convert in cartesian coordinates for i in range(nat): for j in range(nat): dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T, True) diff --git a/cellconstructor/Phonons.py b/cellconstructor/Phonons.py index 5844307a..096fb3d5 100644 --- a/cellconstructor/Phonons.py +++ b/cellconstructor/Phonons.py @@ -4223,8 +4223,10 @@ def InterpolateDynFC(starting_fc, coarse_grid, unit_cell_structure, super_cell_s natsc = np.shape(starting_fc)[0] // 3 nat = natsc // supercell_size - #print "nat:", nat - #print "natsc:", natsc + print('shape',starting_fc.shape) + print('sc',natsc) + print('uc',nat) + # Get the force constant in an appropriate supercell diff --git a/cellconstructor/symmetries.py b/cellconstructor/symmetries.py index 01bd3a01..83001fc8 100644 --- a/cellconstructor/symmetries.py +++ b/cellconstructor/symmetries.py @@ -530,12 +530,8 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): assert cart == 3 nat = int(nat3 / 3) - - # Apply hermitianity - #print("Original:") - #print(dM_drdr[:,:,0]) - dM_drdr += np.einsum("abc->bac", dM_drdr) + dM_drdr += np.einsum("abc -> bac", dM_drdr) dM_drdr /= 2 # Apply the Sum Rule @@ -543,19 +539,12 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): for pol in range(3): CustomASR(dM_drdr[:,:,pol]) - #print("After the sum rule:") - #print(dM_drdr[:,:,0]) - - # Convert in crystal coordinates + # Convert in crystal coordinates MAKE SURE THAT THIS IS CONVERTED BACK IN CARTESIAN COORDINATES for i in range(nat): for j in range(nat): dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T) - #print("Crystal:") - #print(dM_drdr[:,:,0]) - - # Apply translations new_dM = np.zeros(np.shape(dM_drdr), dtype = np.double) if self.QE_translation_nr > 1: @@ -568,22 +557,13 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): dM_drdr[:,:,:] = new_dM / self.QE_translation_nr new_dM[:,:,:] = 0 - - #print("After transl:") - #print(dM_drdr[:,:,0]) - - #self.PrintSymmetries() + # # DEBUG VARIABLE + # debug = np.zeros(np.shape(new_dM)) # Apply rotations for i in range(self.QE_nsym): irt = self.QE_irt[i, :] - 1 - - #print("") - #print("--------------------") - #print("symmetry: {:d}, irt: {}".format(i+1, irt +1)) - #prova = np.zeros(np.shape(new_dM)) - for jat in range(nat): for kat in range(nat): new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] @@ -592,14 +572,19 @@ def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): new_mat = np.einsum("ck, ijk -> ijc", self.QE_s[:,:,i], new_mat) new_mat = np.einsum("bj, ijc -> ibc", self.QE_s[:,:,i], new_mat) new_mat = np.einsum("ai, ibc -> abc", self.QE_s[:,:,i], new_mat) - #prova[3*jat:3*jat+3, 3*kat:3*kat+3,:] = new_mat new_dM[3*jat:3*jat+3, 3*kat:3*kat+3,:] += new_mat - #print(np.einsum("abc->cab", prova)) - #print("--------------------") dM_drdr[:,:,:] = new_dM / self.QE_nsym + + # # CONVERT IN CARTESIAN COORDINATES TO DEBUG + # for _i_ in range(nat): + # for _j_ in range(nat): + # debug[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :] = Methods.convert_3tensor_to_cryst(new_dM[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :],\ + # self.QE_at.T, cryst_to_cart = True) + # np.save('CC_new_{}'.format(i), debug) + - # Convert in crystal coordinates + # Convert in cartesian coordinates for i in range(nat): for j in range(nat): dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T, True) From e2b7793e1964d2311840756b62394168abf7849d Mon Sep 17 00:00:00 2001 From: antonio Date: Tue, 7 Feb 2023 16:13:51 +0100 Subject: [PATCH 03/18] Version of CellConstructor to use with tdscha to symmetrize second order IR and Raman --- .../constants-checkpoint.f90 | 154 +++++ .../eff_charge_interp-checkpoint.f90 | 601 ++++++++++++++++++ .../.ipynb_checkpoints/eqvect-checkpoint.f90 | 25 + .../error_handler-checkpoint.f90 | 134 ++++ .../from_matdyn-checkpoint.f90 | 149 +++++ .../get_q_grid_fast-checkpoint.f90 | 49 ++ .../symmetries-checkpoint.py | 3 +- cellconstructor/symmetries.py | 3 +- 8 files changed, 1116 insertions(+), 2 deletions(-) create mode 100644 FModules/.ipynb_checkpoints/constants-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/eff_charge_interp-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 create mode 100644 FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 diff --git a/FModules/.ipynb_checkpoints/constants-checkpoint.f90 b/FModules/.ipynb_checkpoints/constants-checkpoint.f90 new file mode 100644 index 00000000..45119998 --- /dev/null +++ b/FModules/.ipynb_checkpoints/constants-checkpoint.f90 @@ -0,0 +1,154 @@ +! +! Copyright (C) 2002-2006 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +!---------------------------------------------------------------------------- +MODULE constants + !---------------------------------------------------------------------------- + ! + USE kinds, ONLY : DP + ! + ! ... The constants needed everywhere + ! + IMPLICIT NONE + ! + SAVE + ! + ! ... Mathematical constants + ! + REAL(DP), PARAMETER :: pi = 3.14159265358979323846_DP + REAL(DP), PARAMETER :: tpi = 2.0_DP * pi + REAL(DP), PARAMETER :: fpi = 4.0_DP * pi + REAL(DP), PARAMETER :: sqrtpi = 1.77245385090551602729_DP + REAL(DP), PARAMETER :: sqrtpm1= 1.0_DP / sqrtpi + REAL(DP), PARAMETER :: sqrt2 = 1.41421356237309504880_DP + ! + ! ... Physical constants, SI (NIST CODATA 2006), Web Version 5.1 + ! http://physics.nist.gov/constants + REAL(DP), PARAMETER :: H_PLANCK_SI = 6.62606896E-34_DP ! J s + REAL(DP), PARAMETER :: K_BOLTZMANN_SI = 1.3806504E-23_DP ! J K^-1 + REAL(DP), PARAMETER :: ELECTRON_SI = 1.602176487E-19_DP ! C + REAL(DP), PARAMETER :: ELECTRONVOLT_SI = 1.602176487E-19_DP ! J + REAL(DP), PARAMETER :: ELECTRONMASS_SI = 9.10938215E-31_DP ! Kg + REAL(DP), PARAMETER :: HARTREE_SI = 4.35974394E-18_DP ! J + REAL(DP), PARAMETER :: RYDBERG_SI = HARTREE_SI/2.0_DP ! J + REAL(DP), PARAMETER :: BOHR_RADIUS_SI = 0.52917720859E-10_DP ! m + REAL(DP), PARAMETER :: AMU_SI = 1.660538782E-27_DP ! Kg + REAL(DP), PARAMETER :: C_SI = 2.99792458E+8_DP ! m sec^-1 + REAL(DP), PARAMETER :: MUNOUGHT_SI = fpi*1.0E-7_DP ! N A^-2 + REAL(DP), PARAMETER :: EPSNOUGHT_SI = 1.0_DP / (MUNOUGHT_SI * & + C_SI**2) ! F m^-1 + ! + ! ... Physical constants, atomic units: + ! ... AU for "Hartree" atomic units (e = m = hbar = 1) + ! ... RY for "Rydberg" atomic units (e^2=2, m=1/2, hbar=1) + ! + REAL(DP), PARAMETER :: K_BOLTZMANN_AU = K_BOLTZMANN_SI / HARTREE_SI + REAL(DP), PARAMETER :: K_BOLTZMANN_RY = K_BOLTZMANN_SI / RYDBERG_SI + ! + ! ... Unit conversion factors: energy and masses + ! + REAL(DP), PARAMETER :: AUTOEV = HARTREE_SI / ELECTRONVOLT_SI + REAL(DP), PARAMETER :: RYTOEV = AUTOEV / 2.0_DP + REAL(DP), PARAMETER :: AMU_AU = AMU_SI / ELECTRONMASS_SI + REAL(DP), PARAMETER :: AMU_RY = AMU_AU / 2.0_DP + ! + ! ... Unit conversion factors: atomic unit of time, in s and ps + ! + REAL(DP), PARAMETER :: AU_SEC = H_PLANCK_SI/tpi/HARTREE_SI + REAL(DP), PARAMETER :: AU_PS = AU_SEC * 1.0E+12_DP + ! + ! ... Unit conversion factors: pressure (1 Pa = 1 J/m^3, 1GPa = 10 Kbar ) + ! + REAL(DP), PARAMETER :: AU_GPA = HARTREE_SI / BOHR_RADIUS_SI ** 3 & + / 1.0E+9_DP + REAL(DP), PARAMETER :: RY_KBAR = 10.0_DP * AU_GPA / 2.0_DP + ! + ! ... Unit conversion factors: 1 debye = 10^-18 esu*cm + ! ... = 3.3356409519*10^-30 C*m + ! ... = 0.208194346 e*A + ! ... ( 1 esu = (0.1/c) Am, c=299792458 m/s) + ! + REAL(DP), PARAMETER :: DEBYE_SI = 3.3356409519_DP * 1.0E-30_DP ! C*m + REAL(DP), PARAMETER :: AU_DEBYE = ELECTRON_SI * BOHR_RADIUS_SI / & + DEBYE_SI + ! + REAL(DP), PARAMETER :: eV_to_kelvin = ELECTRONVOLT_SI / K_BOLTZMANN_SI + REAL(DP), PARAMETER :: ry_to_kelvin = RYDBERG_SI / K_BOLTZMANN_SI + ! + ! .. Unit conversion factors: Energy to wavelength + ! + REAL(DP), PARAMETER :: EVTONM = 1E+9_DP * H_PLANCK_SI * C_SI / & + &ELECTRONVOLT_SI + REAL(DP), PARAMETER :: RYTONM = 1E+9_DP * H_PLANCK_SI * C_SI / RYDBERG_SI + ! + ! Speed of light in atomic units + ! + REAL(DP), PARAMETER :: C_AU = C_SI / BOHR_RADIUS_SI * AU_SEC + ! + ! ... zero up to a given accuracy + ! + REAL(DP), PARAMETER :: eps4 = 1.0E-4_DP + REAL(DP), PARAMETER :: eps6 = 1.0E-6_DP + REAL(DP), PARAMETER :: eps8 = 1.0E-8_DP + REAL(DP), PARAMETER :: eps12 = 1.0E-12_DP + REAL(DP), PARAMETER :: eps14 = 1.0E-14_DP + REAL(DP), PARAMETER :: eps16 = 1.0E-16_DP + REAL(DP), PARAMETER :: eps24 = 1.0E-24_DP + REAL(DP), PARAMETER :: eps32 = 1.0E-32_DP + ! + REAL(DP), PARAMETER :: gsmall = 1.0E-12_DP + ! + REAL(DP), PARAMETER :: e2 = 2.0_DP ! the square of the electron charge + REAL(DP), PARAMETER :: degspin = 2.0_DP ! the number of spins per level + ! + !!!!!! COMPATIBIILITY + ! + REAL(DP), PARAMETER :: BOHR_RADIUS_CM = BOHR_RADIUS_SI * 100.0_DP + REAL(DP), PARAMETER :: BOHR_RADIUS_ANGS = BOHR_RADIUS_CM * 1.0E8_DP + REAL(DP), PARAMETER :: ANGSTROM_AU = 1.0_DP/BOHR_RADIUS_ANGS + REAL(DP), PARAMETER :: DIP_DEBYE = AU_DEBYE + REAL(DP), PARAMETER :: AU_TERAHERTZ = AU_PS + REAL(DP), PARAMETER :: AU_TO_OHMCMM1 = 46000.0_DP ! (ohm cm)^-1 + REAL(DP), PARAMETER :: RY_TO_THZ = 1.0_DP / AU_TERAHERTZ / FPI + REAL(DP), PARAMETER :: RY_TO_GHZ = RY_TO_THZ*1000.0_DP + REAL(DP), PARAMETER :: RY_TO_CMM1 = 1.E+10_DP * RY_TO_THZ / C_SI + ! + REAL(DP), PARAMETER :: AVOGADRO = 6.02214129D+23 + +END MODULE constants + +! perl script to create a program to list the available constants: +! extract with: grep '^!XX!' constants.f90 | sed 's,!XX!,,' > mkconstlist.pl +! then run: perl mkconstlist.pl constants.f90 > testme.f90 +! and compile and run: testme.f90 +!XX!#!/usr/bin/perl -w +!XX! +!XX!use strict; +!XX! +!XX!print <) { +!XX! if ( /REAL\s*\(DP\)\s*,\s*PARAMETER\s*::\s*([a-zA-Z_0-9]+)\s*=.*$/ ) { +!XX! print " WRITE (*,'(A18,G24.17)') '$1:',$1\n"; +!XX! } +!XX!} +!XX! +!XX!print < add/subtract rigid-ion term + logical :: loto_2d ! 2D LOTO correction + ! + ! local variables + ! + real(DP):: geg, gp2, r ! , For 2d loto: gp2, r + integer :: na,nb, i,j, m1, m2, m3 + integer :: nr1x, nr2x, nr3x + real(DP) :: alph, fac,g1,g2,g3, facgd, arg, gmax, alat_new + real(DP) :: zag(3),zbg(3),zcg(3), fnat(3), reff(2,2) + real :: time1, time2 + complex(dp) :: facg + ! + ! alph is the Ewald parameter, geg is an estimate of G^2 + ! such that the G-space sum is convergent for that alph + ! very rough estimate: geg/4/alph > gmax = 14 + ! (exp (-14) = 10^-6) + ! + call cpu_time(time1) + gmax= 14.d0 + alph= 1.0d0 + geg = gmax*alph*4.0d0 + + ! print *, "" + ! print *, "[RGD_BLK] Q = ", q + ! print *, "[RGD_BLK] NAT:", nat + ! print *, "[RGD_BLK] OMEGA:", omega + ! print *, "[RGD_BLK] ZEU:" + ! do i = 1, nat + ! do j = 1, 3 + ! print *, zeu(:, j, i) + ! enddo + ! end do + ! print *, "[RGD_BLK] ALAT:", alat + ! print *, "[RGD_BLK] BG:" + ! do i = 1, 3 + ! print *, bg(:, i) + ! end do + ! print *, "[RGD_BLK] TAU:" + ! do i = 1, nat + ! print *, tau(:, i) + ! end do + + ! print *, "[RGD_BLK] EPSIL:" + ! do i = 1, 3 + ! print *, epsil(:, i) + ! end do + + ! print *, "[RGD_BLK] DYN:" + ! do na = 1, nat + ! do nb = 1, nat + ! do i = 1, 3 + ! print *, dyn(:, i, na, nb) + ! end do + ! end do + ! end do + + ! Silicon + alat_new = 10.0d0 + + + ! Estimate of nr1x,nr2x,nr3x generating all vectors up to G^2 < geg + ! Only for dimensions where periodicity is present, e.g. if nr1=1 + ! and nr2=1, then the G-vectors run along nr3 only. + ! (useful if system is in vacuum, e.g. 1D or 2D) + ! + if (nr1 == 1) then + nr1x=0 + else + nr1x = int ( sqrt (geg) / & + (sqrt (bg (1, 1) **2 + bg (2, 1) **2 + bg (3, 1) **2) )) + 1 + endif + if (nr2 == 1) then + nr2x=0 + else + nr2x = int ( sqrt (geg) / & + ( sqrt (bg (1, 2) **2 + bg (2, 2) **2 + bg (3, 2) **2) )) + 1 + endif + if (nr3 == 1) then + nr3x=0 + else + nr3x = int ( sqrt (geg) / & + (sqrt (bg (1, 3) **2 + bg (2, 3) **2 + bg (3, 3) **2) )) + 1 + endif + ! + + !print *, "[RGD_BLK] integration grid:", nr1x, nr2x, nr3x + if (abs(sign) /= 1.0_DP) & + call errore ('rgd_blk',' wrong value for sign ',1) + ! + IF (loto_2d) THEN + fac = sign*e2*fpi/omega*0.5d0*alat/bg(3,3) + reff=0.0d0 + DO i=1,2 + DO j=1,2 + reff(i,j)=epsil(i,j)*0.5d0*tpi/bg(3,3) ! (eps)*c/2 in 2pi/a units + ENDDO + ENDDO + DO i=1,2 + reff(i,i)=reff(i,i)-0.5d0*tpi/bg(3,3) ! (-1)*c/2 in 2pi/a units + ENDDO + ELSE + fac = sign*e2*fpi/omega + ENDIF + do m1 = -nr1x,nr1x + do m2 = -nr2x,nr2x + do m3 = -nr3x,nr3x + ! + g1 = m1*bg(1,1) + m2*bg(1,2) + m3*bg(1,3) + g2 = m1*bg(2,1) + m2*bg(2,2) + m3*bg(2,3) + g3 = m1*bg(3,1) + m2*bg(3,2) + m3*bg(3,3) + ! + IF (loto_2d) THEN + geg = g1**2 + g2**2 + g3**2 + r=0.0d0 + gp2=g1**2+g2**2 + IF (gp2>1.0d-8) THEN + r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 + r=r/gp2 + ENDIF + ELSE + geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & + g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & + g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) + ENDIF + ! + if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then + ! + IF (loto_2d) THEN + facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) + ELSE + facgd = fac*exp(-geg/alph/4.0d0)/geg + ENDIF + ! + do na = 1,nat + zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) + fnat(:) = 0.d0 + do nb = 1,nat + arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & + g2 * (tau(2,na)-tau(2,nb))+ & + g3 * (tau(3,na)-tau(3,nb))) + zcg(:) = g1*zeu(1,:,nb) + g2*zeu(2,:,nb) + g3*zeu(3,:,nb) + fnat(:) = fnat(:) + zcg(:)*cos(arg) + end do + do j=1,3 + do i=1,3 + dyn(i,j,na,na) = dyn(i,j,na,na) - facgd * & + zag(i) * fnat(j) + end do + end do + end do + end if + ! + g1 = g1 + q(1) + g2 = g2 + q(2) + g3 = g3 + q(3) + ! + IF (loto_2d) THEN + geg = g1**2+g2**2+g3**2 + r=0.0d0 + gp2=g1**2+g2**2 + IF (gp2>1.0d-8) THEN + r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 + r=r/gp2 + ENDIF + ELSE + geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & + g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & + g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) + ENDIF + ! + if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then + ! + IF (loto_2d) THEN + facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) + ELSE + facgd = fac*exp(-geg/alph/4.0d0)/geg + ENDIF + ! + do nb = 1,nat + zbg(:)=g1*zeu(1,:,nb)+g2*zeu(2,:,nb)+g3*zeu(3,:,nb) + do na = 1,nat + zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) + arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & + g2 * (tau(2,na)-tau(2,nb))+ & + g3 * (tau(3,na)-tau(3,nb))) + ! + facg = facgd * CMPLX(cos(arg),sin(arg),kind=DP) + do j=1,3 + do i=1,3 + dyn(i,j,na,nb) = dyn(i,j,na,nb) + facg * & + zag(i) * zbg(j) + end do + end do + end do + end do + end if + end do + end do + end do + +! call cpu_time(time2) +! print *, "Elapsed time in rgd_blk: ", time2 - time1 + + +! print *, "" +! print *, "[RGD_BLK] DYN FINAL:" +! do na = 1, nat +! do nb = 1, nat +! do i = 1, 3 +! print *, dyn(:, i, na, nb) +! end do +! end do +! end do + +! print *, "" +! print *, "" +! print *, "----------------------------------------------------" + + + + ! + return + ! + end subroutine rgd_blk + + + +! subroutine rgd_blk (nr1,nr2,nr3,nat,dyn,q,tau,epsil,zeu,bg,omega,alat,loto_2d,sign) +! !----------------------------------------------------------------------- +! ! compute the rigid-ion (long-range) term for q +! ! The long-range term used here, to be added to or subtracted from the +! ! dynamical matrices, is exactly the same of the formula introduced in: +! ! X. Gonze et al, PRB 50. 13035 (1994) . Only the G-space term is +! ! implemented: the Ewald parameter alpha must be large enough to +! ! have negligible r-space contribution +! ! +! use kinds, only: dp +! use constants, only: pi,tpi, fpi, e2 +! implicit none +! integer :: nr1, nr2, nr3 ! FFT grid +! integer :: nat ! number of atoms +! complex(DP) :: dyn(3,3,nat,nat) ! dynamical matrix +! real(DP) & +! q(3), &! q-vector +! tau(3,nat), &! atomic positions +! epsil(3,3), &! dielectric constant tensor +! zeu(3,3,nat), &! effective charges tensor +! at(3,3), &! direct lattice basis vectors +! bg(3,3), &! reciprocal lattice basis vectors +! omega, &! unit cell volume +! alat, &! cell dimension units (The dimension of the first unit cell vector in Bohr) +! sign ! sign=+/-1.0 ==> add/subtract rigid-ion term +! logical :: loto_2d ! 2D LOTO correction +! ! +! ! local variables +! ! +! real(DP):: geg, gp2, r ! , For 2d loto: gp2, r +! integer :: na,nb, i,j, m1, m2, m3 +! integer :: nr1x, nr2x, nr3x +! real(DP) :: alph, fac,g1,g2,g3, facgd, arg, gmax, alat_new +! real(DP) :: zag(3),zbg(3),zcg(3), fnat(3), reff(2,2) +! real :: time1, time2 +! complex(dp) :: facg +! ! +! ! alph is the Ewald parameter, geg is an estimate of G^2 +! ! such that the G-space sum is convergent for that alph +! ! very rough estimate: geg/4/alph > gmax = 14 +! ! (exp (-14) = 10^-6) +! ! +! call cpu_time(time1) +! gmax= 14.d0 +! alph= 1.0d0 +! geg = gmax*alph*4.0d0 + +! print *, "" +! print *, "[RGD_BLK] Q = ", q +! print *, "[RGD_BLK] NAT:", nat +! print *, "[RGD_BLK] OMEGA:", omega +! print *, "[RGD_BLK] ZEU:" +! do i = 1, nat +! do j = 1, 3 +! print *, zeu(:, j, i) +! enddo +! end do +! print *, "[RGD_BLK] ALAT:", alat +! print *, "[RGD_BLK] BG:" +! do i = 1, 3 +! print *, bg(:, i) +! end do +! print *, "[RGD_BLK] TAU:" +! do i = 1, nat +! print *, tau(:, i) +! end do +! print *, "[RGD_BLK] EPSIL:" +! do i = 1, 3 +! print *, epsil(:, i) +! end do + +! print *, "[RGD_BLK] DYN:" +! do na = 1, nat +! do nb = 1, nat +! do i = 1, 3 +! print *, dyn(:, i, na, nb) +! end do +! end do +! end do + +! ! Silicon example +! alat_new = 10.0d0 + + + +! ! Estimate of nr1x,nr2x,nr3x generating all vectors up to G^2 < geg +! ! Only for dimensions where periodicity is present, e.g. if nr1=1 +! ! and nr2=1, then the G-vectors run along nr3 only. +! ! (useful if system is in vacuum, e.g. 1D or 2D) +! ! +! if (nr1 == 1) then +! nr1x=0 +! else +! nr1x = int ( sqrt (geg) / & +! (alat_new * sqrt (bg (1, 1) **2 + bg (2, 1) **2 + bg (3, 1) **2) ) ) + 1 +! endif +! if (nr2 == 1) then +! nr2x=0 +! else +! nr2x = int ( sqrt (geg) / & +! (alat_new * sqrt (bg (1, 2) **2 + bg (2, 2) **2 + bg (3, 2) **2) )) + 1 +! endif +! if (nr3 == 1) then +! nr3x=0 +! else +! nr3x = int ( sqrt (geg) / & +! (alat_new * sqrt (bg (1, 3) **2 + bg (2, 3) **2 + bg (3, 3) **2) )) + 1 +! endif +! ! + +! print *, "[RGD_BLK] integration grid:", nr1x, nr2x, nr3x +! call flush() + + +! if (abs(sign) /= 1.0_DP) & +! call errore ('rgd_blk',' wrong value for sign ',1) +! ! +! IF (loto_2d) THEN +! fac = sign*e2*fpi/omega*0.5d0/(alat * bg(3,3)) +! reff=0.0d0 +! DO i=1,2 +! DO j=1,2 +! reff(i,j)=epsil(i,j)*0.5d0*tpi/(alat * bg(3,3)) ! (eps)*c/2 in 2pi/a units +! ENDDO +! ENDDO +! DO i=1,2 +! reff(i,i)=reff(i,i)-0.5d0*tpi/(alat * bg(3,3)) ! (-1)*c/2 in 2pi/a units +! ENDDO +! ELSE +! fac = sign*e2*fpi/omega +! ENDIF +! do m1 = -nr1x,nr1x +! do m2 = -nr2x,nr2x +! do m3 = -nr3x,nr3x +! ! +! g1 = m1*bg(1,1) + m2*bg(1,2) + m3*bg(1,3) +! g2 = m1*bg(2,1) + m2*bg(2,2) + m3*bg(2,3) +! g3 = m1*bg(3,1) + m2*bg(3,2) + m3*bg(3,3) +! ! +! IF (loto_2d) THEN +! geg = g1**2 + g2**2 + g3**2 +! r=0.0d0 +! gp2=g1**2+g2**2 +! IF (gp2>1.0d-8) THEN +! r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 +! r=r/gp2 +! ENDIF +! ELSE +! geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & +! g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & +! g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) +! ENDIF +! ! +! if (geg > 0.0_DP .and. geg /alph/4.0_DP < gmax ) then +! ! +! IF (loto_2d) THEN +! facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) +! ELSE +! facgd = fac*exp(-geg/alph/4.0d0)/geg +! ENDIF +! ! +! do na = 1,nat +! zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) +! fnat(:) = 0.d0 +! do nb = 1,nat +! arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & +! g2 * (tau(2,na)-tau(2,nb))+ & +! g3 * (tau(3,na)-tau(3,nb))) +! zcg(:) = g1*zeu(1,:,nb) + g2*zeu(2,:,nb) + g3*zeu(3,:,nb) +! fnat(:) = fnat(:) + zcg(:)*cos(arg) +! end do +! do j=1,3 +! do i=1,3 +! dyn(i,j,na,na) = dyn(i,j,na,na) - facgd * & +! zag(i) * fnat(j) +! end do +! end do +! end do +! end if +! ! +! g1 = g1 + q(1) +! g2 = g2 + q(2) +! g3 = g3 + q(3) +! ! +! IF (loto_2d) THEN +! geg = g1**2+g2**2+g3**2 +! r=0.0d0 +! gp2=g1**2+g2**2 +! IF (gp2>1.0d-8) THEN +! r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 +! r=r/gp2 +! ENDIF +! ELSE +! geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & +! g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & +! g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) +! ENDIF +! ! +! if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then +! ! +! IF (loto_2d) THEN +! facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) +! ELSE +! facgd = fac*exp(-geg/alph/4.0d0)/geg +! ENDIF +! ! +! do nb = 1,nat +! zbg(:)=g1*zeu(1,:,nb)+g2*zeu(2,:,nb)+g3*zeu(3,:,nb) +! do na = 1,nat +! zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) +! arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & +! g2 * (tau(2,na)-tau(2,nb))+ & +! g3 * (tau(3,na)-tau(3,nb))) +! ! +! facg = facgd * CMPLX(cos(arg),sin(arg),kind=DP) +! do j=1,3 +! do i=1,3 +! dyn(i,j,na,nb) = dyn(i,j,na,nb) + facg * & +! zag(i) * zbg(j) +! end do +! end do +! end do +! end do +! end if +! end do +! end do +! end do + +! call cpu_time(time2) +! print *, "Elapsed time in rgd_blk: ", time2 - time1 + + +! print *, "" +! print *, "[RGD_BLK] DYN FINAL:" +! do na = 1, nat +! do nb = 1, nat +! do i = 1, 3 +! print *, dyn(:, i, na, nb) +! end do +! end do +! end do + +! print *, "" +! print *, "" +! print *, "----------------------------------------------------" + + +! ! +! return +! ! +! end subroutine rgd_blk + + + +!----------------------------------------------------------------------- +subroutine nonanal(nat, nat_blk, itau_blk, epsil, q, zeu, omega, dyn ) + !----------------------------------------------------------------------- + ! add the nonanalytical term with macroscopic electric fields + ! See PRB 55, 10355 (1997) Eq (60) + ! + use kinds, only: dp + use constants, only: pi, fpi, e2 + implicit none + integer, intent(in) :: nat, nat_blk, itau_blk(nat) + ! nat: number of atoms in the cell (in the supercell in the case + ! of a dyn.mat. constructed in the mass approximation) + ! nat_blk: number of atoms in the original cell (the same as nat if + ! we are not using the mass approximation to build a supercell) + ! itau_blk(na): atom in the original cell corresponding to + ! atom na in the supercell + ! + complex(DP), intent(inout) :: dyn(3,3,nat,nat) ! dynamical matrix + real(DP), intent(in) :: q(3), &! polarization vector + & epsil(3,3), &! dielectric constant tensor + & zeu(3,3,nat_blk), &! effective charges tensor + & omega ! unit cell volume + ! + ! local variables + ! + real(DP) zag(3),zbg(3), &! eff. charges times g-vector + & qeq ! + integer na,nb, &! counters on atoms + & na_blk,nb_blk, &! as above for the original cell + & i,j ! counters on cartesian coordinates + ! + qeq = (q(1)*(epsil(1,1)*q(1)+epsil(1,2)*q(2)+epsil(1,3)*q(3))+ & + q(2)*(epsil(2,1)*q(1)+epsil(2,2)*q(2)+epsil(2,3)*q(3))+ & + q(3)*(epsil(3,1)*q(1)+epsil(3,2)*q(2)+epsil(3,3)*q(3))) + ! +!print*, q(1), q(2), q(3) + if (qeq < 1.d-8) then + write(6,'(5x,"A direction for q was not specified:", & + & "TO-LO splitting will be absent")') + return + end if + ! + do na = 1,nat + na_blk = itau_blk(na) + do nb = 1,nat + nb_blk = itau_blk(nb) + ! + do i=1,3 + ! + zag(i) = q(1)*zeu(1,i,na_blk) + q(2)*zeu(2,i,na_blk) + & + q(3)*zeu(3,i,na_blk) + zbg(i) = q(1)*zeu(1,i,nb_blk) + q(2)*zeu(2,i,nb_blk) + & + q(3)*zeu(3,i,nb_blk) + end do + ! + do i = 1,3 + do j = 1,3 + dyn(i,j,na,nb) = dyn(i,j,na,nb)+ fpi*e2*zag(i)*zbg(j)/qeq/omega +! print*, zag(i),zbg(j),qeq, fpi*e2*zag(i)*zbg(j)/qeq/omega + end do + end do + end do + end do + ! + return +end subroutine nonanal + + +SUBROUTINE trasl( phid, phiq, nq, nr1, nr2, nr3, nat, m1, m2, m3 ) + !---------------------------------------------------------------------------- + ! + USE kinds, ONLY : DP + ! + IMPLICIT NONE + INTEGER, intent(in) :: nr1, nr2, nr3, m1, m2, m3, nat, nq + COMPLEX(DP), intent(in) :: phiq(3,3,nat,nat,48) + COMPLEX(DP), intent(out) :: phid(nr1,nr2,nr3,3,3,nat,nat) + ! + INTEGER :: j1,j2, na1, na2 + ! + DO j1=1,3 + DO j2=1,3 + DO na1=1,nat + DO na2=1,nat + phid(m1,m2,m3,j1,j2,na1,na2) = & + 0.5d0 * ( phiq(j1,j2,na1,na2,nq) + & + CONJG(phiq(j2,j1,na2,na1,nq))) + END DO + END DO + END DO + END DO + ! + RETURN + END SUBROUTINE trasl + \ No newline at end of file diff --git a/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 b/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 new file mode 100644 index 00000000..149c65d7 --- /dev/null +++ b/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 @@ -0,0 +1,25 @@ +! +! Copyright (C) 2001-2008 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +!----------------------------------------------------------------------- +logical function eqvect (x, y, f) + !----------------------------------------------------------------------- + ! + ! This function test if the difference x-y-f is an integer. + ! x, y = 3d vectors in crystal axis, f = fractionary translation + ! + implicit none + double precision, intent(in) :: x (3), y (3), f (3) + ! + double precision, parameter :: accep = 1.0d-4 ! acceptance parameter + ! + eqvect = abs( x(1)-y(1)-f(1) - nint(x(1)-y(1)-f(1)) ) < accep .and. & + abs( x(2)-y(2)-f(2) - nint(x(2)-y(2)-f(2)) ) < accep .and. & + abs( x(3)-y(3)-f(3) - nint(x(3)-y(3)-f(3)) ) < accep + ! + return +end function eqvect diff --git a/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 b/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 new file mode 100644 index 00000000..97a79fce --- /dev/null +++ b/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 @@ -0,0 +1,134 @@ +! +! Copyright (C) 2001-2007 Quantum ESPRESSO group +! This file is distributed under the terms of the +! GNU General Public License. See the file `License' +! in the root directory of the present distribution, +! or http://www.gnu.org/copyleft/gpl.txt . +! +!---------------------------------------------------------------------------- +SUBROUTINE errore( calling_routine, message, ierr ) + !---------------------------------------------------------------------------- + ! + ! ... This is a simple routine which writes an error message to output: + ! ... if ierr <= 0 it does nothing, + ! ... if ierr > 0 it stops. + ! + ! ... **** Important note for parallel execution *** + ! + ! ... in parallel execution unit 6 is written only by the first node; + ! ... all other nodes have unit 6 redirected to nothing (/dev/null). + ! ... As a consequence an error not occurring on the first node + ! ... will be invisible. For T3E and ORIGIN machines, this problem + ! ... is solved by writing an error message to unit * instead of 6. + ! ... Whenever possible (IBM SP machines), we write to the standard + ! ... error, unit 0 (the message will appear in the error files + ! ... produced by loadleveler). + ! + ! + IMPLICIT NONE + ! + CHARACTER(LEN=*), INTENT(IN) :: calling_routine, message + ! the name of the calling calling_routine + ! the output message + INTEGER, INTENT(IN) :: ierr + ! the error flag + INTEGER :: crashunit + INTEGER, EXTERNAL :: find_free_unit + CHARACTER(LEN=6) :: cerr + ! + ! + IF ( ierr <= 0 ) RETURN + ! + ! ... the error message is written un the "*" unit + ! + WRITE( cerr, FMT = '(I6)' ) ierr + WRITE( UNIT = *, FMT = '(/,1X,78("%"))' ) + WRITE( UNIT = *, FMT = '(5X,"Error in routine ",A," (",A,"):")' ) & + TRIM(calling_routine), TRIM(ADJUSTL(cerr)) + WRITE( UNIT = *, FMT = '(5X,A)' ) TRIM(message) + WRITE( UNIT = *, FMT = '(1X,78("%"),/)' ) + ! +#if defined (__MPI) && defined (__AIX) + ! + ! ... in the case of ibm machines it is also written on the "0" unit + ! ... which is automatically connected to stderr + ! + WRITE( UNIT = 0, FMT = '(/,1X,78("%"))') + WRITE( UNIT = 0, FMT = '(5X,"Error in routine ",A," (",A,"):")' ) & + TRIM(calling_routine), TRIM(ADJUSTL(cerr)) + WRITE( UNIT = 0, FMT = '(5X,A)' ) TRIM(message) + WRITE( UNIT = 0, FMT = '(1X,78("%"),/)' ) + ! +#endif + ! + WRITE( *, '(" stopping ...")' ) + ! + CALL flush_unit( 6 ) + ! +#ifdef __PTRACE +#ifdef __INTEL + call tracebackqq(user_exit_code=-1) +#else + WRITE( UNIT = 0, FMT = '(5X,A)' ) "Printing strace..." + CALL ptrace() +#endif +#endif +! +#if defined (__MPI) + ! + ! .. write the message to a file and close it before exiting + ! .. this will prevent loss of information on systems that + ! .. do not flush the open streams + ! .. added by C.C. + ! + crashunit = find_free_unit () + OPEN( UNIT = crashunit, FILE = crash_file, & + POSITION = 'APPEND', STATUS = 'UNKNOWN' ) + ! + WRITE( UNIT = crashunit, FMT = '(/,1X,78("%"))' ) + WRITE( UNIT = crashunit, FMT = '(5X,"task #",I10)' ) mpime + WRITE( UNIT = crashunit, & + FMT = '(5X,"from ",A," : error #",I10)' ) calling_routine, ierr + WRITE( UNIT = crashunit, FMT = '(5X,A)' ) message + WRITE( UNIT = crashunit, FMT = '(1X,78("%"),/)' ) + ! + CLOSE( UNIT = crashunit ) + ! + ! ... try to exit in a smooth way + ! + CALL mp_abort ( 1, world_comm ) + ! +#endif + ! + STOP 1 + ! + RETURN + ! +END SUBROUTINE errore +! +!---------------------------------------------------------------------- +SUBROUTINE infomsg( routine, message ) + !---------------------------------------------------------------------- + ! + ! ... This is a simple routine which writes an info message + ! ... from a given routine to output. + ! + USE io_global, ONLY : stdout, ionode + ! + IMPLICIT NONE + ! + CHARACTER (LEN=*) :: routine, message + ! the name of the calling routine + ! the output message + ! + IF ( ionode ) THEN + ! + WRITE( stdout , '(5X,"Message from routine ",A,":")' ) routine + WRITE( stdout , '(5X,A)' ) message + ! + END IF + ! + RETURN + ! +END SUBROUTINE infomsg +! diff --git a/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 b/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 new file mode 100644 index 00000000..1b7443f5 --- /dev/null +++ b/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 @@ -0,0 +1,149 @@ +! +!----------------------------------------------------------------------- +SUBROUTINE frc_blk(dyn,q,tau,nat,nr1,nr2,nr3,frc,at,rws,nrws, nrwsx) + !----------------------------------------------------------------------- + ! calculates the dynamical matrix at q from the (short-range part of the) + ! force constants + ! + USE io_global, ONLY : stdout + ! + IMPLICIT NONE + INTEGER nr1, nr2, nr3, nat, n1, n2, n3, & + ipol, jpol, na, nb, m1, m2, m3, nint, i,j, nrws, nrwsx + double complex, intent(out) :: dyn(3,3,nat,nat) + double precision, intent(in):: frc(nr1,nr2,nr3,3,3,nat,nat), tau(3,nat), q(3), & + at(3,3), rws(4,nrwsx) + + double precision :: r(3), weight, r_ws(3), total_weight, arg + double precision, EXTERNAL :: wsweight + double precision,ALLOCATABLE :: wscache(:,:,:,:,:) + + double precision, parameter :: tpi = 6.283185307179586 + LOGICAL, PARAMETER :: first=.true. + ! + FIRST_TIME : IF (first) THEN + !first=.false. + ALLOCATE( wscache(-2*nr3:2*nr3, -2*nr2:2*nr2, -2*nr1:2*nr1, nat,nat) ) + DO na=1, nat + DO nb=1, nat + total_weight=0.0d0 + ! + DO n1=-2*nr1,2*nr1 + DO n2=-2*nr2,2*nr2 + DO n3=-2*nr3,2*nr3 + DO i=1, 3 + r(i) = n1*at(i,1)+n2*at(i,2)+n3*at(i,3) + r_ws(i) = r(i) + tau(i,na)-tau(i,nb) + END DO + wscache(n3,n2,n1,nb,na) = wsweight(r_ws,rws,nrws, nrwsx) + ENDDO + ENDDO + ENDDO + ENDDO + ENDDO + ENDIF FIRST_TIME + ! + DO na=1, nat + DO nb=1, nat + total_weight=0.0d0 + DO n1=-2*nr1,2*nr1 + DO n2=-2*nr2,2*nr2 + DO n3=-2*nr3,2*nr3 + ! + ! SUM OVER R VECTORS IN THE SUPERCELL - VERY VERY SAFE RANGE! + ! + DO i=1, 3 + r(i) = n1*at(i,1)+n2*at(i,2)+n3*at(i,3) + END DO + + weight = wscache(n3,n2,n1,nb,na) + IF (weight .GT. 0.0d0) THEN + ! + ! FIND THE VECTOR CORRESPONDING TO R IN THE ORIGINAL CELL + ! + m1 = MOD(n1+1,nr1) + IF(m1.LE.0) m1=m1+nr1 + m2 = MOD(n2+1,nr2) + IF(m2.LE.0) m2=m2+nr2 + m3 = MOD(n3+1,nr3) + IF(m3.LE.0) m3=m3+nr3 + ! + ! FOURIER TRANSFORM + ! + arg = tpi*(q(1)*r(1) + q(2)*r(2) + q(3)*r(3)) + DO ipol=1, 3 + DO jpol=1, 3 + dyn(ipol,jpol,na,nb) = & + dyn(ipol,jpol,na,nb) + & + frc(m1,m2,m3,ipol,jpol,na,nb) & + *DCMPLX(COS(arg),-SIN(arg))*weight + END DO + END DO + END IF + total_weight=total_weight + weight + END DO + END DO + END DO + IF (ABS(total_weight-nr1*nr2*nr3).GT.1.0d-8) THEN + WRITE(stdout,*) "Total weight:", total_weight + WRITE(stdout,*) "NR1,2,3 = ", nr1, nr2, nr3 + CALL errore ('frc_blk','wrong total_weight',1) + END IF + END DO + END DO + ! + RETURN +END SUBROUTINE frc_blk +! +!----------------------------------------------------------------------- +subroutine wsinit(rws,nrwsx,nrws,atw) +!----------------------------------------------------------------------- +! + implicit none + double precision, intent(inout) :: rws(4,nrwsx) + double precision, intent(in) :: atw(3,3) + integer, intent(out) :: nrws + integer, intent(in) :: nrwsx + + integer i, ii, ir, jr, kr , nx + double precision eps + parameter (eps=1.0d-6,nx=2) + ii = 1 + do ir=-nx,nx + do jr=-nx,nx + do kr=-nx,nx + do i=1,3 + rws(i+1,ii) = atw(i,1)*ir + atw(i,2)*jr + atw(i,3)*kr + end do + rws(1,ii)=rws(2,ii)*rws(2,ii)+rws(3,ii)*rws(3,ii)+ & + rws(4,ii)*rws(4,ii) + rws(1,ii)=0.5d0*rws(1,ii) + if (rws(1,ii).gt.eps) ii = ii + 1 + if (ii.gt.nrwsx) call errore('wsinit', 'ii.gt.nrwsx',1) + end do + end do + end do + nrws = ii - 1 + return +end subroutine wsinit +!----------------------------------------------------------------------- +function wsweight(r,rws,nrws, nrwsx) +!----------------------------------------------------------------------- +! + implicit none + integer ir, nreq, nrws, nrwsx + double precision, dimension(4, nrwsx), intent(in) :: rws + double precision r(3), rrt, ck, eps, wsweight + parameter (eps=1.0d-7) +! + wsweight = 0.d0 + nreq = 1 + do ir =1,nrws + rrt = r(1)*rws(2,ir) + r(2)*rws(3,ir) + r(3)*rws(4,ir) + ck = rrt-rws(1,ir) + if ( ck .gt. eps ) return + if ( abs(ck) .lt. eps ) nreq = nreq + 1 + end do + wsweight = 1.d0/DBLE(nreq) + return +end function wsweight diff --git a/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 b/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 new file mode 100644 index 00000000..f1fa0969 --- /dev/null +++ b/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 @@ -0,0 +1,49 @@ + +subroutine get_q_grid (bg, supercell_size, q_list, n_size) + + ! Get the q grid of a given cell. + ! BG: reciprocal lattice vectors + ! BG(x, y) is the y-th reciprocal vector, x coordinate + ! + ! SUPERCELL SIZE a vector of int that gives the supercell size + ! + + implicit none + + double precision, dimension(3,3), intent(in) :: bg + integer, dimension(3), intent(in) :: supercell_size + double precision, dimension(3, n_size), intent(out) :: q_list + integer :: n_size + + logical, parameter :: debug = .true. + + + integer :: nr, i1,i2,i3, ka + + + ! Check if the n_size is of the correct value + nr = supercell_size(1) * supercell_size(2) * supercell_size(3) + + + if (nr .ne. n_size) then + print *, "Error, the dimension of q_list must match the supercell" + call flush() + STOP "ERROR IN get_q_grid SUBROUTINE" + endif + + ka = 0 + do i1 = 1, supercell_size(1) + do i2 = 1, supercell_size(2) + do i3 = 1, supercell_size(3) + + ka = ka + 1 + ! Sum the vector + q_list(:, ka) = i1 * bg(:, 1) / supercell_size(1) + & + i2 * bg(:, 2) / supercell_size(2) + & + i3 * bg(:, 3) / supercell_size(3) + + end do + enddo + enddo + + end subroutine get_q_grid \ No newline at end of file diff --git a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py index 83001fc8..8d34ef0f 100644 --- a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py +++ b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py @@ -665,7 +665,8 @@ def ApplySymmetryToSecondOrderRamanTensor(self, dalpha_drdr, apply_asr = True): # ROTATIONS for i in range(self.QE_nsym): - irt = self.QE_irt[i, :] - 1 # the symmetry applied on irt[at] gives the atom at + # the symmetry applied on irt[at] gives the atom at + irt = self.QE_irt[i, :] - 1 for at1 in range(nat): for at2 in range(nat): diff --git a/cellconstructor/symmetries.py b/cellconstructor/symmetries.py index 83001fc8..8d34ef0f 100644 --- a/cellconstructor/symmetries.py +++ b/cellconstructor/symmetries.py @@ -665,7 +665,8 @@ def ApplySymmetryToSecondOrderRamanTensor(self, dalpha_drdr, apply_asr = True): # ROTATIONS for i in range(self.QE_nsym): - irt = self.QE_irt[i, :] - 1 # the symmetry applied on irt[at] gives the atom at + # the symmetry applied on irt[at] gives the atom at + irt = self.QE_irt[i, :] - 1 for at1 in range(nat): for at2 in range(nat): From 5c747796f89859dce199debca59e56ce8abf32d0 Mon Sep 17 00:00:00 2001 From: antonio Date: Fri, 27 Sep 2024 11:37:17 +0200 Subject: [PATCH 04/18] last changes --- FModules/fc_supercell_from_dyn.f90 | 2 +- .../.ipynb_checkpoints/Bands-checkpoint.py | 290 +++ .../.ipynb_checkpoints/Phonons-checkpoint.py | 8 +- .../.ipynb_checkpoints/Spectral-checkpoint.py | 1984 +++++++++++++++++ cellconstructor/Phonons.py | 8 +- out.dat | 1402 ++++++++++++ 6 files changed, 3687 insertions(+), 7 deletions(-) create mode 100644 cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py create mode 100644 cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py create mode 100644 out.dat diff --git a/FModules/fc_supercell_from_dyn.f90 b/FModules/fc_supercell_from_dyn.f90 index d94cb126..752cf39b 100644 --- a/FModules/fc_supercell_from_dyn.f90 +++ b/FModules/fc_supercell_from_dyn.f90 @@ -81,7 +81,7 @@ subroutine fast_ft_real_space_from_dynq(unit_cell_coords, super_cell_coords, ita double complex :: im, phase im = (0.0d0,1.0d0) - twopi = 6.283185307179586d0 + twopi = 6.283185307179586d0 fc_supercell(:,:) = 0.0d0 diff --git a/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py new file mode 100644 index 00000000..26914a6c --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py @@ -0,0 +1,290 @@ +from __future__ import print_function +from __future__ import division + +""" +This module contains the info about the electronic band structure of a particular structure. +With this module it is possible to compute some optical properties. +""" + +import os +import Structure +import Methods + +import numpy as np +import scipy, scipy.interpolate + +try: + import ase, ase.units + kB = ase.units.kB +except: + kB = 8.617330337217213e-05 + +class Bands: + + def __init__(self, structure): + """ + The band class must be associated to a Cellconstructor structure + """ + + self.structure = structure + self.kpts_cryst = None + self.N_k = 0 + self.N_bands = 0 + self.band_energies = None + self.band_occupations = None + + # Here the final values + self.good_Nk = 0 + self.good_kpts = None + self.good_band_energies = None + self.good_occupations = None + + def setup_from_ase_calc(self, ase_calc): + """ + Setup the band structure starting from an ASE calculator object. + """ + + raise NotImplementedError("Error, this function must be implemented.") + pass + + def standard_interpolation(self, new_kgrid, kind = "linear"): + """ + BAND INTERPOLATION + ================== + + This function performs an interpolation of the bands using a regular grid (scipy). + + Parameters + ---------- + new_kgrid : ndarray(NK_new, 3) + + """ + + self.good_kpts = np.copy(new_kgrid) + self.good_Nk, dumb = np.shape(new_kgrid) + self.good_band_energies = np.zeros((self.good_Nk, self.N_bands), dtype = np.double) + self.good_occupations = np.zeros((self.good_Nk, self.N_bands), dtype = np.double) + + for i in range(self.N_bands): + interp_func = scipy.interpolate.RegularGridInterpolator(self.kpts_cryst, self.band_energies[:, i], method = kind) + self.good_band_energies[:, i] = interp_func(self.good_kpts) + + def compute_occupation_numbers(self, T): + """ + OCCUPATION NUMBERS + ================== + + This is the function that computes the occupation number for the interpolated data. + You need to interpolate the data + + Parameters + ---------- + T : float + Temperature (in K) + """ + + raise NotImplementedError("Error, function not yet implemented") + + + def get_band_ipersurface(self, band_index): + """ + Get an 3d representing the band made as [Nx, Ny, Nz] + """ + + tot_kx, tot_ky, tot_kz = self.good_kpts.T + + kx, index = np.unique(tot_kx, return_index = True) + ky, index= np.unique(tot_ky, return_index = True) + kz, index = np.unique(tot_kz, return_index = True) + + + + + + + + def get_group_velocity(self): + """ + Compute the fermi velocity for each band + + + Results + ------- + v_k = ndarray( size = (N_k, N_bands, 3)) + The vector for each band that is the fermi velocity + """ + + raise NotImplementedError("Error, function not yet implemented") + + + def get_conductivity(self, T): + """ + COMPUTE THE STATIC CONDUCTIVITY + =============================== + + This function exploits the non interactive particle model from the Sommerfeld theory to + compute the conductivity. + + It will compute the conductivity per relaxation time as: + + .. math :: + + \sigma(T) = \frac{1}{\Omega N_k} \sum_{nk} \frac{\partial f}{\partial \varepsilon}(T, \varepsilon_{nk}) |v_{nk}|^2 + + where :math:`\Omega` is the unit cell volume, :math:`f` is the fermi occupation function and :math:`v_{nk}` is the + fermi velocity of the nk band. + + NOTE: You must have interpolated the data and computed the occupation numbers. + + Parameters + ---------- + T : float + Temperature, in K + + Results + ------- + conductivity : float + """ + + volume = np.linalg.det(self.structure.unit_cell) # Volume in A^3 + + v_fermi = self.get_fermi_velocity() + + cond = 0 + + raise NotImplementedError("Error, not yet implemented.") + # for i in range(self.N_bands): + # cond += v_fermi[i,:].dot(v_fermi[i, :]) * get_fermi_derivative(self.fermi_energy, + + + + + + def setup_from_espresso_output(self, filename): + """ + Setup the band structure from a quantum espresso standard output (high verbosity requested) + + Notice that the k points must be on a regular crystal grid, and all printed in output . + + Parameters + ---------- + filename : string + path to the QE pw.x output. A non self-consistent calculation is suggested. + """ + + + # Check if the file exists + if not os.path.exists(filename): + raise IOError("Error, the selected file {} does not exist.".format(filename)) + + # Start to read the file + f = open(filename, "r") + lines = [line.strip() for line in f.readlines()] + f.close() + + + reading_k_points = False + reading_bands = False + reading_occupations = False + reading_energies = False + occupation_read = False + + band_index = -1 + occupations = [] + energies = [] + current_array = [] + + kpts = [] + + + for i, line in enumerate(lines): + data = lines.split() + + # Check if we must start reading the k points + if len(kpts) == 0 and reading_k_points == False: + if lines[0] == "cryst.": + reading_k_points = True + + if reading_k_points: + # We finish to read all the k points + if len(line) == 0: + reading_k_points = False + + kpt = np.zeros(3, dtype = np.double) + kpt[0] = float(data[4]) + kpt[1] = float(data[5]) + kpt[2] = float(data[6]) + + kpts.append(kpt) + + if not reading_k_points and "End" in line and "calculation" in line: + # We must start reading the bands + reading_bands = True + continue + + # Check if we must update the band index + if reading_k_points and "bands" in line: + if band_index >= 0: + if not occupation_read: + raise IOError("Error, I have not found the occupation number. Be sure that QE has been executed in high verbosity.\nFile: {}".format(filename)) + occupations.append(current_array) + band_index += 1 + current_array = [] + + # Chekc if we must read the occupation numbers + if reading_k_points and "occupation" in line: + energies.append(current_array) + current_array = [] + occupation_read = True + + if reading_k_points: + # Check if this line is filled by numbers + all_numbers = False + try: + numbers = [float(x) for x in data] + all_numbers = True + except: + pass + + if all_numbers: + for x in data: + current_array.append(float(x)) + + + # Check if we are reading the fermi energy + if "Fermi" in line: + self.fermi_energy = float(data[4]) + reading_k_points = False + occupations.append(current_array) + break + + + # Check if the reading proceded correctly + self.kpts_cryst = np.array(kpts) + self.band_energies = np.array(energies) + self.band_occupations = np.arrany(occupations) + + N_k, dumb = np.shape(self.kpts_cryst) + self.N_k = N_k + + dumb1, N_bands = np.shape(self.band_energies) + dumb2, dumb3 = np.shape(self.band_occupations) + + assert dumb1 == N_k, "The number of k points in input did not match the one in output." + assert N_bands == dumb3, "The number of bands did non match the occupation number" + + + self.N_bands = N_bands + + +def get_fermi_function(energy, mu, T): + return 1 / (np.exp( (energy - mu) / (kB*T)) + 1) + +def get_fermi_derivative(energy, mu, T): + """ + Get the derivative of the fermi function. + """ + + return - get_fermi_function(energy, mu, T)**2 * np.exp( (energy - mu) / (kB*T)) / (kB*T) + + diff --git a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py index 096fb3d5..308136fb 100644 --- a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py +++ b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py @@ -1869,6 +1869,7 @@ def GenerateSupercellDyn(self, supercell_size, img_thr = 1e-6): supercell_size : array int (size=3) the dimension of the cell on which you want to generate the new Phonon + img_thr def 1e-6 Results ------- @@ -3948,6 +3949,7 @@ def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structur itau : Ndarray(nat_sc) , optional the correspondance between the supercell atoms and the unit cell one. If None is recomputed + img_thr: 1e-5 def Returns ------- fc_supercell : ndarray 3nat_sc x 3nat_sc @@ -4007,12 +4009,12 @@ def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structur #print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) # Check the imaginary part - imag = np.sqrt(np.sum(np.imag(fc)**2)) + imag = np.max(np.abs(np.imag(fc))) ASSERT_ERROR = """ Error, the imaginary part of the real space force constant - is not zero. IMAG={} + is not zero. IMAG={} imthr={} """ - assert imag < img_thr, ASSERT_ERROR.format(imag) + assert imag < img_thr, ASSERT_ERROR.format(imag, img_thr) # Remove anyway the imaginary part return fc - 1j*np.imag(fc) diff --git a/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py new file mode 100644 index 00000000..99493a88 --- /dev/null +++ b/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py @@ -0,0 +1,1984 @@ +from __future__ import print_function +from __future__ import division + +import cellconstructor.Phonons as Phonons +import cellconstructor.Methods as Methods +import cellconstructor.symmetries as symmetries + +import numpy as np + +import itertools + +import symph +import thirdorder + +import cellconstructor as CC +import cellconstructor.symmetries +import cellconstructor.Settings +import cellconstructor.Methods as Methods + + +import time + +from cellconstructor.Settings import ParallelPrint as print + + +""" +In this module we compute the Spectral function +using the interpolation on the third order force constant matrix +""" + +# ========================== STATIC ================================================== + +def get_static_bubble(tensor2, tensor3, k_grid, q, T , verbose = False): + """ + COMPUTE THE STATIC BUBBLE + ========================= + + This function computes the static bubble for a given dynamical matrix, + the third order force constant tensor by using the Fourier interpolation + + + Parameters + ---------- + tensor2 : ForceTensor.Tensor2() + The second order force constant + tensor3 : ForceTensor.Tensor3() + The centered third order force costant + k_grid : (nk1, nk2, nk3) + The grid of k points to be used for the integration + q : ndarray(size = 3) + The q point at which compute the bubble. + T : float + The tempearture of the calculation (default 0 K) + verbose : bool + If true print debugging and timing info + + Results + ------- + dynq : ndarray( size = (3*nat, 3*nat), dtype = np.complex128) + The bubble matrix at the specified q point (only bubble). + """ + + structure = tensor2.unitcell_structure + + # Get the integration points + k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) + + + # Get the phi2 in q + phi2_q = tensor2.Interpolate(q, asr = False) + + + # dynamical matrix in q + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + mm_mat = np.sqrt(np.outer(m, m)) + mm_inv_mat = 1 / mm_mat + # + d2_q = phi2_q * mm_inv_mat + + # Diagonalize the dynamical matrix in q + w2_q, pols_q = np.linalg.eigh(d2_q) + + # Check if the q point is gamma + is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) + + if is_q_gamma: + w2_q[0:3]=0.0 + if not (w2_q >= 0.0).all(): + print('q= ',q, ' (2pi/A)') + print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_q=np.sqrt(w2_q) + + # Allocate the memory for the bubble + n_mod=3*structure.N_atoms + tmp_bubble = np.zeros((n_mod, n_mod), + dtype = np.complex128, order = "F") + + def compute_k(k): + + # phi3 in q, k, -q-k + t1 = time.time() + phi3=tensor3.Interpolate(k,-q-k, asr = False) + t2 = time.time() + + # phi2 in k + phi2_k = tensor2.Interpolate(k, asr = False) + + # phi2 in -q-k + phi2_mq_mk = tensor2.Interpolate(-q-k, asr = False) + + t3 = time.time() + + # dynamical matrices (divide by the masses) + d2_k = phi2_k * mm_inv_mat + d2_mq_mk = phi2_mq_mk * mm_inv_mat + + # Diagonalize the dynamical matrices + w2_k, pols_k = np.linalg.eigh(d2_k) + w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) + + is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) + is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) + + if is_k_gamma: + w2_k[0:3]=0.0 + if not (w2_k >= 0.0).all(): + print('k= ',k, ' (2pi/A)') + print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_k=np.sqrt(w2_k) + + if is_mq_mk_gamma: + w2_mq_mk[0:3]=0.0 + if not (w2_mq_mk >= 0.0).all(): + print('-q-k= ',-q-k, ' (2pi/A)') + print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_mq_mk=np.sqrt(w2_mq_mk) + + # Dividing the phi3 by the sqare root of masses + d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) + + # d3 in mode components + # d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) + d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) + d3_pols = np.einsum("ibc, bj -> ijc", d3_pols, pols_k) + d3_pols = np.einsum("ijc, ck -> ijk", d3_pols, pols_mq_mk) + + t4 = time.time() + + # Fortran duty ==== + + tmp_bubble = thirdorder.third_order_bubble.compute_static_bubble(T,np.array([w_q,w_k,w_mq_mk]).T, + np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), + d3_pols, + n_mod=n_mod) + + t5 = time.time() + + if verbose: + print("Time to interpolate the third order: {} s".format(t2 - t1)) + print("Time to interpolate the second order: {} s".format(t3 - t2)) + print("Time to transform the tensors: {} s".format(t4 - t3)) + print("Time to compute the bubble: {} s".format(t5 - t4)) + + return tmp_bubble + + + CC.Settings.SetupParallel() + tmp_bubble = CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") + # divide by the N_k factor + tmp_bubble /= len(k_points) + # bubble in cartesian + #d_bubble = np.einsum("ab, ia, jb -> ij", tmp_bubble, pols_q, np.conj(pols_q)) + + d_bubble = np.einsum("ij, ai -> aj", tmp_bubble, pols_q) + d_bubble = np.einsum("aj, bj -> ab", d_bubble, np.conj(pols_q)) + + # add to the SSCHA dynamical matrix in q + d2_final_q = d2_q + d_bubble + # and mutiply by the masses ( -> FC) + phi2_final_q = d2_final_q * mm_mat + + return phi2_final_q, w_q + +def get_static_correction(dyn, tensor3, k_grid, list_of_q_points, T): + """ + Get the dyn + static bubble correction for the list of q points + """ + dynq = np.zeros( (len(list_of_q_points), 3*dyn.structure.N_atoms, 3*dyn.structure.N_atoms), dtype = np.complex128 ) + + # Prepare the tensor2 + tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) + tensor2.SetupFromPhonons(dyn) + tensor2.Center() + + for iq, q in enumerate(list_of_q_points): + dynq[iq, :, :] = get_static_bubble(tensor2, tensor3, k_grid, np.array(q),T)[0] + + return dynq + +def get_static_correction_interpolated(dyn, tensor3, T, new_supercell, k_grid): + """ + Interpolate the dyn + the v3 tensor on a new supercell. + The dyn and the tensor3 can be defined on different supercells + + Parameters + ---------- + dyn : Phonons() + The harmonic / SSCHA dynamical matrix + tensor3 : Tensor3() + The third order force constant matrix + T : float + The temperature + new_supercell : list(len = 3) + The new supercell on which you want to interpolate the results + k_grid : list(len = 3) + The integration grid on the Brillouin zone + + Results + ------- + dyn_plus_odd : Phonons() + The dynamical matrix that includes the static bubble. + """ + + new_dyn = Phonons.Phonons(dyn.structure) + + q_tot = symmetries.GetQGrid(dyn.structure.unit_cell, new_supercell) + + # Prepare the q points for the new dynamical matrix + new_dyn.q_tot = q_tot + # For now we fill all the q point in the same star (we will adjust them later) + new_dyn.q_stars = [ [x.copy() for x in q_tot] ] + + # Get the dynamical matrix interpolated along each q point + dynq = get_static_correction(dyn, tensor3, k_grid, q_tot, T) + + # Add all the new computed dynamical matrix + for iq in range(len(q_tot)): + new_dyn.dynmats.append(dynq[iq, :, :]) + + # Adjust the dynamical matrix q points and the stars + new_dyn.AdjustQStar() + + return new_dyn + +def get_static_correction_along_path(dyn, + tensor3, + k_grid, + T=0, + q_path=[0.0,0.0,0.0], + q_path_file=None, + print_path = True, + filename_st="v2+d3static_freq.dat", + print_dyn = False, + name_dyn = "sscha_plus_odd_dyn", + d3_scale_factor = None, + tensor2 = None): + """ + Get the dyn + static bubble correction along a given path and prints the SSCHA and the + corrected frequencies in the file filename_st (path length in 2pi/Angstrom, SSCHA frequencies (cm-1), + SSCHA+static odd correction frequencies (cm-1)). + If print_dyn = True, the dyn+static bubble dynamical matrices are printed in QE format. + + Parameters + ---------- + + dyn : Phonons() + The harmonic / SSCHA dynamical matrix + tensor3 : Tensor3() + The third order force constant matrix + k_grid : list(len = 3) + The integration grid on the Brillouin zone + + Optional + -------- + + T : float + The temperature + (default: 0 K) + q_path : list of triplets + Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, + where the caculation is performed + (defualt: [0.0,0.0,0.0]) + q_path_file : string + Name of the file where the q_path can be read. + Format: column of triples, q points in 2pi/Angstrom + If the name of the file is present, it has the + priority over the q_path value + (default: None) + print_path : logical + If True (and the path is composed of more then one q-point), + a file 'path_len.dat' is printed. + Format: column of 4 values, coordinates of + the q-point and path length (in 2pi/Angstrom) . + (default: True) + filename_st : string + File where the result is written. + Format: length of the path (in 2pi/Alat), + SSCHA freq (cm-1),SSCHA+static bubble freq. (cm-1) + (default: "v2+d3static_freq.dat") + print_dyn : logical + If True, the dyn+odd dynamical matrices are printed + for the q-points of the path (in QE format) + (default: False) + name_dyn : string + Prefix of the name of the dyn+odd dynamical matrix printed + name: prefix#q(progressive_number) + (default: "sscha_plus_odd_dyn") + d3_scale_factor : float + If present, the 3rd order FC is multiplied by this factor + (e.g. it can be used to make tests about the perturbative limit) + (default: None) + tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) + If present, this 2nd order FC overwrites the one + obtained from dyn. + (default: None) + + """ + + print(" ") + print(" ====================================" ) + print(" Bubble static correction " ) + print(" ====================================" ) + print(" ") + print(" T= {:>5.1f} K".format(T)) + print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) + print(" ") + + if ( tensor2 == None ): + # Prepare the tensor2 + tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) + tensor2.SetupFromPhonons(dyn) + tensor2.Center() + + # Scale the FC3 =========================================================================== + if d3_scale_factor != None : + print(" ") + print(" d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) + print(" ") + tensor3.tensor=tensor3.tensor*d3_scale_factor + # ================================== q-PATH =============================================== + if q_path_file == None: + q_path=np.array(q_path) + else: + print(" ") + print(" q_path read from "+q_path_file) + print(" ") + q_path=np.loadtxt(q_path_file) + if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) + # Get the length of the q path + x_length = np.zeros(len(q_path)) + q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) + x_length[1:] = q_tot + x_length=np.cumsum(x_length) + x_length_exp=np.expand_dims(x_length,axis=0) + # print the path q-points and length + if print_path and (q_path.shape[0] > 1) : + fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] + result=np.hstack((q_path,x_length_exp.T)) + np.savetxt('path_len.dat',result,fmt=fmt_txt) + print(" ") + print(" Path printed in path_len.dat ") + print(" ") + # ========================================================================================== + + # Mass matrix + m = np.tile(dyn.structure.get_masses_array(), (3,1)).T.ravel() + mm_mat = np.sqrt(np.outer(m, m)) + + # Allocate frequencies array + nat=dyn.structure.N_atoms + n_mod=3 * nat + frequencies = np.zeros((len(q_path), n_mod), dtype = np.float64 ) # SSCHA+odd freq + v2_wq = np.zeros( (len(q_path), n_mod), dtype = np.float64 ) # pure SSCHA freq + + # =============== core calculation =========================================== + if print_dyn: + print(" ") + print(" dyn+odd dynamical matrices printed in "+name_dyn+"#q") + print(" ") + for iq, q in enumerate(q_path): + dynq, v2_wq[iq,:] = get_static_bubble(tensor2=tensor2, tensor3=tensor3, + k_grid=k_grid, q=np.array(q), + T=T, verbose = False) + + w2, pol = np.linalg.eigh(dynq / mm_mat) + frequencies[iq,:] = np.sign(w2)*np.sqrt(np.abs(w2)) + + if print_dyn: + Methods.save_qe(dyn,q,dynq,frequencies[iq,:],pol,fname=name_dyn+str(iq+1)) + # ============================================================================ + + # === print result ================================== + frequencies *= CC.Units.RY_TO_CM + v2_wq *= CC.Units.RY_TO_CM + result=np.hstack((x_length_exp.T,v2_wq,frequencies)) + fmt_txt='%10.6f\t\t'+n_mod*'%11.7f\t'+'\t'+n_mod*'%11.7f\t' + + print(" ") + print(" Results printed in "+filename_st) + print(" ") + head=("------------------------------------------------------------------------" + "\nlen (2pi/Angstrom), sscha freq (cm-1), sscha + static bubble freq (cm-1)" + "\n------------------------------------------------------------------------") + + np.savetxt(filename_st,result,fmt=fmt_txt,header=head) + # ================================================================================== + + + + + + + + + + + + + # ========================= FULL DYNAMIC ========================= + +def get_full_dynamic_bubble(tensor2, tensor3, k_grid, q, + smear_id, smear, energies, + T, + static_limit, + notransl, diag_approx, + verbose = False ): + + + """ + COMPUTE THE FULL DYNAMIC BUBBLE + ========================= + + This function computes the dynamic bubble for a given dynamical matrix, + the third order force constant tensor by using the Fourier interpolation + + + Parameters + ---------- + tensor2 : ForceTensor.Tensor2() + The second order force constant + tensor3 : ForceTensor.Tensor3() + The centered third order force costant + k_grid : (nk1, nk2, nk3) + The grid of k points to be used for the integration + q : ndarray(size = 3) + The q point at which compute the bubble. + T : float + The tempearture of the calculation (default 0 K) + asr : bool + If true, impose the acoustic sum rule during the Fourier transform + verbose : bool + If true print debugging and timing info + + Results + ------- + dynq : ndarray( size = (3*nat, 3*nat), dtype = np.complex128) + The bubble matrix at the specified q point (only bubble). + """ + + structure = tensor2.unitcell_structure + + # Get the integration points + k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) + + + # Get the phi2 in q + phi2_q = tensor2.Interpolate(q, asr = False) + + + # dynamical matrix in q + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + mm_mat = np.sqrt(np.outer(m, m)) + mm_inv_mat = 1 / mm_mat + # + d2_q = phi2_q * mm_inv_mat + + # Diagonalize the dynamical matrix in q + w2_q, pols_q = np.linalg.eigh(d2_q) + + # Check if the q point is gamma + is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) + + if is_q_gamma: + w2_q[0:3]=0.0 + if not (w2_q >= 0.0).all(): + print('q= ',q, ' (2pi/A)') + print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_q=np.sqrt(w2_q) + + # Allocate the memory for the bubble + ne=energies.shape[0] + nsm=smear.shape[0] + tmp_bubble = np.zeros((ne,nsm,3*structure.N_atoms, 3*structure.N_atoms), + dtype = np.complex128, order = "F") + + + def compute_k(k): + # phi3 in q, k, -q - k + t1 = time.time() + phi3=tensor3.Interpolate(k,-q-k, asr = False) + t2 = time.time() + # phi2 in k + phi2_k = tensor2.Interpolate(k, asr = False) + + # phi2 in -q-k + phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) + + t3 = time.time() + + # dynamical matrices (divide by the masses) + d2_k = phi2_k * mm_inv_mat + d2_mq_mk = phi2_mq_mk * mm_inv_mat + + # Diagonalize the dynamical matrices + w2_k, pols_k = np.linalg.eigh(d2_k) + w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) + + + is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) + is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) + + if is_k_gamma: + w2_k[0:3]=0.0 + if not (w2_k >= 0.0).all(): + print('k= ',k, ' (2pi/A)') + print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_k=np.sqrt(w2_k) + + if is_mq_mk_gamma: + w2_mq_mk[0:3]=0.0 + if not (w2_mq_mk >= 0.0).all(): + print('-q-k= ',-q-k, ' (2pi/A)') + print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_mq_mk=np.sqrt(w2_mq_mk) + + # Dividing the phi3 by the sqare root of masses + d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) + + # d3 in mode components + #d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) + d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) + d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) + d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) + + t4 = time.time() + + # Fortran duty ==== + tmp_bubble = thirdorder.third_order_bubble.compute_dynamic_bubble(energies,smear,static_limit,T, + np.array([w_q,w_k,w_mq_mk]).T, + np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), + d3_pols,diag_approx,ne,nsm,n_mod=3*structure.N_atoms) + + t5 = time.time() + + if verbose: + print("Time to interpolate the third order: {} s".format(t2 - t1)) + print("Time to interpolate the second order: {} s".format(t3 - t2)) + print("Time to transform the tensors: {} s".format(t4 - t3)) + print("Time to compute the bubble: {} s".format(t5 - t4)) + + return tmp_bubble + + + CC.Settings.SetupParallel() + d_bubble_mod = CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") + # divide by the N_k factor + d_bubble_mod /= len(k_points) # (ne,nsmear,3nat,3nat) + # the self-energy bubble in cartesian coord, divided by the sqare root of masses + d_bubble_cart = np.einsum("pqab, ia, jb -> pqij", d_bubble_mod, pols_q, np.conj(pols_q)) + # get the spectral function + no_gamma_pick=bool(is_q_gamma*notransl) + # + if no_gamma_pick : + print(" ") + print(" The acoustic pick in Gamma is discarded ") + print(" ") + # + # + spectral_func=thirdorder.third_order_bubble.compute_spectralf(smear_id, + energies, + d2_q, + d_bubble_cart, + no_gamma_pick, + structure.get_masses_array(), + structure.N_atoms,ne,nsm) + + return spectral_func + + +def get_full_dynamic_correction_along_path(dyn, + tensor3, + k_grid, + e1, de, e0, + sm1, sm0, + sm1_id, sm0_id, + nsm=1, + T=0, + q_path=[0.0,0.0,0.0], + q_path_file=None, + print_path = True, + static_limit = False, + notransl = True, + diag_approx = False, + filename_sp='full_spectral_func', + d3_scale_factor=None, + tensor2 = None): + + """ + Get the spectral function for a list of energies, and several q along a given path. + The calculations are performed for several values of smearings to calculate the self-energy + and the Green function. The resuls is printed in the file + filename_sp_[id_smear]_[smear].dat (path length in 2pi/Angstrom, energies (cm-1), + spectral function (1/cm-1)). + + Parameters + ---------- + + dyn : Phonons() + The harmonic / SSCHA dynamical matrix + tensor3 : Tensor3() + The third order force constant matrix + k_grid : list(len = 3) + The integration grid on the Brillouin zone + e1, de ,e0: float + The list of energies considered (cm-1), from e0 to e1, with interval de + sm0, sm1 : float + Minimum and maximum value of the smearing (cm-1) to compute the self-energy + sm0_id, sm1_id : float + Minimum and maximum value of the smearing (cm-1) for the term of the Green function + proportional to the identity + + Optional + -------- + + nsm : integer + Number of smearings to consider + (default = 1) + T : float + The temperature + (default: 0 K) + q_path : list of triplets + Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, + where the caculation is performed + (defualt: [0.0,0.0,0.0]) + q_path_file : string + Name of the file where the q_path can be read. + Format: column of triples, q points in 2pi/Angstrom + If the name of the file is present, it has the + priority over the q_path value + (default: None) + print_path : logical + If True (and the path is composed of more then one q-point), + a file 'path_len.dat' is printed. + Format: column of 4 values, coordinates of + the q-point and path length (in 2pi/Angstrom) . + (default: True) + static limit : logical + If True the self-energy is evaluated at E=0. + The spectral function is given by delta peaks in correspondence + of the frequencies of the sscha + static bubble correction + (default : False) + notransl : logical + If True, the contribution to the spectral function given by the acoustic + phonons in Gamma is discarded. + (defaul = True) + diag approx : logical + If True, the off-diagonal terms of the slef-energy are discarded + (the same result can be obtained in a cheaper way by using the + corresponding function) + (default : False) + filename_sp : string + filename_sp_[id_smear]_[smear].dat + is the file where the result is written. + Format: length of the path (in 2pi/Alat), + energy (cm-1),spectral function (1/cm-1) + (default: "full_spectral_func") + d3_scale_factor : float + If present, the 3rd order FC is multiplied by this factor + (e.g. it can be used to make tests about the perturbative limit) + (default: None) + tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) + If present, this 2nd order FC overwrites the one + obtained from dyn. + (default: None) + + """ + + print(" ") + print(" ===========================================" ) + print(" Bubble full dynamic correction " ) + print(" ===========================================" ) + print(" ") + print(" T= {:>5.1f} K".format(T)) + print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) + if static_limit : + print(" ") + print(" - The static limit is considered - ") + print(" ") + if diag_approx : + print(" ") + print(" - The off-diagonal terms of the self-energy are discarded - ") + print(" ") + + + if ( tensor2 == None ): + + # Prepare the tensor2 + tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) + tensor2.SetupFromPhonons(dyn) + tensor2.Center() + + # Scale the FC3 =========================================================================== + if d3_scale_factor != None : + print(" ") + print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) + print(" ") + tensor3.tensor=tensor3.tensor*d3_scale_factor + # ================================== q-PATH =============================================== + if q_path_file == None: + q_path=np.array(q_path) + else: + print(" ") + print(" q_path read from "+q_path_file) + print(" ") + q_path=np.loadtxt(q_path_file) + if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) + # Get the length of the q path + x_length = np.zeros(len(q_path)) + q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) + x_length[1:] = q_tot + x_length=np.cumsum(x_length) + x_length_exp=np.expand_dims(x_length,axis=0) + # print the path q-points and length + if print_path and (q_path.shape[0] > 1) : + fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] + result=np.hstack((q_path,x_length_exp.T)) + np.savetxt('path_len.dat',result,fmt=fmt_txt) + print(" ") + print(" Path printed in path_len.dat ") + print(" ") + # ========================================================================================== + + # ======================= Energy & Smearing ========================================== + # energy in input is in cm-1 + # smearing in input is in cm-1 + # converto to Ry + + # list of energies + energies=np.arange(e0,e1,de)/CC.Units.RY_TO_CM + ne=energies.shape[0] + # list of smearing + if nsm == 1 : + sm1=sm0 + sm1_id=sm0_id + smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM + smear_id=np.linspace(sm0_id,sm1_id,nsm)/CC.Units.RY_TO_CM + # ========================================================================================== + # + # + spectralf = np.zeros( (ne, nsm), dtype = np.float64 ) + # + smear_cm = smear * CC.Units.RY_TO_CM + smear_id_cm = smear_id * CC.Units.RY_TO_CM + energies_cm = energies * CC.Units.RY_TO_CM + for iq, q in enumerate(q_path): + spectralf[:, :] = get_full_dynamic_bubble(tensor2, tensor3, k_grid, np.array(q), + smear_id, smear, energies, T, + static_limit, notransl , + diag_approx, verbose=False ) + + # convert from 1/Ry to 1/cm-1 + spectralf /= CC.Units.RY_TO_CM + + + # ================================================================================== + + # print the result + for ism in range(nsm): + # + name="{:5.2f}".format(smear_id_cm[ism]).strip()+"_"+"{:6.1f}".format(smear_cm[ism]).strip() + # + filename_new=filename_sp+'_'+name+'.dat' + if iq == 0: + with open(filename_new,'w') as f: + f.write(" # ------------------------------------------------------------- \n") + f.write(" # len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1) \n") + f.write(" # ------------------------------------------------------------- \n") + for ie, ene in enumerate(energies_cm): + f.write("{:>10.6f}\t{:>11.7f}\t{:>11.7f}\n".format(x_length[iq],ene,spectralf[ie,ism])) + f.write("\n") + else: + with open(filename_new,'a') as f: + for ie, ene in enumerate(energies_cm): + f.write("{:>10.6f}\t{:>11.7f}\t{:>11.7f}\n".format(x_length[iq],ene,spectralf[ie,ism])) + f.write("\n") + + + + print(" ") + print(" Results printed in "+filename_sp+'_[id_smear]_[smear].dat') + print(" ") + + + + + + + +# ========================= DIAGONAL SELF-ENERGY DYNAMIC CORRECTION ========================= + +def get_diag_dynamic_bubble(tensor2, + tensor3, + k_grid, + q, + smear_id, + smear, + energies, + T, + verbose = False ): + + + structure = tensor2.unitcell_structure + + # Get the integration points + k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) + + + # Get the phi2 in q + phi2_q = tensor2.Interpolate(q, asr = False) + + + # dynamical matrix in q + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + mm_mat = np.sqrt(np.outer(m, m)) + mm_inv_mat = 1 / mm_mat + # + d2_q = phi2_q * mm_inv_mat + + # Diagonalize the dynamical matrix in q + w2_q, pols_q = np.linalg.eigh(d2_q) + + # Check if the q point is gamma + is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) + + if is_q_gamma: + w2_q[0:3]=0.0 + if not (w2_q >= 0.0).all(): + print('q= ',q, ' (2pi/A)') + print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_q=np.sqrt(w2_q) + + # Allocate the memory for the bubble + ne=energies.shape[0] + nsm=smear.shape[0] + nat=structure.N_atoms + n_mod=3*nat + tmp_bubble = np.zeros((ne,nsm,n_mod), + dtype = np.complex128, order = "F") + + + def compute_k(k): + # phi3 in q, k, -q - k + t1 = time.time() + phi3=tensor3.Interpolate(k,-q-k, asr = False) + t2 = time.time() + # phi2 in k + phi2_k = tensor2.Interpolate(k, asr = False) + + # phi2 in -q-k + phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) + + t3 = time.time() + + # dynamical matrices (divide by the masses) + d2_k = phi2_k * mm_inv_mat + d2_mq_mk = phi2_mq_mk * mm_inv_mat + + # Diagonalize the dynamical matrices + w2_k, pols_k = np.linalg.eigh(d2_k) + w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) + + + is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) + is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) + + if is_k_gamma: + w2_k[0:3]=0.0 + if not (w2_k >= 0.0).all(): + print('k= ',k, ' (2pi/A)') + print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_k=np.sqrt(w2_k) + + if is_mq_mk_gamma: + w2_mq_mk[0:3]=0.0 + if not (w2_mq_mk >= 0.0).all(): + print('-q-k= ',-q-k, ' (2pi/A)') + print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_mq_mk=np.sqrt(w2_mq_mk) + + # Dividing the phi3 by the sqare root of masses + d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) + + # d3 in mode components + #d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) + d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) + d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) + d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) + + t4 = time.time() + + + # Fortran duty ==== + + # + tmp_bubble = thirdorder.third_order_bubble.compute_diag_dynamic_bubble(energies,smear,T, + np.array([w_q,w_k,w_mq_mk]).T, + np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), + d3_pols,ne,nsm,n_mod=n_mod) + + t5 = time.time() + + if verbose: + print("Time to interpolate the third order: {} s".format(t2 - t1)) + print("Time to interpolate the second order: {} s".format(t3 - t2)) + print("Time to transform the tensors: {} s".format(t4 - t3)) + print("Time to compute the bubble: {} s".format(t5 - t4)) + + return tmp_bubble + + CC.Settings.SetupParallel() + + d_bubble_mod =CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") + + # divide by the N_k factor + d_bubble_mod /= len(k_points) # (ne,nsmear,n_mod) + # + np.savetxt("db_new",d_bubble_mod[:,-1,-1]) + # + spectralf=thirdorder.third_order_bubble.compute_spectralf_diag(smear_id,energies,w_q, + d_bubble_mod, + nat,ne,nsm) + # (ne, n_mod, nsmear) + + w2_q_ext=w2_q[None,None,...] + z=np.sqrt(d_bubble_mod + w2_q_ext) # (A20) PHYSICAL REVIEW B 97, 214101 (2018) + + w_q_ext=w_q[None,None,...] + z_pert=w_q_ext+np.divide(d_bubble_mod, 2*w_q_ext, out=np.zeros_like(d_bubble_mod), where=w_q_ext!=0) + + return spectralf, z, z_pert, w_q + + +def get_diag_dynamic_correction_along_path(dyn, tensor3, + k_grid, + e1, de, e0, + sm1, sm0, + sm1_id=None, sm0_id=None, + nsm=1, + q_path=[0.0,0.0,0.0], + q_path_file=None, + print_path = True, + T=0.0, + filename_sp = 'spectral_func', + filename_z = None, + filename_freq_dyn = 'freq_dynamic', + filename_shift_lw = 'v2_freq_shift_hwhm', + self_consist = False, + iterative=False, + numiter=200, + d3_scale_factor=None, + tensor2 = None): + + + """ + Get the spectral function for a list of energies, and several q along a given path, + in the diagonal approximation (off-diagonal terms of the self-energies are discarded). + The calculations are performed for several values of smearings to calculate the self-energy + and the Green function. The resuls is printed in the file + filename_sp_[id_smear]_[smear].dat (path length in 2pi/Angstrom, energies (cm-1), + spectral function (1/cm-1), mode components of the spectral function (1/cm-1) ). + The Z function [PRB 97 214101 (A20)] is also printed in filename_z_[id_smear]_[smear].dat. + The frequency shift (with respect to the SSCHA frequency) and linewidth are computed in three ways + (one optional). 1. One shot, evaluating the Z function in the SSCHA frequency value.2. Perturbative, + evaluating the perturbative correction. 3. (optional) solving the self-consistent relation (details + in [PRB 97 214101 (A21)]). The corresponding Lorenzian spectral functions are then printed. + + Parameters + ---------- + + dyn : Phonons() + The harmonic / SSCHA dynamical matrix + tensor3 : Tensor3() + The third order force constant matrix + k_grid : list(len = 3) + The integration grid on the Brillouin zone + e1, de ,e0: float + The list of energies considered (cm-1), from e0 to e1, with interval de + sm0, sm1 : float + Minimum and maximum value of the smearing (cm-1) to compute the self-energy + + Optional + -------- + + nsm : integer + Number of smearings to consider + (default = 1) + T : float + The temperature + (default: 0 K) + sm0_id, sm1_id : float + Minimum and maximum value of the smearing (cm-1) for the term of the Green function + proportional to the identity. If not present, it is sm0_id = sm1_id = 2.0 * de + (default: None) + q_path : list of triplets + Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, + where the caculation is performed + (defualt: [0.0,0.0,0.0]) + q_path_file : string + Name of the file where the q_path can be read. + Format: column of triples, q points in 2pi/Angstrom + If the name of the file is present, it has the + priority over the q_path value + (default: None) + print_path : logical + If True (and the path is composed of more then one q-point), + a file 'path_len.dat' is printed. + Format: column of 4 values, coordinates of + the q-point and path length (in 2pi/Angstrom) . + (default: True) + filename_sp : string + filename_sp_[smear].dat + is the file where the spectral function is written. + Format: length of the path (in 2pi/Alat), + energy (cm-1),spectral function (1/cm-1), + single mode contributions to spectral function (1/cm-1) + (default: "spectral_func") + filename_z : string + if present, the file + filename_z_[smear].dat + with the z function is written + Format: length of the path (in 2pi/Alat), + energy (cm-1), z function (cm-1), + (default: None) + filename_shift_lw : string + filename_shift_lw_[method]_[smear].dat + is the file where + len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) + are printed. [method] are "one shot", "perturb" and "self-consist" + (the last one optional) + (default: "v2_freq_shift_hwhm") + filename_freq_dyn : string + filename_freq_dyn_[method]_[smear].dat + is the file where + len (2pi/Angstrom), freq (cm-1) (sorted in ascending order), corresponding HWHM (cm-1) + are printed. [method] are "one shot", "perturb" and "self-consist" + (the last one optional) + (default: "freq_dynamic") + self_consist : Logical + If True, the dynamical frequency is found solving the self-consistent + relation [PRB 97 214101 (A21)] + (default: False) + iterative : Logical + If True, the self-consistent relation is found iteratively + (default: False) + numiter : integer + Number of maximum steps to find the self-consistency iteratively + (default : 200) + d3_scale_factor : float + If present, the 3rd order FC is multiplied by this factor + (e.g. it can be used to make tests about the perturbative limit) + (default: None) + tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) + If present, this 2nd order FC overwrites the one + obtained from dyn. + (default: None) + + """ + + + + print(" ") + print(" ===========================================" ) + print(" Bubble diagonal dynamic correction " ) + print(" ===========================================" ) + print(" ") + print(" T= {:>5.1f} K".format(T)) + print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) + print(" ") + print(" Smearing values: ") + for sm in np.linspace(sm0,sm1,nsm): + print(" sm= {:>6.2f} cm-1".format(sm)) + print(" ") + print(" ===========================================" ) + print(" " ) + + + if sm1_id != None and sm0_id != None: + for sm in np.linspace(sm0_id,sm1_id,nsm): + print(" sm_id= {:>6.2f} cm-1".format(sm)) + else: + sm1_id=de*2.0 + sm0_id=de*2.0 + + if ( tensor2 == None ): + + # Prepare the tensor2 + tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) + tensor2.SetupFromPhonons(dyn) + tensor2.Center() + structure = tensor2.unitcell_structure + + + + # Scale the FC3 =========================================================================== + if d3_scale_factor != None : + print(" ") + print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) + print(" ") + tensor3.tensor=tensor3.tensor*d3_scale_factor + # ================================== q-PATH =============================================== + if q_path_file == None: + q_path=np.array(q_path) + else: + print(" ") + print(" q_path read from "+q_path_file) + print(" ") + q_path=np.loadtxt(q_path_file) + if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) + # Get the length of the q path + x_length = np.zeros(len(q_path)) + q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) + x_length[1:] = q_tot + x_length=np.cumsum(x_length) + x_length_exp=np.expand_dims(x_length,axis=0) + # print the path q-points and length + if print_path and (q_path.shape[0] > 1) : + fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] + result=np.hstack((q_path,x_length_exp.T)) + np.savetxt('path_len.dat',result,fmt=fmt_txt) + print(" ") + print(" Path printed in path_len.dat ") + print(" ") + # ======================= Energy & Smearing ========================================== + # + # energy in input is in cm-1 + # smearing in input is in cm-1 + # converto to Ry + + # list of energies + energies=np.arange(e0,e1,de)/CC.Units.RY_TO_CM + ne=energies.shape[0] + # list of smearing + if nsm == 1 : + sm1=sm0 + sm1_id=sm0_id + smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM + smear_id=np.linspace(sm0_id,sm1_id,nsm)/CC.Units.RY_TO_CM + # + # ========================================================================================== + # + + def Lorenz(x,x0,G): + return G/((x-x0)**2+G**2)/np.pi/2.0 + def findne(val,e0,de): + #return int( round( ((val-e0)/de)+1 ) ) + return int(round( (val-e0)/de ) ) + # + print(" ") + #print(" Spectral function, in diagonal approximation, printed in "+filename_sp+"_[smear_id]_[smear].dat") + print(" Spectral function, in diagonal approximation, printed in "+filename_sp+"_[smear].dat") + print(" ") + if filename_z != None: + print(" ") + print(" Z function [PRB 97 214101 (A21)], printed in "+filename_z+"_[smear].dat") + print(" ") + + print(" ========================================= ") + print(" Frequncies shifts and widths calculations ") + print(" ========================================= ") + print(" ") + print(" Frequencies shifts and linewidths computed with perturbative approximation and one-shot calculation in: ") + print(" ") + #print(" "+filename_shift_lw +"_perturb_[smear_id]_[smear].dat") + #print(" "+filename_shift_lw +"_one_shot_[smear_id]_[smear].dat") + print(" "+filename_shift_lw +"_perturb_[smear].dat") + print(" "+filename_shift_lw +"_one_shot_[smear].dat") + print(" ") + print(" ") + print(" Dynamical frequencies sorted, with HWHM: ") + print(" ") + #print(" "+filename_freq_dyn +"_perturb_[smear_id]_[smear].dat") + #print(" "+filename_freq_dyn +"_one_shot_[smear_id]_[smear].dat") + print(" "+filename_freq_dyn +"_perturb_[smear].dat") + print(" "+filename_freq_dyn +"_one_shot_[smear].dat") + print(" ") + print(" ") + print(" Relative spectral functions in Lorentzian approximation: ") + print(" ") + #print(" "+filename_sp+"_lorentz_perturb_[smear_id]_[smear].dat") + #print(" "+filename_sp+"_one_shot_[smear_id]_[smear].dat") + print(" "+filename_sp+"_lorentz_perturb_[smear].dat") + print(" "+filename_sp+"_one_shot_[smear].dat") + print(" ") + if self_consist: + print(" ************************************************ ") + print(" Self-consistent search for dynamical frequencies ") + print(" ************************************************ ") + print(" ") + print(" Results printed in: ") + print(" ") + #print(" "+filename_shift_lw +"_[smear_id]_[smear].dat") + print(" "+filename_shift_lw +"_self-consist_[smear].dat") + print(" ") + print(" ") + #print(" "+filename_freq_dyn +"_[smear_id]_[smear].dat") + print(" "+filename_freq_dyn +"_self-consist_[smear].dat") + print(" ") + print(" ") + #print(" "+filename_sp+"_lorentz_[smear_id]_[smear].dat") + print(" "+filename_sp+"_lorentz_self-consist_[smear].dat") + print(" ") + print(" ") + # convert from Ry to cm-1 + smear_cm = smear * CC.Units.RY_TO_CM + smear_id_cm = smear_id * CC.Units.RY_TO_CM + energies_cm = energies * CC.Units.RY_TO_CM + + n_mod=3*dyn.structure.N_atoms + # + spectralf = np.zeros( (ne,n_mod,nsm), dtype=np.float64 ) + z = np.zeros( (ne,nsm,n_mod) , dtype=np.complex128 ) + z_pert = np.zeros( (ne,nsm,n_mod), dtype = np.complex128 ) + wq = np.zeros( n_mod, dtype = np.float64 ) + # + + for iq, q in enumerate(q_path): + + spectralf, z , z_pert, wq = get_diag_dynamic_bubble(tensor2, tensor3, + k_grid, np.array(q), + smear_id, smear, energies, + T, verbose=False ) + + + # + z *= CC.Units.RY_TO_CM + z_pert *= CC.Units.RY_TO_CM + wq *= CC.Units.RY_TO_CM + # convert from 1/Ry to 1/cm-1 + spectralf /= CC.Units.RY_TO_CM + + + + + + for ism in range(nsm): + # + # pre-name for writing data + # + #name="{:5.2f}".format(smear_id_cm[ism]).strip()+"_"+"{:6.1f}".format(smear_cm[ism]).strip()# + name="{:6.2f}".format(smear_cm[ism]).strip() + # + # write spectral and z function + # + # ======= + # spectral func + # ======= + filename_new=filename_sp+'_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" + + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ---------------------------------------------------------------------------------------------------------\n") + f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1)\n") + f.write("# ---------------------------------------------------------------------------------------------------------\n") + for ie, ene in enumerate(energies_cm): + out=spectralf[ie,:,ism] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + else: + with open(filename_new,'a') as f: + for ie, ene in enumerate(energies_cm): + out=spectralf[ie,:,ism] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + # ======= + # z func + # ======= + if filename_z != None: + filename_new=filename_z+'_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod)+"\n" + + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ---------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), energy (cm-1), z function (cm-1) \n") + f.write("# ---------------------------------------------------- \n") + for ie, ene in enumerate(energies_cm): + out=z[ie,ism,:] + f.write(fmt.format(x_length[iq],ene,*out)) + else: + with open(filename_new,'a') as f: + for ie, ene in enumerate(energies_cm): + out=z[ie,ism,:] + f.write(fmt.format(x_length[iq],ene,*out)) + + # ====================================== + # compute frequency shift and linewidth + # ====================================== + + if self_consist: + res=np.zeros((n_mod,2),dtype=np.float64) # self-consist shifted freq and linewidth + res_os=np.zeros((n_mod,2),dtype=np.float64) # one-shot shifted freq and linewidth + res_pert=np.zeros((n_mod,2),dtype=np.float64) # perturbative shifted freq and linewidth + + is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q_path[iq]) + for ifreq in range(n_mod): + done=False + if iterative : + # + freqold=wq[ifreq] + freqoldold=freqold + for i in range(numiter): + x=findne(freqold,e0,de) + if i==0: xtriv=x + freqshifted=np.real(z[x-1,ism,ifreq]) # Re(z) is the shifted freq + if abs(freqshifted-freqold)< 2*de: + done=True + break + else: + freqoldold=freqold + freqold=freqshifted + # + else: + xtriv=findne(wq[ifreq],e0,de) + osval=np.real(z[xtriv-1,ism,ifreq]) + diff=np.infty + for x in range(ne): + value=np.real(z[x,ism,ifreq])-energies_cm[x] + if( abs(value ) < 2*de) : + if ( 1.0 < abs(energies_cm[x]) or ( is_q_gamma and ifreq < 3 ) ): + done=True + if ( abs( energies_cm[x]-osval ) < diff ): + diff=abs( energies_cm[x]-osval ) + freqshifted=energies_cm[x] + + + # + if done: + # + res[ifreq,0]=freqshifted + x=findne(freqshifted,e0,de) + res[ifreq,1]=-np.imag(z[x-1,ism,ifreq]) + # + else: + # + print(" Self-consistency for the {:5d}-th mode of the {:5d}-th q-point not reached. " + "One-shot approx. value used".format(ifreq+1,iq+1)) + res[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) + res[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) + # + res_os[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) + res_os[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) + # + res_pert[ifreq,0]=np.real(z_pert[xtriv-1,ism,ifreq]) + res_pert[ifreq,1]=-np.imag(z_pert[xtriv-1,ism,ifreq]) + else: + res_os=np.zeros((n_mod,2),dtype=np.float64) # one-shot shifted freq and linewidth + res_pert=np.zeros((n_mod,2),dtype=np.float64) # perturbative shifted freq and linewidth + + for ifreq in range(n_mod): + xtriv=findne(wq[ifreq],e0,de) + # + res_os[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) + res_os[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) + # + res_pert[ifreq,0]=np.real(z_pert[xtriv-1,ism,ifreq]) + res_pert[ifreq,1]=-np.imag(z_pert[xtriv-1,ism,ifreq]) + + + + + # ======================= + # v2_freq, shift, hwhm + # ======================= + if self_consist: + filename_new=filename_shift_lw+'_self-consist_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ----------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") + f.write("# ----------------------------------------------------------------- \n") + out=np.concatenate((wq[:],res[:,0]-wq[:], res[:,1])) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq[:],res[:,0]-wq[:], res[:,1])) + f.write(fmt.format(x_length[iq],*out)) + + filename_new=filename_shift_lw+'_one_shot_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ----------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") + f.write("# ----------------------------------------------------------------- \n") + out=np.concatenate((wq[:],res_os[:,0]-wq[:], res_os[:,1])) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq[:],res_os[:,0]-wq[:], res_os[:,1])) + f.write(fmt.format(x_length[iq],*out)) + # + filename_new=filename_shift_lw+'_perturb_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ----------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") + f.write("# ----------------------------------------------------------------- \n") + out=np.concatenate((wq[:],res_pert[:,0]-wq[:], res_pert[:,1])) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq[:],res_pert[:,0]-wq[:], res_pert[:,1])) + f.write(fmt.format(x_length[iq],*out)) + # ================================================ + # freq sorted, hwhm && Lorenztian spectral func + # ================================================ + + if self_consist: + + wq_shifted=res[:,0] + hwhm=res[:,1] + + sortidx=np.argsort(wq_shifted,axis=0) + + wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) + hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) + #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted + #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted + # + # freq, freq + # + filename_new=filename_freq_dyn+'_self-consist_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ------------------------------------------------------------ \n") + f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") + f.write("# ------------------------------------------------------------ \n") + #out=np.concatenate((wq_shifted_sorted, + #wq_shifted_sorted_plus, + #wq_shifted_sorted_minus)) + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + # + # Lorenztian spectral func + # + filename_new=filename_sp+'_lorentz_self-consist_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ---------------------------------------------------------------------------------------------------------\n") + f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1)\n") + f.write("# ---------------------------------------------------------------------------------------------------------\n") + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + else: + with open(filename_new,'a') as f: + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + # + wq_shifted=res_os[:,0] + hwhm=res_os[:,1] + + sortidx=np.argsort(wq_shifted,axis=0) + + wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) + hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) + #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted + #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted + # + # freq, freq +/- hwhm + # + filename_new=filename_freq_dyn+'_one_shot_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ------------------------------------------------------------ \n") + f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") + f.write("# ------------------------------------------------------------ \n") + #out=np.concatenate((wq_shifted_sorted, + #wq_shifted_sorted_plus, + # wq_shifted_sorted_minus)) + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + # + # Lorentzian spectral func + # + filename_new=filename_sp+'_lorentz_one_shot_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# --------------------------------------------------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1) \n") + f.write("# --------------------------------------------------------------------------------------------------------- \n") + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + else: + with open(filename_new,'a') as f: + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + + # + wq_shifted=res_pert[:,0] + hwhm=res_pert[:,1] + + sortidx=np.argsort(wq_shifted,axis=0) + + wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) + hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) + #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted + #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted + # + # freq, freq +/- hwhm + # + filename_new=filename_freq_dyn+'_perturb_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ------------------------------------------------------------ \n") + f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") + f.write("# ------------------------------------------------------------ \n") + #out=np.concatenate((wq_shifted_sorted, + #wq_shifted_sorted_plus, + #wq_shifted_sorted_minus)) + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq_shifted_sorted, + hwhm_sorted)) + f.write(fmt.format(x_length[iq],*out)) + # + # Lorenztian spectral func + # + filename_new=filename_sp+'_lorentz_perturb_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# --------------------------------------------------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1) \n") + f.write("# --------------------------------------------------------------------------------------------------------- \n") + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + else: + with open(filename_new,'a') as f: + Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) + for ifreq in range(n_mod): + Lor_spectralf[:,ifreq]=Lorenz(energies_cm, + wq_shifted_sorted[ifreq], + hwhm_sorted[ifreq]+smear_id_cm[ism]) + for ie, ene in enumerate(energies_cm): + out=Lor_spectralf[ie,:] + f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) + f.write("\n") + # + + + + # ===== PERTURBATIVE CORRECTION TO SSCHA FREQUENCY (SHIFT and LINEWIDTH) ===================== + +def get_perturb_dynamic_selfnrg(tensor2, tensor3, + k_grid, q, + smear, + T, + verbose= False): + + structure = tensor2.unitcell_structure + + # Get the integration points + k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) + + + # Get the phi2 in q + phi2_q = tensor2.Interpolate(q, asr = False) + + # dynamical matrix in q + m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() + mm_mat = np.sqrt(np.outer(m, m)) + mm_inv_mat = 1 / mm_mat + # + d2_q = phi2_q * mm_inv_mat + + # Diagonalize the dynamical matrix in q + w2_q, pols_q = np.linalg.eigh(d2_q) + + # Check if the q point is gamma + is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) + + if is_q_gamma: + w2_q[0:3]=0.0 + if not (w2_q >= 0.0).all(): + print('q= ',q, ' (2pi/A)') + print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_q=np.sqrt(w2_q) + + def compute_k(k): + # phi3 in q, k, -q - k + t1 = time.time() + phi3=tensor3.Interpolate(k,-q-k, asr = False) + t2 = time.time() + # phi2 in k + phi2_k = tensor2.Interpolate(k, asr = False) + + # phi2 in -q-k + phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) + + t3 = time.time() + + # dynamical matrices (divide by the masses) + d2_k = phi2_k * mm_inv_mat + d2_mq_mk = phi2_mq_mk * mm_inv_mat + + # Diagonalize the dynamical matrices + w2_k, pols_k = np.linalg.eigh(d2_k) + w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) + + + is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) + is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) + + if is_k_gamma: + w2_k[0:3]=0.0 + if not (w2_k >= 0.0).all(): + print('k= ',k, ' (2pi/A)') + print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_k=np.sqrt(w2_k) + + if is_mq_mk_gamma: + w2_mq_mk[0:3]=0.0 + if not (w2_mq_mk >= 0.0).all(): + print('-q-k= ',-q-k, ' (2pi/A)') + print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') + print('Cannot continue with SSCHA negative frequencies') + exit() + w_mq_mk=np.sqrt(w2_mq_mk) + + # Dividing the phi3 by the sqare root of masses + d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) + + # d3 in mode components + # d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) + d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) + d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) + d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) + + t4 = time.time() + + + nsm=smear.shape[0] + n_mod=3*structure.N_atoms + # Fortran duty ==== + + selfnrg = thirdorder.third_order_bubble.compute_perturb_selfnrg(smear,T, + np.array([w_q,w_k,w_mq_mk]).T, + np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), + d3_pols,nsm,n_mod) + + t5 = time.time() + + if verbose: + print("Time to interpolate the third order: {} s".format(t2 - t1)) + print("Time to interpolate the second order: {} s".format(t3 - t2)) + print("Time to transform the tensors: {} s".format(t4 - t3)) + print("Time to compute the bubble: {} s".format(t5 - t4)) + + return selfnrg + + CC.Settings.SetupParallel() + + selfnrg =CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") + + # divide by the N_k factor + selfnrg /= len(k_points) # (n_mod,nsigma) + + w_q_ext=w_q[...,None] + + shift=np.divide(selfnrg.real, 2*w_q_ext, out=np.zeros_like(selfnrg.real), where=w_q_ext!=0) + hwhm=np.divide(-selfnrg.imag, 2*w_q_ext, out=np.zeros_like(selfnrg.imag), where=w_q_ext!=0) + + return w_q, shift,hwhm + + + +def get_perturb_dynamic_correction_along_path(dyn, tensor3, + k_grid, + sm1, sm0, + nsm=1, + q_path=[0.0,0.0,0.0], + q_path_file=None, + print_path = True, + T=0, + filename_shift_lw = 'v2_freq_shift_hwhm', + filename_freq_dyn = 'freq_dynamic', + d3_scale_factor=None, + tensor2= None): + + + """ + The frequency shift (with respect to the SSCHA frequency) and linewidth are computed with the perturbative + formula with respect to the SSCHA frequency. + + Parameters + ---------- + + dyn : Phonons() + The harmonic / SSCHA dynamical matrix + tensor3 : Tensor3() + The third order force constant matrix + k_grid : list(len = 3) + The integration grid on the Brillouin zone + sm0, sm1 : float + Minimum and maximum value of the smearing (cm-1) to compute the self-energy + + Optional + -------- + + nsm : integer + Number of smearings to consider + (default = 1) + T : float + The temperature + (default: 0 K) + q_path : list of triplets + Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, + where the caculation is performed + (defualt: [0.0,0.0,0.0]) + q_path_file : string + Name of the file where the q_path can be read. + Format: column of triples, q points in 2pi/Angstrom + If the name of the file is present, it has the + priority over the q_path value + (default: None) + print_path : logical + If True (and the path is composed of more then one q-point), + a file 'path_len.dat' is printed. + Format: column of 4 values, coordinates of + the q-point and path length (in 2pi/Angstrom) . + (default: True) + filename_shift_lw : string + filename_shift_lw_[id_smear]_[smear].dat + is the file where + len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) + are printed. + (default: "v2_freq_shit_hwhm") + filename_freq_dyn : string + filename_freq_dyn_[id_smear]_[smear].dat + is the file where + len (2pi/Angstrom), freq (cm-1) (sorted in ascending order), HWHM (cm-1) + are printed. + (default: "freq_dynamic") + d3_scale_factor : float + If present, the 3rd order FC is multiplied by this factor + (e.g. it can be used to make tests about the perturbative limit) + (default: None) + tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) + If present, this 2nd order FC overwrites the one + obtained from dyn. + (default: None) + + """ + + + + print(" ") + print(" ===========================================" ) + print(" Bubble perturbative dynamic correction " ) + print(" ===========================================" ) + print(" ") + print(" T= {:>5.1f} K".format(T)) + print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) + print(" ") + print(" Smearing values: ") + for sm in np.linspace(sm0,sm1,nsm): + print(" sm= {:>6.2f} cm-1".format(sm)) + print(" ") + print(" ===========================================" ) + print(" " ) + + if ( tensor2 == None ): + + # Prepare the tensor2 + tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) + tensor2.SetupFromPhonons(dyn) + tensor2.Center() + + # Scale the FC3 =========================================================================== + if d3_scale_factor != None : + print(" ") + print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) + print(" ") + tensor3.tensor=tensor3.tensor*d3_scale_factor + # ================================== q-PATH =============================================== + if q_path_file == None: + q_path=np.array(q_path) + else: + print(" ") + print(" q_path read from "+q_path_file) + print(" ") + q_path=np.loadtxt(q_path_file) + if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) + # Get the length of the q path + x_length = np.zeros(len(q_path)) + q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) + x_length[1:] = q_tot + x_length=np.cumsum(x_length) + x_length_exp=np.expand_dims(x_length,axis=0) + # print the path q-points and length + if print_path and (q_path.shape[0] > 1) : + fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] + result=np.hstack((q_path,x_length_exp.T)) + np.savetxt('path_len.dat',result,fmt=fmt_txt) + print(" ") + print(" Path printed in path_len.dat ") + print(" ") + # ======================= Smearing ========================================== + # smearing in input is in cm-1 + # converto to Ry + # list of smearing + # + if nsm == 1 : + sm1=sm0 + smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM + # ======================== Calculation ========================================== + n_mod=3*dyn.structure.N_atoms + shift = np.zeros( ( n_mod, nsm), dtype = np.float64 ) # q-point,mode,smear + hwhm = np.zeros( ( n_mod, nsm), dtype = np.float64 ) # q-point,mode,smear + wq = np.zeros( ( n_mod), dtype = np.float64 ) # q-point,mode + # + smear_cm = smear * CC.Units.RY_TO_CM + for iq, q in enumerate(q_path): + wq[:],shift[:,:], hwhm[:,:] = get_perturb_dynamic_selfnrg(tensor2, tensor3, + k_grid, np.array(q), + smear, T, + verbose=False ) + + # print results + wq*=CC.Units.RY_TO_CM + shift*=CC.Units.RY_TO_CM + hwhm*=CC.Units.RY_TO_CM + # + #==================== SORTING =============================== + wq_shifted=wq[...,None]+shift + + sortidx=np.argsort(wq_shifted,axis=0) + + wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) + hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) + #=============== Print Results =============================== + + # + for ism in range(nsm): + # + name="{:6.2f}".format(smear_cm[ism]).strip() + # + # v2 freq, corresponding shift & hwhm + # + filename_new=filename_shift_lw+'_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# --------------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), sscha freq (cm-1), freq shift (cm-1), hwhm (cm-1) \n") + f.write("# --------------------------------------------------------------------- \n") + out=np.concatenate((wq[:],shift[:,ism], + hwhm[:,ism])) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq[:],shift[:,ism], + hwhm[:,ism])) + f.write(fmt.format(x_length[iq],*out)) + # + name="{:6.1f}".format(smear_cm[ism]).strip() + # + # shifted freq sorted, corresponding hwhm + # + filename_new=filename_freq_dyn+'_'+name+'.dat' + fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" + if iq == 0: + with open(filename_new,'w') as f: + f.write("# ----------------------------------------------------------------- \n") + f.write("# len (2pi/Angstrom), sscha+shift freq (sorted) (cm-1), hwhm (cm-1) \n") + f.write("# ----------------------------------------------------------------- \n") + out=np.concatenate((wq_shifted_sorted[:,ism], + hwhm_sorted[:,ism])) + f.write(fmt.format(x_length[iq],*out)) + else: + with open(filename_new,'a') as f: + out=np.concatenate((wq_shifted_sorted[:,ism], + hwhm_sorted[:,ism])) + f.write(fmt.format(x_length[iq],*out)) + + + print(" ") + print(" Results printed in "+filename_shift_lw+'_'+'[smear].dat') + print(" ") + print(" ") + print(" Results printed in "+filename_freq_dyn+'_'+'[smear].dat') + print(" ") + + diff --git a/cellconstructor/Phonons.py b/cellconstructor/Phonons.py index 096fb3d5..308136fb 100644 --- a/cellconstructor/Phonons.py +++ b/cellconstructor/Phonons.py @@ -1869,6 +1869,7 @@ def GenerateSupercellDyn(self, supercell_size, img_thr = 1e-6): supercell_size : array int (size=3) the dimension of the cell on which you want to generate the new Phonon + img_thr def 1e-6 Results ------- @@ -3948,6 +3949,7 @@ def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structur itau : Ndarray(nat_sc) , optional the correspondance between the supercell atoms and the unit cell one. If None is recomputed + img_thr: 1e-5 def Returns ------- fc_supercell : ndarray 3nat_sc x 3nat_sc @@ -4007,12 +4009,12 @@ def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structur #print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) # Check the imaginary part - imag = np.sqrt(np.sum(np.imag(fc)**2)) + imag = np.max(np.abs(np.imag(fc))) ASSERT_ERROR = """ Error, the imaginary part of the real space force constant - is not zero. IMAG={} + is not zero. IMAG={} imthr={} """ - assert imag < img_thr, ASSERT_ERROR.format(imag) + assert imag < img_thr, ASSERT_ERROR.format(imag, img_thr) # Remove anyway the imaginary part return fc - 1j*np.imag(fc) diff --git a/out.dat b/out.dat new file mode 100644 index 00000000..b91a9f94 --- /dev/null +++ b/out.dat @@ -0,0 +1,1402 @@ +running install +running build +running config_cc +INFO: unifing config_cc, config, build_clib, build_ext, build commands --compiler options +running config_fc +INFO: unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options +running build_src +INFO: build_src +INFO: building extension "symph" sources +INFO: f2py options: [] +INFO: f2py:> build/src.linux-x86_64-3.10/symphmodule.c +creating build +creating build/src.linux-x86_64-3.10 +Reading fortran codes... + Reading file 'FModules/symdynph_gq_new.f90' (format:free) + Reading file 'FModules/symm_base.f90' (format:free) + Reading file 'FModules/sgam_ph.f90' (format:free) + Reading file 'FModules/invmat.f90' (format:free) + Reading file 'FModules/set_asr.f90' (format:free) + Reading file 'FModules/error_handler.f90' (format:free) + Reading file 'FModules/io_global.f90' (format:free) + Reading file 'FModules/flush_unit.f90' (format:free) + Reading file 'FModules/symvector.f90' (format:free) + Reading file 'FModules/fc_supercell_from_dyn.f90' (format:free) + Reading file 'FModules/set_tau.f90' (format:free) + Reading file 'FModules/cryst_to_car.f90' (format:free) + Reading file 'FModules/recips.f90' (format:free) + Reading file 'FModules/q2qstar_out.f90' (format:free) + Reading file 'FModules/rotate_and_add_dyn.f90' (format:free) + Reading file 'FModules/trntnsc.f90' (format:free) + Reading file 'FModules/star_q.f90' (format:free) + Reading file 'FModules/eqvect.f90' (format:free) + Reading file 'FModules/symm_matrix.f90' (format:free) + Reading file 'FModules/from_matdyn.f90' (format:free) +Line #110 in FModules/from_matdyn.f90:" parameter (eps=1.0d-6,nx=2) " + get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Reading file 'FModules/interp.f90' (format:free) + Reading file 'FModules/q_gen.f90' (format:free) + Reading file 'FModules/smallgq.f90' (format:free) + Reading file 'FModules/symmetry_high_rank.f90' (format:free) + Reading file 'FModules/unwrap_tensors.f90' (format:free) + Reading file 'FModules/get_latvec.f90' (format:free) + Reading file 'FModules/contract_two_phonon_propagator.f90' (format:free) + Reading file 'FModules/get_q_grid_fast.f90' (format:free) + Reading file 'FModules/kind.f90' (format:free) + Reading file 'FModules/constants.f90' (format:free) + Reading file 'FModules/eff_charge_interp.f90' (format:free) + Reading file 'FModules/get_translations.f90' (format:free) + Reading file 'FModules/get_equivalent_atoms.f90' (format:free) +Post-processing... + Block: symph + Block: symdynph_gq_new + Block: symm_base +In: :symph:FModules/symm_base.f90:symm_base +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/symm_base.f90:symm_base +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: set_accep_threshold + Block: get_accep_threshold + Block: set_at_bg + Block: inverse_s + Block: set_sym_bl +In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: find_sym + Block: sgam_at + Block: sgam_at_mag + Block: set_sym + Block: copy_sym + Block: is_group + Block: eqvect + Block: checksym + Block: checkallsym + Block: s_axis_to_cart + Block: smallg_q + Block: sgam_ph_new + Block: invmat + Block: set_asr + Block: sp_zeu + Block: sp1 + Block: sp2 + Block: sp3 + Block: errore + Block: infomsg +In: :symph:FModules/error_handler.f90:infomsg +get_useparameters: no module io_global info used by infomsg + Block: io_global + Block: io_global_start + Block: meta_io_global_start + Block: io_global_getionode + Block: io_global_getmeta + Block: flush_unit + Block: symvector + Block: fc_supercell_from_dyn + Block: fast_ft_real_space_from_dynq + Block: impose_trans_sc +In: :symph:FModules/fc_supercell_from_dyn.f90:impose_trans_sc +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: dyn_from_fc + Block: set_tau +In: :symph:FModules/set_tau.f90:set_tau +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: cryst_to_cart + Block: recips + Block: q2qstar_out + Block: rotate_and_add_dyn +In: :symph:FModules/rotate_and_add_dyn.f90:rotate_and_add_dyn +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: trntnsc + Block: star_q + Block: eqvect +In: :symph:FModules/eqvect.f90:eqvect +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: symmatrix + Block: cart_to_crys_mat + Block: crys_to_cart_mat + Block: frc_blk +In: :symph:FModules/from_matdyn.f90:frc_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: wsinit +In: :symph:FModules/from_matdyn.f90:wsinit +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: wsweight +In: :symph:FModules/from_matdyn.f90:wsweight +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: get_frc + Block: asign_supercell_index_new + Block: q_gen +In: :symph:FModules/q_gen.f90:q_gen +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: set_irotmq +In: :symph:FModules/smallgq.f90:set_irotmq +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: get_tau_sc_latvec + Block: permute_v3 + Block: permute_v4 + Block: trans_v2 + Block: trans_v3 + Block: trans_v4 + Block: sym_v2 + Block: sym_v3 + Block: sym_v4 + Block: print_symm + Block: threetosix_real + Block: sixtothree_real + Block: get_latvec + Block: contract_two_ph_propagator + Block: get_two_phonon_propagator +In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator +get_parameters[TODO]: implement evaluation of complex expression (0d0, 1d0) +In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: get_q_grid + Block: kinds + Block: print_kind_info + Block: constants +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'kboltzmann' is not defined" on 'kboltzmann/hartree' +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'kboltzmann' is not defined" on 'kboltzmann/rydberg' +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'hartree' is not defined" on 'hartree/electronvolt' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'amu' is not defined" on 'amu/electronmass' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'hplanck' is not defined" on 'hplanck/tpi/hartree' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'electron' is not defined" on 'electron*bohrradius/debye' +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'electronvolt' is not defined" on 'electronvolt/kboltzmann' +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'rydberg' is not defined" on 'rydberg/kboltzmann' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'au' is not defined" on 'au' +In: :symph:FModules/constants.f90:constants +get_parameters: got "name 'au' is not defined" on 'au' +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/constants.f90:constants +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: rgd_blk +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:rgd_blk +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: nonanal +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :symph:FModules/eff_charge_interp.f90:nonanal +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: trasl + Block: get_translations + Block: get_equivalent_atoms + Block: get_closest_vector + Block: get_gr_data +In: :symph:FModules/get_equivalent_atoms.f90:get_gr_data +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 + Block: fix_coords_in_unit_cell + Block: matinv3 +Post-processing (stage 2)... + Block: symph + Block: unknown_interface + Block: symdynph_gq_new + Block: symm_base + Block: set_accep_threshold + Block: get_accep_threshold + Block: set_at_bg + Block: inverse_s + Block: set_sym_bl + Block: find_sym + Block: sgam_at + Block: sgam_at_mag + Block: set_sym + Block: copy_sym + Block: is_group + Block: eqvect + Block: checksym + Block: checkallsym + Block: s_axis_to_cart + Block: smallg_q + Block: sgam_ph_new + Block: invmat + Block: set_asr + Block: sp_zeu + Block: sp1 + Block: sp2 + Block: sp3 + Block: errore + Block: infomsg + Block: io_global + Block: io_global_start + Block: meta_io_global_start + Block: io_global_getionode + Block: io_global_getmeta + Block: flush_unit + Block: symvector + Block: fc_supercell_from_dyn + Block: fast_ft_real_space_from_dynq + Block: impose_trans_sc + Block: dyn_from_fc + Block: set_tau + Block: cryst_to_cart + Block: recips + Block: q2qstar_out + Block: rotate_and_add_dyn + Block: trntnsc + Block: star_q + Block: eqvect + Block: symmatrix + Block: cart_to_crys_mat + Block: crys_to_cart_mat + Block: frc_blk + Block: wsinit + Block: wsweight + Block: get_frc + Block: asign_supercell_index_new + Block: q_gen + Block: set_irotmq + Block: get_tau_sc_latvec + Block: permute_v3 + Block: permute_v4 + Block: trans_v2 + Block: trans_v3 + Block: trans_v4 + Block: sym_v2 + Block: sym_v3 + Block: sym_v4 + Block: print_symm + Block: threetosix_real + Block: sixtothree_real + Block: get_latvec + Block: contract_two_ph_propagator + Block: get_two_phonon_propagator + Block: get_q_grid + Block: kinds + Block: print_kind_info + Block: constants + Block: rgd_blk + Block: nonanal + Block: trasl + Block: get_translations + Block: get_equivalent_atoms + Block: get_closest_vector + Block: get_gr_data + Block: fix_coords_in_unit_cell + Block: matinv3 +Building modules... + Building module "symph"... + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "symdynph_gq_new"... + symdynph_gq_new(xq,phi,s,invs,rtau,irt,irotmq,minus_q,nsymq,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sgam_ph_new"... + rtau = sgam_ph_new(at,bg,nsym,s,irt,tau,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "invmat"... + a_inv = invmat(a,[n]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "set_asr"... + set_asr(asr,axis,tau,dyn,zeu,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sp_zeu"... + sp_zeu(zeu_u,zeu_v,scal,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sp1"... + sp1(u,v,scal,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sp2"... + sp2(u,v,ind_v,scal,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sp3"... + sp3(u,v,i,na,scal,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "errore"... + errore(calling_routine,message,ierr) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "infomsg"... + infomsg(routine,message) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "flush_unit"... + flush_unit(unit_tobeflushed) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "symvector"... + symvector(nsym,irt,s,at,bg,vect,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "fc_supercell_from_dyn"... + phitot_sc = fc_supercell_from_dyn(phitot,q,tau,tau_sc,itau,[nat,nq]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "fast_ft_real_space_from_dynq"... + fc_supercell = fast_ft_real_space_from_dynq(unit_cell_coords,super_cell_coords,itau,q_tot,dynq,[nat,nat_sc,nq]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "impose_trans_sc"... + impose_trans_sc(fc_sc,tau_sc_cryst,itau,[nat_sc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "dyn_from_fc"... + dyn = dyn_from_fc(phitot_sc,q,tau,tau_sc,itau,nq,nat) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "set_tau"... + set_tau(at,at_blk,tau,tau_blk,ityp,ityp_blk,itau_blk,[nat,nat_blk]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "cryst_to_cart"... + cryst_to_cart(vec,trmat,iflag,[nvec]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "recips"... + b1,b2,b3 = recips(a1,a2,a3) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "q2qstar_out"... + dynqstar = q2qstar_out(dyn,at,bg,nsym,s,invs,irt,rtau,nq,sxq,isq,imq,nqtot,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "rotate_and_add_dyn"... + phi2 = rotate_and_add_dyn(phi,isym,s,invs,irt,rtau,sxq,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "trntnsc"... + trntnsc(phi,at,bg,iflg) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "star_q"... + nq,sxq,isq,imq = star_q(xq,at,bg,nsym,s,invs,verbosity) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Creating wrapper for Fortran function "eqvect"("eqvect")... + Constructing wrapper function "eqvect"... + eqvect = eqvect(x,y,f) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "symmatrix"... + symmatrix(matr,s,nsym,at,bg) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "cart_to_crys_mat"... + cart_to_crys_mat(matr,at) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "crys_to_cart_mat"... + crys_to_cart_mat(matr,bg) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "frc_blk"... + dyn = frc_blk(q,tau,frc,at,rws,nrws,[nat,nr1,nr2,nr3,nrwsx]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "wsinit"... + nrws = wsinit(rws,atw,[nrwsx]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Creating wrapper for Fortran function "wsweight"("wsweight")... + Constructing wrapper function "wsweight"... + wsweight = wsweight(r,rws,nrws,[nrwsx]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_frc"... + frc = get_frc(phi_sc,tau,tau_sc,at,itau,size1,size2,size3,[nat,natsc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "asign_supercell_index_new"... + l,m,n = asign_supercell_index_new(vect,at) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "q_gen"... + qbid = q_gen(nsc,at_blk,bg_blk,at,bg) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "set_irotmq"... + irotmq = set_irotmq(xq,s,nsymq,nsym,minus_q,bg,at,lgamma) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_tau_sc_latvec"... + tau_sc_latvec = get_tau_sc_latvec(tau_sc,latvec,at_sc,[nat_sc,nr]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "permute_v3"... + permute_v3(v3,[n]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "permute_v4"... + permute_v4(v4,[n]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "trans_v2"... + trans_v2(v2,tau_sc_latvec,[nat_sc,nr]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "trans_v3"... + trans_v3(v3,tau_sc_latvec,[nat_sc,nr]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "trans_v4"... + trans_v4(v4,tau_sc_latvec,[nat_sc,nr]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sym_v2"... + sym_v2(v2,at_sc,bg_sc,s,irt,nsym,[nat_sc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sym_v3"... + sym_v3(v3,at_sc,s,irt,nsym,[nat_sc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sym_v4"... + sym_v4(v4,at_sc,s,irt,nsym,[nat_sc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "print_symm"... + print_symm(s,nsym,irt,supercell,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "threetosix_real"... + mat6 = threetosix_real(mat3,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "sixtothree_real"... + mat3 = sixtothree_real(mat6,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers2.f90" + Maybe empty "symph-f2pywrappers.f" + Creating wrapper for Fortran subroutine "get_latvec"("get_latvec")... + Constructing wrapper function "get_latvec"... + latvec = get_latvec(tau_sc,tau,itau,nr,[nat,nat_sc]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "contract_two_ph_propagator"... + f_output = contract_two_ph_propagator(w_array,w_mu,t,smearing,m,[n_w,n_modes]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_two_phonon_propagator"... + chi = get_two_phonon_propagator(w_value,ws,t,smearing,[n_w]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_q_grid"... + q_list = get_q_grid(bg,supercell_size,n_size) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "rgd_blk"... + rgd_blk(nr1,nr2,nr3,dyn,q,tau,epsil,zeu,bg,omega,alat,loto_2d,sign,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "nonanal"... + nonanal(itau_blk,epsil,q,zeu,omega,dyn,[nat,nat_blk]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "trasl"... + phid = trasl(phiq,nq,nr1,nr2,nr3,m1,m2,m3,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_translations"... + trans = get_translations(pols,masses,[nmod,nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_equivalent_atoms"... + eq_atoms = get_equivalent_atoms(coords1,coords2,unit_cell,ityp1,ityp2,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_closest_vector"... + new_v_dist = get_closest_vector(unit_cell,v_dist) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "get_gr_data"... + r_value,gr = get_gr_data(cells,coords,ityp,type1,type2,r_min,r_max,n_r,[n_structs,nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "fix_coords_in_unit_cell"... + new_coords = fix_coords_in_unit_cell(coords,unit_cell,[nat]) + Generating possibly empty wrappers" + Maybe empty "symph-f2pywrappers.f" + Constructing wrapper function "matinv3"... + b = matinv3(a) + Constructing F90 module support for "symm_base"... + Variables: eps1 eps2 s sr sname ft ftau nrot nsym nsym_ns nsym_na t_rev no_t_rev time_reversal irt invs invsym d1 d2 d3 allfrac nofrac nosym nosym_evc at bg +getarrdims:warning: assumed shape array, using 0 instead of ':' +getarrdims:warning: assumed shape array, using 0 instead of ':' + Constructing wrapper function "symm_base.set_accep_threshold"... + set_accep_threshold(thr) + Constructing wrapper function "symm_base.get_accep_threshold"... + thr = get_accep_threshold() + Constructing wrapper function "symm_base.set_at_bg"... + set_at_bg(new_at,new_bg) + Constructing wrapper function "symm_base.inverse_s"... + inverse_s() + Constructing wrapper function "symm_base.set_sym_bl"... + set_sym_bl() + Constructing wrapper function "symm_base.find_sym"... + find_sym(tau,ityp,nr1,nr2,nr3,magnetic_sym,m_loc,[nat]) + Constructing wrapper function "symm_base.sgam_at"... + sym = sgam_at(tau,ityp,nr1,nr2,nr3,[nat]) + Constructing wrapper function "symm_base.sgam_at_mag"... + sgam_at_mag(m_loc,sym,[nat]) + Constructing wrapper function "symm_base.set_sym"... + set_sym(tau,ityp,nspin_mag,m_loc,nr1,nr2,nr3,[nat]) + Creating wrapper for Fortran function "copy_sym"("copy_sym")... + Constructing wrapper function "symm_base.copy_sym"... + copy_sym = copy_sym(nrot_,sym) + Creating wrapper for Fortran function "is_group"("is_group")... + Constructing wrapper function "symm_base.is_group"... + is_group = is_group() + Creating wrapper for Fortran function "eqvect"("eqvect")... + Constructing wrapper function "symm_base.eqvect"... + eqvect = eqvect(x,y,f) + Creating wrapper for Fortran function "checksym"("checksym")... + Constructing wrapper function "symm_base.checksym"... + checksym = checksym(irot,ityp,xau,rau,ft_,[nat]) + Constructing wrapper function "symm_base.checkallsym"... + checkallsym(tau,ityp,nr1,nr2,nr3,[nat]) + Constructing wrapper function "symm_base.s_axis_to_cart"... + s_axis_to_cart() + Constructing wrapper function "symm_base.smallg_q"... + minus_q = smallg_q(aq,modenum,sym) + Constructing F90 module support for "io_global"... + Variables: stdout_bn ionode ionode_id meta_ionode meta_ionode_id xmlinputunit xmloutputunit xmltmpunit + Constructing wrapper function "io_global.io_global_start"... + io_global_start(mpime,ionode_set) + Constructing wrapper function "io_global.meta_io_global_start"... + meta_io_global_start(mpime,ionode_set) + Constructing wrapper function "io_global.io_global_getionode"... + ionode_out,ionode_id_out = io_global_getionode() + Constructing wrapper function "io_global.io_global_getmeta"... + io_global_getmeta(myrank,root) + Constructing F90 module support for "kinds"... + Variables: dp sgl i4b + Constructing wrapper function "kinds.print_kind_info"... + print_kind_info(stdout_bn) + Constructing F90 module support for "constants"... + Variables: pi tpi fpi sqrtpi sqrtpm1 sqrt2 h_planck_si k_boltzmann_si electron_si electronvolt_si electronmass_si hartree_si rydberg_si bohr_radius_si amu_si c_si munought_si epsnought_si k_boltzmann_au k_boltzmann_ry autoev rytoev amu_au amu_ry au_sec au_ps au_gpa ry_kbar debye_si au_debye ev_to_kelvin ry_to_kelvin evtonm rytonm c_au eps4 eps6 eps8 eps12 eps14 eps16 eps24 eps32 gsmall e2 degspin bohr_radius_cm bohr_radius_angs angstrom_au dip_debye au_terahertz au_to_ohmcmm1 ry_to_thz ry_to_ghz ry_to_cmm1 avogadro + Wrote C/API module "symph" to file "build/src.linux-x86_64-3.10/symphmodule.c" + Fortran 77 wrappers are saved to "build/src.linux-x86_64-3.10/symph-f2pywrappers.f" + Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/symph-f2pywrappers2.f90" +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. +creating build/src.linux-x86_64-3.10/build +creating build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 +copying /usr/local/lib/python3.10/dist-packages/numpy/f2py/src/fortranobject.c -> build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 +copying /usr/local/lib/python3.10/dist-packages/numpy/f2py/src/fortranobject.h -> build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 +INFO: adding 'build/src.linux-x86_64-3.10/symph-f2pywrappers.f' to sources. +INFO: adding 'build/src.linux-x86_64-3.10/symph-f2pywrappers2.f90' to sources. +INFO: building extension "cc_linalg" sources +INFO: building extension "thirdorder" sources +INFO: f2py options: [] +INFO: f2py:> build/src.linux-x86_64-3.10/thirdordermodule.c +Reading fortran codes... + Reading file 'FModules/third_order_centering.f90' (format:free) + Reading file 'FModules/third_order_ASR.f90' (format:free) + Reading file 'FModules/third_order_interpol.f90' (format:free) + Reading file 'FModules/third_order_dynbubble.f90' (format:free) +Post-processing... + Block: thirdorder + Block: third_order_centering + Block: analysis + Block: center + Block: center_sparse + Block: pre_center + Block: assign + Block: within_dmax + Block: compute_perimeter + Block: three_to_one_len + Block: three_to_one + Block: one_to_three_len + Block: one_to_three + Block: min_el_wise_2 + Block: max_el_wise_2 + Block: min_el_wise_3 + Block: max_el_wise_3 + Block: cryst_to_cart + Block: third_order_asr + Block: initialize_r2index + Block: initialize_perm + Block: geq + Block: f + Block: impose_perm_sym + Block: impose_asr_3rd + Block: impose_asr + Block: third_order_interpol + Block: interpol +In: :thirdorder:FModules/third_order_interpol.f90:third_order_interpol:interpol +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: third_order_bubble + Block: compute_static_bubble + Block: compute_dynamic_bubble + Block: compute_diag_dynamic_bubble + Block: compute_perturb_selfnrg + Block: compute_spectralf +In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:compute_spectralf +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: compute_spectralf_diag +In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:compute_spectralf_diag +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: lambda + Block: lambda_dynamic + Block: lambda_dynamic_value + Block: bose_freq + Block: f_bose +In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:f_bose +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 +In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:f_bose +analyzevars: prefix ('elemental') were not used + Block: df_bose +In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:df_bose +get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 + Block: invzmat + Block: eliminate_transl +Post-processing (stage 2)... + Block: thirdorder + Block: unknown_interface + Block: third_order_centering + Block: analysis + Block: center + Block: center_sparse + Block: pre_center + Block: assign + Block: within_dmax + Block: compute_perimeter + Block: three_to_one_len + Block: three_to_one + Block: one_to_three_len + Block: one_to_three + Block: min_el_wise_2 + Block: max_el_wise_2 + Block: min_el_wise_3 + Block: max_el_wise_3 + Block: cryst_to_cart + Block: third_order_asr + Block: initialize_r2index + Block: initialize_perm + Block: geq + Block: f + Block: impose_perm_sym + Block: impose_asr_3rd + Block: impose_asr + Block: third_order_interpol + Block: interpol + Block: third_order_bubble + Block: compute_static_bubble + Block: compute_dynamic_bubble + Block: compute_diag_dynamic_bubble + Block: compute_perturb_selfnrg + Block: compute_spectralf + Block: compute_spectralf_diag + Block: lambda + Block: lambda_dynamic + Block: lambda_dynamic_value + Block: bose_freq + Block: f_bose + Block: df_bose + Block: invzmat + Block: eliminate_transl +Building modules... + Building module "thirdorder"... + Constructing F90 module support for "third_order_centering"... + Constructing wrapper function "third_order_centering.analysis"... + weight,xr2,xr3 = analysis(far,tol,dmax,sc_size,xr2_list,xr3_list,alat,tau,tensor,[nat,n_blocks]) + Constructing wrapper function "third_order_centering.center"... + centered = center(original,weight,xr2_list,xr2,xr3_list,xr3,far,[nat,n_blocks,n_blocks_old]) + Constructing wrapper function "third_order_centering.center_sparse"... + centered,n_sparse_blocks,xr2_sparse_list,xr3_sparse_list,atom_sparse_list,r_blocks_sparse_list = center_sparse(original,weight,xr2_list,xr2,xr3_list,xr3,far,[nat,n_sup,n_blocks]) + Constructing wrapper function "third_order_centering.pre_center"... + centered,lat_min,lat_max = pre_center(far,nq1,nq2,nq3,tol,alat,tau,original,[nat]) + Constructing wrapper function "third_order_centering.assign"... + centered,x2,x3,r2,r3 = assign(alat,lat_min_prev,lat_max_prev,centered_prev,lat_min,lat_max,n_sup_ws,[nat,n_sup_ws_prev]) + Creating wrapper for Fortran function "within_dmax"("within_dmax")... + Constructing wrapper function "third_order_centering.within_dmax"... + within_dmax = within_dmax(v1,v2,v3,d1,d2,d3,tol) + Creating wrapper for Fortran function "compute_perimeter"("compute_perimeter")... + Constructing wrapper function "third_order_centering.compute_perimeter"... + compute_perimeter = compute_perimeter(v1,v2,v3) + Creating wrapper for Fortran function "three_to_one_len"("three_to_one_len")... + Constructing wrapper function "third_order_centering.three_to_one_len"... + three_to_one_len = three_to_one_len(v,v_min,v_len) + Creating wrapper for Fortran function "three_to_one"("three_to_one")... + Constructing wrapper function "third_order_centering.three_to_one"... + three_to_one = three_to_one(v,v_min,v_max) + Creating wrapper for Fortran function "one_to_three_len"("one_to_three_len")... + Constructing wrapper function "third_order_centering.one_to_three_len"... + one_to_three_len = one_to_three_len(j,v_min,v_len) + Creating wrapper for Fortran function "one_to_three"("one_to_three")... + Constructing wrapper function "third_order_centering.one_to_three"... + one_to_three = one_to_three(j,v_min,v_max) + Creating wrapper for Fortran function "min_el_wise_2"("min_el_wise_2")... + Constructing wrapper function "third_order_centering.min_el_wise_2"... + min_el_wise_2 = min_el_wise_2(a,b) + Creating wrapper for Fortran function "max_el_wise_2"("max_el_wise_2")... + Constructing wrapper function "third_order_centering.max_el_wise_2"... + max_el_wise_2 = max_el_wise_2(a,b) + Creating wrapper for Fortran function "min_el_wise_3"("min_el_wise_3")... + Constructing wrapper function "third_order_centering.min_el_wise_3"... + min_el_wise_3 = min_el_wise_3(a,b,c) + Creating wrapper for Fortran function "max_el_wise_3"("max_el_wise_3")... + Constructing wrapper function "third_order_centering.max_el_wise_3"... + max_el_wise_3 = max_el_wise_3(a,b,c) + Creating wrapper for Fortran function "cryst_to_cart"("cryst_to_cart")... + Constructing wrapper function "third_order_centering.cryst_to_cart"... + cryst_to_cart = cryst_to_cart(v,alat) + Constructing F90 module support for "third_order_asr"... + Variables: perm_initialized p r2index_initialized index_blocks_r2 num_blocks_r2 +getarrdims:warning: assumed shape array, using 0 instead of ':' +getarrdims:warning: assumed shape array, using 0 instead of ':' +getarrdims:warning: assumed shape array, using 0 instead of ':' +getarrdims:warning: assumed shape array, using 0 instead of ':' +getarrdims:warning: assumed shape array, using 0 instead of ':' + Constructing wrapper function "third_order_asr.initialize_r2index"... + initialize_r2index(xr2,xr2list,pbc,[totnum_r2,n_blocks,sclat]) + Constructing wrapper function "third_order_asr.initialize_perm"... + initialize_perm(r23,sclat,pbc,[n_blocks]) + Creating wrapper for Fortran function "geq"("geq")... + Constructing wrapper function "third_order_asr.geq"... + geq = geq(v1,v2,lat,pbc) + Creating wrapper for Fortran function "f"("f")... + Constructing wrapper function "third_order_asr.f"... + f = f(x) + Constructing wrapper function "third_order_asr.impose_perm_sym"... + fcvar,fc_sym = impose_perm_sym(fc,r23,sclat,pbc,verbose,[nat,n_blocks]) + Constructing wrapper function "third_order_asr.impose_asr_3rd"... + fcvar,sum3rd,fc_asr = impose_asr_3rd(fc,xr2,xr2list,pow,sclat,pbc,verbose,[totnum_r2,nat,n_blocks]) + Constructing wrapper function "third_order_asr.impose_asr"... + fc_out = impose_asr(fc,r23,xr2,xr2list,pow,sclat,pbc,threshold,maxite,verbose,[totnum_r2,nat,n_blocks]) + Constructing F90 module support for "third_order_interpol"... + Constructing wrapper function "third_order_interpol.interpol"... + fc_interp = interpol(fc,r2,r3,q2,q3,[n_blocks,nat]) + Constructing F90 module support for "third_order_bubble"... + Constructing wrapper function "third_order_bubble.compute_static_bubble"... + bubble = compute_static_bubble(t,freq,is_gamma,d3,[n_mod]) + Constructing wrapper function "third_order_bubble.compute_dynamic_bubble"... + bubble = compute_dynamic_bubble(energies,sigma,static_limit,t,freq,is_gamma,d3,diag_approx,[ne,nsig,n_mod]) + Constructing wrapper function "third_order_bubble.compute_diag_dynamic_bubble"... + bubble = compute_diag_dynamic_bubble(energies,sigma,t,freq,is_gamma,d3,[ne,nsig,n_mod]) + Constructing wrapper function "third_order_bubble.compute_perturb_selfnrg"... + selfnrg = compute_perturb_selfnrg(sigma,t,freq,is_gamma,d3,[nsig,n_mod]) + Constructing wrapper function "third_order_bubble.compute_spectralf"... + spectralf = compute_spectralf(smear_id,ener,d2,pi,notransl,mass,[nat,ne,nsmear]) + Constructing wrapper function "third_order_bubble.compute_spectralf_diag"... + spectralf = compute_spectralf_diag(smear_id,ener,d2_freq,selfnrg,[nat,ne,nsmear]) + Constructing wrapper function "third_order_bubble.lambda"... + lambda_out = lambda(t,w_q2,w_q3) + Constructing wrapper function "third_order_bubble.lambda_dynamic"... + lambda_out = lambda_dynamic(energies,sigma,t,static_limit,w_q2,w_q3,[ne,nsigma]) + Constructing wrapper function "third_order_bubble.lambda_dynamic_value"... + lambda_out = lambda_dynamic_value(value,sigma,t,w_q2,w_q3,[n_mod,nsigma]) + Constructing wrapper function "third_order_bubble.bose_freq"... + bose = bose_freq(t,freq,[n_mod]) + Creating wrapper for Fortran function "f_bose"("f_bose")... + Constructing wrapper function "third_order_bubble.f_bose"... + f_bose = f_bose(freq,t) + Creating wrapper for Fortran function "df_bose"("df_bose")... + Constructing wrapper function "third_order_bubble.df_bose"... + df_bose = df_bose(freq,t) + Constructing wrapper function "third_order_bubble.invzmat"... + invzmat(a,[n]) + Constructing wrapper function "third_order_bubble.eliminate_transl"... + eliminate_transl(a,mass,[nat]) + Wrote C/API module "thirdorder" to file "build/src.linux-x86_64-3.10/thirdordermodule.c" + Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/thirdorder-f2pywrappers2.f90" +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. +INFO: adding 'build/src.linux-x86_64-3.10/thirdorder-f2pywrappers2.f90' to sources. +INFO: building extension "secondorder" sources +INFO: f2py options: [] +INFO: f2py:> build/src.linux-x86_64-3.10/secondordermodule.c +Reading fortran codes... + Reading file 'FModules/second_order_centering.f90' (format:free) + Reading file 'FModules/second_order_ASR.f90' (format:free) +Post-processing... + Block: secondorder + Block: second_order_centering + Block: analysis + Block: center + Block: within_dmax + Block: compute_perimeter + Block: cryst_to_cart + Block: second_order_asr + Block: initialize_perm + Block: clear_all + Block: geq + Block: impose_perm_sym + Block: impose_asr_2nd + Block: impose_asr +Post-processing (stage 2)... + Block: secondorder + Block: unknown_interface + Block: second_order_centering + Block: analysis + Block: center + Block: within_dmax + Block: compute_perimeter + Block: cryst_to_cart + Block: second_order_asr + Block: initialize_perm + Block: clear_all + Block: geq + Block: impose_perm_sym + Block: impose_asr_2nd + Block: impose_asr +Building modules... + Building module "secondorder"... + Constructing F90 module support for "second_order_centering"... + Constructing wrapper function "second_order_centering.analysis"... + weight,xr2 = analysis(far,tol,dmax,sc_size,xr2_list,alat,tau,tensor,[nat,n_blocks]) + Constructing wrapper function "second_order_centering.center"... + centered = center(original,weight,xr2_list,xr2,far,[nat,n_blocks,n_blocks_old]) + Creating wrapper for Fortran function "within_dmax"("within_dmax")... + Constructing wrapper function "second_order_centering.within_dmax"... + within_dmax = within_dmax(v1,v2,d1,d2,tol) + Creating wrapper for Fortran function "compute_perimeter"("compute_perimeter")... + Constructing wrapper function "second_order_centering.compute_perimeter"... + compute_perimeter = compute_perimeter(v1,v2) + Creating wrapper for Fortran function "cryst_to_cart"("cryst_to_cart")... + Constructing wrapper function "second_order_centering.cryst_to_cart"... + cryst_to_cart = cryst_to_cart(v,alat) + Constructing F90 module support for "second_order_asr"... + Variables: perm_initialized p +getarrdims:warning: assumed shape array, using 0 instead of ':' + Constructing wrapper function "second_order_asr.initialize_perm"... + initialize_perm(r2,sclat,pbc,[n_blocks]) + Constructing wrapper function "second_order_asr.clear_all"... + clear_all() + Creating wrapper for Fortran function "geq"("geq")... + Constructing wrapper function "second_order_asr.geq"... + geq = geq(v1,v2,lat,pbc) + Constructing wrapper function "second_order_asr.impose_perm_sym"... + fcvar,fc_sym = impose_perm_sym(fc,r2,sclat,pbc,verbose,[nat,n_blocks]) + Constructing wrapper function "second_order_asr.impose_asr_2nd"... + fcvar,sum2nd,fc_asr = impose_asr_2nd(fc,pow,sclat,pbc,verbose,[nat,n_blocks]) + Constructing wrapper function "second_order_asr.impose_asr"... + fc_out = impose_asr(fc,r2,pow,sclat,pbc,threshold,maxite,verbose,[nat,n_blocks]) + Wrote C/API module "secondorder" to file "build/src.linux-x86_64-3.10/secondordermodule.c" + Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/secondorder-f2pywrappers2.f90" +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. +INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. +INFO: adding 'build/src.linux-x86_64-3.10/secondorder-f2pywrappers2.f90' to sources. +INFO: build_src: building npy-pkg config files +running build_py +creating build/lib.linux-x86_64-3.10 +creating build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Methods.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/calculators.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Phonons.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/AnharmonicForceFields.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/ForceTensor.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/__init__.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Spectral.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Bands.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Manipulate.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Units.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Settings.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Structure.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/symmetries.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Timer.py -> build/lib.linux-x86_64-3.10/cellconstructor +copying cellconstructor/Moro_object.py -> build/lib.linux-x86_64-3.10/cellconstructor +creating build/lib.linux-x86_64-3.10/cellconstructor/SymData +copying cellconstructor/SymData/64.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData +copying cellconstructor/SymData/36_red.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData +copying cellconstructor/SymData/15.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData +copying cellconstructor/SymData/60.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData +copying cellconstructor/SymData/36.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData +running build_ext +INFO: customize UnixCCompiler +INFO: customize UnixCCompiler using build_ext +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-march=native) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +creating /tmp/tmpcmtles2_/usr +creating /tmp/tmpcmtles2_/usr/local +creating /tmp/tmpcmtles2_/usr/local/lib +creating /tmp/tmpcmtles2_/usr/local/lib/python3.10 +creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages +creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy +creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy/distutils +creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy/distutils/checks +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-march=native' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-O3) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-O3' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-Werror) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-Werror' +INFO: CCompilerOpt.__init__[1782] : check requested baseline +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse2' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE' with flags (-msse -msse2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE2' with flags (-msse -msse2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -Werror' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse3) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse3' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE3' with flags (-msse -msse2 -msse3) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -Werror' +INFO: CCompilerOpt.__init__[1791] : check requested dispatch-able features +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mssse3) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mssse3' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse4.1) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse4.1' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mpopcnt) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mpopcnt' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'POPCNT' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -Werror' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse4.2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse4.2' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mf16c) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mf16c' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx2' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX2' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mavx2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mavx2 -Werror' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mfma) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mfma' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512f -mno-mmx) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx512f -mno-mmx' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512cd) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx512cd' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512vl -mavx512bw -mavx512dq) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx512vl -mavx512bw -mavx512dq' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512vnni) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx512vnni' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_CLX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512vnni) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512vnni -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE41' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'F16C' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -Werror' +INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512ifma -mavx512vbmi) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-mavx512ifma -mavx512vbmi' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_CNL' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512CD' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSSE3' with flags (-msse -msse2 -msse3 -mssse3) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'FMA3' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE42' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -Werror' +INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_SKX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq) +INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC + +INFO: compile options: '-I/usr/include/python3.10 -c' +extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -Werror' +INFO: CCompilerOpt.cache_flush[857] : write cache to path -> /home/antonio/CellConstructor/build/temp.linux-x86_64-3.10/ccompiler_opt_cache_ext.py From abcefc2f437251b58a17b75ed603f3d9a66ab86c Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 9 Oct 2024 15:49:59 +0200 Subject: [PATCH 05/18] Added an example of ASR --- .../.ipynb_checkpoints/asr_scha-checkpoint.py | 126 ++++++++++++++++++ tests/TestASR/asr_scha.py | 126 ++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py create mode 100644 tests/TestASR/asr_scha.py diff --git a/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py b/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py new file mode 100644 index 00000000..6298fc3e --- /dev/null +++ b/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py @@ -0,0 +1,126 @@ +import cellconstructor as CC +import cellconstructor.Phonons +import cellconstructor.Structure +import cellconstructor.Units +import os +import numpy as np +import sys + +if __name__ == "__main__": + """ + THE MEANING OF THE ASR IN THE SCHA + """ + struct = CC.Structure.Structure(2) + struct.atoms = ['H', 'C'] + struct.coords = np.array([[-1, 0., 0.], + [+1, 0., 0.]]) + struct.masses = {'H' : 1837, 'C' : 1837 * 12} + struct.unit_cell = np.eye(3) * 100 + dyn = CC.Phonons.Phonons(struct) + dyn.dynmats[0] = np.random.uniform(size = (6,6)) + dyn.dynmats[0] += dyn.dynmats[0] + dyn.ForcePositiveDefinite() + dyn.ApplySumRule() + Temp = 100 # Kelvin + masses = np.tile(struct.get_masses_array(), (3, 1)).T.ravel() + + T = np.zeros((3, 6)) + for i in range(3): + v = np.zeros(3) + v[i] = 1 + T[i, :] = np.tile(v, 2) + + Y = dyn.GetUpsilonMatrix(Temp) + + # Multiply by a translation + FC_T = np.einsum('ab, ib -> ia', dyn.dynmats[0], T) + + Y_T = np.einsum('ab, ib -> ia', Y, T) + + print('\n### TEST ###') + print('Phi dot T') + for i in range(3): + print('trasl {}'.format(i)) + print(FC_T[i,:]) + if np.any(np.abs(FC_T[i, :]) > 1e-3): + raise ValueError('The traslation of FC does not work') + + print('\n### TEST ###') + print('Y dot T') + for i in range(3): + print('trasl {}'.format(i)) + print(Y_T[i,:]) + if np.any(np.abs(Y_T[i, :]) > 1e-3): + raise ValueError('The traslation of Y does not work') + + + # The matrix A of SCHA + def get_A(T, w, pols, masses): + A = np.zeros((6,6)) + if T < 1e-3: + return A + + a_mu = np.zeros(6) + mask = np.abs(w) < 1e-4 + a_mu[mask] = T * (CC.Units.RY_TO_KELVIN**-1) # beta^-1 + # The BE occupations + n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 + # The other eigenvalues + a_mu[~mask] = 2 * w[~mask] * n * (1 + n) /(1 + 2 * n) + + A = np.einsum('m, am, bm -> ab', a_mu, pols, pols) + # Multiply by the masses + return np.einsum('a, ab, b -> ab', np.sqrt(masses), A, np.sqrt(masses)) + + # The matrix Y of SCHA + def get_Y(T, w, pols, masses): + Y = np.zeros((6,6)) + + if T < 1e-3: + return A + + y_mu = np.zeros(6) + mask = np.abs(w) < 1e-4 + y_mu[mask] = 0. + n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 + y_mu[~mask] = 2 * w[~mask] /(1 + 2 * n) + + Y = np.einsum('m, am, bm -> ab', y_mu, pols, pols) + + return np.einsum('a, ab, b -> ab', np.sqrt(masses), Y, np.sqrt(masses)) + + + + w, pols = dyn.DyagDinQ(0) + A = get_A(Temp, w, pols, masses) + print('\nSCHA frequencies cm-1') + print(w[np.abs(w) > 1e-3] * CC.Units.RY_TO_CM) + + print('\n\nTest of the COM kinetic energy @ {} K'.format(Temp)) + # Check the sum in the COM kinetic energy + totA = 0. + _A_ = A.reshape((2, 3, 2, 3)) + for i in range(2): + for j in range(2): + for alpha in range(3): + totA += _A_[i, alpha, j, alpha] + + print('>The A contribution') + print( totA) + + + # Check the sum in the COM kinetic energy + totY = 0. + _Y_ = Y.reshape((2, 3, 2, 3)) + for i in range(2): + for j in range(2): + for alpha in range(3): + totY += _Y_[i, alpha, j, alpha] + + print(' >The Y contribution') + print( totY) + + print(np.einsum('am, bm -> ab', pols, pols)) + + # print(np.abs(Y - get_Y(Temp, w, pols, masses)).max()) + # print(np.abs(Y - get_Y(Temp, w, pols, masses)).min()) \ No newline at end of file diff --git a/tests/TestASR/asr_scha.py b/tests/TestASR/asr_scha.py new file mode 100644 index 00000000..6298fc3e --- /dev/null +++ b/tests/TestASR/asr_scha.py @@ -0,0 +1,126 @@ +import cellconstructor as CC +import cellconstructor.Phonons +import cellconstructor.Structure +import cellconstructor.Units +import os +import numpy as np +import sys + +if __name__ == "__main__": + """ + THE MEANING OF THE ASR IN THE SCHA + """ + struct = CC.Structure.Structure(2) + struct.atoms = ['H', 'C'] + struct.coords = np.array([[-1, 0., 0.], + [+1, 0., 0.]]) + struct.masses = {'H' : 1837, 'C' : 1837 * 12} + struct.unit_cell = np.eye(3) * 100 + dyn = CC.Phonons.Phonons(struct) + dyn.dynmats[0] = np.random.uniform(size = (6,6)) + dyn.dynmats[0] += dyn.dynmats[0] + dyn.ForcePositiveDefinite() + dyn.ApplySumRule() + Temp = 100 # Kelvin + masses = np.tile(struct.get_masses_array(), (3, 1)).T.ravel() + + T = np.zeros((3, 6)) + for i in range(3): + v = np.zeros(3) + v[i] = 1 + T[i, :] = np.tile(v, 2) + + Y = dyn.GetUpsilonMatrix(Temp) + + # Multiply by a translation + FC_T = np.einsum('ab, ib -> ia', dyn.dynmats[0], T) + + Y_T = np.einsum('ab, ib -> ia', Y, T) + + print('\n### TEST ###') + print('Phi dot T') + for i in range(3): + print('trasl {}'.format(i)) + print(FC_T[i,:]) + if np.any(np.abs(FC_T[i, :]) > 1e-3): + raise ValueError('The traslation of FC does not work') + + print('\n### TEST ###') + print('Y dot T') + for i in range(3): + print('trasl {}'.format(i)) + print(Y_T[i,:]) + if np.any(np.abs(Y_T[i, :]) > 1e-3): + raise ValueError('The traslation of Y does not work') + + + # The matrix A of SCHA + def get_A(T, w, pols, masses): + A = np.zeros((6,6)) + if T < 1e-3: + return A + + a_mu = np.zeros(6) + mask = np.abs(w) < 1e-4 + a_mu[mask] = T * (CC.Units.RY_TO_KELVIN**-1) # beta^-1 + # The BE occupations + n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 + # The other eigenvalues + a_mu[~mask] = 2 * w[~mask] * n * (1 + n) /(1 + 2 * n) + + A = np.einsum('m, am, bm -> ab', a_mu, pols, pols) + # Multiply by the masses + return np.einsum('a, ab, b -> ab', np.sqrt(masses), A, np.sqrt(masses)) + + # The matrix Y of SCHA + def get_Y(T, w, pols, masses): + Y = np.zeros((6,6)) + + if T < 1e-3: + return A + + y_mu = np.zeros(6) + mask = np.abs(w) < 1e-4 + y_mu[mask] = 0. + n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 + y_mu[~mask] = 2 * w[~mask] /(1 + 2 * n) + + Y = np.einsum('m, am, bm -> ab', y_mu, pols, pols) + + return np.einsum('a, ab, b -> ab', np.sqrt(masses), Y, np.sqrt(masses)) + + + + w, pols = dyn.DyagDinQ(0) + A = get_A(Temp, w, pols, masses) + print('\nSCHA frequencies cm-1') + print(w[np.abs(w) > 1e-3] * CC.Units.RY_TO_CM) + + print('\n\nTest of the COM kinetic energy @ {} K'.format(Temp)) + # Check the sum in the COM kinetic energy + totA = 0. + _A_ = A.reshape((2, 3, 2, 3)) + for i in range(2): + for j in range(2): + for alpha in range(3): + totA += _A_[i, alpha, j, alpha] + + print('>The A contribution') + print( totA) + + + # Check the sum in the COM kinetic energy + totY = 0. + _Y_ = Y.reshape((2, 3, 2, 3)) + for i in range(2): + for j in range(2): + for alpha in range(3): + totY += _Y_[i, alpha, j, alpha] + + print(' >The Y contribution') + print( totY) + + print(np.einsum('am, bm -> ab', pols, pols)) + + # print(np.abs(Y - get_Y(Temp, w, pols, masses)).max()) + # print(np.abs(Y - get_Y(Temp, w, pols, masses)).min()) \ No newline at end of file From 6e263f903b3e5966913daa8b666299bed113969c Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:32:28 +0100 Subject: [PATCH 06/18] Delete .ipynb_checkpoints directory --- .ipynb_checkpoints/setup-checkpoint.py | 80 -------------------------- 1 file changed, 80 deletions(-) delete mode 100644 .ipynb_checkpoints/setup-checkpoint.py diff --git a/.ipynb_checkpoints/setup-checkpoint.py b/.ipynb_checkpoints/setup-checkpoint.py deleted file mode 100644 index 6bab55d1..00000000 --- a/.ipynb_checkpoints/setup-checkpoint.py +++ /dev/null @@ -1,80 +0,0 @@ -from numpy.distutils.core import setup, Extension -import sys - -symph_ext = Extension(name = "symph", - sources = ["FModules/symdynph_gq_new.f90", "FModules/symm_base.f90", - "FModules/sgam_ph.f90", "FModules/invmat.f90", "FModules/set_asr.f90", - "FModules/error_handler.f90", "FModules/io_global.f90", - "FModules/flush_unit.f90", "FModules/symvector.f90", - "FModules/fc_supercell_from_dyn.f90", - "FModules/set_tau.f90", "FModules/cryst_to_car.f90", - "FModules/recips.f90", "FModules/q2qstar_out.f90", - "FModules/rotate_and_add_dyn.f90", "FModules/trntnsc.f90", - "FModules/star_q.f90", "FModules/eqvect.f90", - "FModules/symm_matrix.f90", "FModules/from_matdyn.f90", - "FModules/interp.f90", "FModules/q_gen.f90", "FModules/smallgq.f90", - "FModules/symmetry_high_rank.f90", - "FModules/unwrap_tensors.f90", - "FModules/get_latvec.f90", - "FModules/contract_two_phonon_propagator.f90", - "FModules/get_q_grid_fast.f90", - "FModules/kind.f90", - "FModules/constants.f90", - "FModules/eff_charge_interp.f90", - "FModules/get_translations.f90", - "FModules/get_equivalent_atoms.f90"], - libraries= ["lapack", "blas"], - extra_f90_compile_args = ["-cpp"] - ) - - -secondorder_ext = Extension(name = "secondorder", - sources = ["FModules/second_order_centering.f90", - "FModules/second_order_ASR.f90"], - libraries= ["lapack", "blas"], - extra_f90_compile_args = ["-cpp"] - ) - - -thirdorder_ext = Extension(name = "thirdorder", - sources = ["FModules/third_order_centering.f90", - "FModules/third_order_ASR.f90", - "FModules/third_order_interpol.f90", - "FModules/third_order_dynbubble.f90"], - libraries= ["lapack", "blas"], - extra_f90_compile_args = ["-cpp"] - ) - - - -# The C module extension actually depeds on the python version -WRAPPER = "CModules/wrapper3.c" -if sys.version_info[0] < 3: - print("Running python2, changing the C wrapper") - WRAPPER = "CModules/wrapper.c" - -cc_modules_ext = Extension(name = "cc_linalg", - sources = ["CModules/LinAlg.c", WRAPPER] - ) - - - - -setup( name = "CellConstructor", - version = "1.1", - description = "Python utilities that is interfaced with ASE for atomic crystal analysis", - author = "Lorenzo Monacelli", - url = "https://github.com/mesonepigreco/CellConstructor", - packages = ["cellconstructor"], - package_dir = {"cellconstructor": "cellconstructor"}, - package_data = {"cellconstructor": ["SymData/*.dat"]}, - setup_requires = ["numpy", "ase", "scipy"], - license = "MIT", - include_package_data = True, - scripts = ["scripts/symmetrize_dynmat.py", "scripts/cellconstructor_test.py", "scripts/view_scf_atoms.py"], - ext_modules = [symph_ext, cc_modules_ext, thirdorder_ext, secondorder_ext] - ) - -def readme(): - with open("README.md") as f: - return f.read() From 81dfdaa0595c359fd57b3db611b6b07a595c891c Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:32:48 +0100 Subject: [PATCH 07/18] Delete FModules/.ipynb_checkpoints directory --- .../constants-checkpoint.f90 | 154 --- .../cryst_to_car-checkpoint.f90 | 65 - .../eff_charge_interp-checkpoint.f90 | 601 --------- .../.ipynb_checkpoints/eqvect-checkpoint.f90 | 25 - .../error_handler-checkpoint.f90 | 134 -- .../fc_supercell_from_dyn-checkpoint.f90 | 276 ---- .../from_matdyn-checkpoint.f90 | 149 --- .../get_q_grid_fast-checkpoint.f90 | 49 - .../.ipynb_checkpoints/invmat-checkpoint.f90 | 47 - .../symdynph_gq_new-checkpoint.f90 | 235 ---- .../symm_base-checkpoint.f90 | 1054 --------------- .../symm_matrix-checkpoint.f90 | 97 -- .../symmetry_high_rank-checkpoint.f90 | 1180 ----------------- .../.ipynb_checkpoints/trntnsc-checkpoint.f90 | 70 - 14 files changed, 4136 deletions(-) delete mode 100644 FModules/.ipynb_checkpoints/constants-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/cryst_to_car-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/eff_charge_interp-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/invmat-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 delete mode 100755 FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 delete mode 100644 FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 diff --git a/FModules/.ipynb_checkpoints/constants-checkpoint.f90 b/FModules/.ipynb_checkpoints/constants-checkpoint.f90 deleted file mode 100644 index 45119998..00000000 --- a/FModules/.ipynb_checkpoints/constants-checkpoint.f90 +++ /dev/null @@ -1,154 +0,0 @@ -! -! Copyright (C) 2002-2006 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -!---------------------------------------------------------------------------- -MODULE constants - !---------------------------------------------------------------------------- - ! - USE kinds, ONLY : DP - ! - ! ... The constants needed everywhere - ! - IMPLICIT NONE - ! - SAVE - ! - ! ... Mathematical constants - ! - REAL(DP), PARAMETER :: pi = 3.14159265358979323846_DP - REAL(DP), PARAMETER :: tpi = 2.0_DP * pi - REAL(DP), PARAMETER :: fpi = 4.0_DP * pi - REAL(DP), PARAMETER :: sqrtpi = 1.77245385090551602729_DP - REAL(DP), PARAMETER :: sqrtpm1= 1.0_DP / sqrtpi - REAL(DP), PARAMETER :: sqrt2 = 1.41421356237309504880_DP - ! - ! ... Physical constants, SI (NIST CODATA 2006), Web Version 5.1 - ! http://physics.nist.gov/constants - REAL(DP), PARAMETER :: H_PLANCK_SI = 6.62606896E-34_DP ! J s - REAL(DP), PARAMETER :: K_BOLTZMANN_SI = 1.3806504E-23_DP ! J K^-1 - REAL(DP), PARAMETER :: ELECTRON_SI = 1.602176487E-19_DP ! C - REAL(DP), PARAMETER :: ELECTRONVOLT_SI = 1.602176487E-19_DP ! J - REAL(DP), PARAMETER :: ELECTRONMASS_SI = 9.10938215E-31_DP ! Kg - REAL(DP), PARAMETER :: HARTREE_SI = 4.35974394E-18_DP ! J - REAL(DP), PARAMETER :: RYDBERG_SI = HARTREE_SI/2.0_DP ! J - REAL(DP), PARAMETER :: BOHR_RADIUS_SI = 0.52917720859E-10_DP ! m - REAL(DP), PARAMETER :: AMU_SI = 1.660538782E-27_DP ! Kg - REAL(DP), PARAMETER :: C_SI = 2.99792458E+8_DP ! m sec^-1 - REAL(DP), PARAMETER :: MUNOUGHT_SI = fpi*1.0E-7_DP ! N A^-2 - REAL(DP), PARAMETER :: EPSNOUGHT_SI = 1.0_DP / (MUNOUGHT_SI * & - C_SI**2) ! F m^-1 - ! - ! ... Physical constants, atomic units: - ! ... AU for "Hartree" atomic units (e = m = hbar = 1) - ! ... RY for "Rydberg" atomic units (e^2=2, m=1/2, hbar=1) - ! - REAL(DP), PARAMETER :: K_BOLTZMANN_AU = K_BOLTZMANN_SI / HARTREE_SI - REAL(DP), PARAMETER :: K_BOLTZMANN_RY = K_BOLTZMANN_SI / RYDBERG_SI - ! - ! ... Unit conversion factors: energy and masses - ! - REAL(DP), PARAMETER :: AUTOEV = HARTREE_SI / ELECTRONVOLT_SI - REAL(DP), PARAMETER :: RYTOEV = AUTOEV / 2.0_DP - REAL(DP), PARAMETER :: AMU_AU = AMU_SI / ELECTRONMASS_SI - REAL(DP), PARAMETER :: AMU_RY = AMU_AU / 2.0_DP - ! - ! ... Unit conversion factors: atomic unit of time, in s and ps - ! - REAL(DP), PARAMETER :: AU_SEC = H_PLANCK_SI/tpi/HARTREE_SI - REAL(DP), PARAMETER :: AU_PS = AU_SEC * 1.0E+12_DP - ! - ! ... Unit conversion factors: pressure (1 Pa = 1 J/m^3, 1GPa = 10 Kbar ) - ! - REAL(DP), PARAMETER :: AU_GPA = HARTREE_SI / BOHR_RADIUS_SI ** 3 & - / 1.0E+9_DP - REAL(DP), PARAMETER :: RY_KBAR = 10.0_DP * AU_GPA / 2.0_DP - ! - ! ... Unit conversion factors: 1 debye = 10^-18 esu*cm - ! ... = 3.3356409519*10^-30 C*m - ! ... = 0.208194346 e*A - ! ... ( 1 esu = (0.1/c) Am, c=299792458 m/s) - ! - REAL(DP), PARAMETER :: DEBYE_SI = 3.3356409519_DP * 1.0E-30_DP ! C*m - REAL(DP), PARAMETER :: AU_DEBYE = ELECTRON_SI * BOHR_RADIUS_SI / & - DEBYE_SI - ! - REAL(DP), PARAMETER :: eV_to_kelvin = ELECTRONVOLT_SI / K_BOLTZMANN_SI - REAL(DP), PARAMETER :: ry_to_kelvin = RYDBERG_SI / K_BOLTZMANN_SI - ! - ! .. Unit conversion factors: Energy to wavelength - ! - REAL(DP), PARAMETER :: EVTONM = 1E+9_DP * H_PLANCK_SI * C_SI / & - &ELECTRONVOLT_SI - REAL(DP), PARAMETER :: RYTONM = 1E+9_DP * H_PLANCK_SI * C_SI / RYDBERG_SI - ! - ! Speed of light in atomic units - ! - REAL(DP), PARAMETER :: C_AU = C_SI / BOHR_RADIUS_SI * AU_SEC - ! - ! ... zero up to a given accuracy - ! - REAL(DP), PARAMETER :: eps4 = 1.0E-4_DP - REAL(DP), PARAMETER :: eps6 = 1.0E-6_DP - REAL(DP), PARAMETER :: eps8 = 1.0E-8_DP - REAL(DP), PARAMETER :: eps12 = 1.0E-12_DP - REAL(DP), PARAMETER :: eps14 = 1.0E-14_DP - REAL(DP), PARAMETER :: eps16 = 1.0E-16_DP - REAL(DP), PARAMETER :: eps24 = 1.0E-24_DP - REAL(DP), PARAMETER :: eps32 = 1.0E-32_DP - ! - REAL(DP), PARAMETER :: gsmall = 1.0E-12_DP - ! - REAL(DP), PARAMETER :: e2 = 2.0_DP ! the square of the electron charge - REAL(DP), PARAMETER :: degspin = 2.0_DP ! the number of spins per level - ! - !!!!!! COMPATIBIILITY - ! - REAL(DP), PARAMETER :: BOHR_RADIUS_CM = BOHR_RADIUS_SI * 100.0_DP - REAL(DP), PARAMETER :: BOHR_RADIUS_ANGS = BOHR_RADIUS_CM * 1.0E8_DP - REAL(DP), PARAMETER :: ANGSTROM_AU = 1.0_DP/BOHR_RADIUS_ANGS - REAL(DP), PARAMETER :: DIP_DEBYE = AU_DEBYE - REAL(DP), PARAMETER :: AU_TERAHERTZ = AU_PS - REAL(DP), PARAMETER :: AU_TO_OHMCMM1 = 46000.0_DP ! (ohm cm)^-1 - REAL(DP), PARAMETER :: RY_TO_THZ = 1.0_DP / AU_TERAHERTZ / FPI - REAL(DP), PARAMETER :: RY_TO_GHZ = RY_TO_THZ*1000.0_DP - REAL(DP), PARAMETER :: RY_TO_CMM1 = 1.E+10_DP * RY_TO_THZ / C_SI - ! - REAL(DP), PARAMETER :: AVOGADRO = 6.02214129D+23 - -END MODULE constants - -! perl script to create a program to list the available constants: -! extract with: grep '^!XX!' constants.f90 | sed 's,!XX!,,' > mkconstlist.pl -! then run: perl mkconstlist.pl constants.f90 > testme.f90 -! and compile and run: testme.f90 -!XX!#!/usr/bin/perl -w -!XX! -!XX!use strict; -!XX! -!XX!print <) { -!XX! if ( /REAL\s*\(DP\)\s*,\s*PARAMETER\s*::\s*([a-zA-Z_0-9]+)\s*=.*$/ ) { -!XX! print " WRITE (*,'(A18,G24.17)') '$1:',$1\n"; -!XX! } -!XX!} -!XX! -!XX!print < add/subtract rigid-ion term - logical :: loto_2d ! 2D LOTO correction - ! - ! local variables - ! - real(DP):: geg, gp2, r ! , For 2d loto: gp2, r - integer :: na,nb, i,j, m1, m2, m3 - integer :: nr1x, nr2x, nr3x - real(DP) :: alph, fac,g1,g2,g3, facgd, arg, gmax, alat_new - real(DP) :: zag(3),zbg(3),zcg(3), fnat(3), reff(2,2) - real :: time1, time2 - complex(dp) :: facg - ! - ! alph is the Ewald parameter, geg is an estimate of G^2 - ! such that the G-space sum is convergent for that alph - ! very rough estimate: geg/4/alph > gmax = 14 - ! (exp (-14) = 10^-6) - ! - call cpu_time(time1) - gmax= 14.d0 - alph= 1.0d0 - geg = gmax*alph*4.0d0 - - ! print *, "" - ! print *, "[RGD_BLK] Q = ", q - ! print *, "[RGD_BLK] NAT:", nat - ! print *, "[RGD_BLK] OMEGA:", omega - ! print *, "[RGD_BLK] ZEU:" - ! do i = 1, nat - ! do j = 1, 3 - ! print *, zeu(:, j, i) - ! enddo - ! end do - ! print *, "[RGD_BLK] ALAT:", alat - ! print *, "[RGD_BLK] BG:" - ! do i = 1, 3 - ! print *, bg(:, i) - ! end do - ! print *, "[RGD_BLK] TAU:" - ! do i = 1, nat - ! print *, tau(:, i) - ! end do - - ! print *, "[RGD_BLK] EPSIL:" - ! do i = 1, 3 - ! print *, epsil(:, i) - ! end do - - ! print *, "[RGD_BLK] DYN:" - ! do na = 1, nat - ! do nb = 1, nat - ! do i = 1, 3 - ! print *, dyn(:, i, na, nb) - ! end do - ! end do - ! end do - - ! Silicon - alat_new = 10.0d0 - - - ! Estimate of nr1x,nr2x,nr3x generating all vectors up to G^2 < geg - ! Only for dimensions where periodicity is present, e.g. if nr1=1 - ! and nr2=1, then the G-vectors run along nr3 only. - ! (useful if system is in vacuum, e.g. 1D or 2D) - ! - if (nr1 == 1) then - nr1x=0 - else - nr1x = int ( sqrt (geg) / & - (sqrt (bg (1, 1) **2 + bg (2, 1) **2 + bg (3, 1) **2) )) + 1 - endif - if (nr2 == 1) then - nr2x=0 - else - nr2x = int ( sqrt (geg) / & - ( sqrt (bg (1, 2) **2 + bg (2, 2) **2 + bg (3, 2) **2) )) + 1 - endif - if (nr3 == 1) then - nr3x=0 - else - nr3x = int ( sqrt (geg) / & - (sqrt (bg (1, 3) **2 + bg (2, 3) **2 + bg (3, 3) **2) )) + 1 - endif - ! - - !print *, "[RGD_BLK] integration grid:", nr1x, nr2x, nr3x - if (abs(sign) /= 1.0_DP) & - call errore ('rgd_blk',' wrong value for sign ',1) - ! - IF (loto_2d) THEN - fac = sign*e2*fpi/omega*0.5d0*alat/bg(3,3) - reff=0.0d0 - DO i=1,2 - DO j=1,2 - reff(i,j)=epsil(i,j)*0.5d0*tpi/bg(3,3) ! (eps)*c/2 in 2pi/a units - ENDDO - ENDDO - DO i=1,2 - reff(i,i)=reff(i,i)-0.5d0*tpi/bg(3,3) ! (-1)*c/2 in 2pi/a units - ENDDO - ELSE - fac = sign*e2*fpi/omega - ENDIF - do m1 = -nr1x,nr1x - do m2 = -nr2x,nr2x - do m3 = -nr3x,nr3x - ! - g1 = m1*bg(1,1) + m2*bg(1,2) + m3*bg(1,3) - g2 = m1*bg(2,1) + m2*bg(2,2) + m3*bg(2,3) - g3 = m1*bg(3,1) + m2*bg(3,2) + m3*bg(3,3) - ! - IF (loto_2d) THEN - geg = g1**2 + g2**2 + g3**2 - r=0.0d0 - gp2=g1**2+g2**2 - IF (gp2>1.0d-8) THEN - r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 - r=r/gp2 - ENDIF - ELSE - geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & - g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & - g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) - ENDIF - ! - if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then - ! - IF (loto_2d) THEN - facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) - ELSE - facgd = fac*exp(-geg/alph/4.0d0)/geg - ENDIF - ! - do na = 1,nat - zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) - fnat(:) = 0.d0 - do nb = 1,nat - arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & - g2 * (tau(2,na)-tau(2,nb))+ & - g3 * (tau(3,na)-tau(3,nb))) - zcg(:) = g1*zeu(1,:,nb) + g2*zeu(2,:,nb) + g3*zeu(3,:,nb) - fnat(:) = fnat(:) + zcg(:)*cos(arg) - end do - do j=1,3 - do i=1,3 - dyn(i,j,na,na) = dyn(i,j,na,na) - facgd * & - zag(i) * fnat(j) - end do - end do - end do - end if - ! - g1 = g1 + q(1) - g2 = g2 + q(2) - g3 = g3 + q(3) - ! - IF (loto_2d) THEN - geg = g1**2+g2**2+g3**2 - r=0.0d0 - gp2=g1**2+g2**2 - IF (gp2>1.0d-8) THEN - r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 - r=r/gp2 - ENDIF - ELSE - geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & - g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & - g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) - ENDIF - ! - if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then - ! - IF (loto_2d) THEN - facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) - ELSE - facgd = fac*exp(-geg/alph/4.0d0)/geg - ENDIF - ! - do nb = 1,nat - zbg(:)=g1*zeu(1,:,nb)+g2*zeu(2,:,nb)+g3*zeu(3,:,nb) - do na = 1,nat - zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) - arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & - g2 * (tau(2,na)-tau(2,nb))+ & - g3 * (tau(3,na)-tau(3,nb))) - ! - facg = facgd * CMPLX(cos(arg),sin(arg),kind=DP) - do j=1,3 - do i=1,3 - dyn(i,j,na,nb) = dyn(i,j,na,nb) + facg * & - zag(i) * zbg(j) - end do - end do - end do - end do - end if - end do - end do - end do - -! call cpu_time(time2) -! print *, "Elapsed time in rgd_blk: ", time2 - time1 - - -! print *, "" -! print *, "[RGD_BLK] DYN FINAL:" -! do na = 1, nat -! do nb = 1, nat -! do i = 1, 3 -! print *, dyn(:, i, na, nb) -! end do -! end do -! end do - -! print *, "" -! print *, "" -! print *, "----------------------------------------------------" - - - - ! - return - ! - end subroutine rgd_blk - - - -! subroutine rgd_blk (nr1,nr2,nr3,nat,dyn,q,tau,epsil,zeu,bg,omega,alat,loto_2d,sign) -! !----------------------------------------------------------------------- -! ! compute the rigid-ion (long-range) term for q -! ! The long-range term used here, to be added to or subtracted from the -! ! dynamical matrices, is exactly the same of the formula introduced in: -! ! X. Gonze et al, PRB 50. 13035 (1994) . Only the G-space term is -! ! implemented: the Ewald parameter alpha must be large enough to -! ! have negligible r-space contribution -! ! -! use kinds, only: dp -! use constants, only: pi,tpi, fpi, e2 -! implicit none -! integer :: nr1, nr2, nr3 ! FFT grid -! integer :: nat ! number of atoms -! complex(DP) :: dyn(3,3,nat,nat) ! dynamical matrix -! real(DP) & -! q(3), &! q-vector -! tau(3,nat), &! atomic positions -! epsil(3,3), &! dielectric constant tensor -! zeu(3,3,nat), &! effective charges tensor -! at(3,3), &! direct lattice basis vectors -! bg(3,3), &! reciprocal lattice basis vectors -! omega, &! unit cell volume -! alat, &! cell dimension units (The dimension of the first unit cell vector in Bohr) -! sign ! sign=+/-1.0 ==> add/subtract rigid-ion term -! logical :: loto_2d ! 2D LOTO correction -! ! -! ! local variables -! ! -! real(DP):: geg, gp2, r ! , For 2d loto: gp2, r -! integer :: na,nb, i,j, m1, m2, m3 -! integer :: nr1x, nr2x, nr3x -! real(DP) :: alph, fac,g1,g2,g3, facgd, arg, gmax, alat_new -! real(DP) :: zag(3),zbg(3),zcg(3), fnat(3), reff(2,2) -! real :: time1, time2 -! complex(dp) :: facg -! ! -! ! alph is the Ewald parameter, geg is an estimate of G^2 -! ! such that the G-space sum is convergent for that alph -! ! very rough estimate: geg/4/alph > gmax = 14 -! ! (exp (-14) = 10^-6) -! ! -! call cpu_time(time1) -! gmax= 14.d0 -! alph= 1.0d0 -! geg = gmax*alph*4.0d0 - -! print *, "" -! print *, "[RGD_BLK] Q = ", q -! print *, "[RGD_BLK] NAT:", nat -! print *, "[RGD_BLK] OMEGA:", omega -! print *, "[RGD_BLK] ZEU:" -! do i = 1, nat -! do j = 1, 3 -! print *, zeu(:, j, i) -! enddo -! end do -! print *, "[RGD_BLK] ALAT:", alat -! print *, "[RGD_BLK] BG:" -! do i = 1, 3 -! print *, bg(:, i) -! end do -! print *, "[RGD_BLK] TAU:" -! do i = 1, nat -! print *, tau(:, i) -! end do -! print *, "[RGD_BLK] EPSIL:" -! do i = 1, 3 -! print *, epsil(:, i) -! end do - -! print *, "[RGD_BLK] DYN:" -! do na = 1, nat -! do nb = 1, nat -! do i = 1, 3 -! print *, dyn(:, i, na, nb) -! end do -! end do -! end do - -! ! Silicon example -! alat_new = 10.0d0 - - - -! ! Estimate of nr1x,nr2x,nr3x generating all vectors up to G^2 < geg -! ! Only for dimensions where periodicity is present, e.g. if nr1=1 -! ! and nr2=1, then the G-vectors run along nr3 only. -! ! (useful if system is in vacuum, e.g. 1D or 2D) -! ! -! if (nr1 == 1) then -! nr1x=0 -! else -! nr1x = int ( sqrt (geg) / & -! (alat_new * sqrt (bg (1, 1) **2 + bg (2, 1) **2 + bg (3, 1) **2) ) ) + 1 -! endif -! if (nr2 == 1) then -! nr2x=0 -! else -! nr2x = int ( sqrt (geg) / & -! (alat_new * sqrt (bg (1, 2) **2 + bg (2, 2) **2 + bg (3, 2) **2) )) + 1 -! endif -! if (nr3 == 1) then -! nr3x=0 -! else -! nr3x = int ( sqrt (geg) / & -! (alat_new * sqrt (bg (1, 3) **2 + bg (2, 3) **2 + bg (3, 3) **2) )) + 1 -! endif -! ! - -! print *, "[RGD_BLK] integration grid:", nr1x, nr2x, nr3x -! call flush() - - -! if (abs(sign) /= 1.0_DP) & -! call errore ('rgd_blk',' wrong value for sign ',1) -! ! -! IF (loto_2d) THEN -! fac = sign*e2*fpi/omega*0.5d0/(alat * bg(3,3)) -! reff=0.0d0 -! DO i=1,2 -! DO j=1,2 -! reff(i,j)=epsil(i,j)*0.5d0*tpi/(alat * bg(3,3)) ! (eps)*c/2 in 2pi/a units -! ENDDO -! ENDDO -! DO i=1,2 -! reff(i,i)=reff(i,i)-0.5d0*tpi/(alat * bg(3,3)) ! (-1)*c/2 in 2pi/a units -! ENDDO -! ELSE -! fac = sign*e2*fpi/omega -! ENDIF -! do m1 = -nr1x,nr1x -! do m2 = -nr2x,nr2x -! do m3 = -nr3x,nr3x -! ! -! g1 = m1*bg(1,1) + m2*bg(1,2) + m3*bg(1,3) -! g2 = m1*bg(2,1) + m2*bg(2,2) + m3*bg(2,3) -! g3 = m1*bg(3,1) + m2*bg(3,2) + m3*bg(3,3) -! ! -! IF (loto_2d) THEN -! geg = g1**2 + g2**2 + g3**2 -! r=0.0d0 -! gp2=g1**2+g2**2 -! IF (gp2>1.0d-8) THEN -! r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 -! r=r/gp2 -! ENDIF -! ELSE -! geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & -! g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & -! g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) -! ENDIF -! ! -! if (geg > 0.0_DP .and. geg /alph/4.0_DP < gmax ) then -! ! -! IF (loto_2d) THEN -! facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) -! ELSE -! facgd = fac*exp(-geg/alph/4.0d0)/geg -! ENDIF -! ! -! do na = 1,nat -! zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) -! fnat(:) = 0.d0 -! do nb = 1,nat -! arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & -! g2 * (tau(2,na)-tau(2,nb))+ & -! g3 * (tau(3,na)-tau(3,nb))) -! zcg(:) = g1*zeu(1,:,nb) + g2*zeu(2,:,nb) + g3*zeu(3,:,nb) -! fnat(:) = fnat(:) + zcg(:)*cos(arg) -! end do -! do j=1,3 -! do i=1,3 -! dyn(i,j,na,na) = dyn(i,j,na,na) - facgd * & -! zag(i) * fnat(j) -! end do -! end do -! end do -! end if -! ! -! g1 = g1 + q(1) -! g2 = g2 + q(2) -! g3 = g3 + q(3) -! ! -! IF (loto_2d) THEN -! geg = g1**2+g2**2+g3**2 -! r=0.0d0 -! gp2=g1**2+g2**2 -! IF (gp2>1.0d-8) THEN -! r=g1*reff(1,1)*g1+g1*reff(1,2)*g2+g2*reff(2,1)*g1+g2*reff(2,2)*g2 -! r=r/gp2 -! ENDIF -! ELSE -! geg = (g1*(epsil(1,1)*g1+epsil(1,2)*g2+epsil(1,3)*g3)+ & -! g2*(epsil(2,1)*g1+epsil(2,2)*g2+epsil(2,3)*g3)+ & -! g3*(epsil(3,1)*g1+epsil(3,2)*g2+epsil(3,3)*g3)) -! ENDIF -! ! -! if (geg > 0.0_DP .and. geg/alph/4.0_DP < gmax ) then -! ! -! IF (loto_2d) THEN -! facgd = fac*exp(-geg/alph/4.0d0)/SQRT(geg)/(1.0+r*SQRT(geg)) -! ELSE -! facgd = fac*exp(-geg/alph/4.0d0)/geg -! ENDIF -! ! -! do nb = 1,nat -! zbg(:)=g1*zeu(1,:,nb)+g2*zeu(2,:,nb)+g3*zeu(3,:,nb) -! do na = 1,nat -! zag(:)=g1*zeu(1,:,na)+g2*zeu(2,:,na)+g3*zeu(3,:,na) -! arg = 2.d0*pi* (g1 * (tau(1,na)-tau(1,nb))+ & -! g2 * (tau(2,na)-tau(2,nb))+ & -! g3 * (tau(3,na)-tau(3,nb))) -! ! -! facg = facgd * CMPLX(cos(arg),sin(arg),kind=DP) -! do j=1,3 -! do i=1,3 -! dyn(i,j,na,nb) = dyn(i,j,na,nb) + facg * & -! zag(i) * zbg(j) -! end do -! end do -! end do -! end do -! end if -! end do -! end do -! end do - -! call cpu_time(time2) -! print *, "Elapsed time in rgd_blk: ", time2 - time1 - - -! print *, "" -! print *, "[RGD_BLK] DYN FINAL:" -! do na = 1, nat -! do nb = 1, nat -! do i = 1, 3 -! print *, dyn(:, i, na, nb) -! end do -! end do -! end do - -! print *, "" -! print *, "" -! print *, "----------------------------------------------------" - - -! ! -! return -! ! -! end subroutine rgd_blk - - - -!----------------------------------------------------------------------- -subroutine nonanal(nat, nat_blk, itau_blk, epsil, q, zeu, omega, dyn ) - !----------------------------------------------------------------------- - ! add the nonanalytical term with macroscopic electric fields - ! See PRB 55, 10355 (1997) Eq (60) - ! - use kinds, only: dp - use constants, only: pi, fpi, e2 - implicit none - integer, intent(in) :: nat, nat_blk, itau_blk(nat) - ! nat: number of atoms in the cell (in the supercell in the case - ! of a dyn.mat. constructed in the mass approximation) - ! nat_blk: number of atoms in the original cell (the same as nat if - ! we are not using the mass approximation to build a supercell) - ! itau_blk(na): atom in the original cell corresponding to - ! atom na in the supercell - ! - complex(DP), intent(inout) :: dyn(3,3,nat,nat) ! dynamical matrix - real(DP), intent(in) :: q(3), &! polarization vector - & epsil(3,3), &! dielectric constant tensor - & zeu(3,3,nat_blk), &! effective charges tensor - & omega ! unit cell volume - ! - ! local variables - ! - real(DP) zag(3),zbg(3), &! eff. charges times g-vector - & qeq ! - integer na,nb, &! counters on atoms - & na_blk,nb_blk, &! as above for the original cell - & i,j ! counters on cartesian coordinates - ! - qeq = (q(1)*(epsil(1,1)*q(1)+epsil(1,2)*q(2)+epsil(1,3)*q(3))+ & - q(2)*(epsil(2,1)*q(1)+epsil(2,2)*q(2)+epsil(2,3)*q(3))+ & - q(3)*(epsil(3,1)*q(1)+epsil(3,2)*q(2)+epsil(3,3)*q(3))) - ! -!print*, q(1), q(2), q(3) - if (qeq < 1.d-8) then - write(6,'(5x,"A direction for q was not specified:", & - & "TO-LO splitting will be absent")') - return - end if - ! - do na = 1,nat - na_blk = itau_blk(na) - do nb = 1,nat - nb_blk = itau_blk(nb) - ! - do i=1,3 - ! - zag(i) = q(1)*zeu(1,i,na_blk) + q(2)*zeu(2,i,na_blk) + & - q(3)*zeu(3,i,na_blk) - zbg(i) = q(1)*zeu(1,i,nb_blk) + q(2)*zeu(2,i,nb_blk) + & - q(3)*zeu(3,i,nb_blk) - end do - ! - do i = 1,3 - do j = 1,3 - dyn(i,j,na,nb) = dyn(i,j,na,nb)+ fpi*e2*zag(i)*zbg(j)/qeq/omega -! print*, zag(i),zbg(j),qeq, fpi*e2*zag(i)*zbg(j)/qeq/omega - end do - end do - end do - end do - ! - return -end subroutine nonanal - - -SUBROUTINE trasl( phid, phiq, nq, nr1, nr2, nr3, nat, m1, m2, m3 ) - !---------------------------------------------------------------------------- - ! - USE kinds, ONLY : DP - ! - IMPLICIT NONE - INTEGER, intent(in) :: nr1, nr2, nr3, m1, m2, m3, nat, nq - COMPLEX(DP), intent(in) :: phiq(3,3,nat,nat,48) - COMPLEX(DP), intent(out) :: phid(nr1,nr2,nr3,3,3,nat,nat) - ! - INTEGER :: j1,j2, na1, na2 - ! - DO j1=1,3 - DO j2=1,3 - DO na1=1,nat - DO na2=1,nat - phid(m1,m2,m3,j1,j2,na1,na2) = & - 0.5d0 * ( phiq(j1,j2,na1,na2,nq) + & - CONJG(phiq(j2,j1,na2,na1,nq))) - END DO - END DO - END DO - END DO - ! - RETURN - END SUBROUTINE trasl - \ No newline at end of file diff --git a/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 b/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 deleted file mode 100644 index 149c65d7..00000000 --- a/FModules/.ipynb_checkpoints/eqvect-checkpoint.f90 +++ /dev/null @@ -1,25 +0,0 @@ -! -! Copyright (C) 2001-2008 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -!----------------------------------------------------------------------- -logical function eqvect (x, y, f) - !----------------------------------------------------------------------- - ! - ! This function test if the difference x-y-f is an integer. - ! x, y = 3d vectors in crystal axis, f = fractionary translation - ! - implicit none - double precision, intent(in) :: x (3), y (3), f (3) - ! - double precision, parameter :: accep = 1.0d-4 ! acceptance parameter - ! - eqvect = abs( x(1)-y(1)-f(1) - nint(x(1)-y(1)-f(1)) ) < accep .and. & - abs( x(2)-y(2)-f(2) - nint(x(2)-y(2)-f(2)) ) < accep .and. & - abs( x(3)-y(3)-f(3) - nint(x(3)-y(3)-f(3)) ) < accep - ! - return -end function eqvect diff --git a/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 b/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 deleted file mode 100644 index 97a79fce..00000000 --- a/FModules/.ipynb_checkpoints/error_handler-checkpoint.f90 +++ /dev/null @@ -1,134 +0,0 @@ -! -! Copyright (C) 2001-2007 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -!---------------------------------------------------------------------------- -SUBROUTINE errore( calling_routine, message, ierr ) - !---------------------------------------------------------------------------- - ! - ! ... This is a simple routine which writes an error message to output: - ! ... if ierr <= 0 it does nothing, - ! ... if ierr > 0 it stops. - ! - ! ... **** Important note for parallel execution *** - ! - ! ... in parallel execution unit 6 is written only by the first node; - ! ... all other nodes have unit 6 redirected to nothing (/dev/null). - ! ... As a consequence an error not occurring on the first node - ! ... will be invisible. For T3E and ORIGIN machines, this problem - ! ... is solved by writing an error message to unit * instead of 6. - ! ... Whenever possible (IBM SP machines), we write to the standard - ! ... error, unit 0 (the message will appear in the error files - ! ... produced by loadleveler). - ! - ! - IMPLICIT NONE - ! - CHARACTER(LEN=*), INTENT(IN) :: calling_routine, message - ! the name of the calling calling_routine - ! the output message - INTEGER, INTENT(IN) :: ierr - ! the error flag - INTEGER :: crashunit - INTEGER, EXTERNAL :: find_free_unit - CHARACTER(LEN=6) :: cerr - ! - ! - IF ( ierr <= 0 ) RETURN - ! - ! ... the error message is written un the "*" unit - ! - WRITE( cerr, FMT = '(I6)' ) ierr - WRITE( UNIT = *, FMT = '(/,1X,78("%"))' ) - WRITE( UNIT = *, FMT = '(5X,"Error in routine ",A," (",A,"):")' ) & - TRIM(calling_routine), TRIM(ADJUSTL(cerr)) - WRITE( UNIT = *, FMT = '(5X,A)' ) TRIM(message) - WRITE( UNIT = *, FMT = '(1X,78("%"),/)' ) - ! -#if defined (__MPI) && defined (__AIX) - ! - ! ... in the case of ibm machines it is also written on the "0" unit - ! ... which is automatically connected to stderr - ! - WRITE( UNIT = 0, FMT = '(/,1X,78("%"))') - WRITE( UNIT = 0, FMT = '(5X,"Error in routine ",A," (",A,"):")' ) & - TRIM(calling_routine), TRIM(ADJUSTL(cerr)) - WRITE( UNIT = 0, FMT = '(5X,A)' ) TRIM(message) - WRITE( UNIT = 0, FMT = '(1X,78("%"),/)' ) - ! -#endif - ! - WRITE( *, '(" stopping ...")' ) - ! - CALL flush_unit( 6 ) - ! -#ifdef __PTRACE -#ifdef __INTEL - call tracebackqq(user_exit_code=-1) -#else - WRITE( UNIT = 0, FMT = '(5X,A)' ) "Printing strace..." - CALL ptrace() -#endif -#endif -! -#if defined (__MPI) - ! - ! .. write the message to a file and close it before exiting - ! .. this will prevent loss of information on systems that - ! .. do not flush the open streams - ! .. added by C.C. - ! - crashunit = find_free_unit () - OPEN( UNIT = crashunit, FILE = crash_file, & - POSITION = 'APPEND', STATUS = 'UNKNOWN' ) - ! - WRITE( UNIT = crashunit, FMT = '(/,1X,78("%"))' ) - WRITE( UNIT = crashunit, FMT = '(5X,"task #",I10)' ) mpime - WRITE( UNIT = crashunit, & - FMT = '(5X,"from ",A," : error #",I10)' ) calling_routine, ierr - WRITE( UNIT = crashunit, FMT = '(5X,A)' ) message - WRITE( UNIT = crashunit, FMT = '(1X,78("%"),/)' ) - ! - CLOSE( UNIT = crashunit ) - ! - ! ... try to exit in a smooth way - ! - CALL mp_abort ( 1, world_comm ) - ! -#endif - ! - STOP 1 - ! - RETURN - ! -END SUBROUTINE errore -! -!---------------------------------------------------------------------- -SUBROUTINE infomsg( routine, message ) - !---------------------------------------------------------------------- - ! - ! ... This is a simple routine which writes an info message - ! ... from a given routine to output. - ! - USE io_global, ONLY : stdout, ionode - ! - IMPLICIT NONE - ! - CHARACTER (LEN=*) :: routine, message - ! the name of the calling routine - ! the output message - ! - IF ( ionode ) THEN - ! - WRITE( stdout , '(5X,"Message from routine ",A,":")' ) routine - WRITE( stdout , '(5X,A)' ) message - ! - END IF - ! - RETURN - ! -END SUBROUTINE infomsg -! diff --git a/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 b/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 deleted file mode 100644 index d94cb126..00000000 --- a/FModules/.ipynb_checkpoints/fc_supercell_from_dyn-checkpoint.f90 +++ /dev/null @@ -1,276 +0,0 @@ -! -! This code transform in q space the real space dynamical matrix -! Made by Ion Errea -! Originally part of the sscha.x code -! -subroutine fc_supercell_from_dyn (phitot, q, tau, tau_sc, itau, phitot_sc, nat, nq) - - implicit none - - integer, intent(in) :: nq, nat - - double complex, dimension(nq,3,3,nat,nat), intent(in) :: phitot - double precision, dimension(3,nq), intent(in) :: q - double precision, dimension(3,nat), intent(in):: tau - double precision, dimension(3, nat*nq), intent(in) ::tau_sc - ! integer, dimension(nat), intent(in) :: ityp - integer, dimension(nat*nq), intent(in) :: itau - double precision, dimension(3,3,nat*nq,nat*nq), intent(out) :: phitot_sc - - integer :: natsc - integer :: i, j, alpha, beta, qtot - double precision, dimension(3) :: latvec - double complex :: im, one, complex_number - double precision :: twopi - - one = (1.0d0,0.0d0) - im = (0.0d0,1.0d0) - twopi = 6.283185307179586d0 - - natsc = nq * nat - - do i = 1, natsc - do j = 1, natsc - latvec(:) = -( tau_sc(:,i) - tau(:,itau(i)) - tau_sc(:,j) + tau(:,itau(j))) - do alpha = 1, 3 - do beta = 1, 3 - complex_number = (0.0d0,0.0d0) - do qtot = 1, nq - !print * , "THE Q POINT", qtot, "IS", q(:, qtot), & - ! "THE LATTICE VECTOR", i,j, "IS", latvec(:) - - complex_number = complex_number + & - exp( - im * twopi * dot_product(q(:,qtot),latvec)) * & - phitot(qtot,alpha,beta,itau(i),itau(j)) / & - dble(nq) - end do - if (abs(aimag(complex_number)) .gt. 1.0d-5) then - print *, complex_number - print *, '' - print *, ' ERROR: There are force constants in the supercell that ' - print *, ' are complex. This is not possible. ' - print *, ' Stopping... ' - print *, ' 2 ' - !stop - end if - phitot_sc(alpha,beta,i,j) = real(complex_number) - end do - end do - end do - end do - -end subroutine fc_supercell_from_dyn - -! This is a fast version of the Fourier transform -! Equal to the one implemented in python -! But much faster -subroutine fast_ft_real_space_from_dynq(unit_cell_coords, super_cell_coords, itau, nat, nat_sc, nq, q_tot, dynq, fc_supercell) - - integer, intent(in) :: nat, nat_sc, nq - integer, intent(in), dimension(nat_sc) :: itau - double precision, intent(in) :: unit_cell_coords(nat, 3), super_cell_coords(nat_sc, 3) - double precision, intent(in), dimension(nq, 3) :: q_tot - double complex, intent(in), dimension(nq, 3*nat, 3*nat) :: dynq - - double complex, intent(out), dimension(3*nat_sc, 3*nat_sc) :: fc_supercell - - - integer :: i, j, iq, i_uc, j_uc, h, k - double precision :: R(3), arg, twopi - - double complex :: im, phase - - im = (0.0d0,1.0d0) - twopi = 6.283185307179586d0 - - fc_supercell(:,:) = 0.0d0 - - do i = 1, nat_sc - i_uc = itau(i) - do j = 1, nat_sc - j_uc = itau(j) - - ! Get the distance vector between the two atoms - R(:) = super_cell_coords(i, :) - unit_cell_coords(i_uc,:) - R(:) = R(:) - super_cell_coords(j, :) + unit_cell_coords(j_uc, :) - - ! Perform the Fourier transform - do iq = 1, nq - arg = twopi * sum(q_tot(iq, :) * R) - phase = exp(im * arg) / nq - - do h = 1, 3 - do k = 1, 3 - fc_supercell(3*(i-1) + h, 3*(j-1) + k) = fc_supercell(3*(i-1) + h, 3*(j-1) + k) + & - dynq(iq, 3*(i_uc-1) + h, 3*(j_uc-1) + k) * phase - enddo - enddo - enddo - enddo - enddo - - ! Check if the fc supercell has an imaginary value - -end subroutine fast_ft_real_space_from_dynq - -! -!logical function eqvect1 (x, y) -! !----------------------------------------------------------------------- -! ! -! ! This function test if the difference x-y-f is an integer. -! ! x, y = 3d vectors in crystal axis, f = fractionary translation -! ! -! implicit none -! double precision, intent(in) :: x (3), y (3) -! double precision, parameter :: accep = 1.0d-4 ! acceptance parameter -! -! ! -! ! -! eqvect1 = abs( x(1)-y(1) - nint(x(1)-y(1)) ) < accep .and. & -! abs( x(2)-y(2) - nint(x(2)-y(2) )) < accep .and. & -! abs( x(3)-y(3) - nint(x(3)-y(3) ) ) < accep -! ! -! return -!end function eqvect1 -! - -! by Lorenzo Monacelli -! This subrouitne impose the translation in the supercell -! for the force constant matrix. -! Tau_sc must be in crystal coordinates with respect to the supercell basis! -subroutine impose_trans_sc(fc_sc, tau_sc_cryst, itau, nat_sc) - implicit none - - - integer, intent(in) :: nat_sc - double precision, dimension(3, 3, nat_sc, nat_sc), intent(inout) :: fc_sc - double precision, dimension(3, nat_sc), intent(in) :: tau_sc_cryst - integer, dimension(nat_sc), intent(in) :: itau - - ! ----- HERE THE CODE ----- - double precision, dimension(3, 3) :: fc_tmp - double precision, dimension(3, 3, nat_sc, nat_sc) :: fc_new - - double precision, dimension(3) :: latvec_1, latvec_2, zero_vec - integer :: i, j, h, k, counter - double precision, parameter :: small_value = 1d-6 - logical :: is_equivalent - - fc_new = 0.0d0 - do i = 1, nat_sc - do j = 1, nat_sc - - ! Average the force constant matrix for all lattice vectors in the supercell. - counter = 0 - fc_tmp = 0.0d0 - do h = 1, nat_sc - if (itau(h) /= itau(i)) cycle ! Check if they are the same atom - ! Get the lattice vector - latvec_1 = tau_sc_cryst(:, i) - tau_sc_cryst(:, h) - - do k = 1, nat_sc - if (itau(k) /= itau(j)) cycle ! Check if they are the same atom - - ! Get the second lattice vector - latvec_2 = tau_sc_cryst(:, j) - tau_sc_cryst(:, k) - - zero_vec = latvec_1 - latvec_2 - !print *, "DISTANCE:", zero_vec - - ! Check if the two lattice vectors are the same apart from - ! an unit cell vector - ! In that case the fc should be equal - is_equivalent = abs( latvec_1(1)-latvec_2(1) - nint(latvec_1(1)-latvec_2(1)) ) < small_value .and. & - abs( latvec_1(2)-latvec_2(2) - nint(latvec_1(2)-latvec_2(2) )) < small_value .and. & - abs( latvec_1(3)-latvec_2(3) - nint(latvec_1(3)-latvec_2(3) ) ) < small_value - if ( is_equivalent ) then - fc_tmp = fc_tmp + fc_sc(:, :, h, k) - counter = counter + 1 - print *, "ATOMS EQ TO:", i, j, "ARE:", h, k - end if - end do - end do - - ! Copy the symmetrized matrix into the original one - print *, "COUNTER:", counter - fc_new(:, :, i, j) = fc_tmp / counter - end do - end do - fc_sc = fc_new -end subroutine impose_trans_sc - -! The following subroutine instead perform the inverse transform -subroutine dyn_from_fc ( phitot_sc, q, tau, tau_sc, itau, dyn, nq, nat) - - implicit none - - - integer :: nq, nat - double precision, dimension(3,nq), intent(in) :: q - double precision, dimension(3,nat), intent(in) :: tau - double precision, dimension(3,3,nq*nat,nq*nat), intent(in) :: phitot_sc - double precision, dimension(3,nq*nat), intent(in) :: tau_sc - !integer, dimension(:), intent(in) :: nqs - !integer, dimension(:), intent(in) :: ityp - integer, dimension(nq*nat), intent(in) :: itau - double complex, dimension(nq,3,3,nat,nat), intent(out) :: dyn - - integer :: natsc - integer :: i, j, k, alpha, beta, qtot, R - integer :: ka - double precision, dimension(:,:), allocatable :: latvec - double precision, dimension(3) :: vecaux - double complex :: im, one, complex_number - double precision :: twopi, prec - - one = (1.0d0,0.0d0) - im = (0.0d0,1.0d0) - twopi = 6.283185307179586d0 - - prec = 1.0d-6 - - natsc = nq * nat - - allocate(latvec(nq,3)) - - ! Prepare list of lattice vectors - - ka = 0 - - do i = 1, natsc - if (itau(i) .ne. 1) cycle - ka = ka + 1 - latvec(ka,:) = tau_sc(:,i) - tau(:,1) - end do - - ! Print list of lattice vectors - - do i = 1, nq - print *, latvec(i,:) - end do - - do qtot = 1, nq - do i = 1, nat - do j = 1, nat - do alpha = 1, 3 - do beta = 1, 3 - complex_number = (0.0d0,0.0d0) - do R = 1, ka - ! Check what the atom in the supercell is - do k = 1, natsc - vecaux = tau(:,j) + latvec(R,:) - tau_sc(:,k) - if ( sqrt(dot_product(vecaux,vecaux)) .lt. prec ) then - complex_number = complex_number + & - exp( im * twopi * dot_product(q(:,qtot),latvec(R,:))) * & - phitot_sc(alpha,beta,i,k) - end if - end do - end do - dyn(qtot,alpha,beta,i,j) = complex_number - end do - end do - end do - end do - end do - -end subroutine dyn_from_fc diff --git a/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 b/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 deleted file mode 100644 index 1b7443f5..00000000 --- a/FModules/.ipynb_checkpoints/from_matdyn-checkpoint.f90 +++ /dev/null @@ -1,149 +0,0 @@ -! -!----------------------------------------------------------------------- -SUBROUTINE frc_blk(dyn,q,tau,nat,nr1,nr2,nr3,frc,at,rws,nrws, nrwsx) - !----------------------------------------------------------------------- - ! calculates the dynamical matrix at q from the (short-range part of the) - ! force constants - ! - USE io_global, ONLY : stdout - ! - IMPLICIT NONE - INTEGER nr1, nr2, nr3, nat, n1, n2, n3, & - ipol, jpol, na, nb, m1, m2, m3, nint, i,j, nrws, nrwsx - double complex, intent(out) :: dyn(3,3,nat,nat) - double precision, intent(in):: frc(nr1,nr2,nr3,3,3,nat,nat), tau(3,nat), q(3), & - at(3,3), rws(4,nrwsx) - - double precision :: r(3), weight, r_ws(3), total_weight, arg - double precision, EXTERNAL :: wsweight - double precision,ALLOCATABLE :: wscache(:,:,:,:,:) - - double precision, parameter :: tpi = 6.283185307179586 - LOGICAL, PARAMETER :: first=.true. - ! - FIRST_TIME : IF (first) THEN - !first=.false. - ALLOCATE( wscache(-2*nr3:2*nr3, -2*nr2:2*nr2, -2*nr1:2*nr1, nat,nat) ) - DO na=1, nat - DO nb=1, nat - total_weight=0.0d0 - ! - DO n1=-2*nr1,2*nr1 - DO n2=-2*nr2,2*nr2 - DO n3=-2*nr3,2*nr3 - DO i=1, 3 - r(i) = n1*at(i,1)+n2*at(i,2)+n3*at(i,3) - r_ws(i) = r(i) + tau(i,na)-tau(i,nb) - END DO - wscache(n3,n2,n1,nb,na) = wsweight(r_ws,rws,nrws, nrwsx) - ENDDO - ENDDO - ENDDO - ENDDO - ENDDO - ENDIF FIRST_TIME - ! - DO na=1, nat - DO nb=1, nat - total_weight=0.0d0 - DO n1=-2*nr1,2*nr1 - DO n2=-2*nr2,2*nr2 - DO n3=-2*nr3,2*nr3 - ! - ! SUM OVER R VECTORS IN THE SUPERCELL - VERY VERY SAFE RANGE! - ! - DO i=1, 3 - r(i) = n1*at(i,1)+n2*at(i,2)+n3*at(i,3) - END DO - - weight = wscache(n3,n2,n1,nb,na) - IF (weight .GT. 0.0d0) THEN - ! - ! FIND THE VECTOR CORRESPONDING TO R IN THE ORIGINAL CELL - ! - m1 = MOD(n1+1,nr1) - IF(m1.LE.0) m1=m1+nr1 - m2 = MOD(n2+1,nr2) - IF(m2.LE.0) m2=m2+nr2 - m3 = MOD(n3+1,nr3) - IF(m3.LE.0) m3=m3+nr3 - ! - ! FOURIER TRANSFORM - ! - arg = tpi*(q(1)*r(1) + q(2)*r(2) + q(3)*r(3)) - DO ipol=1, 3 - DO jpol=1, 3 - dyn(ipol,jpol,na,nb) = & - dyn(ipol,jpol,na,nb) + & - frc(m1,m2,m3,ipol,jpol,na,nb) & - *DCMPLX(COS(arg),-SIN(arg))*weight - END DO - END DO - END IF - total_weight=total_weight + weight - END DO - END DO - END DO - IF (ABS(total_weight-nr1*nr2*nr3).GT.1.0d-8) THEN - WRITE(stdout,*) "Total weight:", total_weight - WRITE(stdout,*) "NR1,2,3 = ", nr1, nr2, nr3 - CALL errore ('frc_blk','wrong total_weight',1) - END IF - END DO - END DO - ! - RETURN -END SUBROUTINE frc_blk -! -!----------------------------------------------------------------------- -subroutine wsinit(rws,nrwsx,nrws,atw) -!----------------------------------------------------------------------- -! - implicit none - double precision, intent(inout) :: rws(4,nrwsx) - double precision, intent(in) :: atw(3,3) - integer, intent(out) :: nrws - integer, intent(in) :: nrwsx - - integer i, ii, ir, jr, kr , nx - double precision eps - parameter (eps=1.0d-6,nx=2) - ii = 1 - do ir=-nx,nx - do jr=-nx,nx - do kr=-nx,nx - do i=1,3 - rws(i+1,ii) = atw(i,1)*ir + atw(i,2)*jr + atw(i,3)*kr - end do - rws(1,ii)=rws(2,ii)*rws(2,ii)+rws(3,ii)*rws(3,ii)+ & - rws(4,ii)*rws(4,ii) - rws(1,ii)=0.5d0*rws(1,ii) - if (rws(1,ii).gt.eps) ii = ii + 1 - if (ii.gt.nrwsx) call errore('wsinit', 'ii.gt.nrwsx',1) - end do - end do - end do - nrws = ii - 1 - return -end subroutine wsinit -!----------------------------------------------------------------------- -function wsweight(r,rws,nrws, nrwsx) -!----------------------------------------------------------------------- -! - implicit none - integer ir, nreq, nrws, nrwsx - double precision, dimension(4, nrwsx), intent(in) :: rws - double precision r(3), rrt, ck, eps, wsweight - parameter (eps=1.0d-7) -! - wsweight = 0.d0 - nreq = 1 - do ir =1,nrws - rrt = r(1)*rws(2,ir) + r(2)*rws(3,ir) + r(3)*rws(4,ir) - ck = rrt-rws(1,ir) - if ( ck .gt. eps ) return - if ( abs(ck) .lt. eps ) nreq = nreq + 1 - end do - wsweight = 1.d0/DBLE(nreq) - return -end function wsweight diff --git a/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 b/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 deleted file mode 100644 index f1fa0969..00000000 --- a/FModules/.ipynb_checkpoints/get_q_grid_fast-checkpoint.f90 +++ /dev/null @@ -1,49 +0,0 @@ - -subroutine get_q_grid (bg, supercell_size, q_list, n_size) - - ! Get the q grid of a given cell. - ! BG: reciprocal lattice vectors - ! BG(x, y) is the y-th reciprocal vector, x coordinate - ! - ! SUPERCELL SIZE a vector of int that gives the supercell size - ! - - implicit none - - double precision, dimension(3,3), intent(in) :: bg - integer, dimension(3), intent(in) :: supercell_size - double precision, dimension(3, n_size), intent(out) :: q_list - integer :: n_size - - logical, parameter :: debug = .true. - - - integer :: nr, i1,i2,i3, ka - - - ! Check if the n_size is of the correct value - nr = supercell_size(1) * supercell_size(2) * supercell_size(3) - - - if (nr .ne. n_size) then - print *, "Error, the dimension of q_list must match the supercell" - call flush() - STOP "ERROR IN get_q_grid SUBROUTINE" - endif - - ka = 0 - do i1 = 1, supercell_size(1) - do i2 = 1, supercell_size(2) - do i3 = 1, supercell_size(3) - - ka = ka + 1 - ! Sum the vector - q_list(:, ka) = i1 * bg(:, 1) / supercell_size(1) + & - i2 * bg(:, 2) / supercell_size(2) + & - i3 * bg(:, 3) / supercell_size(3) - - end do - enddo - enddo - - end subroutine get_q_grid \ No newline at end of file diff --git a/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 b/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 deleted file mode 100644 index 79baaf6a..00000000 --- a/FModules/.ipynb_checkpoints/invmat-checkpoint.f90 +++ /dev/null @@ -1,47 +0,0 @@ -! -! Copyright (C) 2004 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -subroutine invmat (n, a, a_inv) - !----------------------------------------------------------------------- - ! computes the inverse "a_inv" of matrix "a", both dimensioned (n,n) - ! matrix "a" is unchanged on output - LAPACK - ! - implicit none - integer :: n - double precision, DIMENSION (n,n), intent(in) :: a - double precision, dimension(n,n), intent(out) :: a_inv - - ! - integer :: info, lda, lwork, ipiv (n) - ! info=0: inversion was successful - ! lda : leading dimension (the same as n) - ! ipiv : work space for pivoting (assumed of length lwork=n) - double precision :: work (n) - ! more work space - ! - lda = n - lwork=n - ! - a_inv(:,:) = a(:,:) - ! - call dgetrf (n, n, a_inv, lda, ipiv, info) - !call errore ('invmat', 'error in DGETRF', abs (info) ) - call dgetri (n, a_inv, lda, ipiv, work, lwork, info) - !call errore ('invmat', 'error in DGETRI', abs (info) ) - ! - ! if (n == 3) then - ! da = a(1,1)*(a(2,2)*a(3,3)-a(2,3)*a(3,2)) + & - ! a(1,2)*(a(2,3)*a(3,1)-a(2,1)*a(3,3)) + & - ! a(1,3)*(a(2,1)*a(3,2)-a(3,1)*a(2,2)) - ! IF (ABS(da) < 1.d-10) CALL errore(' invmat ',' singular matrix ', 1) - ! else - ! da = 0.d0 - ! end if - - return -end subroutine invmat - diff --git a/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 b/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 deleted file mode 100644 index b4cbd35a..00000000 --- a/FModules/.ipynb_checkpoints/symdynph_gq_new-checkpoint.f90 +++ /dev/null @@ -1,235 +0,0 @@ -! -! Copyright (C) 2001-2012 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -!----------------------------------------------------------------------- - -! Source edited from the original QE version -! to be wrapped into a python library -subroutine symdynph_gq_new (xq, phi, s, invs, rtau, irt, irotmq, minus_q, & - nsymq, nat) - !----------------------------------------------------------------------- - ! - ! This routine receives as input an unsymmetrized dynamical - ! matrix expressed on the crystal axes and imposes the symmetry - ! of the small group of q. Furthermore it imposes also the symmetry - ! q -> -q+G if present. - ! - ! - implicit none - ! - ! The dummy variables - ! - integer, intent(in) :: nat, nsymq, irotmq - integer, intent(in) :: s (3, 3, 48), irt (48, nat), invs (48) - ! input: the number of atoms - ! input: the symmetry matrices - ! input: the rotated of each vector - ! input: the small group of q - ! input: the inverse of each matrix - ! input: the order of the small gro - ! input: the rotation sending q -> - double precision, intent(in) :: xq (3), rtau (3, 48, nat) - ! input: the q point - ! input: the R associated at each t - - logical, intent(in) :: minus_q - ! input: true if a symmetry q->-q+G - - double complex, intent(inout) :: phi (3, 3, nat, nat) - ! inp/out: the matrix to symmetrize - ! - ! local variables - ! - integer :: isymq, sna, snb, irot, na, nb, ipol, jpol, lpol, kpol, & - iflb (nat, nat) - ! counters, indices, work space - - double precision :: arg - ! the argument of the phase - - double complex :: phip (3, 3, nat, nat), work (3, 3), fase, faseq (nsymq) - - ! Define the 2pi constant - double precision :: tpi - tpi = 6.283185307179586 - - - ! Print all the input variables - ! print *, "-------------------------------------" - ! print *, "xq:", xq - ! print *, "PHI:" - ! do na = 1, nat - ! do nb = 1, nat - ! print *, na, nb - ! do jpol = 1, 3 - ! print *, phi(:, jpol, na, nb) - ! end do - ! end do - ! end do - ! print *, "N sym:", nsymq - ! print *, "Symmetries:" - ! do isymq = 1, nsymq - ! print *, " ", isymq - ! do jpol = 1, 3 - ! print *, s(:, jpol, isymq) - ! end do - ! print *, "irt:", irt(isymq, :) - ! end do - ! print *, "INVS:" - ! print *, invs(:) - ! print *, "MINUS Q:", minus_q, "IROTMQ:", irotmq - ! print *, "-------------------------------------" - - - - ! work space, phase factors - ! - ! We start by imposing hermiticity - ! - do na = 1, nat - do nb = 1, nat - do ipol = 1, 3 - do jpol = 1, 3 - phi (ipol, jpol, na, nb) = 0.5d0 * (phi (ipol, jpol, na, nb) & - + CONJG(phi (jpol, ipol, nb, na) ) ) - phi (jpol, ipol, nb, na) = CONJG(phi (ipol, jpol, na, nb) ) - enddo - enddo - enddo - enddo - ! - ! If no other symmetry is present we quit here - ! - if ( (nsymq == 1) .and. (.not.minus_q) ) return - ! - ! Then we impose the symmetry q -> -q+G if present - ! - if (minus_q) then - do na = 1, nat - do nb = 1, nat - do ipol = 1, 3 - do jpol = 1, 3 - work(:,:) = (0.d0, 0.d0) - sna = irt (irotmq, na) - snb = irt (irotmq, nb) - arg = 0.d0 - do kpol = 1, 3 - arg = arg + (xq (kpol) * (rtau (kpol, irotmq, na) - & - rtau (kpol, irotmq, nb) ) ) - enddo - arg = arg * tpi - fase = DCMPLX(cos (arg), sin (arg)) - do kpol = 1, 3 - do lpol = 1, 3 - work (ipol, jpol) = work (ipol, jpol) + & - s (ipol, kpol, irotmq) * s (jpol, lpol, irotmq) & - * phi (kpol, lpol, sna, snb) * fase - enddo - enddo - phip (ipol, jpol, na, nb) = (phi (ipol, jpol, na, nb) + & - CONJG( work (ipol, jpol) ) ) * 0.5d0 - enddo - enddo - enddo - enddo - phi = phip - endif - - ! - ! Here we symmetrize with respect to the small group of q - ! - if (nsymq == 1) return - - iflb (:, :) = 0 - do na = 1, nat - do nb = 1, nat - if (iflb (na, nb) == 0) then - work(:,:) = (0.d0, 0.d0) - do isymq = 1, nsymq - ! Print the symmetry - ! if (na .eq. 1 .and. nb .eq. 1) then - ! print *, "Symmetry", isymq - ! print *, s(:, 1, isymq) - ! print *, s(:, 2, isymq) - ! print *, s(:, 3, isymq) - ! print *, "" - ! end if - - - irot = isymq - sna = irt (irot, na) - snb = irt (irot, nb) - arg = 0.d0 - do ipol = 1, 3 - arg = arg + (xq (ipol) * (rtau (ipol, irot, na) - & - rtau (ipol, irot, nb) ) ) - enddo - arg = arg * tpi - faseq (isymq) = DCMPLX(cos (arg), sin (arg)) - do ipol = 1, 3 - do jpol = 1, 3 - do kpol = 1, 3 - do lpol = 1, 3 - work (ipol, jpol) = work (ipol, jpol) + & - s (ipol, kpol, irot) * s (jpol, lpol, irot) & - * phi (kpol, lpol, sna, snb) * faseq (isymq) - enddo - enddo - enddo - enddo - enddo - - ! ! Print the Phi matrix - ! print "(A10, I8,A10,I8)", "NA = ", na, "NB =", nb - ! print *, "Phi:" - ! print *, phi(:, 1, na, nb) - ! print *, phi(:, 2, na, nb) - ! print *, phi(:, 3, na, nb) - - ! print *, "" - ! print *, "Work:" - ! print *, work(:, 1) - ! print *, work(:, 2) - ! print *, work(:, 3) - - do isymq = 1, nsymq - irot = isymq - sna = irt (irot, na) - snb = irt (irot, nb) - do ipol = 1, 3 - do jpol = 1, 3 - phi (ipol, jpol, sna, snb) = (0.d0, 0.d0) - do kpol = 1, 3 - do lpol = 1, 3 - phi (ipol, jpol, sna, snb) = phi (ipol, jpol, sna, snb) & - + s (ipol, kpol, invs (irot) ) * s (jpol, lpol, invs (irot) ) & - * work (kpol, lpol) * CONJG(faseq (isymq) ) - enddo - enddo - enddo - enddo - iflb (sna, snb) = 1 - enddo - - endif - enddo - enddo - phi (:, :, :, :) = phi (:, :, :, :) / DBLE(nsymq) - - - ! print *, "OUT PHI:" - ! do na = 1, nat - ! do nb = 1, nat - ! print *, na, nb - ! do jpol = 1, 3 - ! print *, phi(:, jpol, na, nb) - ! end do - ! end do - ! end do - - return -end subroutine symdynph_gq_new diff --git a/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 b/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 deleted file mode 100644 index 69d61ec9..00000000 --- a/FModules/.ipynb_checkpoints/symm_base-checkpoint.f90 +++ /dev/null @@ -1,1054 +0,0 @@ -! -! Copyright (C) 2010-2011 Quantum ESPRESSO group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -!-------------------------------------------------------------------------- -! -MODULE symm_base - - ! - ! ... The variables needed to describe the symmetry properties - ! ... and the routines to find crystal symmetries - ! - ! ... these are acceptance criteria - ! - double precision, parameter :: eps1 = 1.0d-6, eps2 = 1.0d-5 - double precision :: accep - ! - SAVE - ! - PRIVATE :: accep - ! - ! ... Exported variables - ! - PUBLIC :: s, sr, sname, ft, ftau, nrot, nsym, nsym_ns, nsym_na, t_rev, & - no_t_rev, time_reversal, irt, invs, invsym, d1, d2, d3, & - allfrac, nofrac, nosym, nosym_evc - INTEGER :: & - s(3,3,48), &! symmetry matrices, in crystal axis - invs(48), &! index of inverse operation: S^{-1}_i=S(invs(i)) - ftau(3,48), &! fractional translations, in FFT coordinates - nrot, &! number of bravais lattice symmetries - nsym = 1, &! total number of crystal symmetries - nsym_ns = 0, &! nonsymmorphic (fractional translation) symms - nsym_na = 0 ! excluded nonsymmorphic symmetries because - ! fract. transl. is noncommensurate with FFT grid - DOUBLE PRECISION :: & - ft (3,48), &! fractional translations, in crystal axis - sr (3,3,48), &! symmetry matrices, in cartesian axis - at(3,3), &! Unit cell vectors (the first index is the cartesian coordinate, the second the vector) - bg(3,3) ! Reciprocal lattice vectors - ! - ! ... note: ftau are used for symmetrization in real space (phonon, exx) - ! ... in which case they must be commensurated with the FFT grid - ! - CHARACTER(LEN=45) :: sname(48) ! name of the symmetries - INTEGER :: & - t_rev(48) = 0 ! time reversal flag, for noncolinear magnetism - INTEGER, ALLOCATABLE :: & - irt(:,:) ! symmetric atom for each atom and sym.op. - LOGICAL :: & - time_reversal=.true., &! if .TRUE. the system has time reversal symmetry - invsym, &! if .TRUE. the system has inversion symmetry - nofrac= .FALSE., &! if .TRUE. fract. translations are not allowed - allfrac= .FALSE., &! if .TRUE. all fractionary transations allowed, - ! even those not commensurate with FFT grid - nosym = .FALSE., &! if .TRUE. no symmetry is used - nosym_evc = .FALSE., &! if .TRUE. symmetry is used only to symmetrize - ! k points - no_t_rev=.FALSE. ! if .TRUE. remove the symmetries that - ! require time reversal - double precision,TARGET :: & - d1(3,3,48), &! matrices for rotating spherical - d2(5,5,48), &! harmonics (d1 for l=1, ...) - d3(7,7,48) ! - ! - ! ... Exported routines - ! - PUBLIC :: find_sym, inverse_s, copy_sym, checkallsym, & - s_axis_to_cart, set_sym, set_sym_bl, set_at_bg - ! -CONTAINS - subroutine set_accep_threshold(thr) - ! Set the acceptance threshold for the symmetry operation. - implicit none - double precision, intent(in) :: thr - - accep = thr - end subroutine set_accep_threshold - - subroutine get_accep_threshold(thr) - ! Get the acceptance threshold for the symmetry operation - implicit none - double precision, intent(out) :: thr - thr = accep - end subroutine get_accep_threshold - - ! - SUBROUTINE set_at_bg(new_at, new_bg) - ! SETUP THE unit cell vector and reciprocal lattice - implicit none - double precision, dimension(3,3), intent(in) :: new_at, new_bg - at = new_at - bg = new_bg - END SUBROUTINE set_at_bg - - SUBROUTINE inverse_s ( ) - !----------------------------------------------------------------------- - ! - ! Locate index of S^{-1} - ! - IMPLICIT NONE - ! - INTEGER :: isym, jsym, ss (3, 3) - LOGICAL :: found - ! - DO isym = 1, nsym - found = .FALSE. - DO jsym = 1, nsym - ! - ss = MATMUL (s(:,:,jsym),s(:,:,isym)) - ! s(:,:,1) is the identity - IF ( ALL ( s(:,:,1) == ss(:,:) ) ) THEN - invs (isym) = jsym - found = .TRUE. - END IF - END DO - IF ( .NOT.found) stop "ERROR, not a group" - END DO - ! - END SUBROUTINE inverse_s - ! -!----------------------------------------------------------------------- -subroutine set_sym_bl ( ) - !----------------------------------------------------------------------- - ! - ! Provides symmetry operations for all bravais lattices - ! Tests first the 24 proper rotations for the cubic lattice; - ! then the 8 rotations specific for the hexagonal axis (special axis c); - ! then inversion is added - ! - implicit none - ! - ! sin3 = sin(pi/3), cos3 = cos(pi/3), msin3 = -sin(pi/3), mcos3 = -cos(pi/3) - ! - double precision, parameter :: sin3 = 0.866025403784438597d0, cos3 = 0.5d0, & - msin3 =-0.866025403784438597d0, mcos3 = -0.5d0 - double precision :: s0(3, 3, 32), overlap (3, 3), rat (3), rot (3, 3), value - ! s0: the s matrices in cartesian axis - ! overlap: inverse overlap matrix between direct lattice - ! rat: the rotated of a direct vector ( cartesian ) - ! rot: the rotated of a direct vector ( crystal axis ) - ! value: component of the s matrix in axis basis - integer :: jpol, kpol, mpol, irot - ! counters over the polarizations and the rotations - - character :: s0name (64) * 45 - ! full name of the rotational part of each symmetry operation - - data s0/ 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, & - -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, & - -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, & - 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, & - 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, & - 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, & - 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, & - 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, & - 0.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 0.d0, & - 0.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, 0.d0, & - 0.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, 0.d0, & - 0.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, 0.d0, & - -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, 1.d0, 0.d0, & - -1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, -1.d0, 0.d0, & - 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, 1.d0, 0.d0, & - 1.d0, 0.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, -1.d0, 0.d0, & - 0.d0, 0.d0, 1.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, & - 0.d0, 0.d0, -1.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 0.d0, & - 0.d0, 0.d0, -1.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, & - 0.d0, 0.d0, 1.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 0.d0, & - 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, 1.d0, 1.d0, 0.d0, 0.d0, & - 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, -1.d0, 1.d0, 0.d0, 0.d0, & - 0.d0, -1.d0, 0.d0, 0.d0, 0.d0, 1.d0, -1.d0, 0.d0, 0.d0, & - 0.d0, 1.d0, 0.d0, 0.d0, 0.d0, -1.d0, -1.d0, 0.d0, 0.d0, & - cos3, sin3, 0.d0, msin3, cos3, 0.d0, 0.d0, 0.d0, 1.d0, & - cos3, msin3, 0.d0, sin3, cos3, 0.d0, 0.d0, 0.d0, 1.d0, & - mcos3, sin3, 0.d0, msin3, mcos3, 0.d0, 0.d0, 0.d0, 1.d0, & - mcos3, msin3, 0.d0, sin3, mcos3, 0.d0, 0.d0, 0.d0, 1.d0, & - cos3, msin3, 0.d0, msin3, mcos3, 0.d0, 0.d0, 0.d0, -1.d0, & - cos3, sin3, 0.d0, sin3, mcos3, 0.d0, 0.d0, 0.d0, -1.d0, & - mcos3, msin3, 0.d0, msin3, cos3, 0.d0, 0.d0, 0.d0, -1.d0, & - mcos3, sin3, 0.d0, sin3, cos3, 0.d0, 0.d0, 0.d0, -1.d0 / - - data s0name/ 'identity ',& - '180 deg rotation - cart. axis [0,0,1] ',& - '180 deg rotation - cart. axis [0,1,0] ',& - '180 deg rotation - cart. axis [1,0,0] ',& - '180 deg rotation - cart. axis [1,1,0] ',& - '180 deg rotation - cart. axis [1,-1,0] ',& - ' 90 deg rotation - cart. axis [0,0,-1] ',& - ' 90 deg rotation - cart. axis [0,0,1] ',& - '180 deg rotation - cart. axis [1,0,1] ',& - '180 deg rotation - cart. axis [-1,0,1] ',& - ' 90 deg rotation - cart. axis [0,1,0] ',& - ' 90 deg rotation - cart. axis [0,-1,0] ',& - '180 deg rotation - cart. axis [0,1,1] ',& - '180 deg rotation - cart. axis [0,1,-1] ',& - ' 90 deg rotation - cart. axis [-1,0,0] ',& - ' 90 deg rotation - cart. axis [1,0,0] ',& - '120 deg rotation - cart. axis [-1,-1,-1] ',& - '120 deg rotation - cart. axis [-1,1,1] ',& - '120 deg rotation - cart. axis [1,1,-1] ',& - '120 deg rotation - cart. axis [1,-1,1] ',& - '120 deg rotation - cart. axis [1,1,1] ',& - '120 deg rotation - cart. axis [-1,1,-1] ',& - '120 deg rotation - cart. axis [1,-1,-1] ',& - '120 deg rotation - cart. axis [-1,-1,1] ',& - ' 60 deg rotation - cryst. axis [0,0,1] ',& - ' 60 deg rotation - cryst. axis [0,0,-1] ',& - '120 deg rotation - cryst. axis [0,0,1] ',& - '120 deg rotation - cryst. axis [0,0,-1] ',& - '180 deg rotation - cryst. axis [1,-1,0] ',& - '180 deg rotation - cryst. axis [2,1,0] ',& - '180 deg rotation - cryst. axis [0,1,0] ',& - '180 deg rotation - cryst. axis [1,1,0] ',& - 'inversion ',& - 'inv. 180 deg rotation - cart. axis [0,0,1] ',& - 'inv. 180 deg rotation - cart. axis [0,1,0] ',& - 'inv. 180 deg rotation - cart. axis [1,0,0] ',& - 'inv. 180 deg rotation - cart. axis [1,1,0] ',& - 'inv. 180 deg rotation - cart. axis [1,-1,0] ',& - 'inv. 90 deg rotation - cart. axis [0,0,-1] ',& - 'inv. 90 deg rotation - cart. axis [0,0,1] ',& - 'inv. 180 deg rotation - cart. axis [1,0,1] ',& - 'inv. 180 deg rotation - cart. axis [-1,0,1] ',& - 'inv. 90 deg rotation - cart. axis [0,1,0] ',& - 'inv. 90 deg rotation - cart. axis [0,-1,0] ',& - 'inv. 180 deg rotation - cart. axis [0,1,1] ',& - 'inv. 180 deg rotation - cart. axis [0,1,-1] ',& - 'inv. 90 deg rotation - cart. axis [-1,0,0] ',& - 'inv. 90 deg rotation - cart. axis [1,0,0] ',& - 'inv. 120 deg rotation - cart. axis [-1,-1,-1]',& - 'inv. 120 deg rotation - cart. axis [-1,1,1] ',& - 'inv. 120 deg rotation - cart. axis [1,1,-1] ',& - 'inv. 120 deg rotation - cart. axis [1,-1,1] ',& - 'inv. 120 deg rotation - cart. axis [1,1,1] ',& - 'inv. 120 deg rotation - cart. axis [-1,1,-1] ',& - 'inv. 120 deg rotation - cart. axis [1,-1,-1] ',& - 'inv. 120 deg rotation - cart. axis [-1,-1,1] ',& - 'inv. 60 deg rotation - cryst. axis [0,0,1] ',& - 'inv. 60 deg rotation - cryst. axis [0,0,-1] ',& - 'inv. 120 deg rotation - cryst. axis [0,0,1] ',& - 'inv. 120 deg rotation - cryst. axis [0,0,-1] ',& - 'inv. 180 deg rotation - cryst. axis [1,-1,0] ',& - 'inv. 180 deg rotation - cryst. axis [2,1,0] ',& - 'inv. 180 deg rotation - cryst. axis [0,1,0] ',& - 'inv. 180 deg rotation - cryst. axis [1,1,0] ' / - - ! compute the overlap matrix for crystal axis - - do jpol = 1,3 - do kpol = 1,3 - rot(kpol,jpol) = at(1,kpol)*at(1,jpol) +& - at(2,kpol)*at(2,jpol) +& - at(3,kpol)*at(3,jpol) - enddo - enddo - ! - ! then its inverse (rot is used as work space) - ! - call invmat (3, rot, overlap) - - nrot = 1 - do irot = 1,32 - ! - ! for each possible symmetry - ! - do jpol = 1,3 - do mpol = 1,3 - ! - ! compute, in cartesian coordinates the rotated vector - ! - rat(mpol) = s0(mpol,1,irot)*at(1,jpol) +& - s0(mpol,2,irot)*at(2,jpol) +& - s0(mpol,3,irot)*at(3,jpol) - enddo - - do kpol = 1,3 - ! - ! the rotated vector is projected on the direct lattice - ! - rot(kpol,jpol) = at(1,kpol)*rat(1) +& - at(2,kpol)*rat(2) +& - at(3,kpol)*rat(3) - enddo - enddo - ! - ! and the inverse of the overlap matrix is applied - ! - do jpol = 1,3 - do kpol = 1,3 - value = overlap(jpol,1)*rot(1,kpol) +& - & overlap(jpol,2)*rot(2,kpol) +& - & overlap(jpol,3)*rot(3,kpol) - if ( abs(DBLE(nint(value))-value) > eps1 ) then - ! - ! if a noninteger is obtained, this implies that this operation - ! is not a symmetry operation for the given lattice - ! - go to 10 - end if - s(kpol,jpol,nrot) = nint(value) - sname(nrot)=s0name(irot) - enddo - enddo - nrot = nrot+1 -10 continue - enddo - nrot = nrot-1 - ! - ! set the inversion symmetry ( Bravais lattices have always inversion - ! symmetry ) - ! - do irot = 1, nrot - do kpol = 1,3 - do jpol = 1,3 - s(kpol,jpol,irot+nrot) = -s(kpol,jpol,irot) - sname(irot+nrot) = s0name(irot+32) - end do - end do - end do - - nrot = 2*nrot - - return - ! -end subroutine set_sym_bl -! -!----------------------------------------------------------------------- -SUBROUTINE find_sym ( nat, tau, ityp, nr1, nr2, nr3, magnetic_sym, m_loc ) - !----------------------------------------------------------------------- - ! - ! This routine finds the point group of the crystal, by eliminating - ! the symmetries of the Bravais lattice which are not allowed - ! by the atomic positions (or by the magnetization if present) - ! - implicit none - ! - integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 - double precision, intent(in) :: tau (3,nat), m_loc(3,nat) - logical, intent(in) :: magnetic_sym - ! - logical :: sym (48) - ! if true the corresponding operation is a symmetry operation - ! - IF ( ALLOCATED(irt) ) DEALLOCATE(irt) - ALLOCATE( irt( 48, nat ) ) - irt( :, : ) = 0 - ! - ! Here we find the true symmetries of the crystal - ! - CALL sgam_at ( nat, tau, ityp, nr1, nr2, nr3, sym ) - ! - ! Here we check for magnetic symmetries - ! - IF ( magnetic_sym ) CALL sgam_at_mag ( nat, m_loc, sym ) - ! - ! If nosym_evc is true from now on we do not use the symmetry any more - ! - IF (nosym_evc) THEN - sym=.false. - sym(1)=.true. - ENDIF - ! - ! Here we re-order all rotations in such a way that true sym.ops - ! are the first nsym; rotations that are not sym.ops. follow - ! - nsym = copy_sym ( nrot, sym ) - ! - IF ( .not. is_group ( ) ) THEN - print *, "find_sym: Not a group! symmetry disabled" - nsym = 1 - END IF - ! - ! check if inversion (I) is a symmetry. - ! If so, it should be the (nsym/2+1)-th operation of the group - ! - invsym = ALL ( s(:,:,nsym/2+1) == -s(:,:,1) ) - ! - CALL inverse_s ( ) - ! - CALL s_axis_to_cart ( ) - ! - return - ! -END SUBROUTINE find_sym -! -!----------------------------------------------------------------------- -subroutine sgam_at ( nat, tau, ityp, nr1, nr2, nr3, sym ) - !----------------------------------------------------------------------- - ! - ! Given the point group of the Bravais lattice, this routine finds - ! the subgroup which is the point group of the considered crystal. - ! Non symmorphic groups are allowed, provided that fractional - ! translations are allowed (nofrac=.false), that the unit cell is - ! not a supercell, and that they are commensurate with the FFT grid - ! - ! On output, the array sym is set to .true.. for each operation - ! of the original point group that is also a symmetry operation - ! of the crystal symmetry point group - ! - implicit none - ! - integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 - ! nat : number of atoms in the unit cell - ! ityp : species of each atom in the unit cell - ! nr* : dimensions of the FFT mesh - ! - double precision, intent(in) :: tau (3, nat) - ! - ! tau : cartesian coordinates of the atoms - ! - ! output variables - ! - logical, intent(out) :: sym (48) - ! sym(isym) : flag indicating if sym.op. isym in the parent group - ! is a true symmetry operation of the crystal - ! - integer :: na, kpol, nb, irot, i, j - ! counters - double precision , allocatable :: xau (:,:), rau (:,:) - ! atomic coordinates in crystal axis - logical :: fractional_translations - double precision :: ft_(3), ft1, ft2, ft3 - ! - allocate(xau(3,nat)) - allocate(rau(3,nat)) - ! - ! Compute the coordinates of each atom in the basis of - ! the direct lattice vectors - ! - - do na = 1, nat - xau(:,na) = bg(1,:) * tau(1,na) + bg(2,:) * tau(2,na) + bg(3,:) * tau(3,na) - enddo - - - - ! - ! check if the identity has fractional translations - ! (this means that the cell is actually a supercell). - ! When this happens, fractional translations are disabled, - ! because there is no guarantee that the generated sym.ops. - ! form a group - ! - nb = 1 - irot = 1 - ! - fractional_translations = .not. nofrac -! *********************************************************************** -! Be careful here... it was commented... probably for odd3 symmetry stuff -! do na = 2, nat -! if ( fractional_translations ) then -! if (ityp (nb) == ityp (na) ) then -! ft_(:) = xau(:,na) - xau(:,nb) - nint( xau(:,na) - xau(:,nb) ) -! ! -! sym(irot) = checksym ( irot, nat, ityp, xau, xau, ft_ ) -! ! -! if ( sym (irot) .and. & -! (abs (ft_(1) **2 + ft_(2) **2 + ft_(3) **2) < 1.d-8) ) & -! call errore ('sgam_at', 'overlapping atoms', na) -! if (sym (irot) ) then -! fractional_translations = .false. -! WRITE( stdout, '(5x,"Found symmetry operation: I + (",& -! & 3f8.4, ")",/,5x,"This is a supercell,", & -! & " fractional translations are disabled")') ft_ -! endif -! endif -! end if -! enddo -! ! -! ********************************************************************* - nsym_ns = 0 - do irot = 1, nrot - ! COMMENTED BY LORENZO MONACELLI - ! ! - ! ! check that the grid is compatible with the S rotation - ! ! - ! if ( mod (s (2, 1, irot) * nr1, nr2) /= 0 .or. & - ! mod (s (3, 1, irot) * nr1, nr3) /= 0 .or. & - ! mod (s (1, 2, irot) * nr2, nr1) /= 0 .or. & - ! mod (s (3, 2, irot) * nr2, nr3) /= 0 .or. & - ! mod (s (1, 3, irot) * nr3, nr1) /= 0 .or. & - ! mod (s (2, 3, irot) * nr3, nr2) /= 0 ) then - ! sym (irot) = .false. - ! print '(3i4)', ( (s (i, j, irot) , j = 1, 3) , i = 1, 3) - ! goto 100 - ! endif - - do na = 1, nat - ! rau = rotated atom coordinates - rau (:, na) = s (1,:, irot) * xau (1, na) + & - s (2,:, irot) * xau (2, na) + & - s (3,:, irot) * xau (3, na) - enddo - ! - ! first attempt: no fractional translation - ! - ftau (:, irot) = 0 - ft (:, irot) = 0 - ft_(:) = 0.d0 - ! - sym(irot) = checksym ( irot, nat, ityp, xau, rau, ft_ ) - ! - if (.not.sym (irot) .and. fractional_translations) then - nb = 1 - do na = 1, nat - if (ityp (nb) == ityp (na) ) then - ! - ! second attempt: check all possible fractional translations - ! - ft_ (:) = rau(:,na) - xau(:,nb) - nint( rau(:,na) - xau(:,nb) ) - ! - sym(irot) = checksym ( irot, nat, ityp, xau, rau, ft_ ) - ! - if (sym (irot) ) then - nsym_ns = nsym_ns + 1 - ft (:,irot) = ft_(:) - go to 100 - end if - endif - enddo - - endif -100 continue - enddo - ! - ! convert ft to FFT coordinates, check if compatible with FFT grid - ! for real-space symmetrization (if done: currently, exx, phonon) - ! - nsym_na = 0 - do irot =1, nrot - if ( sym(irot) .AND. .NOT. allfrac ) then - ft1 = ft(1,irot) * nr1 - ft2 = ft(2,irot) * nr2 - ft3 = ft(3,irot) * nr3 - ! check if the fractional translations are commensurate - ! with the FFT grid, discard sym.op. if not - ! (needed because ph.x symmetrizes in real space) - ! COMMENTED BY LORENZO MONACELLI (WE DO NOT NEED THIS CHECK) - if (abs (ft1 - nint (ft1) ) / nr1 > eps2 .or. & - abs (ft2 - nint (ft2) ) / nr2 > eps2 .or. & - abs (ft3 - nint (ft3) ) / nr3 > eps2 ) then - ! WRITE( stdout, '(5x,"warning: symmetry operation", & - ! & " # ",i2," not allowed. fractional ", & - ! & "translation:"/5x,3f11.7," in crystal", & - ! & " coordinates")') irot, ft_ - sym (irot) = .false. - nsym_na = nsym_na + 1 - nsym_ns = nsym_ns - 1 - endif - ftau (1, irot) = nint (ft1) - ftau (2, irot) = nint (ft2) - ftau (3, irot) = nint (ft3) - end if - end do - ! - ! deallocate work space - ! - deallocate (rau) - deallocate (xau) - ! - return -end subroutine sgam_at -! -!----------------------------------------------------------------------- -subroutine sgam_at_mag ( nat, m_loc, sym ) - !----------------------------------------------------------------------- - ! - ! Find magnetic symmetries, i.e. point-group symmetries that are - ! also symmetries of the local magnetization - including - ! rotation + time reversal operations - ! - implicit none - ! - integer, intent(in) :: nat - double precision, intent(in) :: m_loc(3, nat) - ! - ! m_loc: local magnetization, must be invariant under the sym.op. - ! - logical, intent(inout) :: sym (48) - ! - ! sym(isym) = .true. if rotation isym is a sym.op. of the crystal - ! (i.e. not of the bravais lattice only) - ! - integer :: na, nb, irot - logical :: t1, t2 - double precision , allocatable :: mxau(:,:), mrau(:,:) - ! magnetization and rotated magnetization in crystal axis - ! - allocate ( mxau(3,nat), mrau(3,nat) ) - ! - ! Compute the local magnetization of each atom in the basis of - ! the direct lattice vectors - ! - do na = 1, nat - mxau (:, na)= bg (1, :) * m_loc (1, na) + & - bg (2, :) * m_loc (2, na) + & - bg (3, :) * m_loc (3, na) - enddo - ! - do irot = 1, nrot - ! - t_rev(irot) = 0 - ! - if ( sym (irot) ) then - ! - ! mrau = rotated local magnetization - ! - do na = 1, nat - mrau(:,na) = s(1,:,irot) * mxau(1,na) + & - s(2,:,irot) * mxau(2,na) + & - s(3,:,irot) * mxau(3,na) - enddo - if (sname(irot)(1:3)=='inv') mrau = -mrau - ! - ! check if this a magnetic symmetry - ! - t1 = .true. - t2 = .true. - do na = 1, nat - ! - nb = irt (irot,na) - if ( nb < 1 .or. nb > nat ) then - stop "check_mag_sym: internal error: out-of-bound atomic index" - end if - ! - t1 = ( abs(mrau(1,na) - mxau(1,nb)) + & - abs(mrau(2,na) - mxau(2,nb)) + & - abs(mrau(3,na) - mxau(3,nb)) < eps2 ) .and. t1 - t2 = ( abs(mrau(1,na) + mxau(1,nb))+ & - abs(mrau(2,na) + mxau(2,nb))+ & - abs(mrau(3,na) + mxau(3,nb)) < eps2 ) .and. t2 - ! - enddo - ! - if ( .not.t1 .and. .not.t2 ) then - ! not a magnetic symmetry - sym(irot) = .false. - else if( t2 .and. .not. t1 ) then - ! magnetic symmetry with time reversal, if allowed - IF (no_t_rev) THEN - sym(irot) = .false. - ELSE - t_rev(irot) = 1 - ENDIF - end if - ! - end if - ! - enddo - ! - ! deallocate work space - ! - deallocate ( mrau, mxau ) - ! - return -END SUBROUTINE sgam_at_mag -! -SUBROUTINE set_sym(nat, tau, ityp, nspin_mag, m_loc, nr1, nr2, nr3) - ! - ! This routine receives as input atomic types and positions, if there - ! is noncollinear magnetism and the initial magnetic moments, the fft - ! dimensions nr1, nr2, nr3; it sets the symmetry elements of this module. - ! Note that at and bg are those in cell_base. It sets nrot, nsym, s, - ! sname, sr, invs, ftau, irt, t_rev, time_reversal, and invsym - ! - !----------------------------------------------------------------------- - ! - IMPLICIT NONE - ! input - INTEGER, INTENT(IN) :: nat, ityp(nat), nspin_mag, nr1, nr2, nr3 - double precision, INTENT(IN) :: tau(3,nat) - DOUBLE PRECISION, INTENT(IN) :: m_loc(3,nat) - ! - time_reversal = (nspin_mag /= 4) - t_rev(:) = 0 - CALL set_sym_bl ( ) - CALL find_sym ( nat, tau, ityp, nr1, nr2, nr3, .not.time_reversal, m_loc ) - ! - RETURN - END SUBROUTINE set_sym -! - -INTEGER FUNCTION copy_sym ( nrot_, sym ) -!----------------------------------------------------------------------- - ! - implicit none - integer, intent(in) :: nrot_ - logical, intent(inout) :: sym(48) - ! - integer :: stemp(3,3), ftemp(3), ttemp, irot, jrot - double precision :: ft_(3) - integer, allocatable :: irtemp(:) - character(len=45) :: nametemp - ! - ! copy symm. operations in sequential order so that - ! s(i,j,irot) , irot <= nsym are the sym.ops. of the crystal - ! nsym+1 < irot <= nrot are the sym.ops. of the lattice - ! on exit copy_sym returns nsym - ! - allocate ( irtemp( size(irt,2) ) ) - jrot = 0 - do irot = 1, nrot_ - if (sym (irot) ) then - jrot = jrot + 1 - if ( irot > jrot ) then - stemp = s(:,:,jrot) - s (:,:, jrot) = s (:,:, irot) - s (:,:, irot) = stemp - ftemp(:) = ftau(:,jrot) - ftau (:, jrot) = ftau (:, irot) - ftau (:, irot) = ftemp(:) - ft_(:) = ft(:,jrot) - ft (:, jrot) = ft (:, irot) - ft (:, irot) = ft_(:) - irtemp (:) = irt (jrot,:) - irt (jrot,:) = irt (irot,:) - irt (irot,:) = irtemp (:) - nametemp = sname (jrot) - sname (jrot) = sname (irot) - sname (irot) = nametemp - ttemp = t_rev(jrot) - t_rev(jrot) = t_rev(irot) - t_rev(irot) = ttemp - endif - endif - enddo - sym (1:jrot) = .true. - sym (jrot+1:nrot_) = .false. - deallocate ( irtemp ) - ! - copy_sym = jrot - return - ! -END FUNCTION copy_sym - -! -!----------------------------------------------------------------------- -LOGICAL FUNCTION is_group ( ) - !----------------------------------------------------------------------- - ! - ! Checks that {S} is a group - ! - IMPLICIT NONE - ! - INTEGER :: isym, jsym, ksym, ss (3, 3) - DOUBLE PRECISION :: st(3), dt(3) - LOGICAL :: found - ! - DO isym = 1, nsym - DO jsym = 1, nsym - ! - ss = MATMUL (s(:,:,isym),s(:,:,jsym)) - st(:)= ft(:,jsym) + s(1,:,jsym)*ft(1,isym) + & - s(2,:,jsym)*ft(2,isym) + & - s(3,:,jsym)*ft(3,isym) - ! - ! here we check that the input matrices really form a group: - ! S(k) = S(i)*S(j) - ! ftau_k = S(j)*ftau_i+ftau_j (modulo a lattice vector) - ! - found = .false. - DO ksym = 1, nsym - dt(:) = ft(:,ksym) - st(:) - NINT( ft(:,ksym) - st(:) ) - IF ( ALL( s(:,:,ksym) == ss(:,:) ) .AND. & - ( ABS ( dt(1) ) < eps2 ) .AND. & - ( ABS ( dt(2) ) < eps2 ) .AND. & - ( ABS ( dt(3) ) < eps2 ) ) THEN - IF (found) THEN - is_group = .false. - RETURN - END IF - found = .true. - END IF - END DO - IF ( .NOT.found) then - is_group = .false. - RETURN - END IF - END DO - END DO - is_group=.true. - RETURN - ! -END FUNCTION is_group - -logical function eqvect (x, y, f) - !----------------------------------------------------------------------- - ! - ! This function test if the difference x-y-f is an integer. - ! x, y = 3d vectors in crystal axis, f = fractionary translation - ! - implicit none - double precision, intent(in) :: x (3), y (3), f (3) - ! - ! - eqvect = abs( x(1)-y(1)-f(1) - nint(x(1)-y(1)-f(1)) ) < accep .and. & - abs( x(2)-y(2)-f(2) - nint(x(2)-y(2)-f(2)) ) < accep .and. & - abs( x(3)-y(3)-f(3) - nint(x(3)-y(3)-f(3)) ) < accep - ! - return -end function eqvect - -! -!----------------------------------------------------------------------- -logical function checksym ( irot, nat, ityp, xau, rau, ft_ ) - !----------------------------------------------------------------------- - ! - ! This function receives as input all the atomic positions xau, - ! and the rotated rau by the symmetry operation ir. It returns - ! true if for each atom na, it is possible to find an atom nb - ! which is of the same type of na, and coincide with it after the - ! symmetry operation. Fractional translations are allowed. - ! - implicit none - ! - integer, intent(in) :: nat, ityp (nat), irot - ! nat : number of atoms - ! ityp: the type of each atom - double precision, intent(in) :: xau (3, nat), rau (3, nat), ft_(3) - ! xau: the initial vectors (in crystal coordinates) - ! rau: the rotated vectors (as above) - ! ft_: fractionary translation (as above) - ! - integer :: na, nb - - ! the testing function - ! - do na = 1, nat - do nb = 1, nat - checksym = ( ityp (na) == ityp (nb) .and. & - eqvect (rau (1, na), xau (1, nb), ft_) ) - if ( checksym ) then - ! - ! the rotated atom does coincide with one of the like atoms - ! keep track of which atom the rotated atom coincides with - ! - irt (irot, na) = nb - goto 10 - endif - enddo - ! - ! the rotated atom does not coincide with any of the like atoms - ! s(ir) + ft is not a symmetry operation - ! - return -10 continue - enddo - ! - ! s(ir) + ft is a symmetry operation - ! - return -end function checksym -! -!----------------------------------------------------------------------- -subroutine checkallsym ( nat, tau, ityp, nr1, nr2, nr3 ) - !----------------------------------------------------------------------- - ! given a crystal group this routine checks that the actual - ! atomic positions and bravais lattice vectors are compatible with - ! it. Used in relaxation/MD runs to check that atomic motion is - ! consistent with assumed symmetry. - ! - implicit none - ! - integer, intent(in) :: nat, ityp (nat), nr1, nr2, nr3 - double precision, intent(in) :: tau (3, nat) - ! - integer :: na, kpol, isym, i, j, k, l - logical :: loksym (48) - double precision :: sx (3, 3), sy(3,3) - double precision , allocatable :: xau(:,:), rau(:,:) - ! - allocate (xau( 3 , nat)) - allocate (rau( 3 , nat)) - ! - ! check that s(i,j, isym) is an orthogonal operation - ! - do isym = 1, nsym - sx = DBLE( s(:,:,isym) ) - sy = matmul ( bg, sx ) - sx = matmul ( sy, transpose(at) ) - ! sx is s in cartesian axis - sy = matmul ( transpose ( sx ), sx ) - ! sy = s*transpose(s) = I - do i = 1, 3 - sy (i,i) = sy (i,i) - 1.0d0 - end do - if (any (abs (sy) > eps1 ) ) & - !call errore ('checkallsym', 'not orthogonal operation', isym) - stop "Checkallsym not orthogonal operation" - enddo - ! - ! Compute the coordinates of each atom in the basis of the lattice - ! - do na = 1, nat - do kpol = 1, 3 - xau (kpol, na) = bg (1, kpol) * tau (1, na) + & - bg (2, kpol) * tau (2, na) + & - bg (3, kpol) * tau (3, na) - enddo - enddo - ! - ! generate the coordinates of the rotated atoms - ! - do isym = 1, nsym - do na = 1, nat - do kpol = 1, 3 - rau (kpol, na) = s (1, kpol, isym) * xau (1, na) + & - s (2, kpol, isym) * xau (2, na) + & - s (3, kpol, isym) * xau (3, na) - enddo - enddo - ! - loksym(isym) = checksym ( isym, nat, ityp, xau, rau, ft(1,isym) ) - ! - enddo - ! - ! deallocate work space - ! - deallocate(rau) - deallocate(xau) - ! - do isym = 1,nsym - if (.not.loksym (isym) ) then - stop "chgeckallsym: the symmetry operation is not satisfied" - end if - - end do - if (ANY (.not.loksym (1:nsym) ) ) then - !call symmetrize_at (nsym, s, invs, ft, irt, nat, tau, at, bg, & - ! alat, omega) - stop "checkallsym some of the original symmetry operations not satisfied" - end if - ! - return -end subroutine checkallsym - -!---------------------------------------------------------------------- -subroutine s_axis_to_cart ( ) - !---------------------------------------------------------------------- - ! - ! This routine transforms symmetry matrices expressed in the - ! basis of the crystal axis into rotations in cartesian axis - ! - implicit none - ! - integer :: isym - double precision:: sa(3,3), sb(3,3) - ! - do isym = 1,nsym - sa (:,:) = DBLE ( s(:,:,isym) ) - sb = MATMUL ( bg, sa ) - sr (:,:, isym) = MATMUL ( at, TRANSPOSE (sb) ) - enddo - ! - end subroutine s_axis_to_cart - - - subroutine smallg_q (aq, modenum, sym, minus_q) - !----------------------------------------------------------------------- - ! - ! This routine selects, among the symmetry matrices of the point group - ! of a crystal, the symmetry operations which leave q unchanged. - ! Furthermore it checks if one of the above matrices send q --> -q+G. - ! In this case minus_q is set true. - ! - ! input-output variables - ! - implicit none - - double precision, intent(in) :: aq (3) - ! input: the q point of the crystal - ! IN CRYSTAL UNITS (REMEMBER TO CONVERT IT) - - integer, intent(in) :: modenum - ! input: main switch of the program, used for - ! q<>0 to restrict the small group of q - ! to operation such that Sq=q (exactly, - ! without G vectors) when iswitch = -3. - ! Note, initialize it with true up to the crystal symmetry - logical, intent(inout) :: sym (48) - logical, intent(out) :: minus_q - ! input-output: .true. if symm. op. S q = q + G - ! output: .true. if there is an op. sym.: S q = - q + G - ! - ! local variables - ! - - double precision :: raq (3), zero (3) - ! q vector in crystal basis - ! the rotated of the q vector - ! the zero vector - - integer :: irot, ipol, jpol - ! counter on symmetry op. - ! counter on polarizations - ! counter on polarizations - - ! logical function, check if two vectors are equa - ! - ! return immediately (with minus_q=.true.) if xq=(0,0,0) - ! - minus_q = .true. - if ( (aq (1) == 0.d0) .and. (aq (2) == 0.d0) .and. (aq (3) == 0.d0) ) & - return - ! - ! Set to zero some variables - ! - minus_q = .false. - zero(:) = 0.d0 - ! - ! Transform xq to the crystal basis - ! - ! aq = xq - ! call cryst_to_cart (1, aq, at, - 1) - ! ! - ! Test all symmetries to see if this operation send Sq in q+G or in -q+G - ! - do irot = 1, nrot - if (.not.sym (irot) ) goto 100 - raq(:) = 0.d0 - do ipol = 1, 3 - do jpol = 1, 3 - raq(ipol) = raq(ipol) + DBLE( s(ipol,jpol,irot) ) * aq( jpol) - enddo - enddo - sym (irot) = eqvect (raq, aq, zero) - ! - ! if "iswitch.le.-3" (modenum.ne.0) S must be such that Sq=q exactly ! - ! - if (modenum.ne.0 .and. sym(irot) ) then - do ipol = 1, 3 - sym(irot) = sym(irot) .and. (abs(raq(ipol)-aq(ipol)) < 1.0d-5) - enddo - endif - if (.not.minus_q) then ! ION ERREA's change -! if (sym(irot).and..not.minus_q) then - raq = - raq - minus_q = eqvect (raq, aq, zero) - endif -100 continue - enddo - ! - ! if "iswitch.le.-3" (modenum.ne.0) time reversal symmetry is not included ! - ! - if (modenum.ne.0) minus_q = .false. - ! - return -end subroutine smallg_q - - -END MODULE symm_base diff --git a/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 b/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 deleted file mode 100644 index 96f4a868..00000000 --- a/FModules/.ipynb_checkpoints/symm_matrix-checkpoint.f90 +++ /dev/null @@ -1,97 +0,0 @@ -SUBROUTINE symmatrix ( matr, s, nsym, at, bg) - !----------------------------------------------------------------------- - ! Symmetrize a function f(i,j), i,j=cartesian components - ! e.g. : stress, dielectric tensor (in cartesian axis) - ! - IMPLICIT NONE - ! - double precision, intent(INOUT) :: matr(3,3) - integer, intent(IN) :: nsym - integer, intent(IN) :: s(3,3,48) - double precision, dimension(3,3), intent(in) :: at, bg - ! - INTEGER :: isym, i,j,k,l - double precision :: work (3,3) - ! - IF (nsym == 1) RETURN - ! - ! bring matrix to crystal axis - ! - CALL cart_to_crys_mat ( matr, at ) - ! - ! symmetrize in crystal axis - ! - work (:,:) = 0.0d0 - DO isym = 1, nsym - DO i = 1, 3 - DO j = 1, 3 - DO k = 1, 3 - DO l = 1, 3 - work (i,j) = work (i,j) + & - s (i,k,isym) * s (j,l,isym) * matr (k,l) - END DO - END DO - END DO - END DO - END DO - matr (:,:) = work (:,:) / DBLE(nsym) - ! - ! bring matrix back to cartesian axis - ! - CALL crys_to_cart_mat ( matr, bg ) - ! - END SUBROUTINE symmatrix - - - SUBROUTINE cart_to_crys_mat ( matr, at ) - !----------------------------------------------------------------------- - ! - IMPLICIT NONE - ! - double precision, intent(INOUT) :: matr(3,3) - double precision, intent(IN) :: at(3,3) - ! - double precision:: work(3,3) - INTEGER :: i,j,k,l - ! - work(:,:) = 0.0d0 - DO i = 1, 3 - DO j = 1, 3 - DO k = 1, 3 - DO l = 1, 3 - work(i,j) = work(i,j) + matr(k,l) * at(k,i) * at(l,j) - END DO - END DO - END DO - END DO - ! - matr(:,:) = work(:,:) - ! -END SUBROUTINE cart_to_crys_mat -! -SUBROUTINE crys_to_cart_mat ( matr, bg) - !----------------------------------------------------------------------- - ! - IMPLICIT NONE - ! - double precision, intent(INOUT) :: matr(3,3) - double precision, intent(in) :: bg(3,3) - ! - double precision :: work(3,3) - INTEGER :: i,j,k,l - ! - work(:,:) = 0.0d0 - DO i = 1, 3 - DO j = 1, 3 - DO k = 1, 3 - DO l = 1, 3 - work(i,j) = work(i,j) + & - matr(k,l) * bg(i,k) * bg(j,l) - END DO - END DO - END DO - END DO - matr(:,:) = work(:,:) - ! -END SUBROUTINE crys_to_cart_mat -! \ No newline at end of file diff --git a/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 b/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 deleted file mode 100755 index fd8138dd..00000000 --- a/FModules/.ipynb_checkpoints/symmetry_high_rank-checkpoint.f90 +++ /dev/null @@ -1,1180 +0,0 @@ - -! This subroutine calculates which atom in the supercell -! is related by a translation vector of the supercell -! to another atom of the supercell - -subroutine get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec, nat_sc, nr ) - - implicit none - - double precision, dimension(3,nat_sc), intent(in) :: tau_sc - double precision, dimension(nr, 3), intent(in) :: latvec - double precision, dimension(3,3), intent(in) :: at_sc - integer, dimension(nat_sc,nr), intent(out) :: tau_sc_latvec - - integer :: nr, nat_sc - double precision, dimension(3) :: diff - double precision, dimension(27,3) :: superlatvec - double precision :: prec - logical, parameter :: debug = .true. - - integer :: ka, i, j, k, r - - ! Define precision for scalar product that - ! decides if two positions are the same - - if (debug) then - print *, "=== DEBUG get_tau_sc_latvec ===" - print *, "NAT_SC:", nat_sc - print *, "NR:", NR - print *, "" - call flush() - endif - - prec = 1.0d-6 - - ! Get integers - - !nr = size(latvec(:,1)) - !nat_sc = size(tau_sc(1,:)) - - ! Create the supercell lattice vectors - - ka = 0 - - do i = -1, 1 - do j = -1, 1 - do k = -1, 1 - ka = ka + 1 - superlatvec(ka,:) = dble(i) * at_sc(:,1) + dble(j) * at_sc(:,2) + dble(k) * at_sc(:,3) - end do - end do - end do - - ! Calculate which is the atom of the supercell related to a given - ! lattice vector - - do i = 1, nat_sc - do r = 1, nr - do j = 1, nat_sc - do ka = 1, 27 - diff(:) = tau_sc(:,i) + latvec(r,:) - tau_sc(:,j) + superlatvec(ka,:) - if (dot_product(diff,diff) .lt. prec) then - tau_sc_latvec(i,r) = j - print *, '' - print '(a,i3,a,3f16.8)', ' Supercell atom ', i, ' : ', tau_sc(:,i) - print '(a,i3,a,3f16.8)', ' Translation ', r, ' : ', latvec(r,:) - print '(a,i3,a,3f16.8)', ' Translate atom ', j, ' : ', tau_sc(:,j) - end if - end do - end do - end do - end do - -end subroutine get_tau_sc_latvec - - -! This subroutine imposes the permutation symmetry in the -! third order force constant matrices. The input is given -! with three indices, where each index represents an atom and -! a Cartesian index - -subroutine permute_v3 (v3,n) - - implicit none - - double precision, dimension(n,n,n), intent(inout) :: v3 - - integer :: n - integer :: a, b, c - - ! Assign permutation symmetry - - do a = 1, n - do b = 1, n - do c = 1, n - v3(a,b,c) = (v3(a,b,c) + v3(a,c,b) + v3(b,a,c) + v3(b,c,a) + v3(c,a,b) + v3(c,b,a)) / 6.0d0 - v3(a,c,b) = v3(a,b,c) - v3(b,a,c) = v3(a,b,c) - v3(b,c,a) = v3(a,b,c) - v3(c,a,b) = v3(a,b,c) - v3(c,b,a) = v3(a,b,c) - end do - end do - end do - -end subroutine permute_v3 - -! This subroutine imposes the permutation symmetry in the -! fourth order force constant matrices. The input is given -! with four indices, where each index represents an atom and -! a Cartesian index - -subroutine permute_v4 (v4, n) - - implicit none - - double precision, dimension(n,n,n,n), intent(inout) :: v4 - - integer :: n - integer :: a, b, c, d - - - ! Assign permutation symmetry - - do a = 1, n - do b = 1, n - do c = 1, n - do d = 1, n - v4(a,b,c,d) = ( v4(a,b,c,d) + v4(a,b,d,c) + v4(a,c,b,d) + v4(a,c,d,b) + v4(a,d,b,c) + v4(a,d,c,b) & - + v4(b,a,c,d) + v4(b,a,d,c) + v4(b,d,a,c) + v4(b,d,c,a) + v4(b,c,a,d) + v4(b,c,d,a) & - + v4(c,a,b,d) + v4(c,a,d,b) + v4(c,b,a,d) + v4(c,b,d,a) + v4(c,d,a,b) + v4(c,d,b,a) & - + v4(d,a,b,c) + v4(d,a,c,b) + v4(d,b,a,c) + v4(d,b,c,a) + v4(d,c,a,b) + v4(d,c,b,a) ) / 24.0d0 - v4(a,b,d,c) = v4(a,b,c,d) - v4(a,c,b,d) = v4(a,b,c,d) - v4(a,c,d,b) = v4(a,b,c,d) - v4(a,d,b,c) = v4(a,b,c,d) - v4(a,d,c,b) = v4(a,b,c,d) - ! - v4(b,a,c,d) = v4(a,b,c,d) - v4(b,a,d,c) = v4(a,b,c,d) - v4(b,d,a,c) = v4(a,b,c,d) - v4(b,d,c,a) = v4(a,b,c,d) - v4(b,c,a,d) = v4(a,b,c,d) - v4(b,c,d,a) = v4(a,b,c,d) - ! - v4(c,a,b,d) = v4(a,b,c,d) - v4(c,a,d,b) = v4(a,b,c,d) - v4(c,b,a,d) = v4(a,b,c,d) - v4(c,b,d,a) = v4(a,b,c,d) - v4(c,d,a,b) = v4(a,b,c,d) - v4(c,d,b,a) = v4(a,b,c,d) - ! - v4(d,a,b,c) = v4(a,b,c,d) - v4(d,a,c,b) = v4(a,b,c,d) - v4(d,b,a,c) = v4(a,b,c,d) - v4(d,b,c,a) = v4(a,b,c,d) - v4(d,c,a,b) = v4(a,b,c,d) - v4(d,c,b,a) = v4(a,b,c,d) - end do - end do - end do - end do - -end subroutine permute_v4 - - -! This subroutine imposes the translational symmetry to the -! second order force constants. -! - -subroutine trans_v2 ( v2, tau_sc_latvec, nat_sc, nr ) - - implicit none - - double precision, dimension(3, 3, nat_sc,nat_sc), intent(inout) :: v2 - integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec - integer :: nat_sc, nr - - integer :: ka, i, j, k, l, r, is, js, la, r1, r2 - double precision, dimension(3,3) :: mat_aux - logical, parameter :: debug = .true. - - !nat = size(tau(1,:)) - !nat_sc = size(tau_sc(1,:)) - - if (debug) then - print *, "=== DEBUG TRANS V2 ===" - print *, "NAT_SC:", nat_sc - print *, "NR:", nr - call flush() - endif - - - ! Impose translational symmetry - - do i = 1, nat_sc - do j = 1, nat_sc - mat_aux = 0.0d0 - do r = 1, nr - mat_aux(:,:) = mat_aux(:,:) & - + v2(:, :, tau_sc_latvec(i,r),tau_sc_latvec(j,r)) - end do - mat_aux(:,:) = mat_aux(:,:) / dble(nr) - do r = 1, nr - v2(:, :, tau_sc_latvec(i,r),tau_sc_latvec(j,r)) = mat_aux(:,:) - end do - end do - end do - -end subroutine trans_v2 - -! This subroutine imposes the translational symmetry to the -! third order force constants. -! -! Both in the input and output the third order force constants -! are given only with three indexes, each representing both -! an atom and a Cartesian index, but inside it is used -! with 6 indexes, separating cartesian and atom indexes. -subroutine trans_v3 ( v3, tau_sc_latvec, nat_sc, nr)!tau, tau_sc, itau, at_sc, nat, nat_sc ) - - implicit none - - double precision, dimension(nat_sc*3,nat_sc*3,nat_sc*3), intent(inout) :: v3 - integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec - !double precision, dimension(3,nat), intent(in) :: tau - !double precision, dimension(3,nat_sc), intent(in) :: tau_sc - !integer, dimension(nat_sc), intent(in) :: itau - !double precision, dimension(3,3), intent(in) :: at_sc - - integer :: nat_sc, nr - !double precision, dimension(3) :: cholat, vect, diff - double precision, dimension(:,:,:,:,:,:), allocatable :: v3_6 - !double precision, dimension(:,:), allocatable :: latvec - double precision :: prec - !integer, dimension(:,:), allocatable :: tau_sc_latvec - !logical, dimension(:), allocatable :: assigned - integer :: ka, i, j, k, l, r, is, js, la, r1, r2 - double precision, dimension(3,3,3) :: mat_aux - logical, parameter :: debug = .true. - - prec = 1.0d-6 - - !nat = size(tau(1,:)) - !nat_sc = size(tau_sc(1,:)) - - !nr = nat_sc / nat - - if (debug) then - print *, "=== DEBUG TRANS V3 ===" - print *, "NAT_SC:", nat_sc - !print *, "NAT:", nat - print *, "NR:", nr - call flush() - endif - - !allocate( assigned(nr) ) - allocate( v3_6(nat_sc,nat_sc,nat_sc,3,3,3) ) - !allocate( latvec(nr,3) ) - !allocate( tau_sc_latvec(nat_sc,nr) ) - - ! Get the lattice vectors of the supercell - - !call get_latvec ( tau_sc, tau, itau, latvec, nat, nat_sc, nr ) - - ! Build the 3rd order force-constant matrices - ! in 6 rank tensor - - call threetosix_real ( v3, v3_6, nat_sc) - - ! Assign which is the transformed atom in the supercell - ! given a particular translation vector - - !call get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec, nat_sc, nr ) - - ! Impose translational symmetry - - do i = 1, nat_sc - do j = 1, nat_sc - do k = 1, nat_sc - mat_aux = 0.0d0 - do r = 1, nr - mat_aux(:,:,:) = mat_aux(:,:,:) & - + v3_6(tau_sc_latvec(i,r),tau_sc_latvec(j,r),tau_sc_latvec(k,r),:,:,:) - end do - mat_aux(:,:,:) = mat_aux(:,:,:) / dble(nr) - do r = 1, nr - v3_6(tau_sc_latvec(i,r),tau_sc_latvec(j,r),tau_sc_latvec(k,r),:,:,:) = mat_aux(:,:,:) - end do - end do - end do - end do - - ! Return to the rank 3 tensor of the third order force constants - ! matrices - - call sixtothree_real ( v3_6, v3, nat_sc) - -end subroutine trans_v3 - -! This subroutine imposes the translational symmetry to the -! fourth order force constants. -! -! Both in the input and output the third order force constants -! are given only with three indexes, each representing both -! an atom and a Cartesian index, but inside it is used -! with 6 indexes, separating cartesian and atom indexes. -! TODO: TO BE CONVERTED IN PYTHONIC -!( v3, tau_sc_latvec, nat_sc, nr) -subroutine trans_v4 ( v4, tau_sc_latvec, nat_sc, nr ) - - implicit none - - double precision, dimension(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc), intent(inout) :: v4 - integer, dimension(nat_sc,nr), intent(in) :: tau_sc_latvec - - ! double precision, dimension(:,:), intent(in) :: tau, tau_sc - ! integer, dimension(:), intent(in) :: itau - ! double precision, dimension(3,3), intent(in) :: at_sc - - integer :: nat, nat_sc, nr - - ! double precision, dimension(3) :: cholat, vect, diff - double precision, dimension(:,:,:,:,:,:), allocatable :: v3_6 - ! double precision, dimension(:,:), allocatable :: latvec - double precision :: prec - logical, dimension(:), allocatable :: assigned - integer :: ka, i, j, k, l, r, is, js, la, r1, r2 - double precision, dimension(3,3,3,3) :: mat_aux - logical, parameter :: debug = .true. - - prec = 1.0d-6 - - nat = nat_sc / nr - - if (debug) then - print *, "=== DEBUG TRANS_V4 ===" - print *, "NAT_SC:", nat_sc - print *, "NR:", nr - print *, "NAT:", nat - call flush() - endif - - - ! allocate( assigned(nr) ) - ! allocate( latvec(nr,3) ) - ! allocate( tau_sc_latvec(nat_sc,nr) ) - - ! ! Get the lattice vectors of the supercell - - ! call get_latvec ( tau_sc, tau, itau, latvec, nat, nat_sc, nr ) - - ! ! Assign which is the transformed atom in the supercell - ! ! given a particular translation vector - - ! call get_tau_sc_latvec ( tau_sc, latvec, at_sc, tau_sc_latvec ) - - ! Impose translational symmetry - - do i = 1, nat_sc - do j = 1, nat_sc - do k = 1, nat_sc - do l = 1, nat_sc - mat_aux = 0.0d0 - do r = 1, nr - mat_aux(:,:,:,:) = mat_aux(:,:,:,:) & - + v4((3*(tau_sc_latvec(i,r)-1)+1):(3*(tau_sc_latvec(i,r)-1)+3), & - (3*(tau_sc_latvec(j,r)-1)+1):(3*(tau_sc_latvec(j,r)-1)+3), & - (3*(tau_sc_latvec(k,r)-1)+1):(3*(tau_sc_latvec(k,r)-1)+3), & - (3*(tau_sc_latvec(l,r)-1)+1):(3*(tau_sc_latvec(l,r)-1)+3)) - end do - mat_aux(:,:,:,:) = mat_aux(:,:,:,:) / dble(nr) - do r = 1, nr - v4((3*(tau_sc_latvec(i,r)-1)+1):(3*(tau_sc_latvec(i,r)-1)+3), & - (3*(tau_sc_latvec(j,r)-1)+1):(3*(tau_sc_latvec(j,r)-1)+3), & - (3*(tau_sc_latvec(k,r)-1)+1):(3*(tau_sc_latvec(k,r)-1)+3), & - (3*(tau_sc_latvec(l,r)-1)+1):(3*(tau_sc_latvec(l,r)-1)+3)) = mat_aux(:,:,:,:) - end do - end do - end do - end do - end do - -end subroutine trans_v4 - -! This subroutine imposes the point group symmetry in the second-order -! force-constants - -subroutine sym_v2 ( v2, at_sc, bg_sc, s, irt, nsym, nat_sc) - - implicit none - - double precision, dimension(3,3,nat_sc,nat_sc), intent(inout) :: v2 - double precision, dimension(3,3), intent(in) :: at_sc - double precision, dimension(3,3), intent(in) :: bg_sc - ! Symmetry stuff - - integer, dimension(3,3,48), intent(in) :: s - integer, dimension(48,nat_sc), intent(in) :: irt - integer :: nsym, nat_sc - - INTEGER :: na, nb, nc, isym, nar, nbr, ncr - double precision, ALLOCATABLE :: work (:,:,:,:) - !double precision, dimension(3,3) :: bg_sc - - integer :: iq, i, j, k, alpha, beta, gamm - logical, parameter :: debug = .true. - - if (debug) then - print *, "=== DEBUG SYM_V2 ===" - print *, "NSYM:", nsym - print *, "NAT_SC:", nat_sc - call flush() - endif - - !logical :: prnt_sym - - ! Extract integers - - !nat_sc = size(tau_sc(1,:)) - - ! Allocate variables - - ! Create reciprocal lattice vectors of supercell - - !CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) - - ! Assign values to print fake dynamical matrix in the supercell - - ! Write fake dynamical matrix - -! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & -! fildyn_prefix, ibrav, celldm, tau_sc, & -! type_name, at_sc, lrigid, epsil, zeu) - - ! Extract all information about symmetries - - - ! Symmetrize the third order force constant matrix - - ALLOCATE (work(3,3,nat_sc,nat_sc)) - ! - ! bring third-order matrix to crystal axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - work(alpha,beta,na,nb) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - work(alpha,beta,na,nb) = work(alpha,beta,na,nb) + & - v2(i,j,na,nb)*at_sc(i,alpha)*at_sc(j,beta) - end do - end do - end do - END DO - END DO - END DO - ! - ! symmetrize in crystal axis - ! - v2 = 0.0d0 - DO na = 1, nat_sc - DO nb = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - DO isym = 1, nsym - nar = irt (isym, na) - nbr = irt (isym, nb) - do i = 1, 3 - do j = 1, 3 - v2(alpha,beta,na,nb) = v2(alpha,beta,na,nb) + & - work(i,j,nar,nbr)*s(alpha,i,isym)*s(beta,j,isym) - end do - end do - end do - end do - END DO - END DO - END DO - work (:,:,:,:) = v2 (:,:,:,:) / DBLE(nsym) - ! - ! bring vector back to cartesian axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - v2(alpha,beta,na,nb) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - v2(alpha,beta,na,nb) = v2(alpha,beta,na,nb) + & - work(i,j,na,nb)*bg_sc(alpha,i)*bg_sc(beta,j) - end do - end do - end do - END DO - END DO - END DO - ! - DEALLOCATE (work) - -end subroutine sym_v2 - -! ========================= -! This subroutine imposes the point group symmetry in the third-order -! force-constants - -subroutine sym_v3 ( v3, at_sc, s, irt, nsym, nat_sc ) - - implicit none - - double precision, dimension(nat_sc*3,nat_sc*3,nat_sc*3), intent(inout) :: v3 - !integer, dimension(nat_sc), intent(in) :: ityp_sc - !double precision, dimension(ntyp), intent(in) :: amass - !integer, intent(in) :: ibrav - !double precision, dimension(6), intent(in) :: celldm - !double precision, dimension(3,nat_sc), intent(in) :: tau_sc - !character (len=3), dimension(:), intent(in) :: type_name - double precision, dimension(3,3), intent(in) :: at_sc - !integer, dimension(nat_sc), intent(in) :: itau - - ! The symmetries - integer, dimension(3,3,48), intent(in) :: s - integer, dimension(48, nat_sc), intent(in) :: irt - - integer :: nsym - integer :: nat_sc - - !double complex, dimension(:,:,:,:,:), allocatable :: phitot - !double precision, dimension(3,1) :: q - !integer, dimension(1) :: nqs - !logical :: lrigid - !character (len=50) :: fildyn_prefix - !character (len=512) :: fildyn - !double precision, dimension(3,3) :: epsil - !double precision, dimension(:,:,:), allocatable :: zeu - !character(len=6), EXTERNAL :: int_to_char - - ! Symmetry stuff - - !integer, dimension(48) :: invs, irgq, isq - !double precision, dimension(3,48) :: sxq - !double precision, dimension(:,:,:), allocatable :: rtau - !integer :: nsymq, irotmq, nsym, imq - !logical :: minus_q - - INTEGER :: na, nb, nc, isym, nar, nbr, ncr - double precision, ALLOCATABLE :: work (:,:,:,:,:,:) - double precision, dimension(3,3) :: bg_sc - - double precision, dimension(:,:,:,:,:,:), allocatable :: v32 - - integer :: iq, i, j, k, alpha, beta, gamm - - ! Extract integers - - !nat_sc = size(tau_sc(1,:)) - - ! Allocate variables - - allocate(v32(nat_sc,nat_sc,nat_sc,3,3,3)) - - !allocate(phitot(1,3,3,nat_sc,nat_sc)) - !allocate(zeu(3,3,nat_sc)) - - !allocate(rtau(3,48,nat_sc)) - !allocate(irt(48,nat_sc)) - - ! Create reciprocal lattice vectors of supercell - - CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) - - ! Write third-order force constants with 6 indexes - - call threetosix_real (v3,v32, nat_sc) - - ! Assign values to print fake dynamical matrix in the supercell - -! q = 0.0d0 -! nqs = 1 -! lrigid = .false. -! epsil = 0.0d0 -! phitot = (0.0d0,0.0d0) -! zeu = 0.0d0 -! fildyn_prefix = 'fake_dyn' - -! ! Write fake dynamical matrix - -! ! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & -! ! fildyn_prefix, ibrav, celldm, tau_sc, & -! ! type_name, at_sc, lrigid, epsil, zeu) -! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & -! fildyn_prefix, 0, celldm, tau_sc, & -! type_name, at_sc, lrigid, epsil, zeu) - -! ! Extract all information about symmetries - -! print *, '' -! print *, ' Extracting symmetries of the supercell... ' -! print *, '' - -! iq = 1 - -! fildyn = trim(fildyn_prefix) // int_to_char(iq) - -! call symmdynmat ( fildyn, nat_sc, phitot(1,:,:,:,:), q, & -! s, invs, rtau, irt, irgq, & -! nsymq, irotmq, minus_q, nsym, nqs, isq, & -! imq, sxq, lrigid, epsil, zeu ) - - call print_symm ( s, nsym, irt, .true., nat_sc) - - ! Symmetrize the third order force constant matrix - - ALLOCATE (work(nat_sc,nat_sc,nat_sc,3,3,3)) - ! - ! bring third-order matrix to crystal axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - work(na,nb,nc,alpha,beta,gamm) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - work(na,nb,nc,alpha,beta,gamm) = work(na,nb,nc,alpha,beta,gamm) + & - v32(na,nb,nc,i,j,k)*at_sc(i,alpha)*at_sc(j,beta)*at_sc(k,gamm) - end do - end do - end do - end do - end do - end do - END DO - END DO - END DO - ! - ! symmetrize in crystal axis - ! - v32 = 0.0d0 - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - DO isym = 1, nsym - nar = irt (isym, na) - nbr = irt (isym, nb) - ncr = irt (isym, nc) - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - v32(na,nb,nc,alpha,beta,gamm) = v32(na,nb,nc,alpha,beta,gamm) + & - work(nar,nbr,ncr,i,j,k)*s(alpha,i,isym)*s(beta,j,isym)*s(gamm,k,isym) - end do - end do - end do - END DO - end do - end do - end do - END DO - END DO - END DO - work (:,:,:,:,:,:) = v32 (:,:,:,:,:,:) / DBLE(nsym) - ! - ! bring vector back to cartesian axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - v32(na,nb,nc,alpha,beta,gamm) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - v32(na,nb,nc,alpha,beta,gamm) = v32(na,nb,nc,alpha,beta,gamm) + & - work(na,nb,nc,i,j,k)*bg_sc(alpha,i)*bg_sc(beta,j)*bg_sc(gamm,k) - end do - end do - end do - end do - end do - end do - END DO - END DO - END DO - ! - DEALLOCATE (work) - - ! Write third-order force constants back with 3 indexes - - call sixtothree_real (v32,v3, nat_sc) - - ! Deallocate stuff - - ! deallocate(phitot) - ! deallocate(zeu) - ! deallocate(rtau) - ! deallocate(irt) -end subroutine sym_v3 - -! This subroutine imposes the point group symmetry in the fourth-order -! force-constants - -subroutine sym_v4 ( v4, at_sc, s, irt, nsym, nat_sc ) - - implicit none - - double precision, dimension(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc), intent(inout) :: v4 - !integer, intent(in) :: ntyp - !integer, dimension(:), intent(in) :: ityp_sc - !double precision, dimension(:), intent(in) :: amass - !integer, intent(in) :: ibrav - !double precision, dimension(6), intent(in) :: celldm - !double precision, dimension(:,:), intent(in) :: tau_sc - !character (len=3), dimension(:), intent(in) :: type_name - double precision, dimension(3,3), intent(in) :: at_sc - integer, dimension(3,3,48), intent(in) :: s - integer, dimension(48, nat_sc), intent(in) :: irt - integer, intent(in) :: nsym - !integer, dimension(:), intent(in) :: itau - - - !double complex, dimension(:,:,:,:,:), allocatable :: phitot - !double precision, dimension(3,1) :: q - !integer, dimension(1) :: nqs - !logical :: lrigid - !character (len=50) :: fildyn_prefix - !character (len=512) :: fildyn - !double precision, dimension(3,3) :: epsil - !double precision, dimension(:,:,:), allocatable :: zeu - !character(len=6), EXTERNAL :: int_to_char - - ! Symmetry stuff - - ! integer, dimension(48) :: invs, irgq, isq - ! double precision, dimension(3,48) :: sxq - ! double precision, dimension(:,:,:), allocatable :: rtau - ! integer :: nsymq, irotmq, nsym, imq - ! logical :: minus_q - - INTEGER :: na, nb, nc, nd, isym, nar, nbr, ncr, ndr - double precision, ALLOCATABLE :: work (:,:,:,:) - double precision, dimension(3,3) :: bg_sc - - integer :: nat_sc - - integer :: iq, i, j, k, l, alpha, beta, gamm, delt - logical, parameter :: debug = .true. - - ! Extract integers - - !nat_sc = size(tau_sc(1,:)) - if (debug) then - print *, "=== DEBUG SYM_V4 ===" - print *, "NAT_SC:", nat_sc - print *, "NSYM:", nsym - call flush() - end if - - ! Allocate variables - - ! allocate(phitot(1,3,3,nat_sc,nat_sc)) - ! allocate(zeu(3,3,nat_sc)) - - ! allocate(rtau(3,48,nat_sc)) - ! allocate(irt(48,nat_sc)) - - ! Create reciprocal lattice vectors of supercell - - CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) - - ! Assign values to print fake dynamical matrix in the supercell - -! q = 0.0d0 -! nqs = 1 -! lrigid = .false. -! epsil = 0.0d0 -! phitot = (0.0d0,0.0d0) -! zeu = 0.0d0 -! fildyn_prefix = 'fake_dyn' - -! ! Write fake dynamical matrix - -! ! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & -! ! fildyn_prefix, ibrav, celldm, tau_sc, & -! ! type_name, at_sc, lrigid, epsil, zeu) -! call write_dyn (phitot, q, ntyp, nqs, ityp_sc, amass, & -! fildyn_prefix, 0, celldm, tau_sc, & -! type_name, at_sc, lrigid, epsil, zeu) - -! ! Extract all information about symmetries - -! print *, '' -! print *, ' Extracting symmetries of the supercell... ' -! print *, '' - -! iq = 1 - -! fildyn = trim(fildyn_prefix) // int_to_char(iq) - -! call symmdynmat ( fildyn, nat_sc, phitot(1,:,:,:,:), q, & -! s, invs, rtau, irt, irgq, & -! nsymq, irotmq, minus_q, nsym, nqs, isq, & -! imq, sxq, lrigid, epsil, zeu ) - - call print_symm ( s, nsym, irt, .true., nat_sc) - - ! Symmetrize the fourth order force constant matrix - - ALLOCATE (work(3*nat_sc,3*nat_sc,3*nat_sc,3*nat_sc)) - ! - ! bring third-order matrix to crystal axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - DO nd = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - do delt = 1, 3 - work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - do l = 1, 3 - work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & - work(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & - v4(3*(na-1)+i,3*(nb-1)+j,3*(nc-1)+k,3*(nd-1)+l)* & - at_sc(i,alpha)*at_sc(j,beta)*at_sc(k,gamm)*at_sc(l,delt) - end do - end do - end do - end do - end do - end do - end do - end do - end do - END DO - END DO - END DO - ! - ! symmetrize in crystal axis - ! - v4 = 0.0d0 - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - DO nd = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - do delt = 1, 3 - DO isym = 1, nsym - nar = irt (isym, na) - nbr = irt (isym, nb) - ncr = irt (isym, nc) - ndr = irt (isym, nd) - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - do l = 1, 3 - v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & - v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & - work(3*(nar-1)+i,3*(nbr-1)+j,3*(ncr-1)+k,3*(ndr-1)+l) * & - s(alpha,i,isym)*s(beta,j,isym)*s(gamm,k,isym)*s(delt,l,isym) - end do - end do - end do - end do - end do - end do - END DO - end do - end do - end do - END DO - END DO - END DO - work (:,:,:,:) = v4 (:,:,:,:) / DBLE(nsym) - ! - ! bring vector back to cartesian axis - ! - DO na = 1, nat_sc - DO nb = 1, nat_sc - DO nc = 1, nat_sc - DO nd = 1, nat_sc - do alpha = 1, 3 - do beta = 1, 3 - do gamm = 1, 3 - do delt = 1, 3 - v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = 0.0d0 - do i = 1, 3 - do j = 1, 3 - do k = 1, 3 - do l = 1, 3 - v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) = & - v4(3*(na-1)+alpha,3*(nb-1)+beta,3*(nc-1)+gamm,3*(nd-1)+delt) + & - work(3*(na-1)+i,3*(nb-1)+j,3*(nc-1)+k,3*(nd-1)+l) * & - bg_sc(alpha,i)*bg_sc(beta,j)*bg_sc(gamm,k)*bg_sc(delt,l) - end do - end do - end do - end do - end do - end do - end do - end do - end do - END DO - END DO - END DO - ! - DEALLOCATE (work) - - ! Deallocate stuff - - ! deallocate(phitot) - ! deallocate(zeu) - ! deallocate(rtau) - ! deallocate(irt) - -end subroutine sym_v4 - -! This subroutine creates replicas for one vector (i.e. forces or displacements) -! in the supercell based on the pointgroup symmetries. The subroutine inputs -! the vector for all the random configuration -! and it outputs in a new array the vector replicated in the new structures - -! subroutine sym_replica (v, at_sc, irt, s, nsym, tau_sc_latvec, trans_replica, vr) - -! implicit none - -! double precision, dimension(:,:,:), intent(in) :: v ! Input vector -! ! Dimension (n_random,nat_sc,3) -! double precision, dimension(3,3), intent(in) :: at_sc ! Lattice vectors of the supercell -! integer, dimension(:,:), intent(in) :: irt ! Rotated atom by a symmetry operation -! integer, dimension(3,3,48), intent(in) :: s ! Symmetry operation matrix -! integer, intent(in) :: nsym ! Number of symmetry operations -! integer, dimension(:,:), allocatable :: tau_sc_latvec ! Tells which is the transformed atom by a particular translation -! logical, intent(in) :: trans_replica ! Logical variable to determine if translational replica are included -! double precision, dimension(:,:,:), intent(out) :: vr ! Output vector -! ! Dimension (n_random*nsym,nat_sc,3) - -! ! Work variables for the subroutine -! integer :: n_random, nat_sc, ntrans -! integer :: na, nar, i, alpha, ran, nr, isym, nt -! double precision, dimension(3,3) :: bg_sc -! double precision, dimension(:,:), allocatable :: work1, work2 -! double precision, dimension(3) :: work_aux - - -! ! Get integers - -! n_random = size (v(:,1,1)) -! nat_sc = size (v(1,:,1)) -! ntrans = size (tau_sc_latvec(1,:)) - -! ! Allcoate arrays - -! allocate ( work1 (nat_sc,3) ) -! allocate ( work2 (nat_sc,3) ) - -! ! Create reciprocal lattice vectors of supercell - -! CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) - -! ! Do loop on random configurations and create replicas - -! nr = 0 ! Counter on replicas - -! do ran = 1, n_random -! ! Now we create replicas based on point group symmetries -! do isym = 1, nsym -! ! Bring vector to crystal axis -! work1 = 0.0d0 -! do na = 1, nat_sc -! do alpha = 1, 3 -! work1(na,alpha) = 0.0d0 -! do i = 1, 3 -! work1(na,alpha) = work1(na,alpha) + v(ran,na,i) * at_sc(i,alpha) -! end do -! end do -! end do -! work2 = 0.0d0 -! ! Make replica -! do na = 1, nat_sc -! do alpha = 1, 3 -! nar = irt (isym, na) -! do i = 1, 3 -! work2(na,alpha) = work2(na,alpha) + work1(nar,i) * s(alpha,i,isym) -! end do -! end do -! end do -! ! Bring replica to cartesian units -! work1 = 0.0d0 -! do na = 1, nat_sc -! do alpha = 1, 3 -! do i = 1, 3 -! work1(na,alpha) = work1(na,alpha) + work2(na,i) * bg_sc (alpha,i) -! end do -! end do -! end do -! ! Now we create replicas based on translations -! if (trans_replica) then -! do nt = 1, ntrans -! nr = nr + 1 -! do na = 1, nat_sc -! vr(nr,tau_sc_latvec(na,nt),:) = work1(na,:) -! end do -! end do -! else -! nr = nr + 1 -! vr(nr,:,:) = work1(:,:) -! end if -! end do -! end do - -! ! Deallcoate arrays - -! deallocate ( work1, work2 ) - -! end subroutine sym_replica - -! ! This subroutine creates replicas for one vector (i.e. forces or displacements) -! ! in the supercell based on the pointgroup symmetries. The subroutine inputs -! ! the vector for all the random configuration -! ! and it outputs in a new array the vector replicated in the new structures - -! subroutine sym_replica2 (v, at_sc, irt, s, nsym, tau_sc_latvec, trans_replica, vr) - -! implicit none - -! double precision, dimension(:,:,:), intent(in) :: v ! Input vector -! ! Dimension (n_random,nat_sc,3) -! double precision, dimension(3,3), intent(in) :: at_sc ! Lattice vectors of the supercell -! integer, dimension(:,:), intent(in) :: irt ! Rotated atom by a symmetry operation -! integer, dimension(3,3,48), intent(in) :: s ! Symmetry operation matrix -! integer, intent(in) :: nsym ! Number of symmetry operations -! integer, dimension(:,:), allocatable :: tau_sc_latvec ! Tells which is the transformed atom by a particular translation -! logical, intent(in) :: trans_replica ! Logical variable to determine if translational replica are included -! double precision, dimension(:,:,:), intent(out) :: vr ! Output vector -! ! Dimension (n_random*nsym,nat_sc,3) - -! ! Work variables for the subroutine -! integer :: n_random, nat_sc, ntrans -! integer :: na, nar, i, alpha, ran, nr, isym, nt -! double precision, dimension(3,3) :: bg_sc -! double precision, dimension(:,:), allocatable :: work1, work2 -! double precision, dimension(3) :: work_aux - - -! ! Get integers - -! n_random = size (v(:,1,1)) -! nat_sc = size (v(1,:,1)) -! ntrans = size (tau_sc_latvec(1,:)) - -! ! Allcoate arrays - -! allocate ( work1 (nat_sc,3) ) -! allocate ( work2 (nat_sc,3) ) - -! ! Create reciprocal lattice vectors of supercell - -! CALL recips(at_sc(1,1),at_sc(1,2),at_sc(1,3),bg_sc(1,1),bg_sc(1,2),bg_sc(1,3)) - -! ! Do loop on random configurations and create replicas - -! nr = 0 ! Counter on replicas - -! do ran = 1, n_random -! ! Now we create replicas based on point group symmetries -! do isym = 1, nsym -! ! Bring vector to crystal axis -! work1 = 0.0d0 -! do na = 1, nat_sc -! do alpha = 1, 3 -! work1(na,alpha) = 0.0d0 -! do i = 1, 3 -! work1(na,alpha) = work1(na,alpha) + v(ran,na,i) * bg_sc(i,alpha) -! end do -! end do -! end do -! work2 = 0.0d0 -! ! Make replica -! do na = 1, nat_sc -! do alpha = 1, 3 -! nar = irt (isym, na) -! do i = 1, 3 -! work2(na,alpha) = work2(na,alpha) + work1(nar,i) * s(alpha,i,isym) -! end do -! end do -! end do -! ! Bring replica to cartesian units -! work1 = 0.0d0 -! do na = 1, nat_sc -! do alpha = 1, 3 -! do i = 1, 3 -! work1(na,alpha) = work1(na,alpha) + work2(na,i) * at_sc (alpha,i) -! end do -! end do -! end do -! ! Now we create replicas based on translations -! if (trans_replica) then -! do nt = 1, ntrans -! nr = nr + 1 -! do na = 1, nat_sc -! vr(nr,tau_sc_latvec(na,nt),:) = work1(na,:) -! end do -! end do -! else -! nr = nr + 1 -! vr(nr,:,:) = work1(:,:) -! end if -! end do -! end do - -! ! Deallcoate arrays - -! deallocate ( work1, work2 ) - -! end subroutine sym_replica2 - - -! This subroutine prints out the symmetries. It prints point group matrix -! and which atom is related to that in the unit cell - -subroutine print_symm ( s, nsym, irt, supercell, nat) - - implicit none - - integer, dimension(3,3,48), intent(in) :: s - integer, intent(in) :: nsym - integer, dimension(48,nat), intent(in) :: irt - logical, intent(in) :: supercell - !integer, dimension(nat), intent(in) :: itau - - integer :: isym, alpha, na - integer :: nat - - !nat = size(irt(1,:)) - - print *, '' - print *, ' Printing symmetries... ' - print *, '' - print '(a,i3)', ' Symmetries found : ', nsym - print *, '' - - do isym = 1, nsym - print '(a,i3)', ' Symmetry ', isym - print '(a)', ' -------- ' - print *, '' - print *, ' point group matrix:' - do alpha = 1, 3 - print '(3i3)', s(alpha,1:3,isym) - end do - print *, ' rotated atoms:' - do na = 1, nat - print '(i3,a,i3)', na , ' -> ', irt(isym,na) - end do - ! if ( supercell ) then - ! print *, ' rotated atoms brought to unit cell:' - ! do na = 1, nat - ! print '(i3,a,i3)', itau(na) , ' -> ', itau(irt(isym,na)) - ! end do - ! end if - end do - -end subroutine print_symm diff --git a/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 b/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 deleted file mode 100644 index ca99e559..00000000 --- a/FModules/.ipynb_checkpoints/trntnsc-checkpoint.f90 +++ /dev/null @@ -1,70 +0,0 @@ -! -! Copyright (C) 2001 PWSCF group -! This file is distributed under the terms of the -! GNU General Public License. See the file `License' -! in the root directory of the present distribution, -! or http://www.gnu.org/copyleft/gpl.txt . -! -! -!----------------------------------------------------------------------- -subroutine trntnsc (phi, at, bg, iflg) - !----------------------------------------------------------------------- - ! - ! trasforms a COMPLEX tensor (like the dynamical matrix) - ! from crystal to cartesian axis (iflg >= 1) or viceversa (iflg <= -1) - ! - implicit none - - integer :: iflg - ! input: gives the versus of the trans. - - double complex :: phi (3, 3) - ! inp/out: the matrix to transform - - double precision :: at (3, 3), bg (3, 3) - ! input: the direct lattice vectors - ! input: the reciprocal lattice - - integer :: i, j, k, l - ! - ! counters on polarizations - ! / - !/ - - - double complex :: wrk (3, 3) - ! a working array - if (iflg.gt.0) then - ! - ! forward transformation (crystal to cartesian axis) - ! - - call zcopy (9, phi, 1, wrk, 1) - do i = 1, 3 - do j = 1, 3 - phi (i, j) = (0.d0, 0.d0) - do k = 1, 3 - do l = 1, 3 - phi (i, j) = phi (i, j) + wrk (k, l) * bg (i, k) * bg (j, l) - enddo - enddo - enddo - enddo - else - ! - ! backward transformation (cartesian to crystal axis) - ! - do i = 1, 3 - do j = 1, 3 - wrk (i, j) = (0.d0, 0.d0) - do k = 1, 3 - do l = 1, 3 - wrk (i, j) = wrk (i, j) + phi (k, l) * at (k, i) * at (l, j) - enddo - enddo - enddo - enddo - call zcopy (9, wrk, 1, phi, 1) - endif - return -end subroutine trntnsc From ef2e336f65f32f494329228da37da4d156d8ab46 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:33:15 +0100 Subject: [PATCH 08/18] Delete cellconstructor/.ipynb_checkpoints directory --- .../.ipynb_checkpoints/Bands-checkpoint.py | 290 -- .../ForceTensor-checkpoint.py | 3078 ----------- .../.ipynb_checkpoints/Methods-checkpoint.py | 1852 ------- .../Moro_object-checkpoint.py | 160 - .../.ipynb_checkpoints/Phonons-checkpoint.py | 4522 ----------------- .../.ipynb_checkpoints/Settings-checkpoint.py | 346 -- .../.ipynb_checkpoints/Spectral-checkpoint.py | 1984 -------- .../Structure-checkpoint.py | 2319 --------- .../.ipynb_checkpoints/Units-checkpoint.py | 27 - .../calculators-checkpoint.py | 525 -- .../symmetries-checkpoint.py | 3013 ----------- 11 files changed, 18116 deletions(-) delete mode 100644 cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/Units-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py delete mode 100644 cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py diff --git a/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py deleted file mode 100644 index 26914a6c..00000000 --- a/cellconstructor/.ipynb_checkpoints/Bands-checkpoint.py +++ /dev/null @@ -1,290 +0,0 @@ -from __future__ import print_function -from __future__ import division - -""" -This module contains the info about the electronic band structure of a particular structure. -With this module it is possible to compute some optical properties. -""" - -import os -import Structure -import Methods - -import numpy as np -import scipy, scipy.interpolate - -try: - import ase, ase.units - kB = ase.units.kB -except: - kB = 8.617330337217213e-05 - -class Bands: - - def __init__(self, structure): - """ - The band class must be associated to a Cellconstructor structure - """ - - self.structure = structure - self.kpts_cryst = None - self.N_k = 0 - self.N_bands = 0 - self.band_energies = None - self.band_occupations = None - - # Here the final values - self.good_Nk = 0 - self.good_kpts = None - self.good_band_energies = None - self.good_occupations = None - - def setup_from_ase_calc(self, ase_calc): - """ - Setup the band structure starting from an ASE calculator object. - """ - - raise NotImplementedError("Error, this function must be implemented.") - pass - - def standard_interpolation(self, new_kgrid, kind = "linear"): - """ - BAND INTERPOLATION - ================== - - This function performs an interpolation of the bands using a regular grid (scipy). - - Parameters - ---------- - new_kgrid : ndarray(NK_new, 3) - - """ - - self.good_kpts = np.copy(new_kgrid) - self.good_Nk, dumb = np.shape(new_kgrid) - self.good_band_energies = np.zeros((self.good_Nk, self.N_bands), dtype = np.double) - self.good_occupations = np.zeros((self.good_Nk, self.N_bands), dtype = np.double) - - for i in range(self.N_bands): - interp_func = scipy.interpolate.RegularGridInterpolator(self.kpts_cryst, self.band_energies[:, i], method = kind) - self.good_band_energies[:, i] = interp_func(self.good_kpts) - - def compute_occupation_numbers(self, T): - """ - OCCUPATION NUMBERS - ================== - - This is the function that computes the occupation number for the interpolated data. - You need to interpolate the data - - Parameters - ---------- - T : float - Temperature (in K) - """ - - raise NotImplementedError("Error, function not yet implemented") - - - def get_band_ipersurface(self, band_index): - """ - Get an 3d representing the band made as [Nx, Ny, Nz] - """ - - tot_kx, tot_ky, tot_kz = self.good_kpts.T - - kx, index = np.unique(tot_kx, return_index = True) - ky, index= np.unique(tot_ky, return_index = True) - kz, index = np.unique(tot_kz, return_index = True) - - - - - - - - def get_group_velocity(self): - """ - Compute the fermi velocity for each band - - - Results - ------- - v_k = ndarray( size = (N_k, N_bands, 3)) - The vector for each band that is the fermi velocity - """ - - raise NotImplementedError("Error, function not yet implemented") - - - def get_conductivity(self, T): - """ - COMPUTE THE STATIC CONDUCTIVITY - =============================== - - This function exploits the non interactive particle model from the Sommerfeld theory to - compute the conductivity. - - It will compute the conductivity per relaxation time as: - - .. math :: - - \sigma(T) = \frac{1}{\Omega N_k} \sum_{nk} \frac{\partial f}{\partial \varepsilon}(T, \varepsilon_{nk}) |v_{nk}|^2 - - where :math:`\Omega` is the unit cell volume, :math:`f` is the fermi occupation function and :math:`v_{nk}` is the - fermi velocity of the nk band. - - NOTE: You must have interpolated the data and computed the occupation numbers. - - Parameters - ---------- - T : float - Temperature, in K - - Results - ------- - conductivity : float - """ - - volume = np.linalg.det(self.structure.unit_cell) # Volume in A^3 - - v_fermi = self.get_fermi_velocity() - - cond = 0 - - raise NotImplementedError("Error, not yet implemented.") - # for i in range(self.N_bands): - # cond += v_fermi[i,:].dot(v_fermi[i, :]) * get_fermi_derivative(self.fermi_energy, - - - - - - def setup_from_espresso_output(self, filename): - """ - Setup the band structure from a quantum espresso standard output (high verbosity requested) - - Notice that the k points must be on a regular crystal grid, and all printed in output . - - Parameters - ---------- - filename : string - path to the QE pw.x output. A non self-consistent calculation is suggested. - """ - - - # Check if the file exists - if not os.path.exists(filename): - raise IOError("Error, the selected file {} does not exist.".format(filename)) - - # Start to read the file - f = open(filename, "r") - lines = [line.strip() for line in f.readlines()] - f.close() - - - reading_k_points = False - reading_bands = False - reading_occupations = False - reading_energies = False - occupation_read = False - - band_index = -1 - occupations = [] - energies = [] - current_array = [] - - kpts = [] - - - for i, line in enumerate(lines): - data = lines.split() - - # Check if we must start reading the k points - if len(kpts) == 0 and reading_k_points == False: - if lines[0] == "cryst.": - reading_k_points = True - - if reading_k_points: - # We finish to read all the k points - if len(line) == 0: - reading_k_points = False - - kpt = np.zeros(3, dtype = np.double) - kpt[0] = float(data[4]) - kpt[1] = float(data[5]) - kpt[2] = float(data[6]) - - kpts.append(kpt) - - if not reading_k_points and "End" in line and "calculation" in line: - # We must start reading the bands - reading_bands = True - continue - - # Check if we must update the band index - if reading_k_points and "bands" in line: - if band_index >= 0: - if not occupation_read: - raise IOError("Error, I have not found the occupation number. Be sure that QE has been executed in high verbosity.\nFile: {}".format(filename)) - occupations.append(current_array) - band_index += 1 - current_array = [] - - # Chekc if we must read the occupation numbers - if reading_k_points and "occupation" in line: - energies.append(current_array) - current_array = [] - occupation_read = True - - if reading_k_points: - # Check if this line is filled by numbers - all_numbers = False - try: - numbers = [float(x) for x in data] - all_numbers = True - except: - pass - - if all_numbers: - for x in data: - current_array.append(float(x)) - - - # Check if we are reading the fermi energy - if "Fermi" in line: - self.fermi_energy = float(data[4]) - reading_k_points = False - occupations.append(current_array) - break - - - # Check if the reading proceded correctly - self.kpts_cryst = np.array(kpts) - self.band_energies = np.array(energies) - self.band_occupations = np.arrany(occupations) - - N_k, dumb = np.shape(self.kpts_cryst) - self.N_k = N_k - - dumb1, N_bands = np.shape(self.band_energies) - dumb2, dumb3 = np.shape(self.band_occupations) - - assert dumb1 == N_k, "The number of k points in input did not match the one in output." - assert N_bands == dumb3, "The number of bands did non match the occupation number" - - - self.N_bands = N_bands - - -def get_fermi_function(energy, mu, T): - return 1 / (np.exp( (energy - mu) / (kB*T)) + 1) - -def get_fermi_derivative(energy, mu, T): - """ - Get the derivative of the fermi function. - """ - - return - get_fermi_function(energy, mu, T)**2 * np.exp( (energy - mu) / (kB*T)) / (kB*T) - - diff --git a/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py b/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py deleted file mode 100644 index d9b9995a..00000000 --- a/cellconstructor/.ipynb_checkpoints/ForceTensor-checkpoint.py +++ /dev/null @@ -1,3078 +0,0 @@ -from __future__ import print_function -from __future__ import division - -# Import for python2/3 compatibility -import cellconstructor.Phonons as Phonons -import cellconstructor.Methods as Methods -import cellconstructor.symmetries as symmetries -import cellconstructor.Units as Units - -import cellconstructor.Settings as Settings -from cellconstructor.Settings import ParallelPrint as print - - -import numpy as np -import scipy, scipy.signal, scipy.interpolate - -import symph -import time -import itertools -import thirdorder -import secondorder - -""" -In this module we create a tensor class. -This is used to store high order tensors defined -in periodic bondary systems, interpolate and multiply between -them -""" - - -class GenericTensor: - """ - This is the generic tensor class. - Do not use this when programming. - This should be used only to define new tensors. - """ - - def __init__(self, unitcell_structure, supercell_structure, supercell_size): - """ - Define the tensors - """ - self.supercell_size = supercell_size - self.itau = supercell_structure.get_itau(unitcell_structure) - 1 - - self.tau=unitcell_structure.coords - - self.nat = unitcell_structure.N_atoms - - self.unitcell_structure = unitcell_structure - self.supercell_structure = supercell_structure - - self.verbose = True - - - -class Tensor2(GenericTensor): - """ - This class defines the 2rank tensors, like force constant matrices. - """ - - def __init__(self, unitcell_structure, supercell_structure, supercell_size): - GenericTensor.__init__(self, unitcell_structure, supercell_structure, supercell_size) - - self.n_R = np.prod(np.prod(supercell_size)) - self.x_r_vector2 = np.zeros((3, self.n_R), dtype = np.intc, order = "F") - self.r_vector2 = np.zeros((3, self.n_R), dtype = np.double, order = "F") - self.tensor = np.zeros((self.n_R, 3*self.nat, 3*self.nat), dtype = np.double) - self.n_sup = np.prod(supercell_size) - - # Prepare the initialization of the effective charges - self.effective_charges = None - self.dielectric_tensor = None - - # Prepare the values ready to be used inside quantum espresso Fortran subroutines - self.QE_tau = None - self.QE_omega = None - self.QE_zeu = None - self.QE_bg = None - - # NOTE: this QE_alat is not the unit of measure like in QE subroutines, - # But rather the dimension of the first unit-cell vector in Bohr. - # It is used for computing the ideal integration size in rgd_blk from symph - self.QE_alat = None - - def SetupFromPhonons(self, phonons): - """ - SETUP FROM PHONONS - ================== - - Setup the 2rank tensor from a phonons class - - Parameters - ---------- - - phonons : Phonons.Phonons() - The dynamical matrix from which you want to setup the tensor - """ - - # Check if the supercell is correct - ERR = """ -Error, the supercell of the phonon object is {}. - it must match with the supercell defined for the Tensor2: {} -""".format(phonons.GetSupercell(), self.supercell_size) - #print(ERR) - - assert np.all([self.supercell_size[i] == phonons.GetSupercell()[i] for i in range(3)]), ERR - - # Check that no 1 atom with 1 q point - if np.prod(phonons.GetSupercell()) == 1 and phonons.structure.N_atoms == 1: - ERR = """ -Error, cannot initialize a tensor from a structure with 1 atom with only Gamma - check if you imported the dynamical matrix with the correct nqirr. - -""" - raise ValueError(ERR) - current_dyn = phonons.Copy() - - # Check if the dynamical matrix has the effective charges - if phonons.effective_charges is not None: - time1 = time.time() - self.effective_charges = current_dyn.effective_charges.copy() - assert current_dyn.dielectric_tensor is not None, "Error, effective charges provided, but not the dielectric tensor." - - self.dielectric_tensor = current_dyn.dielectric_tensor.copy() - - self.QE_alat = phonons.alat * Units.A_TO_BOHR - - # Prepare the coordinates in Bohr for usage in QE subroutines - self.QE_tau = np.zeros((3, self.nat), dtype = np.double, order = "F") - self.QE_tau[:,:] = self.tau.T * Units.A_TO_BOHR / self.QE_alat - self.QE_zeu = np.zeros((3,3,self.nat), dtype = np.double, order = "F") - self.QE_zeu[:,:,:] = np.einsum("sij->ijs", self.effective_charges) # Swap axis (we hope they are good) - self.QE_bg = np.zeros((3,3), dtype = np.double, order = "F") - bg = self.unitcell_structure.get_reciprocal_vectors() - self.QE_bg[:,:] = bg.T * self.QE_alat / (2*np.pi * Units.A_TO_BOHR) - self.QE_omega = self.unitcell_structure.get_volume() * Units.A_TO_BOHR**3 - - # The typical distance in the cell - #self.QE_alat = np.sqrt(np.sum(self.unitcell_structure.unit_cell[0, :]**2)) - #self.QE_alat = Units.A_TO_BOHR - - # Subtract the long range interaction for any value of gamma. - dynq = np.zeros((3, 3, self.nat, self.nat), dtype = np.complex128, order = "F") - for iq, q in enumerate(current_dyn.q_tot): - - t1 = time.time() - # Fill the temporany dynamical matrix in the correct fortran subroutine - for i in range(self.nat): - for j in range(self.nat): - dynq[:,:, i, j] = current_dyn.dynmats[iq][3*i: 3*i+3, 3*j : 3*j+3] - t3 = time.time() - - # Lets go in QE units - QE_q = q * self.QE_alat / Units.A_TO_BOHR - - # Remove the long range interaction from the dynamical matrix - symph.rgd_blk(0, 0, 0, dynq, QE_q, self.QE_tau, self.dielectric_tensor, self.QE_zeu, self.QE_bg, self.QE_omega, self.QE_alat, 0, -1.0, self.nat) - - # Copy it back into the current_dynamical matrix - for i in range(self.nat): - for j in range(self.nat): - current_dyn.dynmats[iq][3*i: 3*i+3, 3*j: 3*j+3] = dynq[:,:, i, j] - - # Impose hermitianity - current_dyn.dynmats[iq][:,:] = 0.5 * (current_dyn.dynmats[iq] + np.conj(current_dyn.dynmats[iq].T)) - - t2 = time.time() - if self.verbose: - print("Time for the step {} / {}: {} s".format(iq+1, len(current_dyn.q_tot), t2 - t1)) - print("(The preparation of the dynq: {} s)".format(t3 - t1)) - print("NAT:", self.nat) - - - - time2 = time.time() - - if self.verbose: - print("Time to prepare the effective charges: {} s".format(time2 - time1)) - - # Get the dynamical matrix in the supercell - time3 = time.time() - - # Apply the acoustic sum rule (could be spoiled by the effective charges) - #iq_gamma = np.argmin(np.sum(np.array(current_dyn.q_tot)**2, axis = 1)) - #symmetries.CustomASR(current_dyn.dynmats[0]) - #current_dyn.Symmetrize() - - - if self.verbose: - print("Generating Real space force constant matrix...") - - # TODO: we could use the fft to speedup this - # fc_q = dyn.GetMatrixFFT() - # fc_real_space = np.conj(np.fft.fftn(np.conj(fc_q), axes = (0,1,2))) / np.prod(current_dyn.GetSupercell()) - # fc_real_space is already in tensor form (first three indices the R_2 components, or maybe -R_2) in crystalline coordinates) - # It must be just rearranged in the correct tensor - super_dyn = current_dyn.GenerateSupercellDyn(phonons.GetSupercell(), img_thr =1e-6) - time4 = time.time() - - if self.verbose: - print("Time to generate the real space force constant matrix: {} s".format(time4 - time3)) - print("TODO: the last time could be speedup with the FFT algorithm.") - - # Setup from the supercell dynamical matrix - self.SetupFromTensor(super_dyn.dynmats[0]) - - - def SetupFromTensor(self, tensor): - """ - SETUP FROM A TENSOR - =================== - - This module setup the tensor from a 3*natsc x 3*natsc matrix. - You should also pass the structure in the supercell to infer - which atom correspond to which one. - - NOTE: The first nat atoms of the superstructure must be a unit cell. - - Parameters - ---------- - - tensor : ndarray( size=(3*nat_sc, 3*nat_sc)) - The matrix to be converted in this Tensor2. - - """ - - nat = self.nat - nat_sc = self.supercell_structure.N_atoms - - # Check if the passed structure is a good superstructure - assert nat_sc % nat == 0, "Error, the given superstructure has a wrong number of atoms" - - natsc3, natsc3_ = np.shape(tensor) - - assert nat_sc == natsc3/3, "Error, the given tensor and superstructure are not compatible" - - assert natsc3 == natsc3_, "Error, the given tensor must be a square matrix" - - nat = self.unitcell_structure.N_atoms - nat_sc = nat * self.n_R - - for i_x in range(self.supercell_size[0]): - for i_y in range(self.supercell_size [1]): - for i_z in range(self.supercell_size[2]): - i_block = self.supercell_size[1] * self.supercell_size[2] * i_x - i_block += self.supercell_size[2] * i_y - i_block += i_z - self.x_r_vector2[:, i_block] = np.array([i_x, i_y, i_z]) - self.r_vector2[:, i_block] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, i_block]) - - for na1 in range(nat): - for na2 in range(nat): - # Get the atom in the supercell corresponding to the one in the unit cell - na2_vect = self.unitcell_structure.coords[na2, :] + self.r_vector2[:, i_block] - nat2_sc = np.argmin( [np.sum( (self.supercell_structure.coords[k, :] - na2_vect)**2) for k in range(nat_sc)]) - - self.tensor[i_block, 3*na1:3*na1+3, 3*na2: 3*na2+3] = tensor[3*na1 : 3*na1+3, 3*nat2_sc : 3*nat2_sc + 3] - - - def SetupFromFile(self, fname,file_format='Phonopy'): - """ - Setup the second order force constant form 2nd order tensor written in a file - - Warning about the D3Q format: - Coerently with the indexing of the 3rd order FCs, in the D3Q format with FC(s,t,R) - we refer to the FC between atoms (s,0) and (t,R). This is different from the format - used in the original d3q code by Lorenzo Paulatto, where the FC(s,t,R) refers to - (s,R) and (t,0), or ,equivalently, (s,0) and (t,-R) - - - Parameters - ---------- - fname : string - The file name - file_format : string - The format of the file - """ - - - if Settings.am_i_the_master(): - - if file_format == 'Phonopy': - - print(" ") - print(" FC2 Phonopy reading format: TODO" ) - print(" ") - exit() - - elif file_format == 'D3Q': - - print(" ") - print(" Reading FC2 from "+fname) - print(" (D3Q format) " ) - print(" ") - - first_nR_read = True - with open(fname, "r") as f: - # ============== Skip header, if present === - if len(f.readline().split()) ==4: - f.seek(0) - else: - f.seek(0) - while True: - if len(f.readline().split()) ==1: - break - f.readline() - # =========================================== - for nat1 in range(self.nat): - for nat2 in range(self.nat): - for alpha in range(3): - for beta in range(3): - [alpha_read,beta_read, - nat1_read, nat2_read]=[int(l)-1 for l in f.readline().split()] - - assert ([nat1,nat2,alpha,beta]==[nat1_read,nat2_read, - alpha_read,beta_read]) - - - nR_read=int(f.readline().split()[0]) - - if (first_nR_read) : - self.n_R=nR_read - self.tensor=np.zeros((self.n_R,3*self.nat,3*self.nat),dtype=np.float64) - self.x_r_vector2=np.zeros((3,self.n_R),dtype=np.int16) - first_nR_read = False - else : - assert ( nR_read == self.n_R ), " Format unknown - different blocks size " - - for iR in range(self.n_R): - - res=[l for l in f.readline().split()] - - [self.x_r_vector2[0, iR], - self.x_r_vector2[1, iR], - self.x_r_vector2[2, iR]]=[int(l) for l in res[:-1]] - - self.tensor[iR, 3*nat1 + alpha, 3*nat2 + beta]=np.double(res[-1]) - - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - - # Broadcast - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - - - def Center(self, nneigh=None, Far=2,tol=1.0e-5): - """ - CENTERING - ========= - - This subroutine will center the second order force constant. - This means that for each atomic indices in the tensor, it will be identified by the lowest - distance between the replica of the atoms. Moreover, in case of existance of other elements - not included in the original supercell with the same distance, the tensor will be equally subdivided between equivalent of atoms. - - This function should be called before performing the Fourier interpolation. - - - Optional Parameters - -------------------- - - nneigh [default= None]: integer - if different from None, it sets a maximum distance allowed to consider - equivalent atoms in the centering. - - nneigh > 0 - - for each atom the maximum distance is: - the average between the nneigh-th and the nneigh+1-th neighbor distance - (if, for the considered atom, the supercell is big enough to find up to - the nneigh+1-th neighbor distance, otherwise it is the maxmimum distance +10%) - - nneigh = 0 - - the maximum distance is the same for all the atoms, equal to - maximum of the minimum distance between equivalent atoms (+ 10%) - - nneigh < 0 - - the maximum distance is the same for all the atoms, equal to - maximum of the |nneigh|-th neighbor distances found for all the atoms - (if, for the considered atom, the supercell is big enough to find up to - the |nneigh|+1-th neighbor distance, otherwise it is the maxmimum distance +10%) - - - Far [default= 1]: integer - - In the centering, supercell equivalent atoms are considered within - -Far,+Far multiples of the super-lattice vectors - """ - # Check if the phonons is initialized TODO - #if np.max(np.abs(self.x_r_vector2)) == 0: - # raise ValueError("Error, Tensor object not initialized!") - - if Settings.am_i_the_master(): - - - t1 = time.time() - - if self.verbose: - print(" ") - print(" ======================= Centering 2nd FCs ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - # by default no maximum distances - dist_range=np.ones((self.nat))*np.infty - # - if nneigh!=None: - # ======================================================================= - uc_struc = self.unitcell_structure - sc_struc = self.supercell_structure - - # lattice vectors in Angstrom in columns - uc_lat = uc_struc.unit_cell.T - sc_lat = sc_struc.unit_cell.T - - # atomic positions in Angstrom in columns - uc_tau = uc_struc.coords.T - sc_tau = sc_struc.coords.T - - uc_nat = uc_tau.shape[1] - sc_nat = sc_tau.shape[1] - - # == calc dmin ================================================== - # calculate the distances between the atoms in the supercell replicas - - # array square distances between i-th and the j-th - # equivalent atoms of the supercell - d2s=np.empty(( (2*Far+1)**3 , sc_nat, sc_nat )) - - rvec_i=sc_tau - for j, (Ls,Lt,Lu) in enumerate( - itertools.product(range(-Far,Far+1),range(-Far,Far+1),range(-Far,Far+1))): - - rvec_j = sc_tau+np.dot(sc_lat,np.array([[Ls,Lt,Lu]]).T) - - d2s[j,:,:]=scipy.spatial.distance.cdist(rvec_i.T,rvec_j.T,"sqeuclidean") - - # minimum of the square distances - d2min=d2s.min(axis=0) - # minimum distance between two atoms in the supercell, - # considering also equivalent supercell images - dmin=np.sqrt(d2min) - # - if nneigh == 0: - dist_range=np.ones((self.nat))*np.amax(dmin)*1.1 - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to ") - print(" the max of the minimum distances between ") - print(" supercell equivalent atoms (+10%): {:8.5f} A".format(dist_range[0])) - print(" ") - - # to include all the equivalent atoms having the smallest distance - # between them. It does not correspond to taking dist_range=+infity - # because, with the centering, smallest triangles with equivalent points can be obtained - # by considering atoms not at the minimum distance between them: - # with dist_range=+infity these triangles are included, with dist_range=np.amax(dmin) - # they are not included (here I add the 10%) - - else: - #== max dist of the n-th neighbors ============================ - # For all the uc_nat atoms of unit cell (due to the lattice translation symmetry, - # I can limit the analysis to the atoms of a unit cell - not the supercell) - # I look for the nneigh-th neighbor distance and build the array dist_range - # - nn=np.abs(nneigh) - warned = False - for i in range(uc_nat): # for each unit cell atom - ds=dmin[i,:].tolist() # list of distances from other atoms - ds.sort() # list of distances from the i-th atom in increasing order - u=[] # same list, without repetitions - for j in ds: - for k in u: - if np.allclose(k,j): - break - else: - u.append(j) - # the list u of increasing distances for i-th atom has been completed - # try do consider the average of the nneigh-th and nneigh+1-th distance to nth_len - # if it fails, because there are not enough atoms to reach the n-th neighbors, - # then consider the maximum distance found (augmented of 10%) - try: - dist_range[i]=(.5*(u[nn]+u[nn+1])) - except IndexError: - if not warned: - print(" Warning: supercell too small to find {}-th neighbors for all the atoms ".format(nn+1)) - warned = True - dist_range[i]=1.1*max(u) - # - if nneigh < 0 : - # - dist_range=np.ones((self.nat))*np.amax(dist_range) - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to {:8.5f} A".format(dist_range[0])) - print(" ") - # - else : - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to:".format(dist_range[0])) - print(" ") - for i in range(self.nat): - print("{:8.5f} A for atom {}".format(dist_range[i],i+1)) - print(" ") - # - #============================================================== - # look for the minimum supercell - # - xRmin=np.min(self.x_r_vector2,1) - xRmax=np.max(self.x_r_vector2,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - - tensor= np.transpose(self.tensor,axes=[1,2,0]) - self.n_sup = np.prod(xRlen) - - alat=self.unitcell_structure.unit_cell - - weight,xR2 = secondorder.second_order_centering.analysis(Far,tol,dist_range,xRlen, - self.x_r_vector2, - alat, - self.tau, tensor,self.nat,self.n_R) - - - mask= weight >0 - - mask=np.repeat(mask[np.newaxis,...], (2*Far+1)*(2*Far+1), axis=0) - - xR2_reshaped=xR2[:,mask] - - xR2_unique = np.unique(xR2_reshaped,axis=1) - nblocks_old=self.n_R - # - self.n_R=xR2_unique.shape[1] - self.x_r_vector2 = xR2_unique - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - - centered=secondorder.second_order_centering.center(tensor,weight, - self.x_r_vector2,xR2, - Far,self.nat, - self.n_R,nblocks_old) - t2 = time.time() - - self.tensor = np.transpose(centered, axes=[2,0,1]) - - if self.verbose: - print(" Time elapsed for computing the centering: {} s".format( t2 - t1)) - print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - print(" ") - print(" ====================================================================") - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - - - - def Apply_ASR(self,PBC=False,power=0,maxiter=1000,threshold=1.0e-12): - """ - Apply_ASR - ========= - - This subroutine apply the ASR to the second order force constants iteratively. - For each iteration, the ASR is imposed on the second index - (any of the two indeces would be equivalent, apart from the subtleties that - would require the implementation for the first index, due to the representation chosen), - and, subsequently, the symmetry by permutation of the two indeces is imposed. - - - Optional Parameters - -------------------- - - PBC [default=False]: logical - - If True, periodic boundary conditions are considered. This is necessary, for example, - to apply this routine to non-centered tensors. - - - power [default=0]: float >= 0 - - The way the ASR is imposed on the second index: - phi(i,j,a,b)=phi(i,j,a,b)-[\sum_b phi(i,j,a,b)]* |phi(i,j,a,b)|^pow / [sum_b |phi(i,j,a,b)|^pow] - - - maxiter [default= 1000]: integer >= 0 - - n>0 Maximum number of iteration to reach the convergence. - n=0 Endless iteration - - If a file STOP is found, the iteration stops. - - - threshold [default= 1.0e-12]: float > 0 - - Threshold for the convergence. The convergence is on two values: the value of sum on the third index and the - variation of the phi after the imposition of the permutation symmetry (both divided by sum |phi|) - """ - - - if Settings.am_i_the_master(): - - - t1 = time.time() - - if self.verbose: - print(" ") - print(" ======================= ASR ========================== ") - print(" ") - - - - xRmin=np.min(self.x_r_vector2,1) - xRmax=np.max(self.x_r_vector2,1) - SClat=xRmax-xRmin+np.ones((3,),dtype=int) - - - tensor=np.transpose(self.tensor,axes=[1,2,0]) - - tensor_out=secondorder.second_order_asr.impose_asr(tensor, - self.x_r_vector2, - power,SClat, - PBC,threshold, - maxiter,self.verbose, - self.nat,self.n_R) - - self.tensor=np.transpose(tensor_out,axes=[2,0,1]) - - t2 = time.time() - - if self.verbose: - print(" ") - print(" Time elapsed for imposing ASR: {} s".format( t2 - t1)) - print(" ") - print(" ============================================================ ") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - - - - def WriteOnFile(self, fname,file_format='Phonopy'): - """ - WRITE ON FILE - ============= - - Save the tensor on a file. - This is usefull if you want to check if everything is working as you expect. - - The file format is the same as phono3py or D3Q - In the first line there is the total number of blocks. - Then for each block there is the lattice vector followed by the atomic index in the unit cell. - Then there is the tensor for each cartesian coordinate - - As follows: - - N_blocks - Block_index - R_x R_y R_z - at_1 at_2 - coord_1 coord_2 tensor_value - coord_1 coord_2 tensor_value - ... - - Warning about the D3Q format: - Coerently with the indexing of the 3rd order FCs, in the D3Q format with FC(s,t,R) - we refer to the FC between atoms (s,0) and (t,R). This is different from the format - used in the original d3q code by Lorenzo Paulatto, where the FC(s,t,R) refers to - (s,R) and (t,0), or ,equivalently, (s,0) and (t,-R) - - - Parameters - ---------- - fname : string - Path to the file on which you want to save the tensor. - """ - - if file_format == 'Phonopy': - - print(" ") - print(" Writing FC2 on "+fname ) - print(" (Phonopy format) ") - print(" ") - - with open(fname, "w") as f: - # Write the total number of blocks - f.write("{:>5}\n".format(self.n_R * self.nat**2)) - - i_block = 1 - for r_block in range(self.n_R): - for nat1 in range(self.nat): - for nat2 in range(self.nat): - # Write the info on the current block - f.write("{:d}\n".format(i_block)) - f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2[:, r_block]))) - f.write("{:>6d} {:>6d}\n".format(nat1 + 1, nat2+1)) - i_block += 1 - - # Write the tensor for the block - # For each cartesian coordinate - for x in range(3): - for y in range(3): - f.write("{:>2d} {:>2d} {:>20.10e}\n".format(x+1, y+1, self.tensor[r_block, 3*nat1 + x, 3*nat2 + y])) - - - elif file_format == 'D3Q': - - print(" ") - print(" Writing FC2 on "+fname ) - print(" (D3Q format) ") - print(" ") - - with open(fname, "w") as f: - #TODO Print header... - - for nat1 in range(self.nat): - for nat2 in range(self.nat): - for alpha in range(3): - for beta in range(3): - f.write("{:>6d} {:>6d} {:>6d} {:>6d} \n".format(alpha+1,beta+1,nat1+1, nat2+1)) - f.write("{:>5}\n".format(self.n_R)) - for r_block in range(self.n_R): - f.write("{:>6d} {:>6d} {:>6d} {:16.8e}\n".format(self.x_r_vector2[0, r_block],self.x_r_vector2[1, r_block],self.x_r_vector2[2, r_block], self.tensor[r_block, 3*nat1 + alpha, 3*nat2 + beta])) - - def Interpolate(self, q2, asr = False, verbose = False, asr_range = None, q_direct = None, lo_to_splitting = True): - """ - Perform the Fourier interpolation to obtain the force constant matrix at a given q - This subroutine automatically performs the ASR - - Parameters - ---------- - q2 : ndarray(size = 3) - The q vector in 2pi/A - asr : bool - If true, apply the acousitc sum rule - asr_range : float, optional - If it is given, then use a gaussian as a activation function - for the asr, with asr_range equal to sigma. - Otherwise, a sin(Nq)/(Nsin(q)) will be used, equal to apply the sum rule on a - grid. - verbose : bool - Print some debugging info - q_direct : ndarray(dtype = 3) - If q2 is gamma and effective charges are present, this vector is used - to pick the direction of the nonanalitical correction to apply. - If it is not initialized, a random versor will be chosen. - If it is the 0 vector, the nonanalitic correction at gamma will be avoided. - lo_to_splitting : bool - If True and the point is gamma, add the nonanalytic correction in a direction - given by q_direct - - Results - ------- - phi2 : ndarray(size = (3*nat, 3*nat), dtype = np.complex128) - The second order force constant at q2. Atomic indices runs over the unit cell - """ - - final_fc = np.zeros((3*self.nat, 3*self.nat), dtype = np.complex128) - - # Perform the fourier transform of the short range real space tensor. - for i in range(self.n_R): - arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i])) - phase = np.exp(np.complex128(-1j) * arg) - final_fc += phase * self.tensor[i, :, :] - - # If effective charges are present, then add the nonanalitic part - if self.effective_charges is not None: - dynq = np.zeros((3,3,self.nat, self.nat), dtype = np.complex, order = "F") - for i in range(self.nat): - for j in range(self.nat): - dynq[:,:, i, j] = final_fc[3*i : 3*i+3, 3*j:3*j+3] - - # Add the nonanalitic part back - QE_q = -q2 * self.QE_alat / Units.A_TO_BOHR - symph.rgd_blk(0, 0, 0, dynq, QE_q, self.QE_tau, self.dielectric_tensor, self.QE_zeu, self.QE_bg, self.QE_omega, self.QE_alat, 0, +1.0, self.nat) - - # Check if the vector is gamma - if np.max(np.abs(q2)) < 1e-12: - q_vect = np.zeros(3, dtype = np.double) - compute_nonanal = lo_to_splitting - if q_direct is not None: - # the - to take into account the difference between QE convension and our - if np.linalg.norm(q_direct) < 1e-8: - compute_nonanal = False - else: - q_vect[:] = -q_direct / np.sqrt(q_direct.dot(q_direct)) - else: - q_vect[:] = np.random.normal(size = 3) - q_vect /= np.sqrt(q_vect.dot(q_vect)) - - # Apply the nonanal contribution at gamma - if compute_nonanal: - QE_itau = np.arange(self.nat) + 1 - symph.nonanal(QE_itau, self.dielectric_tensor, q_vect, self.QE_zeu, self.QE_omega, dynq, self.nat, self.nat) - - # Copy in the final fc the result - for i in range(self.nat): - for j in range(self.nat): - final_fc[3*i : 3*i+3, 3*j:3*j+3] = dynq[:,:, i, j] - - # Apply the acoustic sum rule - if asr: - # Get the reciprocal lattice vectors - bg = Methods.get_reciprocal_vectors(self.unitcell_structure.unit_cell) / (2*np.pi) - nat = self.unitcell_structure.N_atoms - - # Create the ASR projector - Q_proj = np.zeros((3*nat, 3*nat), dtype = np.double) - for i in range(3): - v1 = np.zeros(nat*3, dtype = np.double) - v1[3*np.arange(nat) + i] = 1 - v1 /= np.sqrt(v1.dot(v1)) - - Q_proj += np.outer(v1,v1) - - # Lets pick the minimum and maximum lattice vectors - xRmin = np.min(self.x_r_vector2, axis = 1) - xRmax = np.max(self.x_r_vector2, axis = 1) - - # Now we obtain the total cell size that contains the ASR - N_i = xRmax - xRmin + np.ones((3,), dtype = np.intc) - - if verbose: - print("Supercell WS:", N_i) - - N_i = np.array([2*x + 1 for x in self.supercell_size], dtype = np.intc) - - # We check if they are even, in that case we add 1 - # f(q) is real only if we sum on odd cells - for ik in range(3): - if (N_i[ik] % 2 == 0): - N_i[ik] += 1 - - if verbose: - print("Supercell all odd:", N_i) - - - # We compute the f(q) function - at = self.unitcell_structure.unit_cell - - __tol__ = 1e-8 - f_q2i = np.ones(3, dtype = np.double) - - # We use mask to avoid division by 0, - # As we know that the 0/0 limit is 1 in this case - if asr_range is None: - mask2 = np.abs(np.sin(at.dot(q2) * np.pi)) > __tol__ - f_q2i[mask2] = np.sin(N_i[mask2] * at[mask2,:].dot(q2) * np.pi) / (N_i[mask2] * np.sin(at[mask2,:].dot(q2) * np.pi)) - f_q2 = np.prod(f_q2i) - else: - closest_q = Methods.get_closest_vector(bg * 2 * np.pi, q2) - f_q2 = np.exp( - np.linalg.norm(closest_q)**2 / (2 * asr_range**2)) - - if verbose: - print("The fq:") - print("{:16.8f} {:16.8f}".format(f_q2, f_q2)) - print("q1 = ", Methods.covariant_coordinates(bg * 2 * np.pi, -q2)) - print("q2 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q2)) - - # Now we can impose the acustic sum rule - final_fc -= np.einsum("ai, bi-> ab", final_fc, Q_proj) * f_q2 - final_fc -= np.einsum("ib, ai-> ab", final_fc, Q_proj) * f_q2 - - - return final_fc - - - - - # def GenerateSupercellTensor(self, supercell): - # """ - # GENERATE SUPERCELL TENSOR - # ========================= - - # This function returns a tensor defined in the supercell - # filling to zero all the elemets that have a minimum distance - # greater than the one defined in the current tensor. - # This is the key to interpolate. - - # The supercell atoms are defined using the generate_supercell - # method from the self.structure, so that is the link - # between indices of the returned tensor and atoms in the supercell. - - # Parameters - # ---------- - # - supercell : (nx, ny, nz) - # The dimension of the supercell in which - # you want to compute the supercell tensor - - # Results - # ------- - # - tensor : ndarray(size = ( 3*natsc, 3*natsc)) - # A tensor defined in the given supercell. - # """ - - # # TODO: ADD THE MULTIPLICITY COUNT ON THE SUPERCELL - - # super_structure, itau = self.structure.generate_supercell(supercell, get_itau = True) - - # nat_sc = super_structure.N_atoms - # new_tensor = np.zeros((3 * nat_sc, 3*nat_sc), dtype = np.double) - - # print("Unit cell coordinates:") - # print("\n".join(["{:3d}) {}".format(i, self.structure.coords[i, :]) for i in range(self.structure.N_atoms)])) - # print("Supercell coordinates:") - # print("\n".join(["{:3d}) {}".format(i, super_structure.coords[i, :]) for i in range(super_structure.N_atoms)])) - - - - # nat, nat_sc_old, _ = np.shape(self.r_vectors) - - # for i in range(nat_sc): - # i_cell = itau[i] - # for j in range(nat_sc): - - # r_vector = super_structure.coords[i,:] - super_structure.coords[j,:] - # r_vector = Methods.get_closest_vector(super_structure.unit_cell, r_vector) - - # # Now average all the values that - # # share the same r vector - # #dist_v = self.r_vectors[i_cell, :,:] - np.tile(new_r_vector, (nat_sc_old, 1)) - # #mask = [Methods.get_min_dist_into_cell(super_structure.unit_cell, dist_v[k, :], np.zeros(3)) < 1e-5 for k in range(nat_sc_old)] - # #mask = np.array(mask) - - # mask = Methods.get_equivalent_vectors(super_structure.unit_cell, self.r_vectors[i_cell, :, :], r_vector) - - # if i == 4 and j == 11: - # print("i = {}, j = {}".format(i, j)) - # print("r vector = {}".format(r_vector)) - # print("mask = {}".format(mask)) - - # # Apply the tensor - # n_elements1 = np.sum(mask.astype(int)) - # n_elements2 = 0 - - # # if n_elements1 == 0: - # # print("ZERO:") - # # print("itau[{}] = {}".format(i, i_cell)) - # # print("r to find:", new_r_vector) - # # print("r vectors:") - # # for k in range(nat_sc_old): - # # print("{}) {:12.6f} {:12.6f} {:12.6f}".format(k+1, *list(self.r_vectors[i_cell, k, :]))) - # # print() - # if n_elements1 > 0: - # #print("Apply element {} {} | n = {}".format(i, j, n_elements1)) - # tens = np.sum(self.tensor[i_cell, mask, :, :], axis = 0) / n_elements1 - # #print(tens) - # new_tensor[3*i: 3*i+3, 3*j:3*j+3] = tens - - # # NOTE: Here maybe a problem arising from the - # # double transpose inside the same unit cell - # # If the share a -1 with the vector then we found the transposed element - # if n_elements1 == 0: - # #dist_v2 = self.r_vectors[i_cell, :,:] + np.tile(new_r_vector, (nat_sc_old, 1)) - # mask2 = Methods.get_equivalent_vectors(super_structure.unit_cell, self.r_vectors[i_cell, :, :], -r_vector) - # #mask2 = [Methods.get_min_dist_into_cell(super_structure.unit_cell, dist_v2[k, :], np.zeros(3)) < 1e-5 for k in range(nat_sc_old)] - # #mask2 = np.array(mask2) - # n_elements2 = np.sum(mask2.astype(int)) - # if n_elements2 > 0: - # tens = np.sum(self.tensor[i_cell, mask, :, :], axis = 0) / n_elements2 - # new_tensor[3*j:3*j+3, 3*i:3*i+3] = tens - - - # #print("Elements {}, {} | r_vector = {} | n1 = {} | n2 = {}".format(i+1, j+1, r_vector, n_elements1, n_elements2)) - - # return new_tensor - - def GeneratePhonons(self, supercell, asr = False, lo_to_splitting = False): - """ - GENERATE PHONONS - ================ - - Interpolate the Tensor2 into a supercell and then - transform back into the dynamical matrix with the correct q. - - It might be that the new dynamical matrix should be symmetrized. - - NOTE: The Interpolate method uses a different convension of the Fourier Transform. - For this reason, this method returns the Complex Cojugate of the matrices interpolated at the q points. - This has been fixed, by manually computing the complex conjugate before the return - - - Parameters - ---------- - - supercell : (nx, ny, nz) - The supercell of the dynamical matrix - - asr : bool - If true, the ASR is imposed during the interpolation. - This is the best way to correct the modes even close to gamma - - lo_to_splitting : bool - If true, the phonons at gamma will have the LO-TO splitting - from a random direction. - Note, this will break symmetrization. - - Results - ------- - - dynmat : Phonons.Phonons() - The dynamical matrix interpolated into the new supercell. - It is defined in the unit cell. - """ - - # Prepare the phonons for this supercell - dynmat = Phonons.Phonons(self.unitcell_structure) - - # Prepare the q_points - dynmat.q_tot = symmetries.GetQGrid(self.unitcell_structure.unit_cell, supercell) - q_vectors = [x.copy() for x in dynmat.q_tot] - dynmat.q_stars = [q_vectors] - dynmat.dynmats = [] - - # Interpolate over the q points - for i, q_vector in enumerate(q_vectors): - q_direction = None - if lo_to_splitting: - q_direction = np.random.normal(size = 3) - q_direction /= np.linalg.norm(q_direction) - - dynq = self.Interpolate(-q_vector, asr = asr, q_direct= q_direction, lo_to_splitting=lo_to_splitting) - dynmat.dynmats.append(dynq) - - # Adjust the q star according to symmetries - dynmat.AdjustQStar() - - return dynmat - - - - def GetRDecay(self): - """ - Get a plot of the R decay. - - For each element of the block, plots the maximum intensity in the distance between the data - """ - - r_total = [] - max_intensity = [] - - for i_R in range(self.n_R): - # Get the distance for each atomic couple in the block - for at_1 in range(self.nat): - for at_2 in range(self.nat): - r_dist = self.r_vector2[:, i_R] + self.tau[at_2,:] - self.tau[at_1, :] - - tensor = self.tensor[i_R, 3*at_1 : 3*at_1 + 3, 3*at_2: 3*at_2 + 3] - intensity = np.sqrt(np.trace(tensor.dot(tensor.T))) - - # Skip zero values - if intensity < 1e-10: - continue - - r_mod = np.sqrt(r_dist.dot(r_dist)) - - if len(r_total) == 0: - r_total.append(r_mod) - max_intensity.append(intensity) - continue - - # Check if another vector with the same distance has already been found - distances = np.abs(r_mod - np.array(r_total)) - - if np.min(distances) < 1e-7: - # Compute the tensor intensity - - index = np.argmin(distances) - if max_intensity[index] < intensity: - max_intensity[index] = intensity - else: - r_total.append(r_mod) - max_intensity.append(intensity) - - - r_total = np.array(r_total) - max_intensity = np.array(max_intensity) - - # Return the value sorted by R distance - sort_mask = np.argsort(r_total) - return r_total[sort_mask], max_intensity[sort_mask] - - - # def ApplyKaiserWindow(self, rmax, beta=14, N_sampling = 1000): - # """ - # Apply a Kaiser-Bessel window to the signal. - # This is the best tool to perform the interpolation. - - # Each element of the tensor is multiplied by the kaiser - # function with the given parameters. - # The kaiser function is computed on the corresponding value of distance - - # Parameters - # ---------- - # - rmax : float - # The maximum distance on which the window is defined. - # All that is outside rmax is setted to 0 - # - beta : float - # The shape of the Kaiser window. - # For beta = 0 the window is a rectangular function, - # for beta = 14 it resample a gaussian. The higher beta, the - # narrower the window. - # - N_sampling : int - # The sampling of the kaiser window. - # """ - - # kaiser_data = scipy.signal.kaiser(N_sampling, beta) - - # # Build the kaiser function - # r_value = np.linspace(-rmax, rmax, N_sampling) - # kaiser_function = scipy.interpolate.interp1d(r_value, kaiser_data, bounds_error=False, fill_value= 0) - - # # Build the kaiser window - # kaiser_window = kaiser_function(self.distances) - - # nat, nat_sc = np.shape(self.distances) - - # # Apply the kaiser window on the tensor - # for i in range(nat): - # for j in range(nat_sc): - # self.tensor[i, j, :, :] *= kaiser_window[i,j] - - -# Third order force constant tensor -class Tensor3(): - """ - This class defines the 3rank tensors, like 3rd force constants. - """ - - def __init__(self, unitcell_structure, supercell_structure, supercell_size): - #GenericTensor.__init__(self, *args, **kwargs) - - n_sup = np.prod(supercell_size) - nat = unitcell_structure.N_atoms - - nat_sc= n_sup * nat - - self.n_sup = n_sup - self.n_R = n_sup**2 - n_R = self.n_R - self.nat = nat - self.tensor = np.zeros( (n_R, 3*nat, 3*nat, 3*nat), dtype = np.double) - - self.supercell_size = supercell_size - - - # Cartesian lattice vectors - self.r_vector2 = np.zeros((3, n_R), dtype = np.double, order = "F") - self.r_vector3 = np.zeros((3, n_R), dtype = np.double, order = "F") - - # Crystalline lattice vectors - self.x_r_vector2 = np.zeros((3, n_R), dtype = np.intc, order = "F") - self.x_r_vector3 = np.zeros((3, n_R), dtype = np.intc, order = "F") - - self.itau = supercell_structure.get_itau(unitcell_structure) - 1 - - self.tau=unitcell_structure.coords - - - self.unitcell_structure = unitcell_structure - self.supercell_structure = supercell_structure - - self.verbose = True - - def SetupFromTensor(self, tensor=None): - """ - Setup the third order force constant form 3rd order tensor defined in the supercell - - - Parameters - ---------- - unitcell_structure : Structure() - The structure in the unit cell - supercell_structure : Structure() - The supercell structure on which the tensor has been computed - supercell_size : truple - The number of supercell along each lattice vector - tensor : ndarray(size =(3*nat_sc, 3*nat_sc, 3*nat_sc, dtype = np.double) - The third order tensor - """ - - - n_sup = np.prod(self.supercell_size) - nat = self.unitcell_structure.N_atoms - supercell_size = self.supercell_size - - - nat_sc= n_sup * nat - n_R = self.n_R - - supercell_structure = self.supercell_structure - unitcell_structure = self.unitcell_structure - - - - for index_cell2 in range(n_sup): - n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=[0,0,0], - v_len=supercell_size) - for index_cell3 in range(n_sup): - n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=[0,0,0], - v_len=supercell_size) - # - total_index_cell = index_cell3 + n_sup * index_cell2 - # - self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) - self.r_vector2[:, total_index_cell] = unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:,total_index_cell]) - self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 - self.r_vector3[:, total_index_cell] = unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) - - for na1 in range(nat): - # - for na2 in range(nat): - # Get the atom in the supercell corresponding to the one in the unit cell - na2_vect = unitcell_structure.coords[na2, :] + self.r_vector2[:, total_index_cell] - nat2_sc = np.argmin( [np.sum( (supercell_structure.coords[k, :] - na2_vect)**2) for k in range(nat_sc)]) - # - for na3 in range(nat): - # Get the atom in the supercell corresponding to the one in the unit cell - na3_vect = unitcell_structure.coords[na3, :] + self.r_vector3[:, total_index_cell] - nat3_sc = np.argmin( [np.sum( (supercell_structure.coords[k, :] - na3_vect)**2) for k in range(nat_sc)]) - # - self.tensor[total_index_cell, - 3*na1 : 3*na1+3, - 3*na2 : 3*na2+3, - 3*na3 : 3*na3+3] = tensor[3*na1 : 3*na1 +3, - 3*nat2_sc : 3*nat2_sc + 3, - 3*nat3_sc : 3*nat3_sc + 3] - - def SetupFromFile(self, fname,file_format='Phonopy'): - """ - Setup the third order force constant form 3rd order tensor written in a file - - - Parameters - ---------- - fname : string - The file name - file_format : string - The format of the file - """ - if Settings.am_i_the_master(): - - if file_format == 'Phonopy': - - print(" ") - print(" Reading FC3 from " + fname) - print(" (Phonopy format) " ) - print(" ") - - f = open(fname, "r") - lines = [l.strip() for l in f.readlines()] - f.close() - - n_blocks = int(lines[0]) - self.n_R = n_blocks/self.nat**3 - - - id_block = 0 - total_lat_vec = 0 - lat_vect_2 = 0 - lat_vect_3 = 0 - nat1 = 0 - nat2 = 0 - nat3 = 0 - reading_lat2 = False - reading_lat3 = False - reading_atoms = False - for i, line in enumerate(lines): - if i == 0: - continue - - data = line.split() - if len(data) == 0: - continue - - if len(data) == 1: - id_block = int(data[0]) - 1 - total_lat_vec = id_block // self.nat**3 - nat_id = id_block % self.nat**3 - reading_lat2 = True - continue - - if reading_lat2: - self.r_vector2[:, total_lat_vec] = [float(x) for x in data] - self.x_r_vector2[:, total_lat_vec] = Methods.covariant_coordinates(self.unitcell_structure.unit_cell, self.r_vector2[:, total_lat_vec]) - reading_lat2 = False - reading_lat3 = True - elif reading_lat3: - self.r_vector3[:, total_lat_vec] = [float(x) for x in data] - self.x_r_vector3[:, total_lat_vec] = Methods.covariant_coordinates(self.unitcell_structure.unit_cell, self.r_vector3[:, total_lat_vec]) - reading_lat3 = False - reading_atoms = True - elif reading_atoms: - nat1, nat2, nat3 = [int(x) - 1 for x in data] - reading_atoms = False - print("Reading the vectors: ", self.r_vector2[:, total_lat_vec], self.r_vector3[:, total_lat_vec], total_lat_vec) - - if len(data) == 4: - xx, yy, zz = [int(x)-1 for x in data[:3]] - - self.tensor[total_lat_vec, 3*nat1+xx, 3*nat2+yy, 3*nat3+zz] = np.double(data[-1]) - - elif file_format == 'D3Q': - - print(" ") - print(" Reading FC3 from "+ fname ) - print(" (D3Q format) " ) - print(" ") - - first_nR_read = True - with open(fname, "r") as f: - # ============ Skip the header, if present ==================== - if len(f.readline().split()) ==6: - f.seek(0) - else: - f.seek(0) - while True: - if len(f.readline().split()) ==1: - break - f.readline() - # ============================================================= - for nat1 in range(self.nat): - for nat2 in range(self.nat): - for nat3 in range(self.nat): - for alpha in range(3): - for beta in range(3): - for gamma in range(3): - [alpha_read,beta_read, - gamma_read,nat1_read, - nat2_read, nat3_read]=[int(l)-1 for l in f.readline().split()] - - assert ([nat1,nat2,nat3,alpha,beta,gamma]==[nat1_read,nat2_read,nat3_read, - alpha_read,beta_read,gamma_read]) - - nR_read=int(f.readline().split()[0]) - - if (first_nR_read) : - self.n_R=nR_read - self.tensor=np.zeros((self.n_R,3*self.nat,3*self.nat,3*self.nat),dtype=np.float64) - self.x_r_vector2=np.zeros((3,self.n_R),dtype=np.int16) - self.x_r_vector3=np.zeros((3,self.n_R),dtype=np.int16) - first_nR_read = False - else : - assert ( nR_read == self.n_R ), " Format unknown - different blocks size " - - for iR in range(self.n_R): - - res=[l for l in f.readline().split()] - - [self.x_r_vector2[0, iR], - self.x_r_vector2[1, iR], - self.x_r_vector2[2, iR], - self.x_r_vector3[0, iR], - self.x_r_vector3[1, iR], - self.x_r_vector3[2, iR]]=[int(l) for l in res[:6]] - - self.tensor[iR, 3*nat1 + alpha, 3*nat2 + beta, 3*nat3 + gamma]=np.double(res[6]) - - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) - - # Broadcast - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - - - def WriteOnFile(self,fname,file_format='Phonopy'): - """ - WRITE ON FILE - ============= - - Save the tensor on a file. - - The file format is the same as phono3py or D3Q - - Parameters - ---------- - fname : string - Path to the file in which you want to save the real space force constant tensor. - file_format: string - It could be either 'phonopy' or 'd3q' (not case sensitive) - 'd3q' is the file format used in the thermal.x espresso package, while phonopy is the one - used in phono3py. - """ - - if file_format.lower() == 'phonopy': - - print(" ") - print(" Writing FC3 on "+ fname) - print(" (Phonopy format) " ) - print(" ") - - with open(fname, "w") as f: - - f.write("{:>5}\n".format(self.n_R * self.nat**3)) - - - i_block = 1 - for r_block in range(self.n_R): - for nat1 in range(self.nat): - for nat2 in range(self.nat): - for nat3 in range(self.nat): - f.write("{:d}\n".format(i_block)) - f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2[:, r_block]))) - f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector3[:, r_block]))) - f.write("{:>6d} {:>6d} {:>6d}\n".format(nat1+1, nat2+1, nat3+1)) - i_block += 1 - # - for xyz in range(27): - z = xyz % 3 - y = (xyz %9)//3 - x = xyz // 9 - f.write("{:>2d} {:>2d} {:>2d} {:>20.10e}\n".format(x+1,y+1,z+1, self.tensor[r_block, 3*nat1 + x, 3*nat2 + y, 3*nat3 + z])) - - - elif file_format.upper() == 'D3Q': - - print(" ") - print(" Writing FC3 on "+ fname) - print(" (D3Q format) " ) - print(" ") - - with open(fname, "w") as f: - #TODD Print header... - - for nat1 in range(self.nat): - for nat2 in range(self.nat): - for nat3 in range(self.nat): - for alpha in range(3): - for beta in range(3): - for gamma in range(3): - f.write("{:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:>6d}\n".format(alpha+1,beta+1,gamma+1,nat1+1, nat2+1, nat3+1)) - f.write("{:>5}\n".format(self.n_R)) - for r_block in range(self.n_R): - f.write("{:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:>6d} {:16.8e}\n".format(self.x_r_vector2[0, r_block],self.x_r_vector2[1, r_block],self.x_r_vector2[2, r_block],self.x_r_vector3[0, r_block],self.x_r_vector3[1, r_block],self.x_r_vector3[2, r_block], self.tensor[r_block, 3*nat1 + alpha, 3*nat2 + beta, 3*nat3 + gamma])) - - - def Center(self, nneigh=None, Far=2,tol=1.0e-5): - """ - CENTERING - ========= - - This subroutine will center the third order force constant. - This means that for each atomic indices in the tensor, it will be identified by the lowest - perimeter between the replica of the atoms. Moreover, in case of existance of other elements - not included in the original supercell with the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - This function should be called before performing the Fourier interpolation. - - - Optional Parameters - -------------------- - - nneigh [default= None]: integer - if different from None, it sets a maximum distance allowed to consider - equivalent atoms in the centering. - - nneigh > 0 - - for each atom the maximum distance is: - the average between the nneigh-th and the nneigh+1-th neighbor distance - (if, for the considered atom, the supercell is big enough to find up to - the nneigh+1-th neighbor distance, otherwise it is the maxmimum distance +10%) - - nneigh = 0 - - the maximum distance is the same for all the atoms, equal to - maximum of the minimum distance between equivalent atoms (+ 10%) - - nneigh < 0 - - the maximum distance is the same for all the atoms, equal to - maximum of the |nneigh|-th neighbor distances found for all the atoms - (if, for the considered atom, the supercell is big enough to find up to - the |nneigh|+1-th neighbor distance, otherwise it is the maxmimum distance +10%) - - - Far [default= 1]: integer - - In the centering, supercell equivalent atoms are considered within - -Far,+Far multiples of the super-lattice vectors - """ - - - - - if Settings.am_i_the_master(): - - - t1 = time.time() - - if self.verbose: - print(" ") - print(" ======================= Centering 3rd FCs ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - # by default no maximum distances - dist_range=np.ones((self.nat))*np.infty - # - if nneigh!=None: - # ======================================================================= - uc_struc = self.unitcell_structure - sc_struc = self.supercell_structure - - # lattice vectors in Angstrom in columns - uc_lat = uc_struc.unit_cell.T - sc_lat = sc_struc.unit_cell.T - - # atomic positions in Angstrom in columns - uc_tau = uc_struc.coords.T - sc_tau = sc_struc.coords.T - - uc_nat = uc_tau.shape[1] - sc_nat = sc_tau.shape[1] - - # == calc dmin ================================================== - # calculate the distances between the atoms in the supercell replicas - - # array square distances between i-th and the j-th - # equivalent atoms of the supercell - d2s=np.empty(( (2*Far+1)**3 , sc_nat, sc_nat )) - - rvec_i=sc_tau - for j, (Ls,Lt,Lu) in enumerate( - itertools.product(range(-Far,Far+1),range(-Far,Far+1),range(-Far,Far+1))): - - rvec_j = sc_tau+np.dot(sc_lat,np.array([[Ls,Lt,Lu]]).T) - - d2s[j,:,:]=scipy.spatial.distance.cdist(rvec_i.T,rvec_j.T,"sqeuclidean") - - # minimum of the square distances - d2min=d2s.min(axis=0) - # minimum distance between two atoms in the supercell, - # considering also equivalent supercell images - dmin=np.sqrt(d2min) - # - if nneigh == 0: - dist_range=np.ones((self.nat))*np.amax(dmin)*1.1 - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to ") - print(" the max of the minimum distances between ") - print(" supercell equivalent atoms (+10%): {:8.5f} A".format(dist_range[0])) - print(" ") - - # to include all the equivalent atoms having the smallest distance - # between them. It does not correspond to taking dist_range=+infity - # because, with the centering, smallest triangles with equivalent points can be obtained - # by considering atoms not at the minimum distance between them: - # with dist_range=+infity these triangles are included, with dist_range=np.amax(dmin) - # they are not included (here I add the 10%) - - else: - #== max dist of the n-th neighbors ============================ - # For all the uc_nat atoms of unit cell (due to the lattice translation symmetry, - # I can limit the analysis to the atoms of a unit cell - not the supercell) - # I look for the nneigh-th neighbor distance and build the array dist_range - # - nn=np.abs(nneigh) - warned = False - for i in range(uc_nat): # for each unit cell atom - ds=dmin[i,:].tolist() # list of distances from other atoms - ds.sort() # list of distances from the i-th atom in increasing order - u=[] # same list, without repetitions - for j in ds: - for k in u: - if np.allclose(k,j): - break - else: - u.append(j) - # the list u of increasing distances for i-th atom has been completed - # try do consider the average of the nneigh-th and nneigh+1-th distance to nth_len - # if it fails, because there are not enough atoms to reach the n-th neighbors, - # then consider the maximum distance found (augmented of 10%) - try: - dist_range[i]=(.5*(u[nn]+u[nn+1])) - except IndexError: - if not warned: - print(" Warning: supercell too small to find {}-th neighbors for all the atoms ".format(nn+1)) - warned = True - dist_range[i]=1.1*max(u) - # - if nneigh < 0 : - # - dist_range=np.ones((self.nat))*np.amax(dist_range) - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to {:8.5f} A".format(dist_range[0])) - print(" ") - # - else : - if self.verbose: - print(" ") - print(" Maximum distance allowed set equal to:".format(dist_range[0])) - print(" ") - for i in range(self.nat): - print("{:8.5f} A for atom {}".format(dist_range[i],i+1)) - print(" ") - # - #============================================================== - # look for the minimum supercell - # - xRmin=np.min(self.x_r_vector3,1) - xRmax=np.max(self.x_r_vector3,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - - tensor= np.transpose(self.tensor,axes=[1,2,3,0]) - self.n_sup = np.prod(xRlen) - - # to the Fortran routine the xR3 (faster than xR2) goes on the rightmost place - # which is the place after the reshape in python - - alat=self.unitcell_structure.unit_cell - - weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far,tol,dist_range,xRlen, - self.x_r_vector2,self.x_r_vector3, - alat, - self.tau, tensor,self.nat,self.n_R) - - - mask= weight >0 - mask=np.repeat(mask[np.newaxis,...], (2*Far+1)*(2*Far+1)*(2*Far+1), axis=0) - - xR2_reshaped=xR2[:,mask] - xR3_reshaped=xR3[:,mask] - xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) - nblocks_old=self.n_R - # - self.n_R=xR23.shape[1] - self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) - - centered=thirdorder.third_order_centering.center(tensor,weight, - self.x_r_vector2,xR2, - self.x_r_vector3,xR3, - Far,self.nat, - self.n_R,nblocks_old) - t2 = time.time() - - self.tensor = np.transpose(centered, axes=[3,0,1,2]) - - if self.verbose: - print(" Time elapsed for computing the centering: {} s".format( t2 - t1)) - print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - print(" ") - print(" ====================================================================") - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - - - def Apply_ASR(self,PBC=False,power=0,maxiter=1000,threshold=1.0e-12): - """ - Apply_ASR - ========= - - This subroutine apply the ASR to the third order force constants iteratively. - For each iteration, the ASR is imposed on the third index - (any of the three indeces would be equivalent, apart from the subtleties that - would require the implementation for the first index, due to the representation chosen), - and, subsequently, the symmetry by permutation of the three indeces is imposed. - - - Optional Parameters - -------------------- - - PBC [default=False]: logical - - If True, periodic boundary conditions are considered. This is necessary, for example, - to apply this routine to non-centered tensors. - - - power [default=0]: float >= 0 - - The way the ASR is imposed on the third index: - phi(i,j,k,abc)=phi(i,j,k,a,b,c)-[\sum_c phi(i,j,k,a,b,c)]* |phi(i,j,k,a,b,c)|^pow / [sum_c |phi(i,j,k,a,b,c)|^pow] - - - maxiter [default= 1000]: integer >= 0 - - n>0 Maximum number of iteration to reach the convergence. - n=0 Endless iteration - - If a file STOP is found, the iteration stops. - - - threshold [default= 1.0e-12]: float > 0 - - Threshold for the convergence. The convergence is on two values: the value of sum on the third index and the - variation of the phi after the imposition of the permutation symmetry (both divided by sum |phi|) - """ - - - if Settings.am_i_the_master(): - - - t1 = time.time() - - if self.verbose: - print(" ") - print(" ======================= ASR ========================== ") - print(" ") - - - xR23 = np.vstack((self.x_r_vector2,self.x_r_vector3)) - xR2list=np.unique(self.x_r_vector2,axis=1) - totnum_R2=xR2list.shape[1] - - xRmin=np.min(self.x_r_vector3,1) - xRmax=np.max(self.x_r_vector3,1) - SClat=xRmax-xRmin+np.ones((3,),dtype=int) - - - tensor=np.transpose(self.tensor,axes=[1,2,3,0]) - - tensor_out=thirdorder.third_order_asr.impose_asr(tensor,xR23, - self.x_r_vector2,xR2list, - power,SClat, - PBC,threshold, - maxiter,self.verbose, - totnum_R2,self.nat,self.n_R) - - self.tensor=np.transpose(tensor_out,axes=[3,0,1,2]) - - t2 = time.time() - - if self.verbose: - print(" ") - print(" Time elapsed for imposing ASR: {} s".format( t2 - t1)) - print(" ") - print(" ============================================================ ") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - - - -#============================================================================================================ - - - def Interpolate(self, q2, q3, asr = True, verbose = False): - """ - Interpolate the third order to the q2 and q3 points - - Parameters - ---------- - q2, q3 : ndarray(3) - The q points - asr : bool - If true, the Acoustic sum rule is applied directly in q space - verbose : bool - If true print debugging info - - Results - ------- - Phi3 : ndarray(size = (3*nat, 3*nat, 3*nat)) - The third order force constant in the defined q points. - atomic indices runs over the unit cell - """ - - final_fc = np.zeros((3*self.nat, 3*self.nat, 3*self.nat), - dtype = np.complex128) - for i in range(self.n_R): - arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i]) + - q3.dot(self.r_vector3[:, i])) - phase = np.exp(np.complex128(-1j) * arg) - final_fc += phase * self.tensor[i, :, :, :] - - # Apply the acoustic sum rule if necessary - if asr: - # Get the reciprocal lattice vectors - bg = Methods.get_reciprocal_vectors(self.unitcell_structure.unit_cell) / (2* np.pi) - - nat = self.unitcell_structure.N_atoms - - # Create the projector on the orthonormal space to the ASR - Q_proj = np.zeros((3*nat, 3*nat), dtype = np.double) - for i in range(3): - v1 = np.zeros(nat*3, dtype = np.double) - v1[3*np.arange(nat) + i] = 1 - v1 /= np.sqrt(v1.dot(v1)) - - Q_proj += np.outer(v1, v1) - - # Get the N_i in the centered cell - # First we get the list of vectors in crystal coordinates - xR_list=np.unique(self.x_r_vector3, axis = 1) - - # We pick the minimum and maximum values of the lattice vectors - # in crystal coordinates - xRmin=np.min(xR_list, axis = 1) - xRmax=np.max(xR_list, axis = 1) - - # Now we can obtain the dimension of the cell along each direction. - N_i = xRmax-xRmin+np.ones((3,),dtype=int) - - if verbose: - print("Centered supercell: ", N_i) - - # We check if they are even, in that case we add 1 - # f(q) is real only if we sum on odd cells - for ik in range(3): - if (N_i[ik] % 2 == 0): - N_i[ik] += 1 - - if verbose: - print("Supercell all odd:", N_i) - - - # We compute the f(q) function - at = self.unitcell_structure.unit_cell - - __tol__ = 1e-8 - f_q3i = np.ones(3, dtype = np.double) - f_q2i = np.ones(3, dtype = np.double) - f_q1i = np.ones(3, dtype = np.double) - - # We use mask to avoid division by 0, - # As we know that the 0/0 limit is 1 in this case - - mask3 = np.abs(np.sin(at.dot(q3) * np.pi)) > __tol__ - f_q3i[mask3] = np.sin(N_i[mask3] * at[mask3,:].dot(q3) * np.pi) / (N_i[mask3] * np.sin(at[mask3,:].dot(q3) * np.pi)) - f_q3 = np.prod(f_q3i) - - mask2 = np.abs(np.sin(at.dot(q2) * np.pi)) > __tol__ - f_q2i[mask2] = np.sin(N_i[mask2] * at[mask2,:].dot(q2) * np.pi) / (N_i[mask2] * np.sin(at[mask2,:].dot(q2) * np.pi)) - f_q2 = np.prod(f_q2i) - - q1 = -q2 - q3 - mask1 = np.abs(np.sin(at.dot(q1) * np.pi)) > __tol__ - f_q1i[mask1] = np.sin(N_i[mask1] * at[mask1,:].dot(q1) * np.pi) / (N_i[mask1] * np.sin(at[mask1,:].dot(q1) * np.pi)) - f_q1 = np.prod(f_q1i) - - if verbose: - print("The fq factors:") - print("{:16.8f} {:16.8f} {:16.8f}".format(f_q1, f_q2, f_q3)) - print("q1 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q1)) - print("q2 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q2)) - print("q3 = ", Methods.covariant_coordinates(bg * 2 * np.pi, q3)) - - # Now we can impose the acustic sum rule - final_fc -= np.einsum("abi, ci-> abc", final_fc, Q_proj) * f_q3 - final_fc -= np.einsum("aic, bi-> abc", final_fc, Q_proj) * f_q2 - final_fc -= np.einsum("ibc, ai-> abc", final_fc, Q_proj) * f_q1 - - - - - return final_fc - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# ============================================================================================================================================ -# Tentative Sparse ========================================================================================================================== -# ============================================================================================================================================ - - - - - def WriteOnFile_sparse(self, filename): - """ - """ - - - with open(filename, "w") as f: - #TODD Print header... - f.write("{:>5}\n".format(self.n_R_sparse)) - - - for i_block in range(self.n_R_sparse): - f.write("{:d}\n".format(i_block+1)) - f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector2_sparse[:, i_block]))) - f.write("{:16.8e} {:16.8e} {:16.8e}\n".format(*list(self.r_vector3_sparse[:, i_block]))) - - nat=self.atom_sparse[:,i_block] - f.write("{:>6d} {:>6d} {:>6d}\n".format(nat[0]+1,nat[1]+1,nat[2]+1)) - - # - for xyz in range(27): - z = xyz % 3 - y = (xyz %9)//3 - x = xyz // 9 - f.write("{:>2d} {:>2d} {:>2d} {:>20.10e}\n".format(x+1,y+1,z+1, self.tensor[self.r_blocks_sparse_list[i_block], 3*nat[0] + x, 3*nat[1] + y, 3*nat[2] + z])) - - def Center_sparse(self, Far=1,tol=1.0e-5): - """ - CENTERING - ========= - - This subrouine will center the third order force constant inside the Wigner-Seitz supercell. - This means that for each atomic indices in the tensor, it will be identified by the lowest - perimiter between the replica of the atoms. - Moreover, in case of existance of other elements not included in the original supercell with - the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - This function should be called before performing the Fourier interpolation. - """ - - t1 = time.time() - - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Centering ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - n_sup = np.prod(self.supercell_size) - tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, - 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - alat=self.unitcell_structure.unit_cell - - weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, - nq0,nq1,nq2,tol, - self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) - - - xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) - - self.n_R=xR23.shape[1] - self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) - - centered,self.n_R_sparse,x_r_vector2_sparse,x_r_vector3_sparse,atom_sparse,r_blocks_sparse_list=thirdorder.third_order_centering.center_sparse(tensor_reshaped,weight, - self.x_r_vector2,xR2,self.x_r_vector3,xR3, - Far,self.nat,n_sup,self.n_R) - - - - self.x_r_vector2_sparse=x_r_vector2_sparse[:,0:self.n_R_sparse] - self.x_r_vector3_sparse=x_r_vector3_sparse[:,0:self.n_R_sparse] - self.atom_sparse=atom_sparse[:,0:self.n_R_sparse] - self.r_blocks_sparse_list=r_blocks_sparse_list[0:self.n_R_sparse] - self.r_vector2_sparse=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2_sparse) - self.r_vector3_sparse=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3_sparse) - - t2 = time.time() - - self.tensor = np.transpose(centered, axes=[3,0,1,2]) - - - if self.verbose: - print("Time elapsed for computing the centering: {} s".format( t2 - t1)) - #print("Memory required for the not centered tensor {} Gb".format(self.tensor.nbytes / 1024.**3)) - print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - #print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - # - self.x_r_vector2_sparse = Settings.broadcast(self.x_r_vector2_sparse) - self.x_r_vector3_sparse = Settings.broadcast(self.x_r_vector3_sparse) - self.r_vector2_sparse = Settings.broadcast(self.r_vector2_sparse, enforce_double=True) - self.r_vector3_sparse = Settings.broadcast(self.r_vector3_sparse, enforce_double=True) - self.n_R_sparse = Settings.broadcast(self.n_R_sparse) - self.atom_sparse = Settings.broadcast(self.atom_sparse) - self.r_blocks_sparse_list = Settings.broadcast(self.r_blocks_sparse_list) - - - - - - - -# ============================================================================================================================================ -# Slower/To check/Old ======================================================================================================================= -# ============================================================================================================================================ - - - def ApplySumRule_old(self) : - - t1 = time.time() - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Imposing ASR ==========================") - print(" ") - - - - #tensor_new = np.transpose(self.tensor.reshape((n_sup_WS, n_sup_WS, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - - - - - #xR2_list, xR2_block_index, xR2_num_tot=np.unique(self.x_r_vector2, axis = 1, return_index = True, return_counts=True ) - #xR3_list, xR3_block_index, xR3_num_tot=np.unique(self.x_r_vector3, axis = 1, return_index = True, return_counts=True ) - - xR_list=np.unique(self.x_r_vector3, axis = 1) - totnum_R=xR_list.shape[1] - - xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) - totnum_Rdiff=xRdiff_list.shape[1] - - - #xRmin=np.min(xR_list,1) - #xRmax=np.max(xR_list,1) - #xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - #n_sup=np.prod(xRlen)**2 - - - #xR2_block_index=[ xR2_num_tot[i] for i in xR3_block_index ] - - #print(np.sum(xR2_list-xR3_list)) - #print(np.sum(xR2_block_index-xR3_block_index)) - #print(np.sum(xR2_num_tot-xR3_num_tot)) - - tensor_new = np.transpose(self.tensor,axes=[3,2,1,0]) - phi_asr=thirdorder.third_order_asr.impose_asr(tensor_new,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) - - #phi_asr=thirdorder.third_order_asr.impose_asr(tensor_trnsp,xRlen,n_sup,xR_list,xRdiff_list, - #self.x_r_vector2,self.x_r_vector3, - #totnum_Rdiff, - #self.n_R,totnum_R,self.nat) - - - - - self.tensor=np.transpose(phi_asr, axes=[3,2,1,0]) - - - ## DEBUG - #tensor_block_nonzero = np.sum(self.tensor**2, axis = (1,2,3)) > 1e-8 - #nn_R = np.sum(tensor_block_nonzero.astype(int)) - #print(nn_R) - ## - - - - t2 = time.time() - if self.verbose: - print(" Time elapsed for imposing the ASR: {} s".format( t2 - t1)) - print(" ") - print(" ============================================================") - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - - - - def Center_fast(self, Far=1,tol=1.0e-5): - """ - CENTERING - ========= - - This subrouine will center the third order force constant inside the Wigner-Seitz supercell. - This means that for each atomic indices in the tensor, it will be identified by the lowest - perimiter between the replica of the atoms. - Moreover, in case of existance of other elements not included in the original supercell with - the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - This function should be called before performing the Fourier interpolation. - - Faster but more memory demanding - """ - - t1 = time.time() - - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Centering ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - n_sup = np.prod(self.supercell_size) - tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - alat=self.unitcell_structure.unit_cell - - lat_min_prev=np.array([-Far*nq0,-Far*nq1,-Far*nq2]) - lat_max_prev=np.array([nq0-1+Far*nq0,nq1-1+Far*nq1,nq2-1+Far*nq2]) - lat_len_prev=lat_max_prev-lat_min_prev+np.ones(3,dtype=int) - n_sup_WS_prev=np.prod(lat_len_prev,dtype=int) - - centered_tmp, lat_min_new, lat_max_new =thirdorder.third_order_centering.pre_center(Far, - nq0,nq1,nq2,tol, - self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) - - # - # Reassignement - # - lat_len=lat_max_new-lat_min_new+np.ones(3,dtype=int) - n_sup_WS=np.prod(lat_len,dtype=int) - - - self.n_R=n_sup_WS**2 - - centered,self.x_r_vector2,self.x_r_vector3,self.r_vector2,self.r_vector3= \ - thirdorder.third_order_centering.assign(alat,lat_min_prev,lat_max_prev,centered_tmp,lat_min_new, - lat_max_new,n_sup_WS,self.nat,n_sup_WS_prev) - - - - - t2 = time.time() - - centered = np.transpose(centered, axes=[3,0,1,2]) - - - # Select the element different from zero - tensor_block_nonzero = np.sum(centered**2, axis = (1,2,3)) > 1e-8 - - self.tensor = centered[tensor_block_nonzero, :, :, :] - self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] - self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] - self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] - self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] - self.n_R = np.sum(tensor_block_nonzero.astype(int)) - - if self.verbose: - print("Time elapsed for computing the centering: {} s".format( t2 - t1)) - print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - - - def Center_fast_noreduce(self, Far=1,tol=1.0e-5): - """ - CENTERING - ========= - - This subrouine will center the third order force constant inside the Wigner-Seitz supercell. - This means that for each atomic indices in the tensor, it will be identified by the lowest - perimiter between the replica of the atoms. - Moreover, in case of existance of other elements not included in the original supercell with - the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - This function should be called before performing the Fourier interpolation. - - Faster but more memory demanding - """ - - t1 = time.time() - - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Centering ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - n_sup = np.prod(self.supercell_size) - tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - alat=self.unitcell_structure.unit_cell - - lat_min_prev=np.array([-Far*nq0,-Far*nq1,-Far*nq2]) - lat_max_prev=np.array([nq0-1+Far*nq0,nq1-1+Far*nq1,nq2-1+Far*nq2]) - lat_len_prev=lat_max_prev-lat_min_prev+np.ones(3,dtype=int) - n_sup_WS_prev=np.prod(lat_len_prev,dtype=int) - - centered_tmp, lat_min_new, lat_max_new =thirdorder.third_order_centering.pre_center(Far, - nq0,nq1,nq2,tol, - self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) - - # - # Reassignement - # - lat_len=lat_max_new-lat_min_new+np.ones(3,dtype=int) - n_sup_WS=np.prod(lat_len,dtype=int) - - - self.n_R=n_sup_WS**2 - - centered,self.x_r_vector2,self.x_r_vector3,self.r_vector2,self.r_vector3= \ - thirdorder.third_order_centering.assign(alat,lat_min_prev,lat_max_prev,centered_tmp,lat_min_new, - lat_max_new,n_sup_WS,self.nat,n_sup_WS_prev) - - - - - t2 = time.time() - - centered = np.transpose(centered, axes=[3,0,1,2]) - - - - # Select the element different from zero - tensor_block_nonzero = np.sum(centered**2, axis = (1,2,3)) > -10 - - self.tensor = centered[tensor_block_nonzero, :, :, :] - self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] - self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] - self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] - self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] - self.n_R = np.sum(tensor_block_nonzero.astype(int)) - print(self.n_R) - if self.verbose: - print("Time elapsed for computing the centering: {} s".format( t2 - t1)) - print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - - - #self.tensor = Settings.broadcast(self.tensor) - #self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - #self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - #self.r_vector2 = Settings.broadcast(self.r_vector2) - #self.r_vector3 = Settings.broadcast(self.r_vector3) - #self.n_R = Settings.broadcast(self.n_R) - - - - def Interpolate_fort(self, q2, q3): # OK but slower - """ - Interpolate the third order to the q2 and q3 points - - Parameters - ---------- - q2, q3 : ndarray(3) - The q points - - Results - ------- - Phi3 : ndarray(size = (3*nat, 3*nat, 3*nat)) - The third order force constant in the defined q points. - atomic indices runs over the unit cell - """ - tensor_new = np.zeros( shape = (3*self.nat, 3*self.nat, 3*self.nat, self.n_R), order = "F", dtype = np.complex128) - tensor_new[:,:,:,:] = np.transpose(self.tensor,axes=[3,2,1,0]) - interpolated_fc_tmp=thirdorder.third_order_interpol.interpol(tensor_new, - self.r_vector2,self.r_vector3, - q2,q3,self.n_R,self.nat) - - interpolated_fc=np.transpose(interpolated_fc_tmp,axes=[2,1,0]) - #final_fc = np.zeros((3*self.nat, 3*self.nat, 3*self.nat), - #dtype = np.complex128) - #for i in range(self.n_R): - #arg = 2 * np.pi * (q2.dot(self.r_vector2[:, i]) + - #q3.dot(self.r_vector3[:, i])) - - #phase = np.complex128(np.exp(1j * arg)) - - #final_fc += phase * self.tensor[i, :, :, :] - return interpolated_fc - - - # def Center_py(self,Far=1,tol=1.0e-5): - # """ - # CENTERING - # ========= - - # This is the python routine to center the third order force constant inside the Wigner-Seitz supercell. - # This means that for each atomic indices in the tensor, it will be identified by the lowest - # perimiter between the replica of the atoms. - # Moreover, in case of existance of other elements not included in the original supercell with - # the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - # This function should be called before performing the Fourier interpolation. - # """ - - # if self.verbose: - # print(" ") - # print(" === Centering === ") - # print(" ") - - # # The supercell total size - # nq0=self.supercell_size[0] - # nq1=self.supercell_size[1] - # nq2=self.supercell_size[2] - - # # We prepare the tensor for the Wigner-Seitz cell (twice as big for any direction) - # WS_nsup = 2**3* np.prod(self.supercell_size) - # WS_n_R = (WS_nsup)**2 - - # # Allocate the vector in the WS cell - # # TODO: this could be memory expensive - # # we could replace this tensor with an equivalent object - # # that stores only the blocks that are actually written - # WS_r_vector2 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") - # WS_r_vector3 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") - # WS_xr_vector2 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") - # WS_xr_vector3 = np.zeros((3, WS_n_R), dtype = np.double, order = "F") - - # # Allocate the tensor in the WS cell - # WS_tensor = np.zeros((WS_n_R, 3*self.nat, 3*self.nat, 3*self.nat), dtype = np.double) - - # # Here we prepare the vectors - # # Iterating for all the possible values of R2 and R3 in the cell that encloses the Wigner-Seitz one - # t1 = time.time() - # for i, (a2,b2,c2) in enumerate(itertools.product(range(-nq0, nq0), range(-nq1, nq1), range(-nq2, nq2))): - # for j, (a3,b3,c3) in enumerate(itertools.product(range(-nq0, nq0), range(-nq1, nq1), range(-nq2, nq2))): - - # # Enclose in one index i and j - # total_index = i * WS_nsup + j - - # # Get the crystal lattice - # WS_xr_vector2[:, total_index] = (a2,b2,c2) - # WS_xr_vector3[:, total_index] = (a3,b3,c3) - - - # # Convert all the vectors in cartesian coordinates - # WS_r_vector2[:,:] = self.unitcell_structure.unit_cell.T.dot(WS_xr_vector2) - # WS_r_vector3[:,:] = self.unitcell_structure.unit_cell.T.dot(WS_xr_vector3) - - # # print("WS vectors:") - # # print(WS_r_vector2.T) - - - - # t2 = time.time() - # if (self.verbose): - # print("Time elapsed to prepare vectors in the WS cell: {} s".format(t2-t1)) - - # # Here we create the lattice images - # # And we save the important data - - # # Allocate the distance between the superlattice vectors for each replica - # tot_replicas = (2*Far + 1)**3 - # total_size = tot_replicas**2 - # dR_12 = np.zeros( (total_size, 3)) - # dR_23 = np.zeros( (total_size, 3)) - # dR_13 = np.zeros( (total_size, 3)) - # # Allocate the perimeter of the superlattice for each replica - # PP = np.zeros(total_size) - # # Alloca the the vector of the superlattice in crystalline units - # V2_cryst = np.zeros((total_size,3)) - # V3_cryst = np.zeros((total_size,3)) - - # # Lets cycle over the replica (the first index is always in the unit cell) - # # To store the variables that will be used to compute the perimeters of - # # all the replica - # t1 = time.time() - # for i, (a2,b2,c2) in enumerate(itertools.product(range(-Far, Far+1), range(-1,Far+1),range(-Far, Far+1))): - # xR_2 = np.array((a2, b2, c2)) - # R_2 = xR_2.dot(self.supercell_structure.unit_cell) - # for j, (a3, b3, c3) in enumerate(itertools.product(range(-1, Far+1), range(-1,Far+1),range(-Far, Far+1))): - # xR_3 = np.array((a3, b3, c3)) - # R_3 = xR_3.dot(self.supercell_structure.unit_cell) - - # # Prepare an index that runs over both i and j - # total_index = tot_replicas*i + j - # #print(total_index, i, j) - - # # Store the replica vector in crystal coordinates - # V2_cryst[total_index, :] = np.array((a2,b2,c2)) * np.array(self.supercell_size) - # V3_cryst[total_index, :] = np.array((a3,b3,c3)) * np.array(self.supercell_size) - - # # Compute the distances between the replica of the indices - # dR_12[total_index, :] = xR_2 - # dR_13[total_index, :] = xR_3 - # dR_23[total_index, :] = xR_3 - xR_2 - - # # Store the perimeter of this replica triplet - # PP[total_index] = R_2.dot(R_2) - # PP[total_index]+= R_3.dot(R_3) - # PP[total_index]+= np.sum((R_3 - R_2)**2) - - # #print("R2:", R_2, "R3:", R_3, "PP:", PP[total_index]) - # t2 = time.time() - - # if self.verbose: - # print("Time elapsed to prepare the perimeter in the replicas: {} s".format(t2 - t1)) - - - # # Now we cycle over all the blocks and the atoms - # # For each triplet of atom in a block, we compute the perimeter of the all possible replica - # # Get the metric tensor of the supercell - # G = np.einsum("ab, cb->ac", self.supercell_structure.unit_cell, self.supercell_structure.unit_cell) - - # # We cycle over atoms and blocks - # for iR in range(self.n_R): - # for at1, at2, at3 in itertools.product(range(self.nat), range(self.nat), range(self.nat)): - # # Get the positions of the atoms - # r1 = self.tau[at1,:] - # r2 = self.r_vector2[:, iR] + self.tau[at2,:] - # r3 = self.r_vector3[:, iR] + self.tau[at3,:] - - # # Lets compute the perimeter without the replicas - # pp = np.sum((r1-r2)**2) - # pp+= np.sum((r2-r3)**2) - # pp+= np.sum((r1-r3)**2) - - # # Get the crystalline vectors (in the supercell) - # x1 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r1) - # x2 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r2) - # x3 = Methods.cart_to_cryst(self.supercell_structure.unit_cell, r3) - - # # Now we compute the quantities that do not depend on the lattice replica - # # As the current perimeter and the gg vector - # G_12 = 2*G.dot(x2-x1) - # G_23 = 2*G.dot(x3-x2) - # G_13 = 2*G.dot(x3-x1) - - # # Now we can compute the perimeters of all the replica - # # all toghether - # P = PP[:] + pp - # P[:] += dR_12.dot(G_12) - # P[:] += dR_23.dot(G_23) - # P[:] += dR_13.dot(G_13) - - # # if self.tensor[iR, 3*at1, 3*at2, 3*at3] > 0: - # # #print("all the perimeters:") - # # #print(P) - # # print("The minimum:", np.min(P)) - # # index = np.argmin(P) - # # print("R2 = ", self.r_vector2[:, iR], "R3 = ", self.r_vector3[:,iR]) - # # print("The replica perimeter:", PP[index]) - # # print("The standard perimeter:", pp) - # # print("The the cross values:") - # # print(dR_12.dot(G_12)[index], dR_13.dot(G_13)[index], dR_23.dot(G_23)[index]) - # # print("The replica vectors are:", "R2:", V2_cryst[index,:], "R3:", V3_cryst[index,:]) - - # # Now P is filled with the perimeters of all the replica - # # We can easily find the minimum - # P_min = np.min(P) - - # # We can find how many they are and a mask on their positions - # min_P_mask = (np.abs(P_min - P) < 1e-6).astype(bool) - - # # The number of minimium perimeters - # n_P = np.sum(min_P_mask.astype(int)) - - # # Get the replica vector for the minimum perimeters - # v2_shift = V2_cryst[min_P_mask, :] - # v3_shift = V3_cryst[min_P_mask, :] - - # # Now we can compute the crystalline coordinates of the lattice in the WS cell - # r2_cryst = np.tile(self.x_r_vector2[:, iR], (n_P, 1)) + v2_shift - # r3_cryst = np.tile(self.x_r_vector3[:, iR], (n_P, 1)) + v3_shift - # verb = False - - - # # Get the block indices in the WS cell - # WS_i_R = get_ws_block_index(self.supercell_size, r2_cryst, r3_cryst, verbose = verb) - - # # Now we fill all the element of the WS tensor - # # with the current tensor, dividing by the number of elemets - # new_elemet = np.tile(self.tensor[iR, 3*at1:3*at1+3, 3*at2:3*at2+3, 3*at3:3*at3+3], (n_P, 1,1,1)) - # new_elemet /= n_P - # WS_tensor[WS_i_R, 3*at1: 3*at1+3, 3*at2:3*at2+3, 3*at3:3*at3+3] = new_elemet - - - # t2 = time.time() - - # if self.verbose: - # print("Time elapsed for computing the cenetering: {} s".format( t2 - t1)) - - # # We can update the current tensor - # self.tensor = WS_tensor - # self.r_vector2 = WS_r_vector2 - # self.r_vector3 = WS_r_vector3 - # self.x_r_vector2 = WS_xr_vector2 - # self.x_r_vector3 = WS_xr_vector3 - # self.n_R = WS_n_R - - - def ApplySumRule_py(self): - r""" - Enforce the sum rule by projecting the third order force constant - in the sum rule space defined by - - .. math:: - - P = ( 1 - Q ) - - Q_{st}^{\alpha\beta} = 1 / nat_{sc} \left( \sum_x \delta_{x\alpha}\delta{x\beta}\right) - """ - - # Apply the sum rule on the last index - - n_sup_WS = int(np.sqrt(self.n_R)) - - for I_r2 in range(n_sup_WS): - # Here we want to sum over r3 (indices that goes from total_index to total_index + n_sup_WS - total_index = n_sup_WS * I_r2 - - for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): - for gamma in range(3): - # gamma mask runs over the third atom index - gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() - delta = np.sum(self.tensor[total_index : total_index + n_sup_WS, s, t, gamma_mask]) / (self.nat * self.n_R) - self.tensor[total_index : total_index + n_sup_WS, s, t, gamma_mask] -= delta - - - # Now apply the sum rule on the second index - for I_r3 in range(n_sup_WS): - total_index_mask = (np.arange(self.n_R) % n_sup_WS) == I_r3 - - for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): - for gamma in range(3): - gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() - #print("BLOCK MASK:",total_index_mask) - #print("GAMMA MASK:", gamma_mask) - #print("S:",s , "T:", t) - #print(np.shape(total_index_mask), np.shape(gamma_mask)) - #print(np.shape(self.tensor)) - #print (self.tensor[total_index_mask, s, 0, t]) - delta = 0 - for r in range(self.nat): - delta += np.sum(self.tensor[total_index_mask, s, 3*r + gamma, t]) / (self.nat * self.n_R) - - for r in range(self.nat): - self.tensor[total_index_mask, s, 3*r + gamma, t] -= delta - - - # Now apply the sum rule on the second index - for total_index in range(n_sup_WS**2): - r2 = self.r_vector2[:, total_index] - r3 = self.r_vector3[:, total_index] - - for s, t in itertools.product(range(3 * self.nat) , range(3*self.nat)): - for gamma in range(3): - gamma_mask = np.tile(np.arange(3) == gamma, (self.nat, 1)).ravel() - - delta = 0 - for i_Rt in range(n_sup_WS): - Rt = self.r_vector2[:, i_Rt * n_sup_WS] - Rnew = r3 - r2 + Rt - Rnew = np.tile(Rnew, (n_sup_WS,1)).T - - distances = np.sum(np.abs( Rnew - self.r_vector3[:, :n_sup_WS]), axis = 0) - good_vector = np.argmin(distances) - if distances[good_vector] < 1e-6: - - vect_index = i_Rt * n_sup_WS + good_vector - delta += np.sum(self.tensor[vect_index, gamma_mask, s, t]) / (self.nat * self.n_R) - - self.tensor[total_index, gamma_mask, s, t] -= delta - - def get_ws_block_index(supercell_size, cryst_vectors2, cryst_vectors3, verbose = False): - """ - Get the block in the supercell that contains the WS cell, - given the crystalline positions of vectors, returns the indices of the block. - - The block identifies the supercell of the three atoms as - iR <=> (0, R_2, R_3) - - This subroutines takes R_2 and R_3 in crystalline components, and computes the iR. - Assuming that the Wigner-Seitz cell is initialized correctly - - Parameters - ---------- - supercell_size : ndarray(size = (3), dtype = int) - The size of the original supercell (not the Wigner-Seitz) - cryst_vectors2 : ndarray(size= (N_vectors, 3), dtype = int) - The crystalline coordinates of the second vector. - It could also be a single vector. - cryst_vectors3 : ndarray(size= (N_vectors, 3), dtype = int) - The crystalline coordinates of the third vector. - It could also be a single vector. - - Results - ------- - block_id : ndarray(size = N_vectors, dtype = int) - The index of the block for each crystalline vector. - """ - - # Get the composed index in the iteration of the two vectors - # Rescale the vectors - new_v2 = cryst_vectors2.copy() - new_v2 += np.array(supercell_size) - - new_v3 = cryst_vectors3.copy() - new_v3 += np.array(supercell_size) - - # Get the total dimension of the block for each vector in the WS cell - WS_sup = np.prod(supercell_size) * 8 - - ws_z = 2 * supercell_size[2] - ws_y = 2 * supercell_size[1] - - # Transform the two vectors in the indices of the iterator - i2 = new_v2[:, 2] + new_v2[:,1] * ws_z - i2 += new_v2[:,0] * ws_z * ws_y - - i3 = new_v3[:, 2] + new_v3[:,1] * ws_z - i3 += new_v3[:,0] * ws_z * ws_y - - # Collect everything in the block index - WS_i_R = i2 * WS_sup + i3 - - # Convert in integer for indexing - WS_i_R = WS_i_R.astype(int) - - if verbose: - print("All values of i2:") - print(i2) - print("All values of i3:") - print(i3) - print("The block value:", WS_i_R) - - # Check if the block indices are correct - assert (WS_i_R >= 0).all() - assert (WS_i_R < WS_sup**2).all() - - return WS_i_R - - def Center_old(self, Far=1,tol=1.0e-5): - """ - CENTERING - ========= - - This subrouine will center the third order force constant inside the Wigner-Seitz supercell. - This means that for each atomic indices in the tensor, it will be identified by the lowest - perimiter between the replica of the atoms. - Moreover, in case of existance of other elements not included in the original supercell with - the same perimeter, the tensor will be equally subdivided between equivalent triplets of atoms. - - This function should be called before performing the Fourier interpolation. - """ - - t1 = time.time() - - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Centering ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - n_sup = np.prod(self.supercell_size) - tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, - 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - alat=self.unitcell_structure.unit_cell - - weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, - nq0,nq1,nq2,tol, - self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) - - - xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) - - self.n_R=xR23.shape[1] - self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) - - centered=thirdorder.third_order_centering.center(tensor_reshaped,weight, - self.x_r_vector2,xR2,self.x_r_vector3,xR3, - Far,self.nat,n_sup,self.n_R) - - - - t2 = time.time() - - self.tensor = np.transpose(centered, axes=[3,0,1,2]) - - if self.verbose: - print("Time elapsed for computing the centering: {} s".format( t2 - t1)) - #print("Memory required for the not centered tensor {} Gb".format(self.tensor.nbytes / 1024.**3)) - print("Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - #print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - - def ApplySumRule_old(self) : - - t1 = time.time() - - if Settings.am_i_the_master(): - - if self.verbose: - print(" ") - print(" ======================= Imposing ASR ==========================") - print(" ") - - - - xR_list=np.unique(self.x_r_vector3, axis = 1) - totnum_R=xR_list.shape[1] - - xRmin=np.min(xR_list,1) - xRmax=np.max(xR_list,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - n_Rnew=np.prod(xRlen)**2 - - if n_Rnew != self.n_R: - if self.verbose: - print("*********") - print("Enlarging") - print("*********") - # reassignement - xr2_old=self.x_r_vector2 - xr3_old=self.x_r_vector3 - # - self.x_r_vector2=np.zeros((3,n_Rnew),dtype=np.int) - self.x_r_vector3=np.zeros((3,n_Rnew),dtype=np.int) - self.r_vector2=np.zeros((3,n_Rnew)) - self.r_vector3=np.zeros((3,n_Rnew)) - for index_cell2 in range(np.prod(xRlen)): - n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=xRmin,v_len=xRlen) - for index_cell3 in range(np.prod(xRlen)): - n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=xRmin,v_len=xRlen) - # - total_index_cell = index_cell3 + np.prod(xRlen) * index_cell2 - # - self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) - self.r_vector2[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, total_index_cell]) - self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 - self.r_vector3[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) - ### - tensor_trnsp = np.transpose(self.tensor,axes=[3,2,1,0]) - tensor=thirdorder.third_order_asr.enlarge(tensor_trnsp, - self.x_r_vector2,self.x_r_vector3, - xr2_old,xr3_old, - self.nat,n_Rnew,self.n_R) - - xR_list=np.unique(self.x_r_vector3, axis = 1) - totnum_R=xR_list.shape[1] - - xRmin=np.min(xR_list,1) - xRmax=np.max(xR_list,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - self.n_R=np.prod(xRlen)**2 - - - xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) - totnum_Rdiff=xRdiff_list.shape[1] - - - #phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) - phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xRlen,xR_list,xRdiff_list, - self.x_r_vector2,self.x_r_vector3, - totnum_Rdiff,self.n_R,totnum_R,self.nat) - - phi_asr=np.transpose(phi_asr, axes=[3,2,1,0]) - - # Select the element different from zero ######################## - tensor_block_nonzero = np.sum(phi_asr**2, axis = (1,2,3)) > 1e-8 - - self.tensor = phi_asr[tensor_block_nonzero, :, :, :] - self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] - self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] - self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] - self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] - self.n_R = np.sum(tensor_block_nonzero.astype(int)) - t2 = time.time() - if self.verbose: - print("Time elapsed for imposing the ASR: {} s".format( t2 - t1)) - print("Memory required for the ASR centered tensor: {} Gb".format(phi_asr.nbytes / 1024.**3)) - print("Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - - - - def Center_and_ApplySumRule_old(self,Far=1,tol=1.0e-5) : - - t1 = time.time() - - if Settings.am_i_the_master(): - - - if self.verbose: - print(" ") - print(" ======================= Centering ==========================") - print(" ") - - # The supercell total size - # - nq0=self.supercell_size[0] - nq1=self.supercell_size[1] - nq2=self.supercell_size[2] - - n_sup = np.prod(self.supercell_size) - tensor_reshaped = np.transpose(self.tensor.reshape((n_sup, n_sup, - 3*self.nat, 3*self.nat, 3*self.nat)),axes=[2,3,4,0,1]) - alat=self.unitcell_structure.unit_cell - - weight,xR2,xR3 =thirdorder.third_order_centering.analysis(Far, - nq0,nq1,nq2,tol, - self.unitcell_structure.unit_cell, self.tau, tensor_reshaped,self.nat) - - - xR2_reshaped=np.reshape(xR2,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR3_reshaped=np.reshape(xR3,(3,(2*Far+1)*(2*Far+1)*(2*Far+1)*n_sup*self.nat*self.nat*self.nat*n_sup*n_sup)) - xR23 = np.unique(np.vstack((xR2_reshaped,xR3_reshaped)),axis=1) - - self.n_R=xR23.shape[1] - self.x_r_vector2,self.x_r_vector3 = np.vsplit(xR23,2) - self.r_vector2=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2) - self.r_vector3=self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3) - - centered=thirdorder.third_order_centering.center(tensor_reshaped,weight, - self.x_r_vector2,xR2,self.x_r_vector3,xR3, - Far,self.nat,n_sup,self.n_R) - - t2 = time.time() - - self.tensor = np.transpose(centered, axes=[3,0,1,2]) - - if self.verbose: - print("Time elapsed for computing the centering: {} s".format( t2 - t1)) - print(" Memory required for the centered tensor: {} Gb".format(centered.nbytes / 1024.**3)) - - - if self.verbose: - print(" ") - print(" ======================= Imposing ASR ==========================") - print(" ") - - - - xR_list=np.unique(self.x_r_vector3, axis = 1) - totnum_R=xR_list.shape[1] - - xRmin=np.min(xR_list,1) - xRmax=np.max(xR_list,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - n_Rnew=np.prod(xRlen)**2 - - if n_Rnew != self.n_R: - if self.verbose: - print("*********") - print("Enlarging") - print("*********") - # reassignement - xr2_old=self.x_r_vector2 - xr3_old=self.x_r_vector3 - # - self.x_r_vector2=np.zeros((3,n_Rnew),dtype=int) - self.x_r_vector3=np.zeros((3,n_Rnew),dtype=int) - self.r_vector2=np.zeros((3,n_Rnew)) - self.r_vector3=np.zeros((3,n_Rnew)) - for index_cell2 in range(np.prod(xRlen)): - n_cell_x2,n_cell_y2,n_cell_z2=Methods.one_to_three_len(index_cell2,v_min=xRmin,v_len=xRlen) - for index_cell3 in range(np.prod(xRlen)): - n_cell_x3,n_cell_y3,n_cell_z3=Methods.one_to_three_len(index_cell3,v_min=xRmin,v_len=xRlen) - # - total_index_cell = index_cell3 + np.prod(xRlen) * index_cell2 - # - self.x_r_vector2[:, total_index_cell] = (n_cell_x2, n_cell_y2, n_cell_z2) - self.r_vector2[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector2[:, total_index_cell]) - self.x_r_vector3[:, total_index_cell] = n_cell_x3, n_cell_y3, n_cell_z3 - self.r_vector3[:, total_index_cell] = self.unitcell_structure.unit_cell.T.dot(self.x_r_vector3[:, total_index_cell]) - ### - tensor_trnsp = np.transpose(self.tensor,axes=[3,2,1,0]) - tensor=thirdorder.third_order_asr.enlarge(tensor_trnsp, - self.x_r_vector2,self.x_r_vector3, - xr2_old,xr3_old, - self.nat,n_Rnew,self.n_R) - - xR_list=np.unique(self.x_r_vector3, axis = 1) - totnum_R=xR_list.shape[1] - - xRmin=np.min(xR_list,1) - xRmax=np.max(xR_list,1) - xRlen=xRmax-xRmin+np.ones((3,),dtype=int) - self.n_R=np.prod(xRlen)**2 - - - xRdiff_list=np.unique(self.x_r_vector2-self.x_r_vector3, axis = 1) - totnum_Rdiff=xRdiff_list.shape[1] - - - #phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xR_list,xRdiff_list,self.x_r_vector2,self.x_r_vector3,totnum_Rdiff,self.n_R,totnum_R,self.nat) - phi_asr=thirdorder.third_order_asr.impose_asr(tensor,xRlen,xR_list,xRdiff_list, - self.x_r_vector2,self.x_r_vector3, - totnum_Rdiff,self.n_R,totnum_R,self.nat) - - phi_asr=np.transpose(phi_asr, axes=[3,2,1,0]) - - # Select the element different from zero - tensor_block_nonzero = np.sum(phi_asr**2, axis = (1,2,3)) > 1e-8 - - self.tensor = phi_asr[tensor_block_nonzero, :, :, :] - self.x_r_vector2 = self.x_r_vector2[:, tensor_block_nonzero] - self.x_r_vector3 = self.x_r_vector3[:, tensor_block_nonzero] - self.r_vector2 = self.r_vector2[:, tensor_block_nonzero] - self.r_vector3 = self.r_vector3[:, tensor_block_nonzero] - self.n_R = np.sum(tensor_block_nonzero.astype(int)) - - t3 = time.time() - if self.verbose: - print(" Time elapsed for imposing the ASR: {} s".format( t3 - t2)) - print(" Memory required for the ASR centered tensor: {} Gb".format(phi_asr.nbytes / 1024.**3)) - print(" Memory required after removing zero elements: {} Gb".format(self.tensor.nbytes / 1024.**3)) - print(" ") - print(" ============================================================") - - self.tensor = Settings.broadcast(self.tensor, enforce_double=True) - self.x_r_vector2 = Settings.broadcast(self.x_r_vector2) - self.x_r_vector3 = Settings.broadcast(self.x_r_vector3) - self.r_vector2 = Settings.broadcast(self.r_vector2, enforce_double=True) - self.r_vector3 = Settings.broadcast(self.r_vector3, enforce_double=True) - self.n_R = Settings.broadcast(self.n_R) - self.n_sup = Settings.broadcast(self.n_sup) - - - - -# Plot the phonons in the given k-path -def get_phonons_in_qpath(dynamical_matrix, q_path, center_args = {}): - """ - GET PHONONS IN K-PATH - ===================== - - Compute the phonons in the given q pat. - - Parameters - ---------- - dynamical_matrix : Phonons.Phonons - The dynamical matrix (with effective charges) - q_path : list of ndarray(size of 3) - List of q points in 2pi/A units. - center_args : dict - Dictionary of parameters to be passed to the Center function of - Tensor2 - - Results - ------- - frequencies : ndarray(size= (Nq, Nmodes)) - The frequencies of the dynamical matrix along the given path. - The result is given in cm-1 - - - Examples - -------- - - To get a valid path specifying the special points of the Brilluin zone - you can exploit the ASE library: - - .. code:: - - # Load the dynamical matrix - dyn = CC.Phonons.Phonons("mydyn", nqirr = 6) - - # Get the q-path from high-symmetry points - path = ase.dft.kpoints.bandpath("GKWXULG", # The q-path - dyn.structure.unit_cell, - npoints = 1000) - qpath = path.cartesian_kpts() - - # Compute the frequencies in the q path - ws = CC.ForceTensor.get_phonons_in_qpath(dyn, qpath) - - - # Plot the results (with matplotlib.pyplot as plt) - xaxis, xticks, xlabels = path.get_linear_kpoint_axis() - for i in range(dyn.structure.N_atoms * 3): - plt.plot(x_axis, ws[:, i]) - - plt.gca().set_xticks(xticks) - plt.gca().set_xticklabels(xlabels) - """ - - - # Generate the tensor from the dynamical matrix - t2 = Tensor2(dynamical_matrix.structure, - dynamical_matrix.structure.generate_supercell(dynamical_matrix.GetSupercell()), - dynamical_matrix.GetSupercell()) - - t2.SetupFromPhonons(dynamical_matrix) - t2.Center(**center_args) - t2.Apply_ASR() - - n_k, _ = q_path.shape - - n_modes = 3 * dynamical_matrix.structure.N_atoms - ws = np.zeros((n_k, n_modes), dtype = np.double) - m = dynamical_matrix.structure.get_masses_array() - m = np.tile(m, (3,1)).T.ravel() - _mm_ = np.sqrt(np.outer(m, m)) - - - # Interpolate the result - for i in range(n_k): - dynq = t2.Interpolate(-q_path[i,:]) - fc = dynq / _mm_ - - # Diagonalize - all_freqs = np.linalg.eigvalsh(fc) - ws[i, :] = np.sqrt(np.abs(all_freqs)) * Units.RY_TO_CM * np.sign(all_freqs) - - return ws diff --git a/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py deleted file mode 100644 index cfb6b047..00000000 --- a/cellconstructor/.ipynb_checkpoints/Methods-checkpoint.py +++ /dev/null @@ -1,1852 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jun 6 10:45:50 2018 - -@author: pione -""" -from __future__ import print_function -from __future__ import absolute_import -from __future__ import division -from hashlib import new -from importlib.resources import path - -from numpy import * -import numpy as np -import sys, os -import symph - -import warnings - -#from . import Structure - -BOHR_TO_ANGSTROM = 0.529177249 -__EPSILON__ = 1e-6 - - -__all__ = ["covariant_coordinates", "from_dynmat_to_spectrum", - "put_into_cell", "get_minimal_orthorombic_cell", - "write_dynmat_in_qe_format", "get_gr", "cell2abc_alphabetagamma", - "DistanceBetweenStructures"] - - -def covariant_coordinates(basis, vectors): - """ - Covariant Coordinates - ===================== - - This method returns the covariant coordinates of the given vector in the chosen basis. - Covariant coordinates are the coordinates expressed as: - .. math:: - - \\vec v = \\sum_i \\alpha_i \\vec e_i - - - where :math:`\\vec e_i` are the basis vectors. Note: the :math:`\\alpha_i` are not the - projection of the vector :math:`\\vec v` on :math:`\\vec e_i` if the basis is not orthogonal. - - - Parameters - ---------- - - basis : ndarray(size = (N,N)) - The basis. each :math:`\\vec e_i` is a row. - - vector : ndarray(size = (N_vectors, N)) - The vectors expressed in cartesian coordinates. - It coould be just one ndarray(size=N) - - Results - ------- - - cov_vector : Nx float - The :math:`\\alpha_i` values. - - """ - - M, N = np.shape(basis) - - metric_tensor = np.zeros((N,N)) - - metric_tensor = basis.dot(basis.T) - # for i in range(0, N): - # for j in range(i, N): - # metric_tensor[i, j] = metric_tensor[j,i] = basis[i,:].dot(basis[j, :]) - - imt = np.linalg.inv(metric_tensor) - - contra_vect = vectors.dot(basis.T) - return contra_vect.dot(imt) - -def cryst_to_cart(unit_cell, cryst_vectors): - """ - Convert a vector from crystalline to cartesian. - Many vectors counld be pased toghether, in that case the last axis must be the one with the vector. - - Parameters - ---------- - unit_cell : ndarray((3,3)) - The unit cell vectors. - The i-th cell vector is unit_cell[i, :] - cryst_vectors : ndarray((N_vectors, 3)) or ndarray(3) - The vector(s) in crystalline coordinates that you want to - transform in cartesian coordinates - - Results - ------- - cart_vectors : ndarray((N_vectors, 3)) or ndarray(3) - The vector(s) in cartesian coordinates - """ - - return cryst_vectors.dot(unit_cell) - -def cart_to_cryst(unit_cell, cart_vectors): - """ - Convert a vector from cartesian to crystalline. - Many vectors counld be pased toghether, in that case the last axis must be the one with the vector. - - Parameters - ---------- - unit_cell : ndarray((3,3)) - The unit cell vectors. - The i-th cell vector is unit_cell[i, :] - cart_vectors : ndarray((N_vectors, 3)) or ndarray(3) - The vector(s) in cartesian coordinates that you want to - transform in crystalline coordinates - - Results - ------- - cryst_vectors : ndarray((N_vectors, 3)) or ndarray(3) - The vector(s) in crystalline coordinates - """ - return covariant_coordinates(unit_cell, cart_vectors) - -def get_equivalent_vectors(unit_cell, vectors, target, index = None): - """ - This function returns an array mask of the vectors that are - equivalent to the target vector. - - Parameters - ---------- - - unit_cell : ndarray (size = (3,3)) - unit_cell[i, :] is the i-th lattice vector - - vectors : ndarray(size = (n_vects, 3)) - The vectors to be compared to the target - - target : ndarray(size = 3) - The target vector - - Returns - ------- - - eq_mask : ndarray(size = n_vects, dtype = bool) - A mask that is True if the vectors[i, :] is equivalent - to target. - """ - - # Get the inverse metric tensor - M, N = np.shape(unit_cell) - - metric_tensor = np.zeros((N,N)) - for i in range(0, N): - for j in range(i, N): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - imt = np.linalg.inv(metric_tensor) - - # Transform matrix - # This matrix transforms from cartesian to crystal coordinates - transform = imt.dot(unit_cell) - - # Get the crystal coordinates for each vector - crystal_vectors = vectors.dot(transform.T) - - # Get the crystal coordinates for the target - crystal_target = transform.dot(target) - - - # For each crystal vector, subtract the target - crystal_vectors -= crystal_target - - # Get the mask of those vectors whose components are integers - mask_on_coords = np.abs((crystal_vectors - np.floor(crystal_vectors + .5))) < 1e-5 - - # Here we count, for each vector, how many coordinates are not integers in crystal units - # Then we select only those whose count is 0 (all crystal coordinats are integers) - mask_equal = np.sum(mask_on_coords.astype(int), axis = 1) == 3 - - return mask_equal - - -def get_min_dist_into_cell(unit_cell, v1, v2): - """ - This function obtain the minimum distance between two vector, considering the given unit cell - - - Parameters - ---------- - unit_cell : ndarray 3x3 - The unit cell - v1 : ndarray 3 - Vector 1 - v2 : ndarray 3 - Vector 2 - - Results - ------- - float - The minimum distance between the two fector inside the given unit cell - """ - - - # Get the covariant components - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - imt = np.linalg.inv(metric_tensor) - - # Get contravariant components - contra_vect = np.zeros(3) - for i in range(3): - contra_vect[i] = v1.dot(unit_cell[i, :]) - - # Invert the metric tensor and obtain the covariant coordinates - covect1 = imt.dot(contra_vect) - - contra_vect = np.zeros(3) - for i in range(3): - contra_vect[i] = v2.dot(unit_cell[i, :]) - - # Invert the metric tensor and obtain the covariant coordinates - covect2 = imt.dot(contra_vect) - - covect_distance = covect1 - covect2 - - # Bring the distance as close as possible to zero - covect_distance -= (covect_distance + np.sign(covect_distance)*.5).astype(int) - - # Compute the distance using the metric tensor - return np.sqrt(covect_distance.dot(metric_tensor.dot(covect_distance))) - -def identify_vector(unit_cell, vector_list, target, epsil = 1e-8): - """ - Identify whichone in vector_list is equivalent to the target (given the supercell) - If no vector is identified, then raise a warning and return None. - - This function is much more efficient than calling get_min_dist_into_cell in a loop. - - Parameters - ---------- - unit_cell : ndarray 3x3 - The unit cell - vector_list : ndarray (N_vectors, 3) - The array of vector you want to compare - target : ndarray 3 - The target vector - - Results - ------- - int - the index of the vector_list that is equivalent to target (withint the cell) - """ - - # Get the covariant components - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - imt = np.linalg.inv(metric_tensor) - - N_vectors = vector_list.shape[0] - - # Get contravariant components - crystal_list = vector_list.dot(unit_cell.T) - crystal_list = crystal_list.dot(imt) - - crystal_target = target.dot(unit_cell.T) - crystal_target = imt.dot(crystal_target) - - crystal_distance = crystal_list - np.tile(crystal_target, (N_vectors, 1)) - - # Bring the distance as close as possible to zero - crystal_distance -= (crystal_distance + np.sign(crystal_distance)*.5).astype(int) - - # Compute the distances using the metric tensor - distances = np.einsum("ai, ai -> a", crystal_distance.dot(metric_tensor), crystal_distance) - - min_index = np.argmin(distances) - dist = distances[min_index] - - if dist > epsil: - warnings.warn("Warning in identify_vector, no equivalent atoms found within a threshold of {}".format(epsil)) - return None - - return min_index - - - -def get_reciprocal_vectors(unit_cell): - """ - GET THE RECIPROCAL LATTICE VECTORS - ================================== - - Gives back the reciprocal lattice vectors given the - unit cell. - - P.S. - The output is in rad / alat^-1 - where alat is the unit of measurement of the unit_cell. - - Parameters - ---------- - unit_cell : ndarray( size = (3,3), dtype = np.float64) - The unit cell, rows are the vectors. - - Results - ------- - reciprocal_vectors : ndarray(size = (3,3), dtype = np.float64) - The reciprocal lattice vectors - """ - - reciprocal_vectors = np.zeros( (3,3), dtype = np.float64) - reciprocal_vectors[:,:] = np.transpose(np.linalg.inv(unit_cell)) - return reciprocal_vectors - - -def from_dynmat_to_spectrum(dynmat, struct): - """ - This method takes as input the dynamical matrix and the atomic structure of the system and - returns the spectrum. - - Parameters - ---------- - - dynmat : float, 3*N_atoms x 3*N_atoms - Numpy array that contains the real-space dynamical matrix (Hartree). - - struct : Structure - The structure of the system. The masses must be initialized. - - Results - ------- - - Frequencies : float, 3*N_atoms - Numpy array containing the frequencies in cm-1 - """ - - n_atoms = struct.N_atoms - - # Construct the matrix to be diagonalized - new_phi = np.zeros(np.shape(dynmat)) - for i in range(n_atoms): - M_i = struct.masses[struct.atoms[i]] - for j in range(n_atoms): - M_j = struct.masses[struct.atoms[j]] - new_phi[3*i : 3*(i+1), 3*j : 3*(j+1)] = dynmat[3*i : 3*(i+1), 3*j : 3*(j+1)] / (M_i * M_j) - - - # Diagonalize the matrix - eigval, eigvect = np.linalg.eig(new_phi) - eigval *= 220000. # conversion to cm-1 - - return np.sort(eigval) - - - -def put_into_cell(cell, vector): - """ - This function take the given vector and gives as output the corresponding - one inside the specified cell. - - Parameters - ---------- - - cell : double, 3x3 matrix - The unit cell, a 3x3 matrix whose rows specifies the cell vectors - - vector : double, 3 elements ndarray - The vector to be shifted into the unit cell. - - Results - ------- - - new_vector : double, 3 elements ndarray - The corresponding vector into the unit cell - """ - - # Check if the system has unit cell - if np.linalg.det(cell) == 0: - ERROR_MSG = """ - Error, the structure has no unit cell (or vectors are linearly dependent). - """ - raise ValueError(ERROR_MSG) - - # Put the vector inside the unit cell - # To do this, just obtain the covariant vector coordinates. - - # Get the metric tensor - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = cell[i,:].dot(cell[j, :]) - - # Get contravariant components - contra_vect = np.zeros(3) - for i in range(3): - contra_vect[i] = vector.dot(cell[i, :]) - - # Invert the metric tensor and obta - covect = np.linalg.inv(metric_tensor).dot(contra_vect) - - # print "" - # print "Translating into the unit cell:" - # print "MT:" - # print metric_tensor - # print "IMT:" - # print linalg.inv(metric_tensor) - # print "vector:", vector - # print "contra variant:", contra_vect - # print "covariant:", covect - - for i in range(3): - covect[i] = covect[i] - int(covect[i]) - if covect[i] < 0: - covect[i] += 1 - - - - # Go back - final_vect = np.zeros(3) - for i in range(3): - final_vect += covect[i] * cell[i,:] - - # print "covariant new:", covect - # print "final:", final_vect - # print "" - - return final_vect - - -def get_minimal_orthorombic_cell(euclidean_cell, ita=36): - """ - This function, given an euclidean cell with 90 90 90 angles, returns the minimal - cell. The minimal cell will not have 90 90 90 angles. - - Parameters - ---------- - - euclidean_cell : matrix 3x3, double precision - The rows of this matrix are the unit cell vectors in euclidean cell. - - ita : integer - The group class in ITA standard (36 = Cmc21) - - Results - ------- - - minimal_cell : matrix 3x3, double precision - The rows of this matrix are the new minimal unit cell vectors. - """ - - - # Take the last vector and project into the last-one. - - minimal_cell = euclidean_cell.copy() - - if (ita == 36): - last_vector = .5 * euclidean_cell[1,:] + .5 * euclidean_cell[0,:] - minimal_cell[1,:] = last_vector - else: - raise ValueError("Error on input, ITA = %d not yet implemented." % ita) - - return minimal_cell - - -# ------- -# Compute the g(r) -def _get_gr_slow(structures, type1, type2, r_max, dr): - """ -Radial distribution function -============================ - -Computes the radial distribution function for the system. The -:math:`g_{AB}(r)` is defined as - -.. math:: - - - g_{AB}(r) = \\frac{\\rho_{AB}^{(2)}(r)}{\\rho_A(r) \\rho_B(r)} - - -where :math:`A` and :math:`B` are two different types - -Parameters ----------- - - structures : list type(Structure) - A list of atomic structures on which compute the :math:`g_{AB}(r)` - - type1 : character - The character specifying the :math:`A` atomic type. - - type2 : character - The character specifying the :math:`B` atomic type. - - r_max : float - The maximum cutoff value of :math:`r` - - dr : float - The bin value of the distribution. - -Results -------- - - g_r : ndarray.shape() = (r/dr + 1, 2) - The :math:`g(r)` distribution, in the first column the :math:`r` value - in the second column the corresponding value of g(r) - """ - - # Get the r axis - N_bin = int( r_max / float(dr)) + 1 - r_min = np.linspace(0, r_max, N_bin) - - real_dr = np.mean(np.diff(r_min)) - #print("REAL DR:", real_dr) - real_r = r_min + real_dr * .5 - - # Define the counting array - N_r = np.zeros(N_bin) - - # Count how many atoms are in each shell - for i, struct in enumerate(structures): - # Cycle for all atoms - for first in range(0, struct.N_atoms- 1): - other_type = "" - if struct.atoms[first] == type1: - other_type = type2 - elif struct.atoms[first] == type2: - other_type = type1 - else: - continue - - for second in range(first + 1, struct.N_atoms): - if struct.atoms[second] == other_type: - # Get the distance vector between the two atoms - r_vec = struct.coords[first, :] - struct.coords[second,:] - #print("nat: {}, indexes: {}, {}".format(struct.N_atoms, first, second)) - #print("r_vec:", r_vec) - if struct.has_unit_cell: - r_vec = get_closest_vector(struct.unit_cell, r_vec) - #r = struct.get_min_dist(first, second) - r = np.sqrt(r_vec.dot(r_vec)) # The modulus - index_pos = int( r / real_dr) - if index_pos < N_bin: - N_r[index_pos] += 1 - - - - - # Now get the g(r) from N_r - N_tot = sum(N_r) - V = 4 * np.pi * r_max**3 / 3. - rho = N_tot / V - g_r = N_r / (4 * np.pi * real_r**2 * real_dr * rho) - - # Get the final data and return - data = np.zeros((N_bin, 2)) - data[:, 0] = real_r - data[:, 1] = g_r - return data - - -def get_gr(structures, type1, type2, r_min, r_max, N_r): - """ -Radial distribution function -============================ - -Computes the radial distribution function for the system. The -:math:`g_{AB}(r)` is defined as - -.. math:: - - - g_{AB}(r) = \\frac{\\rho_{AB}^{(2)}(r)}{\\rho_A(r) \\rho_B(r)} - - -where :math:`A` and :math:`B` are two different types - -Parameters ----------- - - structures : list type(Structure) - A list of atomic structures on which compute the :math:`g_{AB}(r)` - - type1 : character - The character specifying the :math:`A` atomic type. - - type2 : character - The character specifying the :math:`B` atomic type. - - r_min, r_max : float - The minimum and maximum cutoff value of :math:`r` - - N_r : float - The number of bins for the distributions - -Results -------- - - g_r : ndarray.shape() = (r/dr + 1, 2) - The :math:`g(r)` distribution, in the first column the :math:`r` value - in the second column the corresponding value of g(r) - """ - n_structs = len(structures) - nat = structures[0].N_atoms - cells = np.zeros((n_structs, 3, 3), order= "F", dtype = np.double) - coords = np.zeros( (n_structs, nat, 3), order = "F", dtype = np.double) - ityp = structures[0].get_ityp() + 1 - t1 = structures[0].get_ityp_from_species(type1) + 1 - t2 = structures[0].get_ityp_from_species(type2) + 1 - - #print(t1, t2, ityp) - - for i in range(n_structs): - cells[i, :, :] = structures[i].unit_cell - coords[i, :, :] = structures[i].coords - - - real_r, g_r = symph.get_gr_data(cells, coords, ityp, t1, t2, r_min, r_max, N_r) - - # Get the final data and return - data = np.zeros((N_r, 2)) - data[:, 0] = real_r - data[:, 1] = g_r - return data - - - - -def cell2abc_alphabetagamma(unit_cell): - """ -This methods return a list of 6 elements. The first three are the three lengths a,b,c of the cell, while the other three -are the angles alpha (between b and c), beta (between a and c) and gamma(between a and b). - -Parameters ----------- - - unit_cell : 3x3 ndarray (a float dtype) - The unit cell in which the lattice vectors are the rows. - -Results -------- - - cell : 6 length ndarray (size = 6, dtype = type(unit_cell)) - The array containing the a,b,c length followed by alpha,beta and gamma (in degrees) - """ - - cell = np.zeros(6, dtype = unit_cell.dtype) - - # Get a,b,c - for i in range(3): - cell[i] = np.sqrt(unit_cell[i, :].dot(unit_cell[i, :])) - - # Get alpha beta gamma - for i in range(3): - j = (i + 1) % 3 - k = (i + 2) % 3 - cosangle = unit_cell[j,:].dot(unit_cell[k, :]) / (cell[j] * cell[k]) - - cell[i + 3] = np.arccos(cosangle) * 180 / np.pi - - return cell - - - -def DistanceBetweenStructures(strc1, strc2, ApplyTrans=True, ApplyRot=False, Ordered = True): - """ -This method computes the distance between two structures. -It is usefull to check the similarity between two structure. - -Note: -Ordered = False is not yet implemented - -Parameters ----------- - - strc1 : type(Structure) - The first structure. It commutes with the strc2. - - strc2 : type(Structure) - The second structure. - - ApplyTrans: bool, default = False - If true both the structures are shifted in a common origin (The first atom). - This works only if the atoms are ordered to match properly. - - ApplyRot : bool, default = False - If true the structure are rotated to reduce the rotational freedom. - - Ordered: bool, default = True - If true the order in which the atoms appears is supposed to match in the two structures. - - -Results -------- - - Similarities: float - Similarity between the two provided structures - """ - - - if not Ordered: - raise ValueError("Error, Ordered = False not yet implemented. Sorry.") - - if strc1.N_atoms != strc2.N_atoms: - print( "Strc1 has ", strc1.N_atoms, " atoms") - print( "Strc2 has ", strc2.N_atoms, " atoms") - raise ValueError("Error, the number of atoms are not the same in the given structures.") - - nat = strc1.N_atoms - coord1 = np.zeros(nat*3) - coord2 = np.zeros(nat*3) - - if ApplyTrans: - # Shift both the strcture so that the first atom is in the origin. - if Ordered: - for i in range(nat): - coord1[3*i : 3*i + 3] = strc1.coords[i,:] - strc1.coords[0,:] - coord2[3*i : 3*i + 3] = strc2.coords[i,:] - strc2.coords[0,:] - else: - for i in range(nat): - coord1[3*i : 3*i + 3] = strc1.coords[i,:] - coord2[3*i : 3*i + 3] = strc2.coords[i,:] - - - # Compute the distance between the two coordinates - return np.sqrt(sum((coord1 - coord2)**2)) - - -def get_unit_cell_from_ibrav(ibrav, celldm): - """ - OBTAIN THE UNIT CELL WITH QUANTUM ESPRESSO IBRAV - ================================================ - - This subroutine reads the quantum espresso variables ibrav and celldm - and built the unit cell according to them. - - NOTE: not all the version are still supported, they will be - added as the developing of the code will go on. - - - Look at quantum espresso pw.x input for a clear explanation - on how they works. - Note the unit of QE are bohr, so we expect the celldm[0] to be - written in bohr. However the output cell will be in angstrom. - - Parameters - ---------- - ibrav : int - This is the ibrav number identification of the cell type. - Note if it is a float, it will be rounded to the closest integer. - For example, 1 means simple cubic, 13 is the base-centered monoclinic ... - - celldm : ndarray (float, 6) - It contains a list of 6 floats that defines the cell axis length and - angles. Their precise meaning can be found on quantum-espresso documentation. - We refer at 6.2.1 version. - - Results - ------- - unit_cell : ndarray (3x3) - The unit cell in angstrom. the i-th cell vector is unit_cell[i,:] - - """ - # Avoid trivial problems if the ibrav is a float - ibrav = int(ibrav + .5) - - #SUPPORTED_IBRAV = [13] - - # Check if the ibrav is in the supported ibrav - #if not ibrav in SUPPORTED_IBRAV: - # raise ValueError("Error, the specified ibrav %d is not supported." % ibrav) - - - # Check if celldm is of the correct length - if len(celldm) != 6: - raise ValueError("Error, celldm shoud be an ndarray of size 6") - - # Get the cell - unit_cell = np.zeros((3,3)) - if ibrav == 1: - # Simple cubic - a = celldm[0] * BOHR_TO_ANGSTROM - unit_cell[0,:] = np.array([1, 0, 0]) * a - unit_cell[1,:] = np.array([0, 1, 0]) * a - unit_cell[2,:] = np.array([0, 0, 1]) * a - elif ibrav == 2: - # Cubic fcc - a = celldm[0] * BOHR_TO_ANGSTROM - unit_cell[0,:] = np.array([-1, 0, 1]) * a / 2 - unit_cell[1,:] = np.array([0, 1, 1]) * a /2 - unit_cell[2,:] = np.array([-1, 1, 0]) * a/2 - elif ibrav == 3: - # Cubic bcc - a = celldm[0] * BOHR_TO_ANGSTROM - unit_cell[0,:] = np.array([1, 1, 1]) * a / 2 - unit_cell[1,:] = np.array([-1, 1, 1]) * a /2 - unit_cell[2,:] = np.array([-1, -1, 1]) * a/2 - elif ibrav == -3: - # Cubic bcc other kind - a = celldm[0] * BOHR_TO_ANGSTROM - unit_cell[0,:] = np.array([-1, 1, 1]) * a / 2 - unit_cell[1,:] = np.array([1, -1, 1]) * a /2 - unit_cell[2,:] = np.array([1, 1, -1]) * a/2 - elif ibrav == 4: - # Hexagonal - a = celldm[0] * BOHR_TO_ANGSTROM - c = celldm[2] * a - - unit_cell[0, :] = np.array([1, 0, 0]) * a - unit_cell[1, :] = np.array([-0.5, np.sqrt(3)/2, 0]) * a - unit_cell[2, :] = np.array([0, 0, 1]) * c - elif ibrav == 5: - a = celldm[0] * BOHR_TO_ANGSTROM - c = celldm[3] - - tx = np.sqrt( (1 - c) / 2.) - ty = np.sqrt( (1-c) / 6.) - tz = np.sqrt( (1+2*c)/3.) - - unit_cell[0, :] = np.array([tx, -ty, tz]) * a - unit_cell[1, :] = np.array([0, 2*ty, tz]) * a - unit_cell[2, :] = np.array([-tx, -ty, tz]) * a - - elif ibrav == 6: - a = celldm[0] * BOHR_TO_ANGSTROM - c = celldm[2] * a - - unit_cell[0, :] = a * np.array([1, 0, 0]) - unit_cell[1, :] = a * np.array([0, 1, 0]) - unit_cell[2, :] = c * np.array([0, 0, 1]) - - elif ibrav == 7: - # Tetragonal I - a = celldm[0] * BOHR_TO_ANGSTROM - c = celldm[2] * a - - unit_cell[0, :] = np.array([a*0.5, -a*0.5, c*0.5]) - unit_cell[1, :] = np.array([a*0.5, a*0.5, c*0.5]) - unit_cell[2, :] = np.array([-a*0.5, -a*0.5, c*0.5]) - - elif ibrav == 8: - # Orthorombic - a = celldm[0] * BOHR_TO_ANGSTROM - b = celldm[1] * a - c = celldm[2] * a - - unit_cell[0, :] = a * np.array([1,0,0]) - unit_cell[1, :] = b * np.array([0,1,0]) - unit_cell[2, :] = c * np.array([0,0,1]) - - elif ibrav == 9: - # Orthorombinc base centered - a = celldm[0] * BOHR_TO_ANGSTROM - b = celldm[1] * a - c = celldm[2] * a - - unit_cell[0, :] = np.array([a/2, b/2, 0]) - unit_cell[1, :] = np.array([-a/2, b/2, 0]) - unit_cell[2, :] = np.array([0, 0, c]) - elif ibrav == -9: - # Orthorombinc base centered (the same but change the first two vectors) - a = celldm[0] * BOHR_TO_ANGSTROM - b = celldm[1] * a - c = celldm[2] * a - - unit_cell[0, :] = np.array([-a/2, b/2, 0]) - unit_cell[1, :] = np.array([a/2, b/2, 0]) - unit_cell[2, :] = np.array([0, 0, c]) - elif ibrav == 91: - # Orthorhombic one-face base-centered A-type - # celldm(2)=b/a - # celldm(3)=c/a - # v1 = (a, 0, 0), v2 = (0,b/2,-c/2), v3 = (0,b/2,c/2) - a = celldm[0] * BOHR_TO_ANGSTROM - b = celldm[1] * a - c = celldm[2] * a - unit_cell[0,:] = np.array([a,0,0]) - unit_cell[1,:] = np.array([0, b/2, -c/2]) - unit_cell[2,:] = np.array([0, b/2, c/2]) - elif ibrav == 13: - # Monoclinic base-centered - - # Create cell - a = celldm[0] * BOHR_TO_ANGSTROM - b = a * celldm[1] - c = a * celldm[2] - cos_ab = celldm[3] - sin_ab = np.sqrt(1 - cos_ab**2) - - unit_cell[0,:] = np.array( [a/2., 0, -c/2.]) - unit_cell[1,:] = np.array( [b * cos_ab, b*sin_ab, 0]) - unit_cell[2,:] = np.array( [a/2., 0, c/2.]) - - elif ibrav == 14: - a = celldm[0] * BOHR_TO_ANGSTROM - b = a * celldm[1] - c = a * celldm[2] - cos_alpha = celldm[3] - cos_beta = celldm[4] - cos_gamma = celldm[5] - - sin_alpha = np.sqrt(1 - cos_alpha**2) - sin_beta = np.sqrt(1 - cos_beta**2) - sin_gamma = np.sqrt(1 - cos_gamma**2) - - unit_cell[0, :] = np.array([a, 0, 0], dtype = np.double) - unit_cell[1, :] = np.array([b * cos_gamma, b * sin_gamma, 0], dtype = np.double) - unit_cell[2, :] = np.array([c * cos_beta, c * (cos_alpha - cos_beta * cos_gamma) / sin_gamma, - c * np.sqrt( 1 + 2*cos_alpha*cos_beta*cos_gamma - cos_alpha**2 - cos_beta**2 - cos_gamma**2) / sin_gamma]) - - else: - raise ValueError("Error, the specified ibrav %d is not supported." % ibrav) - - return unit_cell - -def is_inside(index, indices): - """ - Returns whether idex is inside a couple of indices. - Usefull to check if something is inside or not something like - parenthesys or quotes - """ - if len(indices) == 0: - return False - - a = np.array(indices, dtype = int) - - new_a = (index > a).astype(int) - result = np.sum(new_a) - - if result % 2 == 0: - return False - return True - -def read_namelist(line_list): - """ - READ NAMELIST - ============= - - - This function will read the quantum espresso namelist format from a list of lines. - The info are returned in a dictionary: - - &control - type_cal = "wrong" - ecutrho = 140 - &end - - will be converted in a python dictionary - dict = {"control" : {"type_cal" : "wrong", "ecutrho" : 140}} - - Then the dictionary is returned. Comments must start as in fortran with the '!' - - NOTE: Fotran is not case sensitive, therefore all the keys are converted in lower case - - - - Parameters - ---------- - line_list : list or string (path) - A list of lines read in a file. They should be the row output of f.readlines() function - where f is a file obtained as f = open("something", "r"). You can also directly pass - a string to path - - Returns - ------- - dict : - The dictionary of the namelist - """ - - if isinstance(line_list, str): - if not os.path.exists(line_list): - raise IOError("Error, file %s not found." % line_list) - - # Read the file - fread = open(line_list, "r") - line_list = fread.readlines() - fread.close() - - - inside_namespace = False - current_namespace = "" - namespace = {} - total_dict = {} - - # Start reading - for line in line_list: - # Avoid case sensitivity turning everithing in lower case - #line = line.lower() - # Get thee string content and avoid parsing that - quotes_indices = [] - last_found = 0 - while True: - last_found = line.find('"', last_found + 1) - if last_found != -1: - quotes_indices.append(last_found) - else: - break - - - - - # Delete the line after the comment - # If it is not inside double quotes - if not is_inside(line.find("!"), quotes_indices): - # Delete the comment - line = line[:line.find("!")] - - # Clear the line of tailoring white spaces - line = line.strip() - - # Skip if the line is white - if len(line) == 0: - continue - - # Check if the line begins with an "&" sign - if line[0] == "&": - # Check if we are closing an existing namespace - if line[1:].lower() == "end": - if not inside_namespace: - raise IOError("Error, trying to close a namespace without having open it.") - - total_dict[current_namespace] = namespace.copy() - current_namespace = "" - inside_namespace = False - namespace.clear() - - continue - - # New namelist --- - - # Check if we already are inside a namespace - if inside_namespace: - raise IOError("Error, the namespace %s has not been closed." % current_namespace) - - current_namespace = line[1:].lower() - inside_namespace = True - - # Check if the namespace has a valid name - if len(current_namespace) == 0: - raise IOError("Error, non valid name for a namespace") - else: - # Check if the old namespace closure is used - if line[0] == "/": - if not inside_namespace: - raise IOError("Error, trying to close a namespace without having open it.") - - total_dict[current_namespace] = namespace.copy() - current_namespace = "" - inside_namespace = False - namespace.clear() - continue - - # First of all split for quotes - value = None - new_list_trial = line.split('"') - if len(new_list_trial) == 3: - value = '"' + new_list_trial[1] + '"' - else: - new_list_trial = line.split("'") - if len(new_list_trial) == 3: - value = '"' + new_list_trial[1] + '"' - - # Get the name of the variable - new_list = line.split("=") - - if len(new_list) != 2 and value is None: - raise IOError("Error, I do not understand the line %s" % line) - elif len(new_list) < 2: - raise IOError("Error, I do not understand the line %s" % line) - - variable = new_list[0].strip().lower() - if value is None: - value = new_list[1].strip() - - # Remove ending comma and otehr tailoring space - if value[-1] == ",": - value = value[:-1].strip() - - - # Convert fortran bool - if value.lower() == ".true.": - value = True - elif value.lower() == ".false.": - value = False - elif '"' == value[0]: # Get a string content - # If it is a string cancel the " or ' or , - value = value.replace("\"", "") - elif "'" == value[0]: - value = value.replace("'", "") - elif value.count(" ") >= 1: - value = [float(item) for item in value.split()] - else: - # Check if it is a number - try: - value = float(value.lower().replace("d", "e")) - except: - pass - if inside_namespace: - namespace[variable] = value - else: - total_dict[variable] = value - - # The file has been analyzed - if inside_namespace: - raise IOError("Error, file endend before %s was closed" % current_namespace) - - return total_dict - - - - - -def write_namelist(total_dict): - """ - WRITE ESPRESSO NAMELIST - ======================= - - Given a particular dictionary this subroutine will transform it into an espresso dictionary - - Parameters - ---------- - total_dict : dict - A dictionary of the namespaces in the namelist - - Results - ------- - list - A list of lines that can be written into a file - """ - - - lines = [] - keys = list(total_dict) - new_keys = [] - if "control" in keys: - new_keys.append("control") - keys.remove("control") - if "system" in keys: - new_keys.append("system") - keys.remove("system") - - for k in keys: - new_keys.append(k) - - for key in new_keys: - if type(total_dict[key]) == dict: - # Namelist - lines.append("&%s\n" % key) - for key2 in total_dict[key].keys(): - value = total_dict[key][key2] - valuestr = "" - if isinstance(value, list): - valuestr = " ".join(value) - elif isinstance(value, str): - valuestr = "\"%s\"" % value - elif isinstance(value, bool): - valuestr = ".{}.".format(str(value).lower()) - else: - valuestr = str(value) - - line = "\t%s = %s\n" % (key2, valuestr) - lines.append(line) - - lines.append("&end\n") - else: - value = total_dict[key] - valuestr = "" - if type(value) == list: - valuestr = " ".join(value) - else: - valuestr = str(value) - - line = "\t%s = %s\n" % (key, valuestr) - lines.append(line) - - - return lines - - - -def get_translations(pols, masses): - """ - GET TRANSLATIONS - ================ - - This subroutine analyzes the polarization vectors of a dynamical matrix to recognize the translations. - It is usefull to carefully remove the translations from the frequencies where w -> 0 gives an error in an equation. - - Parmaeters - ---------- - pols : ndarray 2 rank - The polarization vectors as they came out from DyagDinQ(0) method from Phonons. - masses : ndarray (size nat) - The mass of each atom. - - Returns - ------- - is_translation_mask : ndarray(3 * N_atoms) - A bool array of True if the i-th polarization vectors correspond to a translation, false otherwise. - - - Example - ------- - - In this example starting from the frequencies, the translations are removed (let dyn to be Phonons()): - - >>> w, pols = dyn.DyagDinQ(0) - >>> t_mask = get_translations(pols) - >>> w_without_trans = w[ ~t_mask ] - - The same, of course, can be applied to polarization vectors: - - >>> pols = pols[ :, ~t_mask ] - - The ~ caracter is used to get the bit not operation over the t_mask array (to mark False the translational modes and True all the others) - """ - n_atoms = len(pols[:, 0]) // 3 - n_pols = len(pols[0,:]) - - # Check if the masses array is good - if len(masses) * 3 != np.size(pols[:,0]): - raise ValueError("Error, the size of the two array masses and pols are not compatible.") - - # Prepare a mask filled with false - is_translation = np.zeros( n_pols, dtype = bool) - - # Cast to bool - is_translation[:] = symph.get_translations(pols, masses, n_pols, n_atoms) - - return is_translation - - -def _get_translations(pols, masses): - """ - OLD slow implemetation of get_translations - """ - - # Check if the masses array is good - if len(masses) * 3 != np.size(pols[:,0]): - raise ValueError("Error, the size of the two array masses and pols are not compatible.") - - - # Get the number of atoms and the number of polarization vectors - n_atoms = len(pols[:, 0]) // 3 - n_pols = len(pols[0,:]) - - # Prepare a mask filled with false - is_translation = np.zeros( n_pols).astype (bool) - - for i in range(n_pols): - # Check if the polarization vector is oriented in the same way for each atom - thr_val = 0 - for j in range(n_atoms): - thr_val += np.sum( np.abs(pols[3 * j : 3 * j + 3, i]/np.sqrt(masses[j]) - - pols[:3, i] / np.sqrt(masses[0]))**2) - - thr_val = np.sqrt(thr_val) - - if thr_val < __EPSILON__ : - is_translation[i] = True - - return is_translation - - - -def convert_matrix_cart_cryst(matrix, unit_cell, cryst_to_cart = False): - """ - This methods convert the 3x3 matrix into crystalline coordinates using the metric tensor defined by the unit_cell. - - This method is a specular implementation of Quantum ESPRESSO. - This subroutine transforms matrices obtained as open product between vectors. - If you want to transform a linear operator, then use - convert_matrix_cart_cryst2 - - For example, use this to transform a dynamical matrix, - use convert_matrix_cart_cryst2 to transform a symmetry operation. - - .. math:: - - g_{\\alpha\\beta} = \\left< \\vec v_\\alpha | \\vec v_\\beta\\right> - - F_{\\alpha\\beta} = g_{\\alpha\\gamma} g_{\\beta\\delta} F^{\\gamma\\delta} - - F^{\\alpha\\beta} = g^{\\alpha\\gamma} g^{\\beta\\delta} F_{\\gamma\\delta} - - F_{\\alpha\\beta} = \\frac{\\partial E}{\\partial u^\\alpha \\partial u^\\beta} - - F^{\\alpha\\beta} = \\frac{\\partial E}{\\partial u_\\alpha \\partial u_\\beta} - - - Parameters - ---------- - matrix : ndarray 3x3 - The matrix to be converted - unit_cell : ndarray 3x3 - The cell containing the vectors defining the metric (the change between crystalline and cartesian coordinates) - cryst_to_cart : bool, optional - If False (default) the matrix is assumed in cartesian coordinates and converted to crystalline. If True - otherwise. - - Results - ------- - new_matrix : ndarray(3x3) - The converted matrix into the desidered format - """ - - - # Get the metric tensor from the unit_cell - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - # Choose which conversion perform - comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) - comp_matrix_inv = np.linalg.inv(comp_matrix) - - if cryst_to_cart: - return comp_matrix.T.dot( np.dot(matrix, comp_matrix)) - - return comp_matrix_inv.T.dot( np.dot(matrix, comp_matrix_inv)) - -def convert_matrix_cart_cryst2(matrix, unit_cell, cryst_to_cart = False): - """ - This methods convert the 3x3 matrix into crystalline coordinates using the metric tensor defined by the unit_cell. - - This perform the exact transform. - With this method you get a matrix that performs the transformation directly in the other space. - If I have a matrix that transforms vectors in crystalline coordinates, then with this I get the same operator between - vectors in cartesian space. - - This subroutine transforms operators, while the previous one transforms matrices (obtained as open product between vectors) - - For example, use convert_matrix_cart_cryst to transform a dynamical matrix, - use convert_matrix_cart_cryst2 to transform a symmetry operation. - - - Parameters - ---------- - matrix : ndarray 3x3 - The matrix to be converted - unit_cell : ndarray 3x3 - The cell containing the vectors defining the metric (the change between crystalline and cartesian coordinates) - cryst_to_cart : bool, optional - If False (default) the matrix is assumed in cartesian coordinates and converted to crystalline. If True - otherwise. - - Results - ------- - new_matrix : ndarray(3x3) - The converted matrix into the desidered format - """ - - - # Get the metric tensor from the unit_cell - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - # Choose which conversion perform - comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) - comp_matrix_inv = np.linalg.inv(comp_matrix) - - if cryst_to_cart: - return comp_matrix_inv.dot( np.dot(matrix, comp_matrix)) - - return comp_matrix.dot( np.dot(matrix, comp_matrix_inv)) - - -def convert_3tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): - """ - Convert to crystal coordinates - ============================== - - This subroutine converts the 3 rank tensor to crystal coordinates - and vice versa. - - Paramters - --------- - tensor : ndarray( size = (3,3,3)) - The 3rank tensor to be converted - unit_cell : ndarray - The unit cell of the structure - cryst_to_cart : bool - If true, reverse convert crystal to cartesian. - """ - - # Get the metric tensor from the unit_cell - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) - if not cryst_to_cart: - comp_matrix = np.linalg.inv(comp_matrix) - - return np.einsum("ia, jb, kc, ijk -> abc", comp_matrix, comp_matrix, comp_matrix, tensor) - - -def convert_4tensor_to_cryst(tensor, unit_cell, cryst_to_cart = False): - """ - Convert to crystal coordinates - ============================== - - This subroutine converts the 4 rank tensor to crystal coordinates and vice versa. - - Paramters - --------- - tensor : ndarray( size = (3,3,3,3)) - The 4rank tensor to be converted - unit_cell : ndarray - The unit cell of the structure - cryst_to_cart : bool - If true, reverse convert crystal to cartesian. - """ - - # Get the metric tensor from the unit_cell - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = unit_cell[i,:].dot(unit_cell[j, :]) - - comp_matrix = np.einsum("ij, jk", np.linalg.inv(metric_tensor), unit_cell) - if not cryst_to_cart: - comp_matrix = np.linalg.inv(comp_matrix) - - return np.einsum("ia, jb, kc, ld, ijkl -> abcd", comp_matrix, comp_matrix, comp_matrix, comp_matrix, tensor) - - - -def convert_fc(fc_matrix, unit_cell, cryst_to_cart = False): - """ - This method converts the force constant matrix from cartesian to crystal and the opposite. - Check the method convert_matrix_cart_cryst to see more details. - - Parameters - ---------- - fc_matrix : ndarray (3 nat x 3 nat) - The original force constant matrix - unit_cell : ndarray (3x3) - The unit cell of the system - cryst_to_cart : bool, optional, default False - If true convert from crystal to cartesian, the opposite otherwise. - - Results - ------- - new_fc_matrix : ndarray (shape(fc_matrix)) - The new force constant matrix after the conversion. - """ - - # Check if the fc_matrix is a good candidate - if np.shape(fc_matrix)[0] != np.shape(fc_matrix)[1]: - raise ValueError("Error, the force constant matrix must be a square array") - - if len(np.shape(fc_matrix)) != 2: - raise ValueError("Error, the fc_matrix must be a matrix.") - - n_indices = np.shape(fc_matrix)[0] - - if n_indices %3 != 0: - raise ValueError("Error, the size of the force constant matrix must be a multiple of 3") - - # Get the number of atoms - nat = n_indices / 3 - - # Prepare the output matrix - new_fc_matrix = np.zeros(np.shape(fc_matrix)) - for na in range(nat): - for nb in range(na, nat): - # Convert the single matrix - in_mat = fc_matrix[3 * na : 3*(na+1), 3*nb : 3*(nb+1)] - out_mat = convert_matrix_cart_cryst(in_mat, unit_cell, cryst_to_cart) - new_fc_matrix[3 * na : 3*(na+1), 3*nb : 3*(nb+1)] = out_mat - - # Apply hermitianity - if na != nb: - new_fc_matrix[3 * nb : 3*(nb+1), 3*na : 3*(na+1)] = np.conjugate(out_mat.transpose()) - - return new_fc_matrix - - -def get_directed_nn(structure, atom_id, direction): - """ - This function get the closest neighbour along a direction of the atom. - It returns the index of the atom in the structure that is the closest to atom_id in the - specified direction (a vector identifying the direction). - - The algorithm selects a cone around the atom oriented to the direction, and selects - the first atom in the structure inside the cone (the one with lowest projection on the cone axis) - - The method returns -1 if no near neighbour along the specified direction is detected - """ - - nat = structure.N_atoms - new_coords = structure.coords - structure.coords[atom_id, :] - - - - # Center the atoms if the unit cell is present. - if structure.has_unit_cell: - # Put the atom at the center of the cell - new_coords[:,:] += 0.5 * np.sum(structure.unit_cell, axis = 0) - for i in range(nat): - new_coords[i, :] = put_into_cell(structure.unit_cell, new_coords[i, :]) - - # Shift back - new_coords -= new_coords[atom_id, :] - - - - - # Pop the atoms not in the cone - versor_cone = direction / np.sqrt(direction.dot(direction)) - - # Project the coordinate on the versor - p_cone = new_coords.dot(versor_cone) - #print p_cone - - - # Now discard atoms not inside the cone - good_atoms = [] - good_d = [] - for i in range(nat): - if p_cone[i] > 0: - r_vect = new_coords[i, :] - p_cone[i] * versor_cone - r = np.sqrt(r_vect.dot(r_vect)) - #print "%d) p = %.4e, r = %.4e, r_vect = " % (i, p_cone[i], r), r_vect - if r < p_cone[i]: - good_atoms.append(i) - good_d.append(r**2 + p_cone[i]**2) - - #print good_atoms - - if len(good_atoms) == 0: - return -1 - - # Get the minimum value of p_cone among the good atoms - i_best = np.argmin(good_d) - - return good_atoms[i_best] - -def get_closest_vector(unit_cell, v_dist): - """ - This subroutine computes the periodic replica of v_dist - that minimizes its modulus. - - Parameters - ---------- - - unit_cell : ndarray(size = (3,3)) - The unit cell vector, unit_cell[i,:] is the i-th cell vector - - v_dist : ndarray(size = 3) - The distance vector that you want to optimize - - Returns - ------- - - new_v_dist: ndarray(size = 3) - The replica of v_dist that has the minimum modulus - """ - - # Define the metric tensor - g = unit_cell.dot(unit_cell.T) - alphas = covariant_coordinates(unit_cell, v_dist) - - # Define the minimum function - def min_f(n): - tmp = g.dot(2 * alphas + n) - return n.dot(tmp) - - # Get the starting guess for the vector - n_start = -np.floor(alphas + .5) - n_min = n_start.copy() - tot_min = min_f(n_min) - - # Get the supercell shift that minimizes the vector - for n_x in [-1,0,1]: - for n_y in [-1,0,1]: - for n_z in [-1,0,1]: - n_v = n_start + np.array([n_x, n_y, n_z]) - new_min = min_f(n_v) - if new_min < tot_min: - tot_min = new_min - n_min = n_v - - # Get the new vector - new_v = v_dist + n_min.dot(unit_cell) - return new_v - -def three_to_one_len(v,v_min,v_len): - """ - This subroutine converts a triplet index v, - of length v_len and starting from v_min, - into a single progressive index starting from 0 - """ - res=(v[0]-v_min[0])*v_len[1]*v_len[2]+(v[1]-v_min[1])*v_len[2]+(v[2]-v_min[2]) - return int(res) - -def three_to_one(v,v_min,v_max): - """ - This subroutine converts a triplet index v, - going from v_min to v_max, into a single progressive - index starting from 0 - """ - v_len=np.array(v_max)-np.array(v_min)+np.ones(3,dtype=int) - res=(v[0]-v_min[0])*v_len[1]*v_len[2]+(v[1]-v_min[1])*v_len[2]+(v[2]-v_min[2]) - return int(res) - -def one_to_three_len(J,v_min,v_len): - """ - This subroutine converts a single progressive - index J starting from 0, to a triplet of progressive - indexes starting from v_min, of length v_len - """ - x = J // (v_len[2] * v_len[1]) + v_min[0] - y = ( J % (v_len[2] * v_len[1])) // v_len[2] + v_min[1] - z = J % v_len[2] + v_min[2] - return np.array([x,y,z],dtype=int) - -def one_to_three(J,v_min,v_max): - """ - This subroutine coonverts a single progressive - index J starting from 0, to a triplet of progressive - indexes going from v_min to v_max - """ - v_len=np.array(v_max)-np.array(v_min)+np.ones(3,dtype=int) - x = J // (v_len[2] * v_len[1]) + v_min[0] - y = ( J % (v_len[2] * v_len[1])) // v_len[2] + v_min[1] - z = J % v_len[2] + v_min[2] - return np.array([x,y,z],dtype=int) - - - -def is_gamma(unit_cell, q): - """ - Defines if the q point in cartesian (A^-1) is gamma or not - given the unit cell - - Parameters - ---------- - unit_cell : ndarray (size =3,3) - The unit cell of the structure - q : ndarray(size=3) - The q point that you want to check - - Results - ------- - is_gamma : bool - """ - - bg = get_reciprocal_vectors(unit_cell) - new_q = get_closest_vector(bg, q) - - return (np.abs(new_q) < 1e-6).all() - - -def save_qe(dyn,q,dynq,freqs, pol_vects,fname): - """ - SAVE THE DYNMAT - =============== - - This subroutine saves the dynamical matrix in the quantum espresso file format. - The dynmat is the force constant matrix in Ry units. - - .. math:: - - \\Phi_{ab} = \\sum_\\mu \\omega_\\mu^2 e_\\mu^a e_\\mu^b \\sqrt{M_a M_b} - - Where :math:`\\Phi_{ab}` is the force constant matrix between the a-b atoms (also cartesian - indices), :math:`\\omega_\\mu` is the phonon frequency and :math:`e_\\mu` is the - polarization vector. - """ - - A_TO_BOHR = 1.889725989 - RyToCm=109737.37595 - RyToTHz=3289.84377 - - - # Open the file - fp = open(fname, "w") - fp.write("Dynamical matrix file\n") - - # Get the different number of types - types = [] - n_atoms = dyn.structure.N_atoms - for i in range(n_atoms): - if not dyn.structure.atoms[i] in types: - types.append(dyn.structure.atoms[i]) - n_types = len(types) - - # Assign an integer for each atomic species - itau = {} - for i in range(n_types): - itau[types[i]] = i +1 - - # Write the comment line - fp.write("File generated with CellConstructor\n") - fp.write("%d %d %d %22.16f %22.16f %22.16f %22.16f %22.16f %22.16f\n" % - (n_types, n_atoms, 0, dyn.alat * A_TO_BOHR, 0, 0, 0, 0, 0) ) - - # Write the basis vector - fp.write("Basis vectors\n") - # Get the unit cell - for i in range(3): - fp.write(" ".join("%22.16f" % x for x in dyn.structure.unit_cell[i,:] / dyn.alat) + "\n") - - # Set the atom types and masses - for i in range(n_types): - fp.write("\t{:d} '{:<3s}' {:>24.16f}\n".format(i +1, types[i], dyn.structure.masses[types[i]])) - - # Setup the atomic structure - for i in range(n_atoms): - # Convert the coordinates in alat - coords = dyn.structure.coords[i,:] / dyn.alat - fp.write("%5d %5d %22.16f %22.16f %22.16f\n" % - (i +1, itau[dyn.structure.atoms[i]], - coords[0], coords[1], coords[2])) - - # Here the dynamical matrix starts - fp.write("\n") - fp.write(" Dynamical Matrix in cartesian axes\n") - fp.write("\n") - fp.write(" q = ( {:11.9f} {:11.9f} {:11.9f} )\n".format(q[0] * dyn.alat , - q[1] * dyn.alat, q[2] * dyn.alat )) - fp.write("\n") - - # Now print the dynamical matrix - for i in range(n_atoms): - for j in range(n_atoms): - # Write the atoms - fp.write("%5d%5d\n" % (i + 1, j + 1)) - for x in range(3): - line = "%23.16f%23.16f %23.16f%23.16f %23.16f%23.16f" % \ - ( np.real(dynq[3*i + x, 3*j]), - np.imag(dynq[3*i + x, 3*j]), - np.real(dynq[3*i + x, 3*j+1]), - np.imag(dynq[3*i+x, 3*j+1]), - np.real(dynq[3*i + x, 3*j+2]), - np.imag(dynq[3*i+x, 3*j+2]) ) - - fp.write(line + "\n") - - # Print the diagnoalization of the matrix - fp.write("\n") - fp.write(" Diagonalizing the dynamical matrix\n") - fp.write("\n") - fp.write(" q = ( {:11.9f} {:11.9f} {:11.9f} )\n".format(q[0] *dyn.alat , - q[1] *dyn.alat, q[2] *dyn.alat)) - fp.write("\n") - fp.write("*" * 75 + "\n") - - nmodes = len(freqs) - for mu in range(nmodes): - # Print the frequency - fp.write("%7s (%5d) = %14.8f [THz] = %14.8f [cm-1]\n" % - ("freq", mu+1, freqs[mu] * RyToTHz, freqs[mu] * RyToCm)) - - # Print the polarization vectors - for i in range(n_atoms): - fp.write("( %10.6f%10.6f %10.6f%10.6f %10.6f%10.6f )\n" % - (np.real(pol_vects[3*i, mu]), np.imag(pol_vects[3*i,mu]), - np.real(pol_vects[3*i+1, mu]), np.imag(pol_vects[3*i+1,mu]), - np.real(pol_vects[3*i+2, mu]), np.imag(pol_vects[3*i+1,mu]))) - fp.write("*" * 75 + "\n") - fp.close() - -def transform_voigt(tensor, voigt_to_mat = False): - """ - Transforms the voigt notation. - If voit_to_mat is True, the tensor is assumed to be in voigt format. - Otherwise it assumed as a 3x3 symmetric matrix (upper triangle will be read). - """ - - if voigt_to_mat: - assert len(tensor) == 6 - new_tensor = np.zeros((3,3), dtype = type(tensor[0])) - for i in range(3): - new_tensor[i,i] = tensor[i] - - new_tensor[1,2] = new_tensor[2,1] = tensor[3] - new_tensor[0,2] = new_tensor[2,0] = tensor[4] - new_tensor[1,0] = new_tensor[0,1] = tensor[5] - else: - assert tensor.shape == (3,3) - new_tensor = np.zeros(6, dtype = type(tensor[0,0])) - - for i in range(3): - new_tensor[i] = tensor[i,i] - new_tensor[3] = tensor[1,2] - new_tensor[4] = tensor[0,2] - new_tensor[5] = tensor[0,1] - - return new_tensor - - -def get_bandpath(unit_cell, path_string, special_points, n_points = 1000): - """ - GET THE BANDPATH - ================ - - Given the structure, get the kpoints in cartesian coordinates that reproduce the bandpath. - - This method is usefull to plot the phonon dispersion. - - Parameters - ---------- - unit_cell :: ndarray(size = (3,3)) - The primitive cell on which to simulate the bandpath - path_string :: str - The string of the path (for example GXWKG) - special_points : dict - A dictionary containing all the special points in the path and the respective coordinates in crystalline axis (relative to the reciprocal vectors). - n_points : int - The total number of points in which the path is divided. - - - Results - ------- - qpath : ndarray(sizeof=(n_points, 3)) - The q path in cartesian coordinates - (xaxis, xticks, xlabels) : - The xaxis that represent the lenght of the qpath from the first point. - xlabels is the labels of each ticks and xticks - - """ - - # Get the reciprocal lattice - bg = get_reciprocal_vectors(unit_cell) - - - new_special_points = {x : np.array(special_points[x], dtype = np.double).dot(bg) for x in special_points} - print(new_special_points) - - if len(path_string) < 2: - raise ValueError("Error, at least 2 q points needs to be processed") - - path_points = np.zeros((len(path_string), 3), dtype = np.double) - for i, c in enumerate(path_string): - path_points[i, :] = new_special_points[c] - - #print('BG:', bg * 2 * np.pi) - #print('UC:', unit_cell) - #print('SPECIAL POINTS:', {x : new_special_points[x] * 2 * np.pi for x in new_special_points}) - - single_lenghts = np.linalg.norm(np.diff(path_points, axis = 0), axis = 1) - total_lenght = np.sum(single_lenghts) - - xaxis = np.linspace(0, total_lenght, n_points) - xticks = np.zeros(len(path_string)) - for i, ll in enumerate(single_lenghts): - xticks[i+1] = xticks[i] + ll - - xlabels = [x.replace('G', r'$\Gamma$') for x in path_string] - - - q_path = np.zeros((n_points, 3), dtype = np.double) - q_path[-1, :] = path_points[-1,:] # Set the starting point in the path - dq = total_lenght / n_points - counter = 0 - visited = [] - for i in range(1, n_points): - - # Identify in which line it is - xval = xaxis[i] - index = 0 - while xval >= single_lenghts[index] + __EPSILON__: - - print(xval, index, single_lenghts) - xval -= single_lenghts[index] - index += 1 - - - # If the line is changed, add a counter - if not index in visited: - visited.append(index) - counter = 0 - else: - counter += 1 - - q_versor = (path_points[index+1,:] - path_points[index,:]) / single_lenghts[index] - - q_path[i-1, :] = path_points[index, :] + counter * dq * q_versor - - return q_path, (xaxis, xticks, xlabels) diff --git a/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py deleted file mode 100644 index ba3aa243..00000000 --- a/cellconstructor/.ipynb_checkpoints/Moro_object-checkpoint.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# untitled.py -# -# Copyright 2022 Diego Martinez Gutierrez -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA. -# -# -# --------------------------- -# Importación de los módulos -# --------------------------- -import numpy as np -from scipy.stats import qmc -import matplotlib.pyplot as plt -import math -# ------- -# Clases -# ------- -class Moro(object): - def __init__(self): - self.a0 = 2.50662823884 - self.a1 = -18.61500062529 - self.a2 = 41.39119773534 - self.a3 = -25.44106049637 - self.b0 = -8.47351093090 - self.b1 = 23.08336743743 - self.b2 = -21.06224101826 - self.b3 = 3.13082909833 - self.c0 = 0.3374754822726147 - self.c1 = 0.9761690190917186 - self.c2 = 0.1607979714918209 - self.c3 = 0.0276438810333863 - self.c4 = 0.0038405729373609 - self.c5 = 0.0003951896511919 - self.c6 = 0.0000321767881768 - self.c7 = 0.0000002888167364 - self.c8 = 0.0000003960315187 - - def gauss(self,u): - y = u - 0.5 - if (abs(y) < 0.42): - r = y * y - #x = y * (((self.a3*r+self.a2)*r+self.a1)*r+self.a0)/((((self.b3*r+self.b2)*r+self.b1)*r+self.b0)*r+1) - x = y * np.polyval([self.a3,self.a2,self.a1,self.a0],r)/np.polyval([self.b3,self.b2,self.b1,self.b0,1],r) - else: - r = u - if (y > 0): - r = 1-u - r = np.log(-np.log(r)) - #x = self.c0+r*(self.c1+r*(self.c2+r*(self.c3+r*(self.c4+r*(self.c5+r*(self.c6+r*(self.c7+r*self.c8)))))) - x = np.polyval([self.c8,self.c7,self.c6,self.c5,self.c4,self.c3,self.c2,self.c1,self.c0],r) - if (y < 0): - x = -x - return x - - def normalize(self,u): - x = np.zeros(len(u)) - for i in range(len(u)): - x[i] = self.gauss(u[i]) - return x - - def sobol(self,size,n_modes): - sampler = qmc.Sobol(d=1, scramble=False) - size_sobol = int(math.ceil(np.log(size)/np.log(2))) - print ('size=',size,'size_sobol=',size_sobol,'ss=',2**size_sobol) - # x = [] - # for i in range(n_modes): - # x.append(data) - sample0 = sampler.random_base2(m=size_sobol) - sample = sampler.random_base2(m=size_sobol) -# print ('sample=',sample) -# plt.hist(sample, bins=int(size/2)) -# plt.show() -# plt.scatter(sample,range(len(sample))) -# plt.show() -# data = self.normalize((sample+0.01)%1) - data = self.normalize(sample) -# print ('data=',data) -# plt.hist(data, bins=int(size/2)) -# plt.show() -# plt.scatter(data,range(len(data))) -# plt.show() - m = len(data)-size #****Diegom_test**** cut extra points (may work?) - data1 = data[m:] - x = np.resize(data1,(n_modes,size)) - print ('exit data:') - print (x) - return x - - def sobol_modes(self,size,n_modes,scramble): - sampler = qmc.Sobol(d=n_modes, scramble=scramble) - size_sobol = int(math.ceil(np.log(size)/np.log(2))) - sample0 = sampler.random_base2(m=size_sobol) - sample = sampler.random_base2(m=size_sobol) - print ('size=',size,'size_sobol=',size_sobol,'ss=',2**size_sobol) - #print (sample) - data = np.zeros(shape=(size,n_modes)) - for i in range(size): - for j in range(n_modes): - data[i][j] = self.gauss(sample[i][j]) - m = len(data)-size #****Diegom_test**** cut extra points (may work?) - x = data[m:] - #print ('exit data:') - #print (x) - - return x - - def sobol_big(self,size,n_modes,scramble): # in case the number of vibrational modes excedes the max setting of the generator. - if (n_modes>21201): - number = n_modes-21201 - #if (number>21201): #Do it recursive??? - x1 = self.sobol_modes(size = size, n_modes = 21201 , scramble = scramble) - x2 = self.sobol_big(size = size, n_modes = number, scramble = scramble) - x = x1.append(x2) - else: - x = self.sobol_modes(size = size, n_modes = n_modes , scramble = scramble) - return x - -# ---------- -# Funciones -# ---------- - -def main(args): - size = 32 - n_modes = 3 - Sobol = Moro() - # data = Sobol.sobol(size,n_modes) - # for i in (range(n_modes)): - # plt.hist(data[i], bins=20)#int(size/2)) - # plt.show() - # plt.scatter(data[i],range(len(data[i]))) - # plt.show() - - data = Sobol.sobol_modes(size,n_modes) - for i in (range(n_modes)): - plt.hist(data.T[i], bins=20)#int(size/2)) - plt.show() - plt.scatter(data.T[i],range(len(data.T[i]))) - plt.show() - - return 0 - -if __name__ == '__main__': - import sys - sys.exit(main(sys.argv)) diff --git a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py deleted file mode 100644 index 308136fb..00000000 --- a/cellconstructor/.ipynb_checkpoints/Phonons-checkpoint.py +++ /dev/null @@ -1,4522 +0,0 @@ - -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -""" -Created on Wed Jun 6 10:29:32 2018 -@author: pione -""" - -from __future__ import print_function -from __future__ import division -from cellconstructor import Settings - -import numpy as np -import os, sys -import scipy, scipy.optimize -from scipy.stats import qmc - -import itertools, math -import cellconstructor.Structure as Structure -import cellconstructor.symmetries as symmetries -import cellconstructor.ForceTensor as ForceTensor -import cellconstructor.Methods as Methods -from cellconstructor.Units import * - -import cellconstructor.calculators as calculators -from cellconstructor.Moro_object import Moro - -import warnings - -# Import the Fortran Code -import symph - -import time - - -try: - from mpi4py import MPI - __MPI__ = True -except: - __MPI__ = False - -try: - import spglib - __SPGLIB__ = True -except: - __SPGLIB__ = False - -__EPSILON__ = 1e-5 -__EPSILON_W__ = 3e-9 - -class Phonons: - """ - Phonons - ================ - - - This class contains the phonon of a given structure. - It can be used to show and display dinamical matrices, as well as for operating - with them - """ - def __init__(self, structure = None, nqirr = 1, full_name = False, use_format = False, force_real = False): - """ - INITIALIZE PHONONS - ================== - - The dynamical matrix for a given structure. - - Parameters - ---------- - - structure : type(Structure) or type(string) - This is the atomic structure for which you want to use the phonon calculation. - It is needed to correctly initialize all the arrays. - It can be both the Structure, or a filepath containing a quantum ESPRESSO - dynamical matrix. Up to now only ibrav0 dymat are supported. - - nqirr : type(int) , default 1 - The number of irreducible q point of the supercell on which you want - to compute the phonons. - Use 1 if you want to perform a Gamma point calculation. - - full_name : bool - If full_name is True, then the structure is loaded without appending the - q point index. This is compatible only with nqirr = 1. - - force_real : bool - If True, force the dynamical matrix allocated to be real. This is usefull to spare memory when - generating a dynamical matrix at Gamma in real space, that is real by construction. - - Results - ------- - - Phonons : this - It returns the Phonon class initializated. - """ - - # Initialize standard variables - self.dynmats = [] - self.nqirr = nqirr - # Q tot contains the total q points (also those belonging to the same star) - self.q_tot = [] - - # Prepare additional information that can be loaded - self.dielectric_tensor = None # (3x3 matrix) - self.effective_charges = None # 3-rank (Natoms, pol electric field, atomic coords) = (nat, 3, 3) - self.raman_tensor = None # 3-rank (incoming field, outcoming field, atomic coords) = (3,3, 3*nat) - - # This alat is read just from QE, but not used - self.alat = 1 - - # If this is true then the dynmat can be used - self.initialized = False - - # This contains all the q points in the stars of the irreducible q point - self.q_stars = [] - self.structure = None - - dtype = np.complex128 - if force_real: - dtype = np.float64 - - # Check whether the structure argument is a path or a Structure - if (type(structure) == type("hello there!")): - # Quantum espresso - self.LoadFromQE(structure, nqirr, full_name = full_name, use_format = use_format) - elif (type(structure) == type(Structure.Structure())): - # Get the structure - self.structure = structure - - if structure.N_atoms <= 0: - raise ValueError("Error, the given structure cannot be empty.") - - # Check that nqirr has a valid value - if nqirr <= 0: - raise ValueError("Error, nqirr argument must be a strictly positive number.") - - self.dynmats = [] - for i in range(nqirr): - # Create a dynamical matrix - self.dynmats.append(np.zeros((3 * structure.N_atoms, 3*structure.N_atoms), dtype = dtype)) - - # Initialize the q vectors - self.q_stars.append([np.zeros(3, dtype = np.float64)]) - self.q_tot.append(np.zeros(3, dtype = np.float64)) - - - def LoadFromQE(self, fildyn_prefix, nqirr=1, full_name = False, use_format= False, is_fulltext = False): - r""" - This Function loads the phonons information from the quantum espresso dynamical matrix. - the fildyn prefix is the prefix of the QE dynamical matrix, that must be followed by numbers from 1 to nqirr. - All the dynamical matrices are loaded. - - - Parameters - ---------- - - fildyn_prefix : type(string) - Quantum ESPRESSO dynmat prefix (the files are followed by the q irreducible index) - - nqirr : type(int), default 1 - Number of irreducible q points in the space group (supercell phonons). - If 0 or negative an exception is raised. - - full_name : bool, optional - If it is True, then the dynamical matrix is loaded without appending the q index. - This is compatible only with gamma point matrices. - - use_format : bool - If true, the IQ index of the dynamical matrix is replaced in the specified format, i.e. - a standard matrix with prefix dyn (dyn1, dyn2, ...) will be dyn{} with the format notation. - This allows the user to insert the IQ index in many formats and any position of the file name. - - is_fulltext : bool - If true (default false), the fildyn_prefix is ment to be the full text of the dynamical matrix - instead to the path of the file. - """ - - # Check if the nqirr is correct - if nqirr <= 0: - raise ValueError("Error, the specified nqirr is not valid: it must be positive!") - - if full_name and nqirr > 1: - raise ValueError("Error, with full_name only gamma matrices are loaded.") - - if is_fulltext and nqirr > 1: - raise ValueError("Error, with is_fulltext only gamma single dynamical matrices can be loaded.") - - # Initialize the atomic structure - self.structure = Structure.Structure() - - # Start processing the dynamical matrices - for iq in range(nqirr): - # Check if the selected matrix exists - if not is_fulltext: - if use_format: - filepath = fildyn_prefix.format(iq+1) - else: - if not full_name: - filepath = "%s%i" % (fildyn_prefix, iq + 1) - else: - filepath = fildyn_prefix - - if not os.path.isfile(filepath): - raise ValueError("Error, file %s does not exist." % filepath) - - # Load the matrix as a regular file - dynfile = open(filepath, "r") - dynlines = [line.strip() for line in dynfile.readlines()] - dynfile.close() - else: - dynlines = [x.strip() for x in fildyn_prefix.split("\n")] - - if (iq == 0): - # This is a gamma point file, generate the structure - # Go to the third line - struct_info = dynlines[2].split() - - # Check if the ibrav is 0 - ibrav = int(struct_info[2]) - celldm = np.zeros(6) - celldm[0] = float(struct_info[3]) - celldm[1] = float(struct_info[4]) - celldm[2] = float(struct_info[5]) - celldm[3] = float(struct_info[6]) - celldm[4] = float(struct_info[7]) - celldm[5] = float(struct_info[8]) - -# if ibrav != 0: -# raise ValueError("Error, only ibrav 0 supported up to now") - - nat = int(struct_info[1]) - ntyp = int(struct_info[0]) - self.alat = np.float64(struct_info[3]) * BOHR_TO_ANGSTROM # We want a structure in angstrom - - # Allocate the coordinates - self.structure.N_atoms = nat - self.structure.coords = np.zeros((nat, 3)) - - # Read the unit cell - unit_cell = np.zeros((3,3)) - if ibrav == 0: - for i in range(3): - unit_cell[i, :] = np.array([np.float64(item) for item in dynlines[4 + i].split()]) * self.alat - else: - unit_cell = Methods.get_unit_cell_from_ibrav(ibrav, celldm) - # Insert 4 lines to match the same number of lines as in ibrav = 0 - dynlines.insert(3, "") - dynlines.insert(3, "") - dynlines.insert(3, "") - dynlines.insert(3, "") - - # Read the atomic type - atoms_dict = {} - masses_dict = {} - for atom_index in range(1, ntyp + 1): - atm_line = dynlines[6 + atom_index] - atoms_dict[atom_index] = atm_line.split("'")[1].strip() - - # Get also the atomic mass - masses_dict[atoms_dict[atom_index]] = np.float64(atm_line.split("'")[-1].strip()) - - self.structure.set_masses(masses_dict) - - - self.structure.unit_cell = unit_cell - self.structure.has_unit_cell = True - - # Read the atoms - for i in range(nat): - # Jump the lines up to the structure - line_index = 7 + ntyp + i - atom_info = np.array([np.float64(item) for item in dynlines[line_index].split()]) - self.structure.atoms.append(atoms_dict[int(atom_info[1])]) - self.structure.coords[i, :] = atom_info[2:] * self.alat - - - # From now start reading the dynamical matrix ----------------------- - reading_dyn = True - q_star = [] - - # Pop the beginning of the matrix - while reading_dyn: - # Pop the file until you reach the dynamical matrix - if "cartesian axes" in dynlines[0]: - reading_dyn = False - dynlines.pop(0) - - # Get the small q point - reading_dyn = True - index = -1 - current_dyn = np.zeros((3*self.structure.N_atoms, 3*self.structure.N_atoms), dtype = np.complex128) - - # The atom indices - atm_i = 0 - atm_j = 0 - coordline = 0 - - dielectric_read = 0 - pol_read = 0 - - # Info about what I'm reading - reading_dielectric = False - reading_eff_charges = False - reading_raman = False - - while reading_dyn: - # Advance in the reading - index += 1 - - if index >= len(dynlines): - reading_dyn = False - self.dynmats.append(current_dyn.copy()) - continue - - # Setup what I'm reading - if "Diagonalizing" in dynlines[index]: - reading_dyn = False - self.dynmats.append(current_dyn.copy()) - - continue - if "Dielectric" in dynlines[index]: - reading_dielectric = True - reading_eff_charges = False - reading_raman = False - - # Reset the dielectric tensor - self.dielectric_tensor = np.zeros((3,3)) - dielectric_read = 0 - - continue - elif "Effective" in dynlines[index]: - reading_dielectric = False - reading_eff_charges = True - reading_raman = False - - - # Reset the effective charges - self.effective_charges = np.zeros((self.structure.N_atoms, 3, 3)) - - continue - elif "Raman" in dynlines[index]: - reading_dielectric = False - reading_eff_charges = False - reading_raman = True - - # Reset the raman tensor - self.raman_tensor = np.zeros((3,3, 3*self.structure.N_atoms)) - continue - elif "q = " in dynlines[index]: - #Read the q - qpoint = np.array([float(item) for item in dynlines[index].replace("(", ")").split(')')[1].split()]) - q_star.append(qpoint / self.alat) - self.q_tot.append(qpoint / self.alat) - reading_dielectric = False - reading_eff_charges = False - reading_raman = False - continue - elif "ynamical" in dynlines[index]: - # Save the dynamical matrix - self.dynmats.append(current_dyn.copy()) - reading_dielectric = False - reading_eff_charges = False - reading_raman = False - continue - - - # Read what is needed - numbers_in_line = dynlines[index].split() - if len(numbers_in_line) == 0: - continue - - if reading_dielectric: - # Reading the dielectric - if len(numbers_in_line) == 3: - self.dielectric_tensor[dielectric_read, :] = np.array([np.float64(x) for x in numbers_in_line]) - dielectric_read += 1 - elif reading_eff_charges: - if numbers_in_line[0].lower() == "atom": - atm_i = int(numbers_in_line[2]) - 1 - dielectric_read = 0 - elif len(numbers_in_line) == 3: - self.effective_charges[atm_i, dielectric_read,:] = np.array([np.float64(x) for x in numbers_in_line]) - dielectric_read += 1 - elif reading_raman: - if numbers_in_line[0].lower() == "atom": - atm_i = int(numbers_in_line[2]) - 1 - pol_read = int(numbers_in_line[4]) - 1 - dielectric_read = 0 - elif len(numbers_in_line) == 3: - self.raman_tensor[dielectric_read,:, 3*atm_i + pol_read] = np.array([np.float64(x) for x in numbers_in_line]) - dielectric_read += 1 - else: - # Read the numbers - if (len(numbers_in_line) == 2): - # Setup which atoms are - atm_i = int(numbers_in_line[0]) - 1 - atm_j = int(numbers_in_line[1]) - 1 - coordline = 0 - elif(len(numbers_in_line) == 6): - # Read the dynmat - for k in range(3): - current_dyn[3 * atm_i + coordline, 3*atm_j + k] = np.float64(numbers_in_line[2*k]) + 1j*np.float64(numbers_in_line[2*k + 1]) - coordline += 1 - - - # Append the new stars for the irreducible q point - self.q_stars.append(q_star) - - - # Ok, the matrix has been initialized - self.initialized = True - - def DyagDinQ(self, iq, force_real_at_gamma = True): - """ - Dyagonalize the dynamical matrix in the given q point index. - This methods returns both frequencies and polarization vectors. - The frequencies and polarization are ordered. Negative frequencies are to - be interpreted as instabilities and imaginary frequency, as for QE. - - They are returned. - - NOTE: The normalization is forced, as it is problematic for degenerate modes - NOTE: if the q point is gamma, then the matrix is forced to be real - - Parameters - ---------- - - iq : int - Tbe index of the q point of the matrix to be dyagonalized. - - force_real_at_gamma : bool, optional - If True (default) the matrix is forced to be real during the - dyagonalization (if q = 0). This assures to have real eigenvectors. - This is usefull for supercells. - - - Results - ------- - - frequencies : ndarray (float) - The frequencies (square root of the eigenvalues divided by the masses). - These are in Ry units. - - pol_vectors : ndarray (N_modes x 3)^2 - The polarization vectors for the dynamical matrix. They are returned - in a Fortran fashon order: pol_vectors[:, i] is the i-th polarization vector. - """ - - - - # First of all get correct dynamical matrix by dividing per the masses. - real_dyn = np.zeros((3* self.structure.N_atoms, 3*self.structure.N_atoms), dtype = np.complex128) - for i, atm_type1 in enumerate(self.structure.atoms): - m1 = self.structure.masses[atm_type1] - for j, atm_type2 in enumerate(self.structure.atoms): - m2 = self.structure.masses[atm_type2] - real_dyn[3*i : 3*i + 3, 3*j : 3*j + 3] = 1 / np.sqrt(m1 * m2) - - - real_dyn *= self.dynmats[iq] - - q_vec = self.q_tot[iq] - if np.sqrt(q_vec.dot(q_vec)) < __EPSILON__: - eigvals, pol_vects = np.linalg.eigh(np.real(real_dyn)) - else: - eigvals, pol_vects = np.linalg.eigh(real_dyn) - - f2 = eigvals - - # Check for imaginary frequencies (unstabilities) and return them as negative - frequencies = np.zeros(len(f2), dtype = np.double) - frequencies[f2 > 0] = np.sqrt(f2[f2 > 0]) - frequencies[f2 < 0] = -np.sqrt(-f2[f2 < 0]) - - # Order the frequencies and the polarization vectors - sorting_mask = np.argsort(frequencies) - frequencies = frequencies[sorting_mask] - pol_vects = pol_vects[:, sorting_mask] - - # Force normalization - for i in range(3 * self.structure.N_atoms): - # Check the normalization - norm = np.sqrt(pol_vects[:, i].dot(np.conj(pol_vects[:, i]))) - if abs(norm - 1) > __EPSILON__: - sys.stderr.write("WARNING: Phonon mode %d at q point %d not normalized!\n" % (i, iq)) - print ("WARNING: Normalization of the phonon %d mode at %d q = %16.8f" % (i, iq, norm)) - - # Check if it is an eigenvector - not_eigen = np.sqrt(np.sum( abs(real_dyn.dot(pol_vects[:, i]) - eigvals[i] * pol_vects[:, i])**2)) - - if not_eigen > 1e-2: - sys.stderr.write("WARNING: Phonon mode %d at q point %d not an eigenvector!\n" % (i, iq)) - print ("WARNING: Error of the phonon %d mode eigenvector %d q = %16.8f" % (i, iq, not_eigen)) - - pol_vects[:, i] /= norm - - return frequencies, pol_vects - - def Copy(self): - """ - Return an exact copy of itself. - This will implies copying all the dynamical matricies and structures inside. - So take care if the structure is big, because it will overload the memory. - - - """ - - ret = Phonons() - ret.structure = self.structure.copy() - ret.q_tot = [x.copy() for x in self.q_tot] - ret.nqirr = self.nqirr - ret.initialized = self.initialized - ret.q_stars = [] - for qstar in self.q_stars: - ret.q_stars.append([x.copy() for x in qstar]) - - ret.alat = self.alat - - for i, dyn in enumerate(self.dynmats): - ret.dynmats.append(dyn.copy()) - - if not self.effective_charges is None: - ret.effective_charges = self.effective_charges.copy() - if not self.raman_tensor is None: - ret.raman_tensor = self.raman_tensor.copy() - if not self.dielectric_tensor is None: - ret.dielectric_tensor = self.dielectric_tensor.copy() - - return ret - - def CheckCompatibility(self, other): - """ - This function checks the compatibility between two dynamical matrices. - The check includes the number of atoms and the atomic type. - Parameters - ---------- - - other : Phonons.Phonons() - The other dynamical matrix to check the compatibility. - - Returns - ------- - bool - """ - - # First of all, check if other is a dynamical matrix: - if type(other) != type(self): - return False - - # Check if the two structures shares the same number of atoms: - if self.structure.N_atoms != other.structure.N_atoms: - return False - - # Check if they belong to the same supercell: - if self.nqirr != other.nqirr: - return False - - # Then they are compatible - return True - - def GetUpsilonMatrix(self, T, min_w_threshold = __EPSILON_W__, debug = False, verbose = False, w_pols = None): - """ - This subroutine returns the inverse of the correlation matrix. - It is computed as following - - .. math:: - - \\Upsilon_{ab} = \\sqrt{M_aM_b}\\sum_\\mu \\frac{2\\omega_\\mu}{(1 + 2n_\\mu)\\hbar} e_\\mu^a e_\\mu^b - - It is used to compute the probability of a given atomic displacement. - The resulting matrix is a 3N x 3N one ordered as the dynamical matrix here. - The result is in bohr^-2, please be carefull. - - - Parameters - ---------- - T : float - Temperature of the calculation (Kelvin) - min_w_threshold: float - The threshold for frequency under which the modes are considered fixed and neglected (as Gamma acoustic modes). - w_pols: (list of w and pols) - If different from None, contains the frequencies and polarization vectors of this matrix. - Usefull to avoid multiple diagonalizations - Returns - ------- - ndarray(3N x3N), dtype = np.float64 - The inverse of the correlation matrix in the supercell. - N is the number of atoms in the supercell - """ - K_to_Ry=6.336857346553283e-06 - - if T < 0: - raise ValueError("Error, T must be posititive (or zero)") -# -# if self.nqirr != 1: -# raise ValueError("Error, this function yet not supports the supercells.") - - # We need frequencies and polarization vectors - if w_pols is None: - t1 = time.time() - w, pols = self.DiagonalizeSupercell() #self.DyagDinQ(iq) - t2 = time.time() - if verbose: - print("[GET UPS] Time to diagonalize the dynamical matrix {} s".format(t2-t1)) - else: - w = w_pols[0] - pols = w_pols[1] - # Transform the polarization vector into real one - #pols = np.real(pols) - - # Remove translations if we are at Gamma - type_cal = np.float64#np.complex128 - - super_struct = self.structure.generate_supercell(self.GetSupercell()) - t3 = time.time() - trans_mask = Methods.get_translations(pols, super_struct.get_masses_array()) - - t4 = time.time() - if verbose: - print("[GET UPS] Time to prepare the supercell structure: {} s".format(t3-t2)) - print("[GET UPS] Time to get translations: {} s".format(t4-t3)) - - # Exclude also other w = 0 modes - locked_original = np.abs(w) < min_w_threshold - if np.sum(locked_original.astype(int)) > np.sum(trans_mask.astype(int)): - trans_mask = locked_original - - no_trans = ~trans_mask - - # Discard translations - w = w[no_trans] - pols = pols[:, no_trans] - - - pols_conj = np.conj(pols) - - # Get the bosonic occupation number - nw = np.zeros(np.shape(w)) - if T < __EPSILON__: - nw = np.float64(0) - #print "T = 0" - else: - nw = 1. / (np.exp(w/(K_to_Ry * T)) -1) - #print "T > 0" - - # Compute the matrix - factor = 2 * w / (1. + 2*nw) - t1 = time.time() - - if verbose: - print("[GET UPS] Time to prepare the upsilon computation: {} s".format(t1-t3)) - - pols_mod = np.einsum("ab,b -> ab", pols_conj, factor) - Upsilon = pols.dot(pols_mod.T) - t2 = time.time() - - if verbose: - print("[GET UPS] Time to build the Upsilon matrix: {} s".format(t2 - t1)) - - if debug: - Upsilon_old = np.einsum( "i, ji, ki", factor, pols, pols_conj, dtype = type_cal) - assert np.max(np.abs(Upsilon - Upsilon_old)) < 1e-10, "Error, the new Upsilon calculation is wrong" - #_p1_, _p1vect_ = np.linalg.eigh(Upsilon) - #np.savetxt("factor.dat", np.transpose([factor * RY_TO_CM / 2, _p1_[3:]* RY_TO_CM / 2])) - - # Get the masses for the final multiplication - t1 = time.time() - mass_sqrt = np.sqrt(np.tile(super_struct.get_masses_array(), (3,1)).T.ravel()) - - #mass1 = np.zeros( 3*super_struct.N_atoms) - #for i in range(self.structure.N_atoms): - # mass1[ 3*i : 3*i + 3] = np.sqrt(self.structure.masses[ super_struct.atoms[i]]) - - _m1_ = np.tile(mass_sqrt, (3 * super_struct.N_atoms, 1)) - _m2_ = np.tile(mass_sqrt, (3 * super_struct.N_atoms, 1)).transpose() - - Upsilon *= _m1_ * _m2_ - t2 = time.time() - if verbose: - print("[GET UPS] Time to multiply the masses: {} s".format(t2 -t1)) - return Upsilon - - def GetProbability(self, displacement, T, upsilon_matrix = None, normalize = True, return_braket_vals = False): - """ - This function, given a particular displacement, returns the probability density - of finding the system around that displacement. This in practical computes - density matrix of the system in this way - - .. math:: - - \\rho(\\vec u) = \\sqrt{\\det(\\Upsilon / 2\\pi)} \\times \\exp\\left[-\\frac 12 \\sum_{ab} u_a \\Upsilon_{ab} u_b\\right] - - Where :math:`\\vec u` is the displacement, :math:`\\Upsilon` is the inverse of the covariant matrix - computed through the method self.GetUpsilonMatrix(). - - NOTE: I think there is an error in the implementation, in fact the Upsilon matrix is in bohr^-2 while displacements are in Angstrom. - - Parameters - ---------- - displacement : ndarray(3xN) or ndarray(N, 3) - The displacement on which you want to compute the probability. - It can be both an array of dimension 3 x self.structure.N_atoms or - a bidimensional array of structure (N_atoms, 3). - T : float - Temperature (Kelvin) for the calculation. It will be discarded - if a costum upsilon_matrix is provided. - upsilon_matrix : ndarray (3xN)^2, optional - If you have to compute many times this probability it can be convenient - to compute only once the upsilon matrix, and recycle it. If it is - None (as default) the upsilon matrix will be recomputed each time. - normalize : bool, optional - If false (default true) the probability distribution will not be normalized. - Useful to check if the exponential weight is the same after some manipulation - return_braket_vals : bool, optional - If true the value returned is only the braket followed by the - eigenvalues of the Upsilon matrix. - - Returns - ------- - float - The probability density of finding the system in the given displacement. - - """ - - - disp = np.zeros( 3 * self.structure.N_atoms) - - # Reshape the displacement - if len(np.shape(displacement)) == 2: - disp = displacement.reshape( len(disp)) - else: - disp = displacement - - - if upsilon_matrix is None: - upsilon_matrix = self.GetUpsilonMatrix(T) - - # Compute the braket - braket = np.einsum("i, ij, j", disp, upsilon_matrix, disp) - - # Get the normalization - vals = np.linalg.eigvals(upsilon_matrix) - vals = vals[np.argsort(np.abs(vals))] - - vals /= 2*np.pi - det = np.prod(vals[3:]) - - if return_braket_vals: - return braket, vals - - if normalize: - return np.sqrt(det) * np.exp(-braket) - else: - return np.exp(-braket) - - def GetRatioProbability(self, structure, T, dyn0, T0): - """ - IMPORTANCE SAMPLING - =================== - - This method compute the ration of the probability of extracting a given structure at temperature T - generated with dyn0 at T0 if the extraction is made with the self dynamical matrix. - - It is very usefull to perform importance sampling tests. - - .. math:: - - w(\\vec u) = \\frac{\\rho_{D_1}(\\vec u, T)}{\\rho_{D_0}(\\vec u, T_0)} - - Where :math:`D_1` is the current dynamical matrix, while :math:`D_0` is the - dynamical matrix that has been actually used to generate dyn0 - - TODO: It seems to return wrong results - NOTE: This subroutine seems to return fake results, please be carefull. - - Parameters - ---------- - structure : Structure.Structure() - The atomic structure generated according to dyn0 and T0 to evaluate the statistical significance ratio. - T : float - The target temperature - dyn0 : Phonons.Phonons() - The dynamical matrix used to generate the given structure. - T0 : float - The temperature used in the generation of the structure - - Results - ------- - float - The ratio :math:`w(\\vec u)` between the probabilities. - """ - K_to_Ry = 6.336857346553283e-06 - - if not self.CheckCompatibility(dyn0): - raise ValueError("Error, dyn0 and the current dyn are incompatible") - - # Get the displacement respect the two central atomic positions - disp1 = structure.get_displacement(self.structure) - disp0 = structure.get_displacement(dyn0.structure) - - # # TODO: Improve the method with a much more reliable one - # # In fact the ratio between them is much easier (this can be largely affected by rounding) - # #print "disp1:", disp1 - # #print "Ratio1:", self.GetProbability(disp1, T) , "Ratio2:", dyn0.GetProbability(disp0, T0) - - # b1, v1 = self.GetProbability(disp1, T, return_braket_vals = True) - # b2, v2 = dyn0.GetProbability(disp0, T0, return_braket_vals = True) - # new_v = v1[3:] / v2[3:] - # ret = np.exp(b2- b1) * np.prod(np.sqrt(new_v)) - - # #print "comparison:", ret, self.GetProbability(disp1, T) / dyn0.GetProbability(disp0, T0) - - - # This should be the fastest way - w1, pols1 = self.DyagDinQ(0) - w0, pols0 = dyn0.DyagDinQ(0) - - # Remove translations (acustic modes in gamma) - tmask1 = Methods.get_translations(pols1, self.structure.get_masses_array()) - tmask0 = Methods.get_translations(pols0, dyn0.structure.get_masses_array()) - - - w1 = w1[ ~tmask1 ] - pols1 = pols1[:, ~tmask1] - w0 = w0[~tmask0] - pols0 = pols0[:, ~tmask0] - - #print "TMASK:", tmask0, tmask1 - - - _m1_ = np.zeros(self.structure.N_atoms * 3) - _m0_ = np.zeros(dyn0.structure.N_atoms * 3) - - for i in range(self.structure.N_atoms): - _m1_[3*i : 3*i + 3] = self.structure.masses[self.structure.atoms[i]] - _m0_[3*i : 3*i + 3] = dyn0.structure.masses[dyn0.structure.atoms[i]] - - # Get the q values - q1 = np.real(np.einsum("i, ij, i", np.sqrt(_m1_), pols1, disp1.reshape(3 * self.structure.N_atoms))) - q0 = np.real(np.einsum("i, ij, i", np.sqrt(_m0_), pols0, disp0.reshape(3 * self.structure.N_atoms))) - - a1 = np.zeros(np.shape(w1)) - a0 = np.zeros(np.shape(w0)) - - if T == 0: - a1 = 1 / np.sqrt(2* w1) - else: - beta = 1 / (K_to_Ry*T) - a1 = 1 / np.sqrt( np.tanh(beta*w1 / 2) *2* w1) - - if T0 == 0: - a0 = 1 / np.sqrt(2* w0) - else: - beta = 1 / (K_to_Ry*T0) - a0 = 1 / np.sqrt( np.tanh(beta*w0 / 2) *2* w0) - - weight = np.prod((a0 / a1) * np.exp(- (q1 / (a1))**2 + (q0 / (a0))**2)) - - #print "COMPARISON:", ret, weight - - return weight - - def AdjustToNewCell(self, new_cell, symmetrize = True): - """ - ADJUST THE DYNAMICAL MATRIX IN A NEW CELL - ========================================= - - This method is used, if you want to change the unit cell, - to adjust the dynamical matrix, as the q points, in the new cell. - - The method forces also the symmetrization after the strain - - Parameters - ---------- - new_cell : ndarray(size=(3,3), dtype=np.float64) - The new unit cell - """ - - new_qs = symmetries.GetNewQFromUnitCell(self.structure.unit_cell, new_cell, self.q_tot) - - # Get the new structure - self.structure.change_unit_cell(new_cell) - - # Get the new q points - for iq, q in enumerate(new_qs): - self.q_tot[iq] = q - - count = 0 - for iqirr in range(len(self.q_stars)): - for iq in range(len(self.q_stars[iqirr])): - self.q_stars[iqirr][iq] = new_qs[count] - count += 1 - - self.AdjustQStar() - - # Force the symmetrization in the new structure - # NOTE: This will rise an exception if something is wrong - if symmetrize: - qe_sym = symmetries.QE_Symmetry(self.structure) - fcq = np.array(self.dynmats, dtype = np.complex128) - qe_sym.SymmetrizeFCQ(fcq, self.q_stars) - for iq, q in enumerate(self.q_tot): - self.dynmats[iq] = fcq[iq, :, :] - - def GetStrainMatrix(self, new_cell, T,threshold=1e-5,x_start = 0.01): - """ - STRAIN THE DYNAMICAL MATRIX - =========================== - - This function strains the dynamical matrix to fit into the new cell. - It will modify both the polarization vectors and the frequencies. - - The strain is performed on the covariance matrix. - - .. math:: - - {\\Upsilon_{axby}^{-1}}' = \\sum_{\\alpha,\\beta = x,y,z} \\varepsilon_{x\\alpha}\\varepsilon_{y\\beta}\\Upsilon_{a\\alpha b\\beta}^{-1} - - Then the new :math:`\\Upsilon^{-1}` matrix is diagonalized, eigenvalues and eigenvector are built, - and from them the new dynamical matrix is computed. - - NOTE: This works only at Gamma - I think there is a bug if T != 0 in the solver. BE CAREFULL! - - Parameters - ---------- - new_cell : ndarray 3x3 - The new unit cell after the strain. - T : float - The temperature of the strain (default 0) - threshold : float - The threshold for the convergence of the newton algorithm to find the - frequencies given the eigenvalues of the upsilon matrix. - x_start : float - The initial guess for the newton algorithm. - - Results - ------- - dyn : Phonons.Phonons() - A new dynamical matrix strained. Note, the current dynamical matrix will not be modified. - """ - K_to_Ry=6.336857346553283e-06 - - if T < 0: - raise ValueError("Error, the temperature must be positive.") - - # Get the polarization vectors and frequencies - w, pol_vects = self.DyagDinQ(0) - - n_modes = len(w) - - # Strain the polarization vectors - new_vect = np.zeros(np.shape(pol_vects)) - for i in range(3, n_modes): - for j in range(self.structure.N_atoms): - # Get the crystal representation of the polarization vector - cov_coord = Methods.covariant_coordinates(self.structure.unit_cell, - pol_vects[3*j: 3*(j+1), i]) - - # Transform the crystal representation into the cartesian in the new cell - new_vect[3*j: 3*(j+1), i] = np.einsum("ij, i", new_cell, cov_coord) - - # Now prepare the new Covariance Matrix - factor = np.zeros(n_modes) - if T == 0: - factor[3:] = 1 / (2. * w[3:]) - else: - n = 1 / (np.exp(w[3:] / (K_to_Ry * T)) - 1) - factor[3:] = (1. + 2*n) / (2*w[3:]) - - cmat = np.einsum("i, hi,ki", factor, new_vect, new_vect) - - # Diagonalize once again - newf, new_pols = np.linalg.eig(cmat) -# -# # DEBUG PRINT -# prova1 = np.sort(newf) -# prova2 = np.sort(factor) -# for i in range(n_modes): -# print "New: %e | Old: %e" % (prova1[i], prova2[i]) -# - - # Sort the results - sort_mask = np.argsort(newf) - newf = newf[sort_mask] - new_pols = new_pols[:, sort_mask] - - # Initialize the array of the new frequencies - new_w = np.zeros(n_modes) - new_w[3:] = 1. / (2 * newf[3:]) - - - - # If the temperature is different from zero, we must obtain a new frequency - # using a numerical nonlinear solver - if T != 0: - - #def opt_func(w): - # ret = 2*w*newf - 1./( 1 - np.exp(w / (K_to_Ry * T))) - # if not np.shape(w): - # if np.abs(w) < __EPSILON__: - # return 0 - # else: - # ret[np.abs(w) < __EPSILON__] = 0 - # return ret - - - - - - #try: - # for k in range(len(new_w)): - # def new_func(x): - # _x_ = np.ones(np.shape(newf)) * x - # return opt_func(_x_)[k] - # if np.abs(new_w[k]) < __EPSILON__: - # continue - # new_w[k] = scipy.optimize.anderson(new_func, new_w[k], verbose = True) - - for k in range(3,36): - def g(w): - f1= 2*w*newf[k]-1/np.tanh(w*0.5/(K_to_Ry*T)) - return f1 - - def g_prime(w): - f2=2*newf[k]+0.5/(K_to_Ry*T*(np.sinh(w*0.5/(K_to_Ry*T)))**2) - return f2 - - x_old=x_start - while True : - x_new=x_old-g(x_old)/g_prime(x_old) - if np.abs(g(x_new)) < threshold : - break - else: - x_old=x_new - new_w[k]=x_new - - #except ValueError: - # print "Error, Nan encountered during the scipy minimization (T != 0)" - # print "Starting w value:" - # print new_w - # print "new_f value:" - # print newf - # print "T:", T - # raise ValueError("Aborting, error in scipy minimization.") - - - - # - # print "Compare frequencies:" -# for i in range(0,n_modes): -# print "New: %e | Old: %e" % (new_w[i], w[i]) - - - # Sort once again - sort_mask = np.argsort(new_w) - new_w = new_w[sort_mask] - new_pols = new_pols[:, sort_mask] - - - # Now we can rebuild the dynamical matrix - out_dyn = self.Copy() - out_dyn.structure.change_unit_cell(new_cell) - out_dyn.dynmats[0] = np.einsum("i, hi, ki", new_w**2, new_pols, new_pols) - - # Get the masses for the final multiplication - mass1 = np.zeros( 3*self.structure.N_atoms) - for i in range(self.structure.N_atoms): - mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] - - _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) - _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() - - out_dyn.dynmats[0] *= np.sqrt( _m1_ * _m2_ ) - - return out_dyn - - - def save_qe(self, filename, full_name = False): - """ - SAVE THE DYNMAT - =============== - - This subroutine saves the dynamical matrix in the quantum espresso file format. - The dynmat is the force constant matrix in Ry units. - - .. math:: - - \\Phi_{ab} = \\sum_\\mu \\omega_\\mu^2 e_\\mu^a e_\\mu^b \\sqrt{M_a M_b} - - Where :math:`\\Phi_{ab}` is the force constant matrix between the a-b atoms (also cartesian - indices), :math:`\\omega_\\mu` is the phonon frequency and :math:`e_\\mu` is the - polarization vector. - - - Parameters - ---------- - filename : string - The path in which the quantum espresso dynamical matrix will be written. - full_name : bool - If true only the gamma matrix will be saved, and the irreducible q - point index will not be appended. Otherwise all the file filenameIQ - where IQ is an integer between 0 and self.nqirr will be generated. - filename0 will contain all the information about the Q points and the supercell. - """ - #A_TO_BOHR = 1.889725989 - #RyToCm=109737.37595 - RyToTHz=3289.84377 - - # Check if all the dynamical matrix must be saved, or only the - nqirr = self.nqirr - if full_name: - nqirr = 1 - - # The following counter counts the total number of q points - count_q = 0 - for iq in range(nqirr): - # Prepare the file name appending the q point index - fname = filename - if not full_name: - fname += str(iq+1) - - # Open the file - fp = open(fname, "w") - fp.write("Dynamical matrix file\n") - - # Get the different number of types - types = [] - n_atoms = self.structure.N_atoms - for i in range(n_atoms): - if not self.structure.atoms[i] in types: - types.append(self.structure.atoms[i]) - n_types = len(types) - - # Assign an integer for each atomic species - itau = {} - for i in range(n_types): - itau[types[i]] = i +1 - - # Write the comment line - fp.write("File generated with the CellConstructor by Lorenzo Monacelli\n") - fp.write("%d %d %d %22.16f %22.16f %22.16f %22.16f %22.16f %22.16f\n" % - (n_types, n_atoms, 0, self.alat * A_TO_BOHR, 0, 0, 0, 0, 0) ) - - # Write the basis vector - fp.write("Basis vectors\n") - # Get the unit cell - for i in range(3): - fp.write(" ".join("%22.16f" % x for x in self.structure.unit_cell[i,:] / self.alat) + "\n") - - # Set the atom types and masses - for i in range(n_types): - fp.write("\t%d '%s ' %22.16f\n" % (i +1, types[i], self.structure.masses[types[i]])) - - # Setup the atomic structure - for i in range(n_atoms): - # Convert the coordinates in alat - coords = self.structure.coords[i,:] / self.alat - fp.write("%5d %5d %22.16f %22.16f %22.16f\n" % - (i +1, itau[self.structure.atoms[i]], - coords[0], coords[1], coords[2])) - - # Iterate over all the q points in the star - nqstar = len(self.q_stars[iq]) - q_star = self.q_stars[iq] #* self.alat - - # Store the first matrix index of the star - # This will be used to dyagonalize the matrix in the end of the file - dyag_q_index = count_q - - for jq in range(nqstar): - # Here the dynamical matrix starts - fp.write("\n") - fp.write(" Dynamical Matrix in cartesian axes\n") - fp.write("\n") - fp.write(" q = ( %18.12f %18.12f %18.12f )\n" % - (q_star[jq][0] * self.alat , q_star[jq][1]*self.alat, q_star[jq][2]*self.alat )) - fp.write("\n") - - # Now print the dynamical matrix - for i in range(n_atoms): - for j in range(n_atoms): - # Write the atoms - fp.write("%5d%5d\n" % (i + 1, j + 1)) - for x in range(3): - line = "%23.16f%23.16f %23.16f%23.16f %23.16f%23.16f" % \ - ( np.real(self.dynmats[count_q][3*i + x, 3*j]), np.imag(self.dynmats[count_q][3*i + x, 3*j]), - np.real(self.dynmats[count_q][3*i + x, 3*j+1]), np.imag(self.dynmats[count_q][3*i+x, 3*j+1]), - np.real(self.dynmats[count_q][3*i + x, 3*j+2]), np.imag(self.dynmats[count_q][3*i+x, 3*j+2]) ) - - fp.write(line + "\n") - - # Go to the next q point - count_q += 1 - - # Here save the Dielectric tensor, the effective charges and the Raman response - if not self.dielectric_tensor is None: - fp.write("\n") - fp.write(" Dielectric Tensor:\n") - fp.write("\n") - for i in range(3): - fp.write("{:24.12f} {:24.12f} {:24.12f}\n".format(*list(self.dielectric_tensor[i,:]))) - - if not self.effective_charges is None: - fp.write("\n") - fp.write(" Effective Charges E-U: Z_{alpha}{s,beta}\n") - fp.write("\n") - for i in range(self.structure.N_atoms): - fp.write(" atom # {:5d}\n".format(i+1)) - for j in range(3): - fp.write("{:24.12e} {:24.12e} {:24.12e}\n".format(*list(self.effective_charges[i, j, :]))) - - if not self.raman_tensor is None: - fp.write("\n") - fp.write(" Raman tensor (A^2)\n") - fp.write("\n") - for i_atm in range(self.structure.N_atoms): - for j_pol in range(3): - fp.write(" atom # {:5d} pol. {:2d}\n".format(i_atm+1, j_pol+1)) - for k in range(3): - fp.write("{:24.12e} {:24.12e} {:24.12e}\n".format(*list(self.raman_tensor[k, :, 3*i_atm + j_pol]))) - - - - # Print the diagnoalization of the matrix - fp.write("\n") - fp.write(" Diagonalizing the dynamical matrix\n") - fp.write("\n") - fp.write(" q = ( %18.12f %18.12f %18.12f )\n" % - (q_star[0][0] *self.alat , q_star[0][1] *self.alat, q_star[0][2] *self.alat)) - fp.write("\n") - fp.write("*" * 75 + "\n") - - # Diagonalize the dynamical matrix - freqs, pol_vects = self.DyagDinQ(dyag_q_index) - - # Compute the displacemets from the polarization vectors - _m_ = self.structure.get_masses_array() - _m_ = np.tile(_m_, (3,1)).T.ravel() - - # Compute the atomic displacements - atomic_disp = np.einsum("ab, a -> ab", pol_vects, 1 / np.sqrt(_m_) ) - # Normalize the displacements - atomic_disp[:,:] /= np.tile( np.sqrt(np.sum(np.abs(atomic_disp)**2, axis = 0)), (self.structure.N_atoms * 3, 1)) - - nmodes = len(freqs) - for mu in range(nmodes): - # Print the frequency - fp.write("%7s (%5d) = %14.8f [THz] = %14.8f [cm-1]\n" % - ("freq", mu+1, freqs[mu] * RyToTHz, freqs[mu] * RY_TO_CM)) - - # Print the polarization vectors - for i in range(n_atoms): - fp.write("( %10.6f%10.6f %10.6f%10.6f %10.6f%10.6f )\n" % - (np.real(atomic_disp[3*i, mu]), np.imag(atomic_disp[3*i,mu]), - np.real(atomic_disp[3*i+1, mu]), np.imag(atomic_disp[3*i+1,mu]), - np.real(atomic_disp[3*i+2, mu]), np.imag(atomic_disp[3*i+1,mu]))) - fp.write("*" * 75 + "\n") - fp.close() - - def save_phononpy(self, *args, **kwargs): - "Mapping to save_phonopy" - warnings.warn("[DEPRECATION WARNING] save_phononpy is deprecated: use save_phonopy instead.") - self.save_phonopy(*args, **kwargs) - - def save_phonopy(self, path = ".", supercell_size = None): - """ - EXPORT THE DYN IN THE PHONONPY FORMAT - ===================================== - - This tool export the dynamical matrix into the PHONONPY plain text format. - We save them in Ry/bohr^2, as the quantum espresso format. Please, remember - this when using Phononpy for the conversion factors. - - It will create a file called FORCE_CONSTANTS, one called unitcell.in - with the info on the structure - - Parameters - ---------- - path: str - Path to the directory in which the FORCE_CONSTANTS and unitcell.in files are created. - supercell_size : list of 3 - The supercell that defines the dynamical matrix, note phononpy - works in the supercell. If none, it is inferred from the q points - - - """ - if supercell_size is None: - supercell_size = self.GetSupercell() - - # Save it into the phononpy in the supercell - superdyn = self.GenerateSupercellDyn(supercell_size) - filename = os.path.join(path, "FORCE_CONSTANTS") - - nat_sc = superdyn.structure.N_atoms - nat = self.structure.N_atoms - - # This is the text to be written - lines = [] - lines.append("%d %d\n" % (nat_sc, nat_sc)) - for i in range(nat_sc): - for j in range(nat_sc): - lines.append("%4d\t%4d\n" % (i, j)) - mat = np.real(superdyn.dynmats[0][3*i : 3*i+ 3, 3*j: 3*j+3]) - lines.append("%16.8f %16.8f %16.8f\n" % (mat[0,0], mat[0,1], mat[0,2])) - lines.append("%16.8f %16.8f %16.8f\n" % (mat[1,0], mat[1,1], mat[1,2])) - lines.append("%16.8f %16.8f %16.8f\n" % (mat[2,0], mat[2,1], mat[2,2])) - - # Write to the file - f = open(filename, "w") - f.writelines(lines) - f.close() - - # Produce the unit cell - lines = [] - lines.append("&system\n") - lines.append("ibrav = 0\n") - lines.append("celldm(1) = 1.889726125836928\n") - lines.append("nat = %d\n" % self.structure.N_atoms) - - typs = self.structure.masses.keys() - lines.append("ntyp = %d\n" % len(typs)) - lines.append("&end\n") - - # Write the atomic species - lines.append("ATOMIC_SPECIES\n") - for i in typs: - m = self.structure.masses[i] - lines.append("%s %16.8f XXX\n" % (i, m / 911.444243096)) - - # Write the unit cell - lines.append("CELL_PARAMETERS alat\n") - for i in range(3): - uc_v = self.structure.unit_cell[i, :] #* 1.889726125836928 - lines.append("%16.8f %16.8f %16.8f\n" % (uc_v[0], uc_v[1], uc_v[2])) - - lines.append("ATOMIC_POSITIONS crystal\n") - for i in range(nat): - atm = self.structure.atoms[i] - cov_vect = Methods.covariant_coordinates(self.structure.unit_cell, self.structure.coords[i, :]) - lines.append("%s %16.8f %16.8f %16.8f\n" % (atm, cov_vect[0], cov_vect[1], cov_vect[2])) - - - f = open(os.path.join(path, "unitcell.in"), "w") - f.writelines(lines) - f.close() - - def load_phonopy(self, yaml_filename = "phonopy.yaml", fc_filename = None): - """ - LOAD FROM PHONOPY FORCE CONSTANTS - ================================= - - This subroutine load the dynamical matrix from the phonopy FORCE_CONSTANT file. - It needs two files: the file with the structure information, - and the file with the force constant matrix. - - Parameters - ---------- - yaml_filename : string - Path to the YAML file, this contains the info of the structure and the supercell. - fc_filename: string - Path to the FORCE_CONSTANTS file. If None, a file called FORCE_CONSTANTS in the same directory - as phonopy.yaml will be looked for. - """ - - unit_cell = np.zeros((3,3), dtype = np.double) - supercell = np.zeros(3, dtype = np.intc) - coords = [] - atoms = [] - masses = {} - - superstruct = None - unit_cell_itau = [] - - with open(yaml_filename, "r") as fp: - - read_primitive_cell = False - read_coord = False - read_lattice = False - read_supercell = False - read_superstruct = False - counter = 0 - for line in fp.readlines(): - - line = line.strip() - if not line: - continue - - data = line.replace(",","").split() - - if line == "supercell_matrix:": - read_supercell = True - counter = 0 - continue - - if read_supercell and len(data) == 6: - supercell[counter] = int(data[2 + counter]) - counter += 1 - - if counter == 3: - counter = 0 - read_supercell = False - - if line == "unit_cell:": - read_primitive_cell = True - continue - - if line == "lattice:": - read_lattice = True - counter = 0 - continue - - if read_lattice and len(data) == 8: - unit_cell[counter, :] = [float(data[x]) for x in range(2, 5)] - counter += 1 - if counter == 3: - counter = 0 - read_lattice = False - - if line == "points:": - read_coord = True - atoms = [] - coords = [] - continue - - if read_coord: - if "symbol" in line: - atoms.append(data[2]) - if "coordinates" in line: - vector = np.array([float(data[x]) for x in range(2, 5)]) - coords.append(Methods.cryst_to_cart(unit_cell, vector)) - if "mass" in line: - if not atoms[-1] in masses: - masses[atoms[-1]] = float(data[1]) / MASS_RY_TO_UMA - if "reduced_to" in line: - if read_primitive_cell: - unit_cell_itau.append(int(data[1]) - 1) - - if "supercell" in line: - if read_primitive_cell: - self.structure = Structure.Structure(len(atoms)) - self.structure.atoms = atoms - self.structure.coords[:,:] = np.array(coords) * BOHR_TO_ANGSTROM - self.structure.masses = masses - self.structure.has_unit_cell = True - self.structure.unit_cell = unit_cell.copy() * BOHR_TO_ANGSTROM - read_coord = False - read_lattice = False - read_primitive_cell = False - read_superstruct = True - continue - - # Now create the superstructure - if read_superstruct: - superstruct = Structure.Structure(len(atoms)) - superstruct.atoms = atoms - superstruct.coords[:,:] = np.array(coords) * BOHR_TO_ANGSTROM - superstruct.masses = masses - superstruct.unit_cell = unit_cell.copy() * BOHR_TO_ANGSTROM - superstruct.has_unit_cell = True - - # Get the Equivalent atoms in the unit cell - itau = superstruct.get_itau(self.structure) - 1 - - # Now load the Force constant matrix - if fc_filename is None: - fc_filename = os.path.join(os.path.dirname(yaml_filename), "FORCE_CONSTANTS") - - fc = np.zeros( (superstruct.N_atoms * 3, superstruct.N_atoms * 3), dtype = np.double) - FC_TMP = np.zeros((3,3), dtype = np.double) - - with open(fc_filename, "r") as fp: - - x = 0 - y = 0 - counter = 0 - FC = np.zeros((3,3), dtype = np.double) - for i, line in enumerate(fp.readlines()): - line = line.strip() - data = line.split() - - - if i == 0: - nat_prim = int(data[0]) - nat_tot = int(data[1]) - continue - - iteration = (i - 1) // 4 - counter = (i-1) % 4 - x = iteration // nat_tot - y = iteration % nat_tot - - if counter > 0: - for new_x in np.arange(superstruct.N_atoms)[itau == x]: - fc[3 * new_x + counter -1, 3*y: 3*y + 3] = [float(fx) for fx in data] - fc[3*y: 3*y + 3, 3 * new_x + counter -1] = [float(fx) for fx in data] - # counter += 1 - - # if counter == 3: - # # Save the FC in the correct blocks - # counter = 0 - # for ia, ib in blocks: - # fc[3*ia : 3*ia + 3, 3*ib: 3*ib + 3] = FC_TMP - # fc[3*ib : 3*ib + 3, 3*ia: 3*ia + 3] = FC_TMP - - - # if len(data) == 2: - # #x = int(data[0]) - 1 - # #y = int(data[1]) - 1 - # #x = itau[x] - # counter = 0 - - # # Get the blocks - # blocks = [] - # #print(x, y) - # DR = self.structure.coords[x, :] - superstruct.coords[y,:] - # for ia in range(superstruct.N_atoms): - # if unit_cell_itau[itau[ia]] != x: - # continue - # for ib in range(superstruct.N_atoms): - # if unit_cell_itau[itau[ib]] != unit_cell_itau[itau[y]]: - # continue - - # # Check if the two ia and ib are the correct block - # delta_r = superstruct.coords[ia, :] - superstruct.coords[ib, :] - # dist = Methods.get_closest_vector(superstruct.unit_cell, DR - delta_r) - # if np.linalg.norm(dist) < __EPSILON__: - # blocks.append((ia,ib)) - - # elif len(data) == 3: - # FC_TMP[counter, :] = [float(fx) for fx in data] - # counter += 1 - - # if counter == 3: - # # Save the FC in the correct blocks - # counter = 0 - # for ia, ib in blocks: - # fc[3*ia : 3*ia + 3, 3*ib: 3*ib + 3] = FC_TMP - # fc[3*ib : 3*ib + 3, 3*ia: 3*ia + 3] = FC_TMP - - - # Now transform back in real space - q_tot = symmetries.GetQGrid(self.structure.unit_cell, supercell) - dynq = GetDynQFromFCSupercell(fc, np.array(q_tot), self.structure, superstruct, itau) - self.dynmats = [None] * len(q_tot) - self.q_tot = q_tot - self.q_stars = [q_tot] - - for iq in range(len(q_tot)): - self.dynmats[iq] = dynq[iq, :, :] - - self.AdjustQStar() - - - def ForcePositiveDefinite(self): - """ - FORCE TO BE POSITIVE DEFINITE - ============================= - - This method force the matrix to be positive defined. - Usefull if you want to start with a matrix for a SCHA calculation. - - It will take the Dynamical matrix and rebuild it as - - .. math:: - - \\Phi'_{ab} = \\sqrt{M_aM_b}\sum_{\mu} |\omega_\mu^2| e_\\mu^a e_\\mu^b - - - In this way the dynamical matrix will be always positive definite. - """ - - # Prepare the masses matrix - mass1 = np.zeros( 3*self.structure.N_atoms) - for i in range(self.structure.N_atoms): - mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] - - _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) - _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() - - for iq in range(len(self.dynmats)): - # Diagonalize the matrix - w, pols = self.DyagDinQ(iq) - - matrix = np.einsum("i, ji, ki", w**2, pols, np.conj(pols)) * np.sqrt(_m1_ * _m2_) - self.dynmats[iq] = matrix - - - def ForcePositiveDefinite_2(self): - """ - FORCE TO BE POSITIVE DEFINITE - ============================= - - This method force the matrix to be positive defined. - Usefull if you want to start with a matrix for a SCHA calculation. - - It will take the Dynamical matrix and rebuild it as - - .. math:: - - \\Phi'_{ab} = \\sqrt{M_aM_b}\sum_{\mu} (\omega_\mu + \\min_\\mu \\omega)^2 e_\\mu^a e_\\mu^b - - - In this way the dynamical matrix will be always positive definite. - """ - - # Prepare the masses matrix - mass1 = np.zeros( 3*self.structure.N_atoms) - for i in range(self.structure.N_atoms): - mass1[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] - - _m1_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)) - _m2_ = np.tile(mass1, (3 * self.structure.N_atoms, 1)).transpose() - - - numq=len(self.dynmats) - w=np.zeros((numq,3*self.structure.N_atoms), dtype = np.float64) - pols=np.zeros((numq,3*self.structure.N_atoms,3*self.structure.N_atoms), dtype = np.complex128 ) - - for iq in range(numq): - # Diagonalize the matrix - w[iq,:], pols[iq,:,:] = self.DyagDinQ(iq) - - - fact=np.amin(w) - - if fact < 0.0 : - w+=np.abs(fact)*0.1 - - for iq in range(numq): - v=pols[iq,:,:] - fr=w[iq,:] - matrix = np.einsum("i, ji, ki", fr**2, v, np.conj(v)) * np.sqrt(_m1_ * _m2_) - self.dynmats[iq] = matrix - - - - def GetRamanResponce(self, pol_in, pol_out, T = 0): - r""" - RAMAN RESPONSE - ============== - - Evaluate the raman response using the Mauri-Lazzeri equation. - This subroutine needs the Raman tensor to be defined, and computes the intensity for each mode. - It returns a list of intensity associated to each mode. - - .. math:: - - I_{\nu} = \left| \sum_{xy} \epsilon_x^{(1)} A^\nu_{xy} \epsilon_y^{(2)}\right|^2 \frac{n_\nu + 1}{\omega_\nu} - - Where :math:`\epsilon` are the polarization vectors of the incoming/outcoming light, :math:`n_\nu` is the bosonic - occupation number associated to the :math:`\nu` mode, and :math:`A^\nu_{xy}` is the Raman tensor in the mode rapresentation - - Parameters - ---------- - pol_in : ndarray 3 - The polarization versor of the incominc electric field - pol_out : ndarray 3 - The polarization versor of the outcoming electric field - T : float - The tempearture of the calculation - - Results - ------- - ndarray (nmodes) - Intensity for each mode of the current dynamical matrix. - """ - - K_to_Ry=6.336857346553283e-06 - - - if self.raman_tensor is None: - raise ValueError("Error, to get the raman responce the raman tensor must be defined") - - w, pol_vects = self.DyagDinQ(0) - - # Get the mass array - _m_ = np.zeros( 3*self.structure.N_atoms) - for i in range(self.structure.N_atoms): - _m_[ 3*i : 3*i + 3] = self.structure.masses[ self.structure.atoms[i]] - - # Apply translation - trans = Methods.get_translations(pol_vects, self.structure.get_masses_array()) - pol_vects[:, trans] = 0 - - # The super sum - #print np.shape(self.raman_tensor), np.shape(pol_vects), np.shape(_m_), np.shape(pol_in), np.shape(pol_out) - I = np.einsum("ijk, kl, k, i, j", self.raman_tensor, pol_vects, 1/np.sqrt(_m_), pol_in, pol_out) - - # Get the bosonic occupation number - n = np.zeros(len(w)) - if T > 0: - beta = 1 / (K_to_Ry*T) - n = 1 / (np.exp(beta * w) - 1.) - - return np.abs(I**2) * (1. + n) / w - - def GetIRIntensities(self): - """ - GET THE IR INTENSITIES - ====================== - - This function uses the effective charges to compute the infrared responce. - - A list of value is returned, at each index the IR intensity of the - relative mode. - """ - - if self.effective_charges is None: - raise ValueError("Error, I cannot compute IR intensities without effective charges") - - w, pols = self.DyagDinQ(0) - m = self.structure.get_masses_array() - - - # Get the eigendisplacement z - nat3, nmodes = np.shape(pols) - z = np.zeros( (nmodes, self.structure.N_atoms, 3), dtype = np.float64) - for i in range(self.structure.N_atoms): - z[:, i, :] = pols[3*i: 3*(i+1), :].T / np.sqrt(m[i]) - - # Get the I_mu,i where mu is the mode and i is the polarization of the light - I = np.einsum("cbd, acd->ab", self.effective_charges, z) - # Average over polarizations - I = np.sum( I*I, axis = 1) * 2 - - return I - - def GetIRActivityVector(self): - """ - GET THE IR VECTOR - ================= - - This vector returns the activity of the infrared mode. - It is the matrix element to compute the responce function of the IR experiment. - - Results - ------- - v_ir : ndarray(size = (3, 3*natoms), dtype = np.double) - The ir activity amplitude for each polarization mode, for each polarizations of the incoming field - """ - - if self.effective_charges is None: - raise ValueError("Error, I cannot compute IR intensities without effective charges") - - w, pols = self.DyagDinQ(0) - m = self.structure.get_masses_array() - - # Get the eigendisplacement z - nat3, nmodes = np.shape(pols) - z = np.zeros( (nmodes, self.structure.N_atoms, 3), dtype = np.float64) - for i in range(self.structure.N_atoms): - z[:, i, :] = pols[3*i: 3*(i+1), :].T / np.sqrt(m[i]) - - # Get the I_mu,i where mu is the mode and i is the polarization of the light - v_ir = np.einsum("cbd, acd->ba", self.effective_charges, z) - - return v_ir - - - def GetRamanVector(self, pol_in, pol_out): - r""" - GET THE RAMAN VECTOR - ==================== - - Get the Raman vector. It is the vector obtained from the Raman Tensor: - - .. math:: - - v_\nu = \sum_{xy} \epsilon^{(1)}_x \epsilon_y^{(2)} A^{\nu}_{xy} - - This is defined in real space. - - Parameters - ---------- - pol_in : ndarray(size = 3) - Incoming polarization - pol_out : ndarray(size = 3) - Outcoming polarization - Results - ------- - vnu : ndarray(size = 3*nat) - The raman intensity vector along each atomic displacement. - """ - - if self.raman_tensor is None: - raise ImportError("Error, the raman tensor is not defined.") - - v = np.einsum("ija, i, j", self.raman_tensor, pol_in, pol_out) - - # Take out the translations from v - #t1 = np.tile(np.array([1,0,0], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() - #t2 = np.tile(np.array([0,1,0], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() - #t3 = np.tile(np.array([0,0,1], dtype = np.float64), (self.structure.N_atoms, 1)).ravel() - - #v -= t1.dot(v) - #v -= t2.dot(v) - #v -= t3.dot(v) - - nat = np.shape(self.raman_tensor)[-1] // 3 - dtype = type(v[0]) - - trans = np.eye(3*nat, dtype = dtype) - for i in range(3): - v1 = np.zeros(3*nat, dtype = dtype) - v1[3*np.arange(nat)+i] = 1 - v1 /= np.sqrt(v1.dot(v1)) - - trans -= np.outer(v1,v1) - - return trans.dot(v) - - def GetRamanActive(self, use_spglib = False): - """ - This simple subroutines tries to guess by symmetry analisys which mode is active or not. - If a raman tensor is present, it will be used to test the activity, otherwise, a random one will - be generated - - Parameters - ---------- - use_spblib: bool - If True the spglib library is used to initialize symmetries. - Usefull if the phonon matrix is in a super cell. - - Results - ------- - raman_activity_mask : ndarray(size = (3*nat), dtype = bool) - A mask that is False or True if a mode in the unit cell is Raman-active or not. - """ - - there_is_raman_tensor = True - if self.raman_tensor is None: - there_is_raman_tensor = False - self.raman_tensor = np.zeros((3,3, 3*self.structure.N_atoms), dtype = np.double) - self.raman_tensor[:,:,:] = np.random.uniform( size = self.raman_tensor.shape) - - # Get the symmetries - qe_sym = symmetries.QE_Symmetry(self.structure) - if use_spglib: - qe_sym.SetupFromSPGLIB() - else: - qe_sym.SetupQPoint() - - # Symmetrize the effective charges - qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) - - # Save a debugging one - self.save_qe("Raman") - - # Simulate the Raman signal for all possible incoming and outcoming polarizations - res = np.zeros(self.structure.N_atoms * 3, dtype = np.double) - for i, j in itertools.product(range(3) , range(3)): - pol_in = np.zeros(3) - pol_in[i] = 1 - pol_out = np.zeros(3) - pol_out[j] = 1 - - res += self.GetRamanResponce(pol_in, pol_out) - - print("total_raman_res:", res) - - is_raman_active = res > 1e-5 - - # Delete the random raman tensor if any - if not there_is_raman_tensor: - self.raman_tensor = None - - return is_raman_active - - - - def GenerateSupercellDyn(self, supercell_size, img_thr = 1e-6): - """ - GENERATE SUPERCEL DYN - ===================== - - This method returns a Phonon structure as it was computed directly in the supercell. - - - NOTE: For now this neglects bohr effective charges - - Parameters - ---------- - supercell_size : array int (size=3) - the dimension of the cell on which you want to generate the new - Phonon - img_thr def 1e-6 - - Results - ------- - dyn_supercell : Phonons() - A Phonons class of the supercell - - """ - # First check if the q vectors are compatible with the supercell - if not symmetries.CheckSupercellQ(self.structure.unit_cell, supercell_size, self.q_tot): - print("Q points:", self.q_tot) - print("Supercell size:", supercell_size) - print("Unit cell:", self.structure.unit_cell) - raise ValueError("Error, the list of q point does not match the given supercell.") - - super_struct = self.structure.generate_supercell(supercell_size) - - dyn_supercell = Phonons(super_struct, nqirr = 1, force_real = True) - - dyn_supercell.dynmats[0] = self.GetRealSpaceFC(supercell_size, img_thr = img_thr) - - return dyn_supercell - - - def GetMatrixCFFT(self): - """ - Generate the dynamical matrix ready for the Fast Fourier Transform. - This is an alternative way to go in real space. - NOTE: Use only for debug purpouses - """ - - s1, s2, s3 = self.GetSupercell() - nat = self.structure.N_atoms - output_dyn = np.zeros((s1, s2, s3, 3 * nat, 3 * nat), dtype = np.complex128, order = "F") - - super_struct = self.structure.generate_supercell((s1,s2,s3)) - bg = super_struct.get_reciprocal_vectors() / (2 * np.pi) - - for iq, q in enumerate(self.q_tot): - x_vect = Methods.covariant_coordinates(bg, q) - x1 = int((x_vect[0] + s1) % s1 + .5) - x2 = int((x_vect[1] + s2) % s2 + .5) - x3 = int((x_vect[2] + s3) % s3 + .5) - - #print("Q = ", q, "| xv:", x_vect, "x = ", x1, x2,x3) - - output_dyn[x1, x2, x3, :, :] = self.dynmats[iq] - - return output_dyn - - - def ExtractRandomStructures(self, size=1, T=0, isolate_atoms = [], project_on_vectors = None, - lock_low_w = False, remove_non_isolated_atoms = False, sobol = False, sobol_scramble = False, sobol_scatter = 0.0): - """ - EXTRACT RANDOM STRUCTURES - ========================= - - This method is used to extract a pool of random structures according to the current dinamical matrix. - - Parameters - ---------- - size : int - The number of structures to be generated - T : float - The temperature for the generation of the ensemble - isolate_atoms : list, optional - A list of the atom index. Only the atoms present in this list will be randomize. - If remove_non_isolated_atoms is True, then the output structures contain only non isolated atoms. - project_on_vectors : ndarray - Vectors in Cartesian Space on which the random displacements are projected. Usefull if you want to remove some - mode or atomic motion. - lock_low_w : bool - If True, frequencies below __EPSILON_W__ are fixed. - remove_non_isolated_atoms : bool - If true it removes atoms non included in the isolate_atoms list (if not empty) - sobol : bool, optional (Default = False) - Defines if the calculation uses random Gaussian generator or Sobol Gaussian generator. - sobol_scramble : bool, optional (Default = False) - Set the optional scrambling of the generated numbers taken from the Sobol sequence. - sobol_scatter : real (0.0 to 1) (Deafault = 0.0) - Set the scatter parameter to displace the Sobol positions randommly. - - Returns - ------- - list - A list of Structure.Structure() - """ - K_to_Ry=6.336857346553283e-06 - - def sobol_norm_rand(size,n_modes,scramble=False,sobol_salt=0.0): # **** Diegom_test **** adding random 'salt' - Sobol = Moro() - #data = Sobol.sobol_modes(size,n_modes,scramble=scramble) -# If n_modes is bigger than 21201 comment upper line and uncomment lower line. This will be a strange ocurrence due to the fact that 21201 vibrational eigenmodes implies a dinamical matrix with more than 449482401 elements. - data = Sobol.sobol_big(size,n_modes,scramble=scramble) - if (sobol_salt!=0.0): - for i in range(size): - for j in range(n_modes): - data[i][j]=data[i][j]+(np.random.rand()-0.5)*sobol_salt - return data - - # Check if isolate atoms is good - if len(isolate_atoms): - if np.max(isolate_atoms) >= self.structure.N_atoms: - raise ValueError("Error, index in isolate_atoms out of boundary") - - # Now extract the values - ws, pol_vects = self.DiagonalizeSupercell() - super_structure, itau = self.structure.generate_supercell(self.GetSupercell(), get_itau= True) - - # get the new isolated_atoms in the supercell - if len(isolate_atoms): - new_isolate_atoms = [] - for i, it in enumerate(itau): - if it in isolate_atoms: - new_isolate_atoms.append(i) - - - # Remove translations - trans_mask = Methods.get_translations(pol_vects, super_structure.get_masses_array()) - - # Exclude also other w = 0 modes - if lock_low_w: - locked_original = np.abs(ws) < __EPSILON_W__ - if np.sum(locked_original.astype(int)) > np.sum(trans_mask.astype(int)): - trans_mask = locked_original - - ws = ws[~trans_mask] - pol_vects = pol_vects[:, ~trans_mask] - - nat = self.structure.N_atoms * np.prod(self.GetSupercell()) - - # Check that the matrix is positive definite - if any([w < 0 for w in ws]): - ERR_MSG = """ - Error, the current matrix is not positive definite. - I cannot extract a random ensamble. - If you want to skip this error, - consider calling the method ForcePositiveDefinite() before extracting the ensemble. - - It could also be a consequence of a sum rule not well imposed. - Try to run Symmetrize() to force the sum rule. - """ - - raise ValueError(ERR_MSG) - - n_modes = len(ws) - if T == 0: - a_mu = 1 / np.sqrt(2* ws) * BOHR_TO_ANGSTROM - else: - beta = 1 / (K_to_Ry*T) - a_mu = 1 / np.sqrt( np.tanh(beta*ws / 2) *2* ws) * BOHR_TO_ANGSTROM - - # Prepare the random numbers - size = int(size) - if (not sobol): - rand = np.random.normal(size = (size, n_modes)) - elif (sobol): - rand = sobol_norm_rand(size, n_modes, scramble = sobol_scramble, sobol_salt = sobol_scatter) # ***** Diegom_test ****** - else: - raise ValueError('sobol is not True or False') # This should never raise - - # Get the masses for the final multiplication - mass1 = np.tile(super_structure.get_masses_array(), (3, 1)).T.ravel() - - # TODO: I believe this is the heavy part of the extraction - total_coords = np.einsum("ij, i, j, kj->ik", pol_vects, 1/np.sqrt(mass1), a_mu, rand) - - - - # Project the displacements along the selected modes - if not project_on_vectors is None: - check, N_proj = np.shape(project_on_vectors) - if check != 3*nat: - print("Expected nat: " + str(nat) + " project_on_modes nat: " + str(check/3)) - raise ValueError("Error, the input project_on_modes has a wrong shape") - - for confid in range(size): - new_coords = np.zeros( nat*3, dtype = np.float64) - for i in range(N_proj): - new_coords += project_on_vectors[:, i].dot(total_coords[:, confid]) * project_on_vectors[:, i] - - total_coords[:, confid] = new_coords - - # Prepare the structures - final_structures = [] - for i in range(size): - tmp_str = super_structure.copy() - # Prepare the new atomic positions - - - # TODO: THis is the heavy part, probably we can replace this for loop - tmp_str.coords[:,:] += total_coords[:,i].reshape((tmp_str.N_atoms, 3)) - #for k in range(tmp_str.N_atoms): - # tmp_str.coords[k,:] += total_coords[3*k : 3*(k+1), i] - - # Check if you must to pop some atoms: - if len (isolate_atoms): - - if remove_non_isolated_atoms: - tmp_str = tmp_str.isolate_atoms(new_isolate_atoms) # Use the list in the supercell - else: - tmp_str.N_atoms = len(isolate_atoms) * np.prod(self.GetSupercell()) - new_coords = tmp_str.coords.copy() - for j, x in enumerate(isolate_atoms): - tmp_str.coords[j,:] = new_coords[x,:] - final_structures.append(tmp_str) - - - return final_structures - - def GetHarmonicFreeEnergy(self, T, allow_imaginary_freq = False, w_pols = None): - """ - COMPUTE THE HARMONIC QUANTUM FREE ENERGY - ======================================== - - The dynamical matrix can be used to obtain the vibrational contribution - to the Free energy. - - ..math:: - - F(\\Phi) = \\sum_\mu \\left[\\frac{\\hbar \\omega_\\mu}{2} + kT \\ln\\left(1 + e^{-\\beta \hbar\\omega_\\mu}\\right)\\right] - - - Acustic modes at Gamma are discarded from the summation. - An exception is raised if there are imaginary frequencies. - - Parameter - --------- - T : float - Temperature (in K) of the system. - w_pols : (w, pols) - If given, it should be a len=2 tuple with the frequencies and the polarization - vectors as obtaind from DiagonalizeSupercell method - - Returns - ------- - fe : float - Free energy (in Ry) at the given temperature. - """ - - K_to_Ry=K_B / RY_TO_EV#6.336857346553283e-06 - - if w_pols is None: - w, pols = self.DiagonalizeSupercell() - else: - w = w_pols[0].copy() - pols = w_pols[1].copy() - - # Remove translations - tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) - - # Exclude also other w = 0 modes (good for rotations) - locked_original = np.abs(w) < __EPSILON__ - if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): - tmask = locked_original - - w = w[ ~tmask ] - - # if imaginary frequencies are allowed, put w->0 - if allow_imaginary_freq: - w[w<0] = __EPSILON__ - - if len(w[w < 0]) >= 1: - raise ValueError("Error while computing the free energy, the dynamical matrix has imaginary frequencies") - - # Zero point energy - free_energy = np.sum( w / 2) - - # Add also the entropy - if T > 0: - beta = 1 / (K_to_Ry * T) - free_energy += np.sum( 1 / beta * np.log(1 - np.exp(-beta * w))) - - return free_energy - - def get_harmonic_entropy(self, T, w_pols = None, small_w_freq = __EPSILON_W__, allow_imaginary_freq = False): - """ - Get the harmonic entropy. - - Parameters - ---------- - T : float - Temperature in K - w_pols : (ndarray, ndarray) - Frequencies and polarization vectors of the diagonalized dynamical matrix. - Obtained from self.DiagonalizeSupercell - This way the diagonalization is performed only once if computed in a cycle. - small_w_freq : float - If provided, all the frequencies below this value are neglected - allow_imaginary_freq : bool - If true, imaginary frequencies are ignored. - - Results - ------- - entropy : float - The entropy in Ry / K for the whole supercell structure - """ - - if w_pols is None: - w, pols = self.DiagonalizeSupercell() - else: - w, pols = w_pols - - # Remove translations - tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) - - # Exclude also other w = 0 modes (good for rotations) - locked_original = np.abs(w) < __EPSILON_W__ - if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): - tmask = locked_original - - w = w[ ~tmask ] - - if allow_imaginary_freq: - w = w[w > 0] - - # Check the presence of imaginary frequencie - if not np.all( w>0): - raise ValueError("Error, the entropy is not defined when the dynamical matrix has imaginary frequencies!") - - beta = RY_TO_KELVIN / T - Kb_ry = K_B / RY_TO_EV - - - # Compute the entropy for each mode - exp_factor = np.exp(-beta * w) - entropy = -Kb_ry * np.log(1 - exp_factor) + Kb_ry* beta*w * (exp_factor / (1 - exp_factor)) - #av_energy = Kb_ry * beta * w / (2 * np.tanh(beta * w / 2)) - #entropy = av_energy - Kb_ry * np.log(2*np.sinh(beta * w / 2)) - - - return np.sum(entropy) - - def get_harmonic_heat_capacity(self, T, w_pols = None, small_w_freq = __EPSILON_W__, allow_imaginary_freq = False): - r""" - HEAT CAPACITY - ============= - - Compute the (quantum) harmonic heat capacity by deriving the entropy with respect to temperature - - - .. math:: - - C_v = \sum_\mu k_b \beta^2\omega_\mu^2 \frac{e^{\beta\omega_\mu}}{(e^{\beta\omega_\mu} - 1)^2} - - Parameters - ---------- - T : float - Temperature in K - w_pols : (ndarray, ndarray) - Frequencies and polarization vectors of the diagonalized dynamical matrix. - Obtained from self.DiagonalizeSupercell - This way the diagonalization is performed only once if computed in a cycle. - small_w_freq : float - If provided, all the frequencies below this value are neglected - allow_imaginary_freq : bool - If true, imaginary frequencies are ignored. - - Results - ------- - heat_capacity : float - The heat_capacity in Ry / K for the whole supercell structure - """ - - if T < __EPSILON__: - return 0 - - - if w_pols is None: - w, pols = self.DiagonalizeSupercell() - else: - w, pols = w_pols - - # Remove translations - tmask = Methods.get_translations(pols, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) - - # Exclude also other w = 0 modes (good for rotations) - locked_original = np.abs(w) < __EPSILON_W__ - if np.sum(locked_original.astype(int)) > np.sum(tmask.astype(int)): - tmask = locked_original - - w = w[ ~tmask ] - - if allow_imaginary_freq: - w = w[w > 0] - - # Check the presence of imaginary frequencie - if not np.all( w>0): - raise ValueError("Error, the entropy is not defined when the dynamical matrix has imaginary frequencies!") - - beta = RY_TO_KELVIN / T - Kb_ry = K_B / RY_TO_EV - - # Compute the specific heat for each mode - exp_factor2 = np.exp(beta * w) - cv = Kb_ry * (beta*w)**2 * exp_factor2 / (exp_factor2 - 1)**2 - - # Sum the result in the full supercell - return np.sum(cv) - - - - def get_phonon_dos(self, w_array, smearing, exclude_acoustic = True, use_cm = False, w_pols = None): - r""" - GET THE PHONON DOS - ================== - - This function plots the phonon dos. - - Parameters - ---------- - w_array : ndarray - The frequencies at which you want to compute the phonon dos [in Ry] (or cm-1, see use_cm). - smearing : float - The smearing [in Ry] (or cm-1, see use_cm). - exclude_acoustic : bool - If true, the acoustic modes at gamma are excluded. - use_cm : bool - If true, the frequency array and the smearing is supposed to be given in cm-1 - instead of Ry. - w_pols : (frequencies, polarizations) - If provided, it avoids performing a new diagonalization - - Results - ------- - dos : ndarray(size = (w_array), dtype = np.float64) - The phonon density of state. - """ - - if use_cm: - w_array = w_array.copy() / RY_TO_CM - smearing /= RY_TO_CM - - dos = np.zeros(np.shape(w_array), dtype = np.float64) - if w_pols is None: - w, p = self.DiagonalizeSupercell() - trans = Methods.get_translations(p, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) - w = w[~trans] - else: - w, p = w_pols - - for w0 in w: - dos += np.exp( -(w_array - w0)**2 / (2 * smearing*smearing)) / np.sqrt(2 * np.pi * smearing*smearing) - - - return dos - - - def get_two_phonon_dos(self, w_array, smearing, temperature, q_index = 0, exclude_acoustic = True): - r""" - COMPUTE THE TWO PHONON DOS - ========================== - - This subroutine compute the two phonon DOS of the given dynamical matrix. - It analyzes all possible phonon-phonon scattering and decayment to - build the two body density of states. This can be used to get an idea how much - each phonon can interact with the other in presence of anharmonicity just - considering energy conservation law and Bose-Einstein statistic. - - The DOS equation is - - .. math :: - - \rho^{(2)}(q, \omega) = \int d^3k_1d^3k_2\sum_{\mu\nu}\left[(n_\mu + n_\nu + 1)\delta(\omega - \omega_\mu(k_1) - \omega_\nu(k_2))\delta^3(\vec k_1 + \vec k_2 - \vec q)\right. - - \left. + 2 (n_\mu - n_\nu)\delta(\omega - \omega_\mu(k_1) + \omega_\nu(k_2))\delta^3(\vec q + \vec k_1 - \vec k_2)\right] - - - Where the Delta function are replaced by the Lorenzian shape to consider a smearing. - - Parameters - ---------- - w_array : ndarray - The frequency of the dos - smearing : float - The smearing used to compute the DOS. - To converge the smearing you need to study the limit - :math:`\lim_{\sigma\rightarrow 0} \lim_{N_q\rightarrow\infty} DOS` - q_index : int - The q point in which to compute the phonon DOS. - You must pass the index that matches the q_tot list. - exclude_acustic : bool, default = False - If True the acoustic modes at gamma are neglected in the DOS. - NOTE: if you have few q points, you will not see the frequencies of the real mode in the DOS! - - Results - ------- - dos : ndarray - The array of the density of state returned. Same shape as w_array - """ - K_to_Ry=6.336857346553283e-06 - - - q_vector = self.q_tot[q_index] - bg = Methods.get_reciprocal_vectors(self.structure.unit_cell) - - nat = self.structure.N_atoms - - - DOS = np.zeros( np.shape(w_array), dtype = np.float64) - for k1_i, k1 in enumerate(self.q_tot): - # Get the k vectors from the delta relations - k2_dists = [Methods.get_min_dist_into_cell(bg, k1, q_vector - x) for x in self.q_tot] - k2p_dists = [Methods.get_min_dist_into_cell(bg, k1, x - q_vector) for x in self.q_tot] - - k2_i = np.argmin(k2_dists) - k2p_i = np.argmin(k2p_dists) - - k2 = self.q_tot[k2_i] - k2p = self.q_tot[k2p_i] - - # Get the frequencies at the correct Q points - _wmu_, _pmu_ = self.DyagDinQ(k1_i) - _wnu_, _pnu_ = self.DyagDinQ(k2_i) - _wnu2_, _pnu2_ = self.DyagDinQ(k2p_i) - - trans1 = Methods.get_translations(_pmu_, self.structure.get_masses_array()) - trans2 = Methods.get_translations(_pnu_, self.structure.get_masses_array()) - trans3 = Methods.get_translations(_pnu2_, self.structure.get_masses_array()) - - # Sum over mu nu - for mu in range(3*nat): - if exclude_acoustic and trans1[mu]: - continue - w_mu = _wmu_[mu] - n_mu = 0 - if temperature > 0: - n_mu = 1 / (np.exp(w_mu / (temperature * K_to_Ry)) - 1) - for nu in range(3*nat): - w_nu = _wnu_[nu] - n_nu = 0 - if temperature > 0: - n_nu = 1 / (np.exp(w_nu / (temperature * K_to_Ry)) - 1) - - chi1 = 0 - if not (exclude_acoustic and trans2[nu]): - chi1 = 2*smearing * w_array * (w_mu + w_nu) * (n_nu + n_mu + 1) - chi1 /= 4 * smearing**2*w_array**2 + ( (w_mu + w_nu)**2 - w_array**2)**2 - chi1 /= w_mu * w_nu - - w_nu = _wnu2_[nu] - if temperature > 0: - n_nu = 1 / (np.exp(w_nu / (temperature * K_to_Ry)) - 1) - - chi2 = 0 - if not (exclude_acoustic and trans3[nu]): - chi2 = 2 * smearing * w_array * (w_mu - w_nu) * (n_nu - n_mu) - chi2 /= 4*smearing**2 *w_array**2 + ( (w_nu - w_mu)**2 - w_array**2)**2 - chi2 /= w_mu*w_nu - - DOS += chi1 + chi2 - - return DOS / 2 # We need a 1/2 factor - - - def get_phonon_propagator(self, w_array, smearing = 1e-5, only_gamma = False): - r""" - GET THE SINGLE PHONON PROPAGATOR - ================================ - - This method computes the single phonon harmonic propagator. - It is computed in the supercell - - .. math:: - - G_{ab}(\omega) = \sum_{\mu}\frac{e_\mu^a e_\mu^b}{(\omega - i\eta)^2 - \omega_\mu^2} - - This is in real space - - Parameters - ---------- - - w_array : ndarray - The frequencies at which you want to compute the propagator. - In [Ry] - - smearing : float - The :math:`\eta` value. - - only_gamma : bool - If True, only the phonons at gamma will be used - - Results - ------- - - G_abw : ndarray(size = (3nat, 3nat, len(w))) - The real space green function - - """ - - if not only_gamma: - w, pols = self.DiagonalizeSupercell() - - super_struct = self.structure.generate_supercell(self.GetSupercell()) - trans = Methods.get_translations(pols, super_struct.get_masses_array()) - nat = super_struct.N_atoms - else: - w, pols = self.DyagDinQ(0) - trans = Methods.get_translations(pols, self.structure.get_masses_array()) - nat = self.structure.N_atoms - - G_final = np.zeros( (3*nat, 3*nat, len(w_array)), dtype = np.complex128) - - w = w[~trans] - pols = pols[:, ~trans] - - nmodes = len(w) - for mu in range(nmodes): - epol = np.outer(pols[:, mu], pols[:, mu]) - freq = 1 / ((w_array + 1j*smearing)**2 - w[mu]**2) - G_final[:,:,:] += np.einsum("ab,c ->abc", epol, freq) - - return G_final - - def get_two_phonon_propagator(self, w, T, smearing = 1e-5): - r""" - GET THE TWO PHONONS PROPAGATOR - ========================= - - This subroutine computes the two phonons propagator defined as - - .. math :: - - \chi_{\mu\nu}(z, q) = \frac{1}{\beta} \sum_{l} G_\mu(i\Omega_l, \vec k) G_\nu(z - i\Omega_l, \vec q - \vec k) - - \chi_{\mu\nu}(z, q) = \frac{\hbar}{2\omega_\mu\omega_\nu}\left[ \frac{(\omega_\nu + \omega_\mu)[1 + n_\nu + n_\mu]}{(\omega_\nu + \omega_\mu)^2 - z^2} - \frac{(\omega_\nu - \omega_\mu)[n_\nu - n_\mu]}{(\omega_\nu - \omega_\mu)^2 -z^2}\right] - - This is the phonon dynamical bubble. - This is computed in the polarization basis. - The translational modes are discarted. - - - - Parameters - ---------- - w : ndarray - The values of the dynamical frequency to compute the phonon propagator. - T : float - The temperature to compute the bosonic occupation numbers :math:`n_\mu`. - semaring : float, default = 1e-5 - The smearing [Ry] to achieve a faster convergence with the k-mesh sampling. - - Result - ------ - chi : ndarray(size=(3*nat, 3*nat), dtype = np.complex128) - The bubble phonon propagator - """ - - - K_to_Ry=6.336857346553283e-06 - - - # Get the frequencies at the correct Q points - _w_, _p_ = self.DiagonalizeSupercell() - - # Get the translational vectors - trans = Methods.get_translations(_p_, self.structure.generate_supercell(self.GetSupercell()).get_masses_array()) - - _w_ = _w_[~trans] - _p_ = _p_[~trans] - - nmodes = len(_w_) - ChiMuNu = np.zeros( (nmodes, nmodes, len(w)), dtype = np.complex128) - - # Sum over mu nu - for mu, w_mu in enumerate(_w_): - n_mu = 0 - dn_dw = 0 - if T > __EPSILON__: - n_mu = 1 / (np.exp(w_mu / (T * K_to_Ry)) - 1) - dn_dw = -n_mu / (T * K_to_Ry * (1 - np.exp(-w_mu / (T * K_to_Ry)))) - for nu, w_nu in enumerate(_w_): - n_nu = 0 - if T > __EPSILON__: - n_nu = 1 / (np.exp(w_nu / (T * K_to_Ry)) - 1) - - chi1 = np.zeros(np.shape(w), dtype = np.complex128) - chi2 = np.zeros(np.shape(w), dtype = np.complex128) - chi1 = (w_mu + w_nu) * (n_nu + n_mu + 1) - chi1 /= ( (w_mu + w_nu)**2 - (w - 1j*smearing)**2 ) - chi1 /= 2*w_mu*w_nu - - #if np.abs(w) < __EPSILON__ and np.abs(w_nu - w_mu) < __EPSILON__: - # chi2 = - dn_dw / (2*w_mu*w_nu) - #else: - chi2 = (w_mu - w_nu) * (n_nu - n_mu) - chi2 /= ( (w_nu - w_mu)**2 - (w - 1j*smearing)**2 ) - chi2 /= 2*w_mu*w_nu - - if np.isnan(chi1 + chi2).any(): - print("NaN value found in the propagator.") - print("NaN value error details:") - print("chi1: ", chi1) - print("chi2: ", chi2) - print("mu = %d, nu = %d" % (mu, nu)) - print("w_mu = %10.4f, n_mu = %10.4f" % (w_mu * RY_TO_CM, n_mu)) - print("w_nu = %10.4f, n_nu = %10.4f" % (w_nu * RY_TO_CM, n_nu)) - raise ValueError("Error, the propagator is NAN, check stdout for details.") - ChiMuNu[mu, nu, :] = chi1 + chi2 - - return ChiMuNu - - def get_energy_forces(self, structure, vector1d = False, real_space_fc = None, super_structure = None, supercell = None, - displacement = None, use_unit_cell = True, w_pols = None): - """ - COMPUTE ENERGY AND FORCES - ========================= - - This subroutine computes the harmonic energy and the forces - for the given dynamical matrix at harmonic level. - - .. math:: - - E = \frac 12 \\sum_{\\alpha\\beta} \\left(r_{\\alpha} - r^0_{\\alpha}\\right)\\Phi_{\\alpha\\beta} \\left(r_\\beta - r^0_\\beta\right) - - F_\\alpha = \\sum_{\\beta} \\Phi_{\\alpha\\beta} \\left(r_\\beta - r^0_\\beta\right) - - The energy is given in Rydberg, while the force is given in Ry/Angstrom - - NOTE: In this very moment it has been tested only at Gamma (unit cell) - - Parameters - ---------- - structure : Structure.Structure() - A unit cell structure in which energy and forces on atoms are computed - vector1d : bool, optional - If true the forces are returned in a reshaped 1d vector. - real_space_fc : ndarray 3nat_sc x 3nat_sc, optional (default None) - If provided the real space force constant matrix is not recomputed each time the - method is called. Usefull if you have to repeat this calculation many times. - You can get the real_space_fc using the method GetRealSpaceFC. - super_structure : Structure.Structure() - Optional, not required. If given is the superstructure used to compute the distance from the - target one. You can pass it to avoid regenerating it each time this subroutine is called. - If you do not pass it, you must provide the supercell size (if different than the unit cell) - super_cell : list of 3 items - This is the supercell on which compute the energy and force. If none it is inferred by the dynamical matrix. - displacement: - The displacements from the self average position to be used. It is not - necessary since they can be recomputed, however if provided, the calculation is faster. - It must be in Angstrom. - To speedup the calculations, many displacements can be provided, in the form: - displacement.shape = (N_config, 3*nat_sc) - where N_config are the number of configurations, nat_sc the atoms in the supercell - use_unit_cell : bool - If ture, do not compute the real space force constant matrix on the super cell. This is the fastest option. - Put it to false only for debugging purpouses. - w_pols : list of (w, pols) - If given, the frequencies and polarization vectors are not recomputed from scratch - - Returns - ------- - energy : float (or ndarray.shape(N_config)) - The harmonic energy (in Ry) of the structure - force : ndarray N_atoms x 3 or N_config, nat_sc, 3) - The harmonic forces that acts on each atoms (in Ry / A) - """ - - if supercell is None: - supercell = self.GetSupercell() - - # Convert the displacement vector in bohr - #A_TO_BOHR=np.float64(1.889725989) - if super_structure is None: - super_structure = self.structure.generate_supercell(supercell) - - # Get the displacement vector (bohr) - if displacement is None: - rv = structure.get_displacement(super_structure).reshape(structure.N_atoms * 3) * A_TO_BOHR - else: - rv = displacement * A_TO_BOHR - - # Check how many configurations - many_configs = False - if len(rv.shape) > 1: - many_configs = True - n_configs = rv.shape[0] - - # Fast computation - if use_unit_cell: - if w_pols is not None: - w = w_pols[0] - pols = w_pols[1] - else: - w, pols = self.DiagonalizeSupercell() - - # Correctly account for not positive definite dynamical matrices - w2 = w**2 * np.sign(w) - - m = np.tile(super_structure.get_masses_array(), (3,1)).T.ravel() - m_sqrt = np.sqrt(m) - - epols = np.einsum("ab, a -> ab", pols, m_sqrt) - x_mu = rv.dot(epols) - - # Check if more configurations needs to be used - - # TODO: add the possibility to pass several structures toghether - # to avoid computing many times the same passages - # This works only if the displacements are passed - if not many_configs: - energy = 0.5 * np.sum(x_mu**2 * w2) - forces = - epols.dot( w2 * x_mu) - else: - w2_tile = np.tile(w2, (n_configs, 1)) - energy = 0.5 * np.sum(x_mu**2 * w2_tile, axis = 1) - forces = - (w2_tile * x_mu).dot(epols.T) - else: - if real_space_fc is None: - real_space_fc = self.GetRealSpaceFC(supercell) - - if many_configs: - raise NotImplementedError("Error, use the use_unit_cell = True if you want to compute many configurations.") - - # Get the energy - energy = 0.5 * rv.dot ( np.real(real_space_fc)).dot(rv) - - # Get the forces (Ry/ bohr) - forces = - real_space_fc.dot(rv) - - nat_sc = self.structure.N_atoms * np.prod(supercell) - - # Translate the force in Ry / A - forces *= A_TO_BOHR - if not vector1d: - if not many_configs: - forces = forces.reshape( (nat_sc, 3)) - else: - forces = forces.reshape( (n_configs, nat_sc, 3)) - - return energy, forces - - - def GetRealSpaceFC(self, supercell_array = (1,1,1), super_structure = None, img_thr = 1e-6): - """ - GET THE REAL SPACE FORCE CONSTANT - ================================= - - This subroutine uses the fourier transformation to get the real space force constant, - starting from the fourer space matrix. - - .. math:: - - C_{k\\alpha,k'\\beta}(0, b) = \\frac{1}{N_q} \\sum_q \\tilde C_{k\\alpha k'\\beta}(q) e^{i\\vec q \\cdot \\vec R_b} - - Then the translationa property is applied. - - .. math:: - - C_{k\\alpha,k'\\beta}(a, b) = C_{k\\alpha,k'\\beta}(0, b-a) - - Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\\alpha` is the - cartesian indices. - - NOTE: This method just call the GetSupercellFCFromDyn, look at its documentation for further info. - - Returns - ------- - fc_supercell : ndarray 3nat_sc x 3nat_sc - The force constant matrix in the supercell. - If it is a supercell structure, it is use that structure to determine the supercell array - super_structure : Structure() - If given, it is used to generate the supercell. Note that in this - case the supercell_array argument is ignored - - """ - - nq = len(self.q_tot) - nat = self.structure.N_atoms - nat_sc = nat * nq - - if super_structure is None: - super_structure = self.structure.generate_supercell(supercell_array) - - # Check the consistency of the argument with the number of q point - if nat_sc != super_structure.N_atoms: - raise ValueError("Error, the super_structure number of atoms %d does not match %d computed from the q points." % (super_structure.N_atoms, nat_sc)) - - dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") - - # Fill the dynamical matrix - for i, q in enumerate(self.q_tot): - dynmat[i, :,:] = self.dynmats[i] - - fc = GetSupercellFCFromDyn(dynmat, np.array(self.q_tot), self.structure, super_structure, img_thr = img_thr) - return fc - -# -# # Define the number of q points, atoms and unit cell atoms -# nq = len(self.q_tot) -# nat = self.structure.N_atoms -# nat_sc = nq*nat -# -# # Check if the supercell array matches the number of q points -# if np.prod(supercell_array) != nq: -# raise ValueError("Error, the number of supercell %d must match the number of q points %d." % (np.prod(supercell_array), nq)) -# -# dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") -# fc = np.zeros((3*nat_sc, 3*nat_sc), dtype = np.complex128) -# -# print "NQ:", nq -# -# R_vectors_cart = np.zeros((nq,3), dtype = np.float64, order = "F") -# q_vect = np.zeros((nq,3), dtype = np.float64, order = "F") -# -# -# -# # Fill the dynamical matrix -# for i, q in enumerate(self.q_tot): -# dynmat[i, :,:] = self.dynmats[i] -# -# a_x = i % supercell_array[0] -# a_y = (i / supercell_array[0]) % supercell_array[1] -# a_z = i / (supercell_array[0] * supercell_array[1]) -# R_vectors_cart[i,:] = a_x * self.structure.unit_cell[0,:] + a_y * self.structure.unit_cell[1,:] + a_z * self.structure.unit_cell[2,:] -# -# q_vect[i,:] = 2*np.pi * q / self.alat -# -# -# # For now, to test, just the unit cell -# for i in range(nq): -# start_index = 3 * nat * i -# for j in range(nq): -# end_index = 3 * nat * j -# q_dot_R = np.einsum("ab, b", q_vect, R_vectors_cart[j,:] - R_vectors_cart[i,:]) -# #print "%d, %d => q dot R = " % (i, j), np.exp(1j * q_dot_R) -# fc[end_index: end_index + 3*nat, start_index: start_index + 3*nat ] += np.einsum("abc, a", dynmat, np.exp(1j* q_dot_R)) / nq -# -# -# #np.sum(dynmat * np.exp(), axis = 0) / nq -# print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) -# -# return fc - - def GetSupercell(self): - """ - GET SUPERCELL - ============= - - Return the supercell along which this matrix has been generated. - - Results - ------- - supercell : list of 3 int - The supercell in each direction. - """ - return symmetries.GetSupercellFromQlist(self.q_tot, self.structure.unit_cell) - - def InterpolateMesh(self, mesh_dim, lo_to_splitting = False): - """ - INTERPOLATE THE DYNAMICAL MATRIX IN A FINER Q MESH - ================================================== - - This method employs the Tensor2 interpolateion functions - from the ForceTensor module to perform the interpolation. - - Parameters - ---------- - mesh_dim : list of int - The dimension of the q-mesh on which perform the interpolation. - - Results - ------- - new_dyn : Phonons.Phonons() - A new dynamical matrix defined on the desidered mesh. - """ - - # Setup the force constant tensor - current_mesh = self.GetSupercell() - t2 = ForceTensor.Tensor2(self.structure, self.structure.generate_supercell(current_mesh), current_mesh) - t2.SetupFromPhonons(self) - - out_dyn = t2.GeneratePhonons(mesh_dim, lo_to_splitting=lo_to_splitting) - return out_dyn - - - - - - def Interpolate(self, coarse_grid, fine_grid, support_dyn_coarse = None, - support_dyn_fine = None, symmetrize = False): - """ - INTERPOLATE THE DYNAMICAL MATRIX IN A FINER Q MESH - ================================================== - - This method interpolates the dynamical matrix in a finer mesh. - It is possible to use a different dynamical matrix as a support, - then only the difference of the current dynamical matrix - with the support is interpolated. In this way you can easier achieve convergence. - - NOTE: This method ignores effective charges. - If you want to account for effective charges you should use the ForceTensor.Tensor2 class - to interpolate. - - NOTE: This is going to be replaced with the InterpolateMesh function, - accounting properly for effective charges - - Parameters - ---------- - coarse_grid : ndarray(size=3, dtype = int) - The current q point mesh size - fine_grid : ndarray(size=3, dtype = int) - The final q point mesh size - support_dyn_coarse : Phonons(), optional - A dynamical matrix used as a support in the same q grid as this one. - Note that the q points must coincide with the one of this matrix. - support_dyn_fine : Phonons(), optional - The support dynamical matrix in the finer cell. - If given, the fine_grid is read - by the q points of this matrix, and must be compatible - with the fine_grid. - symmetrize : bool, optional - If true activate the symmetrization for the new matrix - - Results - ------- - interpolated_dyn : Phonons() - The dynamical matrix interpolated. - """ - - - # Check if the support dynamical matrix is given: - is_dync = support_dyn_coarse is not None - is_dynf = support_dyn_fine is not None - if is_dync != is_dynf: - raise ValueError("Error, you must provide both support matrix") - - nqtot = np.prod(fine_grid) - - # Get the q list - q_list = symmetries.GetQGrid(self.structure.unit_cell, fine_grid) - #print "The q list:" - #print q_list - - if is_dync and is_dynf: - # Check if the is_dynf has the correct number of q points - if nqtot != len(support_dyn_fine.q_tot): - raise ValueError("Error, the number of q points of the support must coincide with the fine grid") - - assert self.GetSupercell() == support_dyn_coarse.GetSupercell(), """ -Error, support dyn is defined on a different supercell - supercell self: {} - supercell support_dyn_coarse: {} -""".format(self.GetSupercell(), support_dyn_coarse.GetSupercell()) - - assert self.structure.N_atoms == support_dyn_coarse.structure.N_atoms, """ -Error, support_dyn is defined on a wrong structure. -""" - - # # Check if the support dyn course q points coincides - # bg = Methods.get_reciprocal_vectors(self.structure.unit_cell) - # for iq, q in enumerate(self.q_tot): - # if Methods.get_min_dist_into_cell(bg, q, support_dyn_coarse.q_tot[iq]) > __EPSILON__: - # # Get the NQIRR - # limit = iq - # for nqirr in range(len(self.q_stars)): - # limit -= len(self.q_stars[nqirr]) - # if limit < 0: - # break - - # print ("ERROR, NOT MATCHING Q IN STAR NUMBER ({}):".format(nqirr +1)) - # print ("self q1 = ", q) - # print ("support coarse q2 = ", support_dyn_coarse.q_tot[iq]) - # raise ValueError("Error, the coarse support grid as a q point that does not match the self one") - - - # Overwrite the q list - q_list = support_dyn_fine.q_tot[:] - - - # Prepare the super variables - if not is_dynf: - new_dynmat = Phonons(self.structure.copy(), nqtot) - new_dynmat.q_stars = [[]] - new_dynmat.initialized = True - new_dynmat.nqirr = 1 - new_dynmat.alat = self.alat - else: - new_dynmat = support_dyn_fine.Copy() - - - super_structure = self.structure.generate_supercell(fine_grid) - superstruct_coarse = self.structure.generate_supercell(coarse_grid) - - nat = self.structure.N_atoms - fcq = np.zeros( (len(self.q_tot), 3 * nat, 3*nat), dtype = np.complex128) - for iq, q in enumerate(self.q_tot): - fcq[iq, :, :] = self.dynmats[iq].copy() - #if is_dync: - # fcq[iq, :, :] -= support_dyn_coarse.dynmats[iq] - - # Get the real space force constant matrix - #r_fcq = GetSupercellFCFromDyn(fcq, np.array(self.q_tot), self.structure, super_structure) - r_fcq = GetSupercellFCFromDyn(fcq, np.array(self.q_tot), self.structure, superstruct_coarse) - - - if is_dync: - fcq = np.zeros( (len(self.q_tot), 3 * nat, 3*nat), dtype = np.complex128) - for iq, q in enumerate(self.q_tot): - fcq[iq, :, :] = support_dyn_coarse.dynmats[iq].copy() - r_fcq -= GetSupercellFCFromDyn(fcq, np.array(support_dyn_coarse.q_tot), self.structure, superstruct_coarse) - - #r_fcq = self.GetRealSpaceFC(coarse_grid) - - - q_star_i = 0 - passed_qstar = 0 - for iq, q in enumerate(q_list): - new_dynmat.q_tot[iq][:] = q - - # Use the same star as the support matrix - if is_dynf: - if iq - passed_qstar == len(support_dyn_fine.q_stars[q_star_i]): - q_star_i += 1 - passed_qstar = iq - - print ("WORKING ON:", q) - new_dynmat.q_stars[q_star_i].append(q) - new_dynmat.dynmats[iq] += InterpolateDynFC(r_fcq, coarse_grid, self.structure, self.structure.generate_supercell(coarse_grid), q) - - - new_dynmat.AdjustQStar() - - if symmetrize: - new_dynmat.Symmetrize() - - - if self.effective_charges is not None: - WARN_TXT=""" -WARNING: Effective charges are not accounted by this method - You should generate a ForceTensor.Tensor2 object - To account for the interpolation of long-range forces. - """ - - print(WARN_TXT) - warnings.warn(WARN_TXT, DeprecationWarning) - - - return new_dynmat - - - def AdjustQStar(self, use_spglib = False): - """ - ADJUST THE Q STAR - ================= - - This function uses the quantum espresso symmetry finder to - divide the q points into the proper q stars, reordering the current dynamical matrix. - - - Parameters - ---------- - use_spglib : bool - If true, the SPGLIB is used to perform the symmetrization. - Otherwise the quantum espresso default symmetry route is used. - """ - - # Initialize the symmetries - qe_sym = symmetries.QE_Symmetry(self.structure) - - if use_spglib: - #raise NotImplementedError("Error, the symmetry module from SPGLIB is not yet able to compute the q star") - - qe_sym.SetupFromSPGLIB() - else: - qe_sym.SetupQPoint() - - i_gamma = -1 - for iq, q in enumerate(self.q_tot): - if np.max(np.abs(q)) < __EPSILON__: - i_gamma = iq - - if i_gamma != 0: - mydyn = self.dynmats[0].copy() - self.dynmats[0] = self.dynmats[iq].copy() - self.dynmats[iq] = mydyn - self.q_tot[iq] = self.q_tot[0].copy() - self.q_tot[0][:] = 0 - - - # Get the q_stars - q_stars, q_order = qe_sym.SetupQStar(self.q_tot) - - # Reorder the dynamical matrix - new_dynmats = [] - q_tot = [] - for i in range(len(q_order)): - iq = q_order[i] - q = self.q_tot[iq] - new_dynmats.append(self.dynmats[iq]) - - self.dynmats = new_dynmats - self.q_stars = q_stars - self.q_tot = [y for x in q_stars for y in x] # Unwrap the q points - self.nqirr = len(q_stars) - - # # Now, the q_stars respect the correct fourier convention - # q_tot = [] - # for q_star in q_stars: - # for q in q_star: - # q_tot.append(q) - # self.q_tot = q_tot - - def SwapQPoints(self, other_dyn): - """ - Adjust the order of the q points of this dynamical matrix (self) to match the one of the passed dynamical matrix. - This is usefull if you want to compare the two dynamical matrices. - - The method also checks if the q points are in different brilluin zones. - - NOTE: this method will match the q points, this means that the q star could be destroyed. - You need to call AdjustQStar to correctly generate the star after this method. - - """ - - ## Check of consistency between the dynamical matrices - assert len(self.q_tot) == len(other_dyn.q_tot) - assert self.GetSupercell() == other_dyn.GetSupercell() - - order_mask = [] - bg = self.structure.get_reciprocal_vectors() / (2*np.pi) - for i, qi in enumerate(other_dyn.q_tot): - found = False - for j, qj in enumerate(self.q_tot): - # Skip if it has already been identified - if j in order_mask: - continue - - # Check if qi and qj are the same vector - dist = Methods.get_min_dist_into_cell(bg, qi, qj) - if dist < __EPSILON__: - order_mask.append(j) - found = True - break - - assert found, "Error, mismatching between q points: this matrix has q = {} missing in the other one".format(qi) - - # Reorder the dynamical matrix - print("Order: {}".format(order_mask)) - self.dynmats = [ self.dynmats[x] for x in order_mask ] - self.q_tot = [ self.q_tot[x] for x in order_mask ] - self.q_stars = [ self.q_tot ] - - - - - - - - def SymmetrizeSupercell(self, supercell_size = None): - """ - Testing function, it applies symmetries in the supercell. - """ - - if supercell_size == None: - supercell_size = self.GetSupercell() - - - if not __SPGLIB__: - raise ImportError("Error, the SymmetrizeSupercell method of the Phonon class requires spglib") - - superdyn = self.GenerateSupercellDyn(supercell_size) - - # Apply the sum rule - symmetries.CustomASR(superdyn.dynmats[0]) - - qe_sym = symmetries.QE_Symmetry(superdyn.structure) - qe_sym.SetupFromSPGLIB() - #qe_sym.SetupQPoint() - qe_sym.ApplySymmetriesToV2(superdyn.dynmats[0]) - - #spgsym = spglib.get_symmetry(superdyn.structure.get_ase_atoms()) - #syms = symmetries.GetSymmetriesFromSPGLIB(spgsym, False) - #superdyn.ForceSymmetries(syms) - - # Get the dynamical matrix back - fcq = GetDynQFromFCSupercell_parallel(superdyn.dynmats[0], np.array(self.q_tot), self.structure, superdyn.structure) - - for iq, q in enumerate(self.q_tot): - self.dynmats[iq] = fcq[iq, :, :] - - # Symmetrize also the effective charges and the Raman Tensor if any - # To do this, the symmetries must be initialized once again in the unit cell - qe_sym = symmetries.QE_Symmetry(self.structure) - qe_sym.SetupFromSPGLIB() - if not self.effective_charges is None: - qe_sym.ApplySymmetryToEffCharge(self.effective_charges) - if not self.raman_tensor is None: - qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) - - def Symmetrize(self, verbose = False, asr = "custom", use_spglib = False): - """ - SYMMETRIZE THE DYNAMICAL MATRIX - =============================== - - This subroutine uses the QE symmetrization procedure to obtain - a full symmetrized dynamical matrix. - - Parameters - ---------- - verbose : bool - If true a lot of info regarding the symmetrization are printed. - asr : string - The kind of the acustic sum rule. Allowed are 'crystal', 'simple' or 'custom'. - for crystal and simple refer to the quantum-espresso guide. - use_spglib : bool - If True, the simmetrization is performed with SPGLIB in the supercell - """ - - if use_spglib: - self.SymmetrizeSupercell() - else: - qe_sym = symmetries.QE_Symmetry(self.structure) - fcq = np.array(self.dynmats, dtype = np.complex128) - qe_sym.SymmetrizeFCQ(fcq, self.q_stars, asr = asr, verbose = verbose) - - for iq,q in enumerate(self.q_tot): - self.dynmats[iq] = fcq[iq, :, :] - - # Symmetrize also the effective charges and the Raman Tensor if any - if not self.effective_charges is None: - qe_sym.ApplySymmetryToEffCharge(self.effective_charges) - if not self.raman_tensor is None: - qe_sym.ApplySymmetryToRamanTensor(self.raman_tensor) - - - - def ApplySumRule(self, kind = "custom"): - """ - ACUSTIC SUM RULE - ================ - - The acustic sum rule is a way to impose translational symmetries on the dynamical matrix. - It affects also the effective charges if any (the total effective charge must be zero). - For the dynamical matrix it allows to have the self interaction terms: - .. math:: - - \\Phi_{n_a, n_a}^{x,y} = - \\sum_{n_b \\neq n_a} \\Phi_{n_a,n_b}^{x,y} - - Parameters - ---------- - kind : string - - "custom" : The polarization vectors asigned to the translation are removed from the - gamma dynamical matrix. - - "normal" : The equation written in this doc_string is applied. - A NotImplementedError is raised if kind differs from these types. - """ - - # Apply the sum rule on the dynamical matrix - if kind == "custom": - # Apply the sum rule - symmetries.CustomASR(self.dynmats[0]) - elif kind == "normal": - nb = np.arange(self.structure.N_atoms) - for i in range(9): - x = i / 3 - y = i % 3 - for na in range(self.structure.N_atoms): - sum_value = np.sum(self.dynmats[0][3 * na + x, 3 * nb[(nb != na)] + y]) - self.dynmats[0][3 * na + x, 3 * na + y] = - sum_value - else: - raise NotImplementedError("Error, the specified kind for the sum rule is unknown {}".format(kind)) - - - # Apply the sum rule on the effective charge - if self.effective_charges is not None: - total_charge = np.sum(self.effective_charges, axis = 0) - - # Subtract to each atom an average of the total charges - self.effective_charges = np.einsum("aij, ij -> aij", self.effective_charges, - total_charge / self.structure.N_atoms) - - - def GetIRActive(self, use_spglib = False): - """ - GET IF A MODE IS IR ACTIVE - ========================== - - This subroutine uses group theory to get if a mode is IR active. - - Parameters - ---------- - use_spglib : bool - If True, spglib is used for group theory. - Good if you are in a supercell. - - Results - ------- - is_ir_active : ndarray (size = 3*nat) - Returns a bool array with True for each mode at gamma - that is IR active. - """ - - there_are_eff_charges = True - if self.effective_charges is None: - there_are_eff_charges = False - self.effective_charges = np.zeros((self.structure.N_atoms, 3,3), dtype = np.double) - self.effective_charges = np.random.uniform( size = self.effective_charges.shape) - - # Get the symmetries - qe_sym = symmetries.QE_Symmetry(self.structure) - if use_spglib: - qe_sym.SetupFromSPGLIB() - else: - qe_sym.SetupQPoint() - - # Symmetrize the effective charges - qe_sym.ApplySymmetryToEffCharge(self.effective_charges) - - # Simulate the IR signal - Ir = self.GetIRIntensities() - is_ir_active = Ir > 1e-8 - - # Delete the random effective charges if added - if not there_are_eff_charges: - self.effective_charges = None - - return is_ir_active - - - - def ApplySymmetry(self, symmat, irt = None): - """ - APPLY SYMMETRY - ============== - - This function apply a symmetry to the force constant matrix - The matrix must be a 3 rows x 4 columns array containing the rotation and the subsequent translation of the vectors. - - The symmetry check is performed by comparing the two force constant matrix within the given threshold. - - .. math:: - - \\Phi_{s(a)s(b)}^{ij} = \\sum_{h,k = 1}^3 S_{ik} S_{jh} \\Phi_{ab}^{kh} - - \\Phi = S \\Phi S^\\dagger - - where :math:`s(a)` is the atom in which the :math:`a` atom is mapped by the symmetry. - - Note: this works only in supercells at gamma point - - Parameters - ---------- - symmat : ndarray 3x4 - The symmetry matrix to be checked. the last column contains the translations. Trans - irt : ndarray (size = N_atoms) - The atoms the symmetry is mapping to. - - Results - ------- - ndarray 3Nat x 3Nat - The new force constant matrix after the application of the symmetries - """ - #A_TO_BOHR = 1.889725989 - - - # Check if the matrix has been initialized - if len(self.dynmats) == 0: - raise ValueError("Error, the phonon force constant has not been initialized. Please consider loading the phonon info.") - - if self.nqirr != 1: - raise ValueError("Error, this method only works for gamma point calculations") - - - # Get the way atoms are echanged - if irt is None: - aux_struct = self.structure.copy() - aux_struct.apply_symmetry(symmat, delete_original = True) - aux_struct.fix_coords_in_unit_cell() - - eq_atoms = self.structure.get_equivalent_atoms(aux_struct) - else: - eq_atoms = irt - #print eq_atoms - - # Get the number of atoms - n_atoms = self.structure.N_atoms - - # Get only the rotational part of the symmetry - new_s_mat = symmat[:3, :3] - - out_fc = np.zeros(np.shape(self.dynmats[0]), dtype = np.complex128) - in_fc = self.dynmats[0] - - # Apply the symmetry to the force constant matrix - for na in range(n_atoms): - for nb in range(0, n_atoms): - # Get the atoms projection of the symmetries - s_na = eq_atoms[na] - s_nb = eq_atoms[nb] - - # Extract the matrix referring to na and nb atoms - current_m = in_fc[3 * na : 3*na + 3, 3*nb : 3*nb + 3] - - # Conver the matrix in crystalline - new_m = Methods.convert_matrix_cart_cryst(current_m, self.structure.unit_cell * A_TO_BOHR) - - # Apply the symmetry - #new_m_sym = new_s_mat.dot(new_m.dot( new_s_mat.transpose())) - new_m_sym = new_s_mat.transpose().dot(new_m.dot( new_s_mat)) - - #new_m_sym =new_m.copy() - - # Convert back to cartesian coordinates - new_m = Methods.convert_matrix_cart_cryst(new_m_sym, self.structure.unit_cell * A_TO_BOHR, cryst_to_cart=True) - - #print "%d -> %d , %d -> %d)" % (na, s_na, nb, s_nb)#, "d = %.5f" % np.real(np.sqrt(np.sum( (new_m - current_m)**2))) - - # Write the matrix into the output - out_fc[3 * s_na : 3*s_na + 3, 3*s_nb : 3* s_nb + 3] = new_m.copy() - - - #out_fc[3 * s_nb : 3*s_nb + 3, 3*s_na : 3 * s_na + 3] = np.conj(new_m.copy().transpose()) - #print "Test of the transpose. d = ", np.real(np.sqrt(np.sum( (in_fc[3 * nb : 3*nb + 3, 3*na : 3*na + 3].transpose() - out_fc[3 * nb : 3*nb + 3, 3*na : 3 * na + 3])**2))) - - # Return the symmetrized result - #print "Total distance:", np.sqrt(np.sum( (out_fc - np.real(in_fc))**2)) - return out_fc - - - - def ForceSymmetries(self, symmetries, irt = None, apply_sum_rule = True): - """ - FORCE THE PHONON TO RESPECT THE SYMMETRIES - ========================================== - - This method forces the phonon dynamical matrix to respect - the given symmetries. - - It uses the method ApplySymmetry to manipulate the force constant matrix. - - Note: This method only affect the force constant matrix, the structure is supposed to respect the symmetries. - - Note: This works only with gamma matrices (i.e. supercells) - - Parameters - ---------- - symmetries : list of ndarray 3x4 - List of the symmetries matrices. The last column is the fractional translation. - irt : ndarray(size = (N_sym, N_atoms_sc), dtype = np.intc) - For each symmetry s, the atom i is mapped into the atom irt[s, i] - If None, irt is recomputed with the symmetries module. - apply_sum_rule: bool - If true the default sum rule is applied. - """ - - # Apply the symmetries - new_fc = np.zeros( np.shape(self.dynmats[0]), dtype = np.complex128 ) - - - self.structure.fix_coords_in_unit_cell() - for i, sym in enumerate(symmetries): - # Check if the structure satisfy the symmetry - if not self.structure.check_symmetry(sym): - print (sym) - new_sym = sym.copy() - new_sym[:, :3] = np.transpose( sym[:, :3]) - print ("Satisfy transpose?", self.structure.check_symmetry(new_sym)) - raise ValueError("Error, the given structure do not satisfy the %d-th symmetry." % (i+1)) - - # Get the force constant - current_irt = None - if not irt is None: - current_irt = irt[i, :] - current_fc = self.ApplySymmetry(sym, irt = current_irt) - - print (i) - - # Try to add the sum rule here - #newP = self.Copy() - #newP.dynmats[0] = current_fc -# #newP.ApplySumRule() -# -# distance = np.sum( (self.dynmats[0] - current_fc)**2) -# distance = np.real(np.sqrt(distance)) -# - #print "%d) d = " % (i+1), distance - - new_fc += current_fc - - # Average all the symmetrized structures - new_fc /= len(symmetries) - - - print ("DIST_SYM_FORC:", np.sqrt(np.sum( (new_fc - self.dynmats[0])**2))) - self.dynmats[0] = new_fc.copy() - - - # Print the phonons all toghether - #print "\n".join( ["\t".join("%.4e" % (xval - freqs[0,j]) for xval in freqs[:, j]) for j in range(3 * self.structure.N_atoms)]) - - # Apply the acustic sum rule - if apply_sum_rule: - self.ApplySumRule() - - def DiagonalizeSupercell(self, verbose = False, lo_to_split = None): - r""" - DYAGONALIZE THE DYNAMICAL MATRIX IN THE SUPERCELL - ================================================= - - This method dyagonalizes the dynamical matrix using the supercell approach. - - In this way we simply generate the polarization vector in the supercell - using those in the unit cell. - - This is performed using the following equation: - - .. math :: - - e_\mu^0(R_0) = \frac{\sqrt{|\tilde e_{q\nu}^a|^2}}{N_q} - - e_\mu^a(R_a) = \frac{\cos(\vec q\cdot \Delta R_{a0}) \Re\left[\tilde e_{q\nu}^a\tilde {e_{q\nu}^b}^\dagger\right] - \sin(\vec q\cdot \Delta R_{a0}) \Im\left[\tilde e_{q\nu}^a\tilde {e_{q\nu}^b}^\dagger\right]}{e_\mu^0(R_0)N_q} - - Here the :math:`\tilde e_{q\nu}` are the complex polarization vectors in the q point so that :math:`\omega_{q\nu} = \omega_{\mu}`. - - Parameters - ---------- - lo_to_split : string or ndarray - Could be a string with random, or a ndarray indicating the direction on which the - LO-TO splitting is computed. If None it is neglected. - If LO-TO is specified but no effective charges are present, then a warning is print and it is ignored. - Results - ------- - w_mu : ndarray( size = (n_modes), dtype = np.double) - Frequencies in the supercell - e_mu : ndarray( size = (3*Nat_sc, n_modes), dtype = np.double, order = "F") - Polarization vectors in the supercell - """ - - supercell_size = len(self.q_tot) - nat = self.structure.N_atoms - - nmodes = 3*nat*supercell_size - nat_sc = nat*supercell_size - - w_array = np.zeros( nmodes, dtype = np.double) - e_pols_sc = np.zeros( (nmodes, nmodes), dtype = np.double, order = "F") - - # Get the structure in the supercell - super_structure = self.structure.generate_supercell(self.GetSupercell()) - - # Get the supercell correspondence vector - itau = super_structure.get_itau(self.structure) - 1 # Fort2Py - - # Get the itau in the contracted indices (3*nat_sc -> 3*nat) - itau_modes = (np.tile(np.array(itau) * 3, (3,1)).T + np.arange(3)).ravel() - - # Get the position in the supercell - R_vec = np.zeros((nmodes, 3), dtype = np.double) - for i in range(nat_sc): - R_vec[3*i : 3*i+3, :] = np.tile(super_structure.coords[i, :] - self.structure.coords[itau[i], :], (3,1)) - - i_mu = 0 - bg = self.structure.get_reciprocal_vectors() / (2*np.pi) - for iq, q in enumerate(self.q_tot): - # Check if the current q point has been seen (we do not distinguish between q and -q) - skip_this_q = False - for jq, q_prev in enumerate(self.q_tot): - if jq >= iq: - break - - # Check if q and q_prev are related by a G-q operation - dist = Methods.get_min_dist_into_cell(bg, -q, q_prev) - if dist < __EPSILON__: - skip_this_q = True - break - - if skip_this_q: - continue - - - # Check if this q = -q + G - is_minus_q = False - if Methods.get_min_dist_into_cell(bg, q, -q) < 1e-6: - is_minus_q = True - - # The dynamical matrix must be real - re_part = np.real(self.dynmats[iq]) - - assert np.max(np.abs(np.imag(self.dynmats[iq]))) < __EPSILON__, "Error, at point {} (q = -q + G) the dynamical matrix is complex".format(iq) - - # Enforce reality to avoid complex polarization vectors - self.dynmats[iq] = re_part - - # Check if this is gamma (to apply the LO-TO splitting) - if Methods.get_min_dist_into_cell(bg, q, np.zeros(3)) < 1e-16 and lo_to_split is not None: - if self.effective_charges is None: - warnings.warn("WARNING: Requested LO-TO splitting without effective charges. LO-TO ignored.") - - # Initialize the Force Constant - t2 = ForceTensor.Tensor2(self.structure, self.structure.generate_supercell(self.GetSupercell()), self.GetSupercell()) - t2.SetupFromPhonons(self) - - if lo_to_split.lower() == "random": - fc_gamma = t2.Interpolate(np.zeros(3)) - else: - fc_gamma = t2.Interpolate(np.zeros(3), q_direct= -lo_to_split) - - _m_ = np.tile(self.structure.get_masses_array(), (3,1)).T.ravel() - d_gamma = fc_gamma / np.sqrt(np.outer(_m_, _m_)) - wq2, eq = np.linalg.eigh(d_gamma) - - wq = np.sqrt(np.abs(wq2)) * np.sign(wq2) - else: - # Diagonalize the matrix in the given q point - wq, eq = self.DyagDinQ(iq) - - # Iterate over the frequencies of the given q point - nm_q = i_mu - for i_qnu, w_qnu in enumerate(wq): - - tilde_e_qnu = eq[:, i_qnu] - - # If this is a minus_q, enforce reality of the vector - # To correctly fix the gauge - if is_minus_q: - # Get the phase factor from the first non zero value - phase_gauge = 0 - for e_a in tilde_e_qnu: - if np.abs(e_a) > __EPSILON__: - phase_gauge = np.angle(e_a) - break - - # Work only if it is not already real - if np.abs(phase_gauge) > __EPSILON__ and np.abs(phase_gauge - np.pi) > __EPSILON__: - # Apply the phase factor to the polarization vector - tilde_e_qnu *= np.exp(-1j * phase_gauge) - - # Check if the polarization vector is real - re_tilde_e_qnu = np.real(tilde_e_qnu) - - print("Phase:", phase_gauge) - print("Vector:", tilde_e_qnu) - assert np.max(np.abs(re_tilde_e_qnu - tilde_e_qnu)) < __EPSILON__, "Error while enforcing reality of {}".format(tilde_e_qnu) - - tilde_e_qnu = re_tilde_e_qnu - - phase = R_vec.dot(q) * 2 * np.pi - c_e_sc = tilde_e_qnu[itau_modes] * np.exp(1j*phase) / np.sqrt(supercell_size) - c_e_sc_mq = np.conj(c_e_sc) - - # Get the real and imaginary part - evec_1 = np.real(.5 * (c_e_sc + c_e_sc_mq)) - evec_2 = np.real((c_e_sc - c_e_sc_mq) / ( 2*1j)) - - # Check if they are not zero - norm1 = evec_1.dot(evec_1) - norm2 = evec_2.dot(evec_2) - scalar_dot = 0 - EPSILON = 1e-5 - - if norm2 > EPSILON and norm1 > EPSILON: - scalar_dot = evec_1.dot(evec_2) / np.sqrt(norm1 * norm2) - - if verbose: - print("IQ: {}, MODE: {} has norm1 = {} | norm2 = {} | scalar_dot = {}".format(iq, i_qnu, np.sqrt(norm1), np.sqrt(norm2), scalar_dot)) - - # Check if add to the polarization both 1 and 2 - add_1 = False - add_2 = False - - if norm1 > EPSILON: - add_1 = True - - if norm2 > EPSILON: - add_2 = True - - if is_minus_q: - if add_1 and add_2: - if np.abs(np.abs(scalar_dot) - 1) > EPSILON: - raise ValueError("Error, with q = -q + G, the two vectors should be linearly dependent") - - # In this case remove the one with lower norm (higher numerical accuracy) - if norm1 > norm2: - add_2 = False - else: - add_1 = False - - - - # If this is a q != -q point, this q point must contribute also for -q - # Thus twice the elements should be present. - if not is_minus_q: - if not (add_1 and add_2): - raise ValueError("Error, the q_point = {} {} {} should contribute also for -q, something went wrong".format(*list(q))) - - - if add_1 and add_2: - # Since both real and imaginary should match in this case - # Add only one of them - if is_minus_q: - add_2 = False - - if verbose: - print(" add_1 = {}; add_2 = {}".format(add_1, add_2)) - - - # Add the vectors - if add_1: - w_array[i_mu] = w_qnu - e_pols_sc[:, i_mu] = evec_1 / np.sqrt(norm1) - i_mu += 1 - if add_2: - w_array[i_mu] = w_qnu - e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) - i_mu += 1 - - - # # Add the second vector - # if norm1 > EPSILON: - # #q_cryst = Methods.covariant_coordinates(bg, q) - # #print ("IMU: {}, IQ: {}, IQNU: {}, TOTQ: {}, Q = {}, N1 = {:.3e}, N2 = {:.3e}, DOT = {:.3e}".format(i_mu, iq, i_qnu, len(self.q_tot), q_cryst, norm1, norm2, evec_1.dot(evec_2))) - # w_array[i_mu] = w_qnu - # e_pols_sc[:, i_mu] = evec_1 / np.sqrt(norm1) - # i_mu += 1 - - # # If there is another q point - # if not is_minus_q: #scalar_dot < EPSILON: - # if norm2 < EPSILON: - # raise ValueError("Error, the q_point = {} {} {} should contribute also for -q, something went wrong".format(*list(q))) - - - # w_array[i_mu] = w_qnu - # e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) - # i_mu += 1 - # else: - # w_array[i_mu] = w_qnu - # e_pols_sc[:, i_mu] = evec_2 / np.sqrt(norm2) - # i_mu += 1 - - # Print how many vectors have been extracted - if verbose: - print("The {} / {} q point produced {} nodes".format(iq, len(self.q_tot), i_mu - nm_q)) - - - - - # Sort the frequencies - sort_mask = np.argsort(w_array) - w_array = w_array[sort_mask] - e_pols_sc = e_pols_sc[:, sort_mask] - - - # Get the check for the polarization vector normalization - assert np.max(np.abs(np.einsum("ab, ab->b", e_pols_sc, e_pols_sc) - 1)) < __EPSILON__ - - return w_array, e_pols_sc - - - - - def ReadInfoFromESPRESSO(self, filename, read_dielectric_tensor = True, read_eff_charges = True, read_raman_tensor = True): - """ - READ INFO FROM ESPRESSO - ======================= - - This method reads the effective charges, the dielectric tensor as well as - the Raman tensor from an espresso phonon output file. - It is usefull if you want to run the electric field perturbation without computing - all the phonon spectrum, deriving only with respect to the electric field. - - - Parameters - ---------- - filename : string - Path to the standard output of the ph.x calculation. - read_dielectric_constant: bool - If False, the dielectric tensor will be ignored - read_effective_charge : bool - If False, the effective charges will be ignored - read_raman_tensor : bool - If False, the Raman tensor will be ignored. - """ - - if not os.path.exists(filename): - raise IOError("Error, the given file {} does not exist".format(filename)) - - # Read all the file - f = open(filename, "r") - lines = [l.strip() for l in f.readlines()] - f.close() - - # The triggers to know what I am reading - reading_dielectric = False - reading_eff_charges = False - reading_raman = False - - reading_index = 0 - reading_atom = 0 - reading_pol = 0 - - - if read_dielectric_tensor and len([x for x in lines if "Dielectric constant in " in x]): - self.dielectric_tensor = np.zeros((3,3), dtype = np.double) - if read_eff_charges and len([x for x in lines if "Effective charges" in x]): - self.effective_charges = np.zeros( (self.structure.N_atoms, 3, 3), dtype = np.double) - if read_raman_tensor and len([x for x in lines if "Raman tensor" in x]): - self.raman_tensor = np.zeros((3,3, 3* self.structure.N_atoms), dtype = np.double) - - # Start the analysis - for line in lines: - data = line.split() - if len(data) == 0: - continue - - # Check the number of atoms coincides - if "atoms/cell" in line: - nat = int(data[4]) - if nat != self.structure.N_atoms: - raise ValueError("Error, this Phonon has {} atoms, while the {} calculations contains {} atoms".format(self.structure.N_atoms, filename, nat)) - - # Check if we are reading the dielectric - if "Dielectric constant in " in line: - reading_dielectric = True - reading_eff_charges = False - reading_raman = False - reading_index = 0 - reading_atom = 0 - reading_pol = 0 - elif "Effective charges" in line: - reading_dielectric = False - reading_eff_charges = True - reading_raman = False - reading_index = 0 - reading_atom = 0 - reading_pol = 0 - elif "Raman tensor (A^2)" in line: - reading_dielectric = False - reading_eff_charges = False - reading_raman = True - reading_index = 0 - reading_atom = 0 - reading_pol = 0 - - - # Check if we must read the dielectric file - if reading_dielectric and read_dielectric_tensor: - if len(data) == 5 and data[0] == "(": - self.dielectric_tensor[reading_index, :] = [float(x) for x in data[1:4]] - reading_index += 1 - - if reading_eff_charges and read_eff_charges: - if data[0] == "atom": - reading_atom = int(data[1]) - 1 - reading_index = 0 - # Check the consistency of the atom type - atm_type = data[2] - if self.structure.atoms[reading_atom] != atm_type: - error = """ -Error while reading {}: - atom index {} shoud be {}, while it is {} (index {}) -""".format(filename, reading_atom, self.structure.atoms[reading_atom], atm_type, reading_atom+1) - raise ValueError(error) - if len(data) == 6 and data[0][0] == 'E': - self.effective_charges[reading_atom, reading_index, :] = [float(x) for x in data[2:5]] - reading_index += 1 - - # Check if we ended - if reading_atom == self.structure.N_atoms - 1 and reading_index == 3: - reading_eff_charges = False - - - # Reading the raman - if reading_raman and read_raman_tensor: - if data[0] == "atom": - reading_atom = int(data[2]) - 1 - reading_index = 0 - reading_pol = int(data[4]) - 1 - - if reading_atom >= self.structure.N_atoms: - error_msg = """ - Error, trying to read atom {} from inputfile {}. - I expect a maximum of {} atoms from this structure. -""".format(reading_atom + 1, filename, self.structure.N_atoms) - raise ValueError(error_msg) - - if len(data) == 3: - is_good_line = False - try: - float(data[0]) - is_good_line = True - except: - pass - - if is_good_line: - numbers = [float(x) for x in data] - self.raman_tensor[reading_index, :, 3 * reading_atom + reading_pol] = numbers - reading_index += 1 - - - - - - -def ImposeSCTranslations(fc_supercell, unit_cell_structure, supercell_structure, itau = None): - """ - IMPOSE TRANSLATION IN THE SUPERCELL - =================================== - - This subroutine imposes the unit cell translations of the supercell force constant matrix. - Note that it is very different from the acustic sum rule. - - .. math:: - - C_{k\\alpha,k'\\beta}(a,b) = C_{k\\alpha,k'\\beta}(0, b-a) - - - Parameters - ---------- - fc_supercell : ndarray (3nat_sc x 3nat_sc) - The input-output force constant matrix in real space. - unit_cell_structure: Structure() - The structure in the unit cell - supercell_structure : Structure() - The structure of the supercell - itau : optional, ndarray (int) - The equivalence between unit_cell and supercell atoms. If None it is - extracted by the given structures. Note it must be in fortran language - """ - - - if itau is None: - # Get the fortran one - itau = supercell_structure.get_itau(unit_cell_structure) - - nat_sc = supercell_structure.N_atoms - fc_tmp = np.zeros( (3,3, nat_sc, nat_sc), dtype = np.float64, order = "F") - tau_sc_cryst = np.zeros( (3, nat_sc), dtype = np.float64, order = "F") - - for i in range(nat_sc): - tau_sc_cryst[:,i] = Methods.covariant_coordinates(supercell_structure.unit_cell, supercell_structure.coords[i, :]) - - # Fill the force constant matrix - for i in range(nat_sc): - for j in range(nat_sc): - fc_tmp[:,:, i, j] = fc_supercell[3*i : 3*i + 3, 3*j: 3*j+3] - - # Call the fortran suboruitne - symph.impose_trans_sc(fc_tmp, tau_sc_cryst, itau, nat_sc) - - #Revert it in the original force constant matrix - for i in range(nat_sc): - for j in range(nat_sc): - fc_supercell[3*i : 3*i + 3, 3*j: 3*j+3] = fc_tmp[:,:, i, j] - - - - - -def GetSupercellFCFromDyn(dynmat, q_tot, unit_cell_structure, supercell_structure, itau = None, img_thr = 1e-5): - """ - GET THE REAL SPACE FORCE CONSTANT - ================================= - - This subroutine uses the fourier transformation to get the real space force constant, - starting from the fourer space matrix. - - .. math:: - - C_{k\\alpha,k'\\beta}(0, b) = \\frac{1}{N_q} \\sum_q \\tilde C_{k\\alpha k'\\beta}(q) e^{i\\vec q \\cdot \\vec R_b} - - Then the translationa property is applied. - - .. math:: - - C_{k\\alpha,k'\\beta}(a, b) = C_{k\\alpha,k'\\beta}(0, b-a) - - Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\\alpha` is the - cartesian indices. - - - Parameters - ---------- - dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) - The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. - q_tot : ndarray ( nq, 3) - The q vectors in Angstrom^-1 - unit_cell_structure : Structure() - The reference structure of the unit cell. - supercell_structure : Structure() - The reference structure of the supercell. It is used to keep the same indices of the atomic positions. - Note, it is required that consecutive atoms are placed sequently - itau : Ndarray(nat_sc) , optional - the correspondance between the supercell atoms and the unit cell one. - If None is recomputed - img_thr: 1e-5 def - Returns - ------- - fc_supercell : ndarray 3nat_sc x 3nat_sc - The force constant matrix in the supercell. - - """ - - # Define the number of q points, atoms and unit cell atoms - nq = len(q_tot) - nat = np.shape(dynmat)[1] //3 - nat_sc = nq*nat - - - if itau is None: - itau = supercell_structure.get_itau(unit_cell_structure)-1 - - #dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") - fc = np.zeros((3*nat_sc, 3*nat_sc), dtype = np.complex128) - - - fc = symph.fast_ft_real_space_from_dynq(unit_cell_structure.coords, supercell_structure.coords, itau+1, np.array(q_tot), dynmat, unit_cell_structure.N_atoms, supercell_structure.N_atoms, q_tot.shape[0]) - - - - """ - for i in range(nat_sc): - i_uc = itau[i] - t1 = time.time() - for j in range(nat_sc): - j_uc = itau[j] - R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] - R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] - - # q_dot_R is 1d array that for each q contains the scalar product with R - q_dot_R = q_tot.dot(R) - - t2 = time.time() - fc[3*i : 3*i + 3, 3*j : 3*j + 3] += np.einsum("abc, a", dynmat[:, 3*i_uc : 3*i_uc + 3, 3*j_uc: 3*j_uc + 3], np.exp(1j * 2*np.pi * q_dot_R)) / nq - t3 = time.time() - - print("Time to do a single cycle: ", t3 - t2) - print("Total number of cycles = {} / {}".format(nat_sc * i + j + 1, nat_sc**2)) - print("Time for a whole cycle: ", t3 - t1) """ -# -# # For now, to test, just the unit cell -# for i in range(nq): -# start_index = 3*nat*i -# for j in range(nq): -# end_index = 3*nat*j -# -# q_dot_R = np.sum(q_tot[i,:] * R_vectors_cart[j,:]) -# -# fc[end_index: end_index + 3*nat, start_index: start_index + 3*nat ] += dynmat[i,:,:] * np.exp(1j* 2 * np.pi* q_dot_R) / nq -# - - #np.sum(dynmat * np.exp(), axis = 0) / nq - #print "Imaginary:", np.sqrt(np.sum(np.imag(fc)**2)) - - # Check the imaginary part - imag = np.max(np.abs(np.imag(fc))) - ASSERT_ERROR = """ - Error, the imaginary part of the real space force constant - is not zero. IMAG={} imthr={} - """ - assert imag < img_thr, ASSERT_ERROR.format(imag, img_thr) - - # Remove anyway the imaginary part - return fc - 1j*np.imag(fc) - - - -def GetDynQFromFCSupercell(fc_supercell, q_tot, unit_cell_structure, supercell_structure, itau = None, fc2 = None): - r""" - GET THE DYNAMICAL MATRICES - ========================== - - This subroutine uses the fourier transformation to get the dynamical matrices, - starting from the real space force constant. - - .. math:: - - \tilde C_{k\alpha k'\beta}(q) = \sum_{b}C_{k\alpha,k'\beta}(0, b)e^{i\vec q \cdot \vec R_b} - - - Here :math:`k` is the atom index in the unit cell, :math:`a` is the supercell index, :math:`\alpha` is the - cartesian indices. - - - Parameters - ---------- - fc_supercell : ndarray 3nat_sc x 3nat_sc - The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. - q_tot : ndarray ( nq, 3) - The q vectors in Angstrom^-1 - unit_cell_structure : Structure() - The structure of the unit cell - supercell_structure : Structure() - The structure of the supercell - Returns - ------- - dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) - The force constant matrix in the supercell. - - """ - - # Define the number of q points, atoms and unit cell atoms - nq = np.shape(q_tot)[0] - nat_sc = np.shape(fc_supercell)[0]//3 - nat = nat_sc // nq - - if itau is None: - itau = supercell_structure.get_itau(unit_cell_structure)-1 - - - #dynmat = np.zeros( (nq, 3*nat, 3*nat), dtype = np.complex128, order = "F") - dynmat = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) - - if fc2 is not None: - dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) - #print "NQ:", nq - - for i in range(nat_sc): - i_uc = itau[i] - for j in range(nat_sc): - j_uc = itau[j] - R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] - R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] - - # q_dot_R is 1d array that for each q contains the scalar product with R - q_dot_R = q_tot.dot(R) - - dynmat[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc_supercell[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq - - if fc2 is not None: - dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq - -# -# # Fill the dynamical matrix -# for i in range(nq): -# -# a_x = i % supercell_array[0] -# a_y = (i / supercell_array[0]) % supercell_array[1] -# a_z = i / (supercell_array[0] * supercell_array[1]) -# R_vectors_cart[i,:] = a_x * unit_cell[0,:] + a_y * unit_cell[1,:] + a_z * unit_cell[2,:] -# -# -# -# # For now, to test, just the unit cell -# for i in range(nq): -# start_index = 3 * nat * i -# q_dot_R = np.einsum("ab, b", q_tot, R_vectors_cart[i,:]) -# #print "%d, %d => q dot R = " % (i, j), np.exp(1j * q_dot_R) -# dynmat[:,:,:] += np.einsum("bc, a->abc", fc_supercell[:3 *nat, start_index : start_index + 3*nat], np.exp(1j* 2 * np.pi* q_dot_R)) - - - - if fc2 is not None: - return dynmat, dynmat2 - else: - return dynmat - - -def GetDynQFromFCSupercell_parallel(fc_supercell, q_tot, unit_cell_structure, supercell_structure, itau = None, fc2 = None): - r""" - Look at GetDynQFromFCSupercell. This is the mpi enabled version of that subroutine. - TODO: Still to be tested properly - - Parameters - ---------- - fc_supercell : ndarray 3nat_sc x 3nat_sc - The dynamical matrix at each q point. Note nq must be complete, not only the irreducible. - q_tot : ndarray ( nq, 3) - The q vectors in Angstrom^-1 - unit_cell_structure : Structure() - The structure of the unit cell - supercell_structure : Structure() - The structure of the supercell - Returns - ------- - dynmat : ndarray (nq, 3nat, 3nat, dtype = np.complex128) - The force constant matrix in the supercell. - - """ - - # Define the number of q points, atoms and unit cell atoms - nq = np.shape(q_tot)[0] - nat_sc = np.shape(fc_supercell)[0]//3 - nat = nat_sc // nq - - if itau is None: - itau = supercell_structure.get_itau(unit_cell_structure)-1 - - def fourier_transform_reduction(ij_pair): - i, j = ij_pair - i_uc = itau[i] - j_uc = itau[j] - R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] - R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] - - # q_dot_R is 1d array that for each q contains the scalar product with R - q_dot_R = q_tot.dot(R) - - dynmat = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) - dynmat[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] = np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc_supercell[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq - #if fc2 is not None: - # dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) - # dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] += np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq - - return dynmat - - # Prepare the inputs TODO: this can be speedup - list_of_inputs = [] - for i in range(nat_sc): - for j in range(nat_sc): - list_of_inputs.append([i,j]) - - dynmat = Settings.GoParallel(fourier_transform_reduction, list_of_inputs, "+") - - if fc2 is not None: - def fourier_transform_reduction2(ij_pair): - i, j = ij_pair - i_uc = itau[i] - j_uc = itau[j] - R = supercell_structure.coords[i, :] - unit_cell_structure.coords[i_uc,:] - R -= supercell_structure.coords[j, :] - unit_cell_structure.coords[j_uc,:] - - # q_dot_R is 1d array that for each q contains the scalar product with R - q_dot_R = q_tot.dot(R) - - dynmat2 = np.zeros((nq, 3*nat, 3*nat), dtype = np.complex128) - dynmat2[:,3*i_uc: 3*i_uc +3,3*j_uc: 3*j_uc + 3] = np.einsum("a, bc", np.exp(-1j * 2*np.pi * q_dot_R), fc2[3*i : 3*i + 3, 3*j : 3*j + 3]) / nq - - return dynmat2 - - dynmat2 = Settings.GoParallel(fourier_transform_reduction2, list_of_inputs, "+") - - return dynmat, dynmat2 - - return dynmat - - - - -def InterpolateDynFC(starting_fc, coarse_grid, unit_cell_structure, super_cell_structure, q_point): - """ - INTERPOLATE FORCE CONSTANT MATRIX - ================================= - - Interpolate the real space force constant matrix in a bigger supercell. - This can be used to obtain a dynamical matrix in many other q points. - This function uses the quantum espresso matdyn.x subroutines. - - Parameters - ---------- - starting_fc : ndarray(size=(3*natsc , 3*natsc), dtype = float64) - Array of the force constant matrix in real space. - coarse_grid : ndarray(size=3, dtype=int) - The dimension of the supercell that defines the starting_fc. - unit_cell_structure : Structure() - The structure in the unit cell - super_cell_structure : Structure() - The structure in the super cell - q_point : ndarray(size=3, dtype=float64) - The q point in which you want to interpolate the dynamical matrix. - - Results - ------- - dyn_mat : ndarray(size=(3*nat, 3*nat), dtype = complex128) - The interpolated dynamical matrix in the provided q point. - """ - # Get some info about the size - supercell_size = np.prod(coarse_grid) - natsc = np.shape(starting_fc)[0] // 3 - nat = natsc // supercell_size - - print('shape',starting_fc.shape) - print('sc',natsc) - print('uc',nat) - - - - # Get the force constant in an appropriate supercell - QE_frc = np.zeros((coarse_grid[0], coarse_grid[1], coarse_grid[2], 3, 3, nat, nat), dtype = np.float64, order = "F") - QE_fc = np.zeros((3,3,natsc, natsc), dtype = np.float64, order = "F") - QE_itau = super_cell_structure.get_itau(unit_cell_structure) - QE_tau = np.zeros((3, nat), dtype = np.float64, order = "F") - QE_tau_sc = np.zeros((3, natsc), dtype = np.float64, order = "F") - QE_at = np.zeros((3,3), dtype = np.float64, order = "F") - QE_at_sc = np.zeros((3,3), dtype = np.float64, order = "F") - - for i in range(natsc): - for j in range(natsc): - QE_fc[:,:, i, j] = starting_fc[3*i : 3*(i+1), 3*j : 3*(j+1)] - - - QE_at[:,:] = unit_cell_structure.unit_cell.transpose() - QE_at_sc[:,:] = super_cell_structure.unit_cell.transpose() - QE_tau[:,:] = unit_cell_structure.coords.transpose() - QE_tau_sc[:,:] = super_cell_structure.coords.transpose() - - #print "ENTERING IN GET_FRC" - QE_frc[:,:,:,:,:,:,:] = symph.get_frc(QE_fc, QE_tau, QE_tau_sc, QE_at, QE_itau, - coarse_grid[0], coarse_grid[1], coarse_grid[2], nat, natsc) - #print "EXITING IN GET_FRC" - - # Initialize the interpolation - nrwsx = 200 - QE_rws = np.zeros((4, nrwsx), dtype = np.float64, order = "F") - #print "ENTERING IN WSINIT" - nrws = symph.wsinit(QE_rws, QE_at_sc, nrwsx) - #print "EXTING FROM WSINIT" - - # Perform the interpolation - QE_q = np.array(q_point, dtype = np.float64) - #print "ENTERING:" - #print "TAU SHAPE:", np.shape(QE_tau) - #print "FRC SHAPE:", np.shape(QE_frc) - new_dyn = symph.frc_blk(QE_q, QE_tau, QE_frc, QE_at, QE_rws, nrws, nat, - coarse_grid[0], coarse_grid[1], coarse_grid[2]) - - # Conver the dynamical matrix in the Cellconstructor format - output_dyn = np.zeros( (3*nat, 3*nat), dtype = np.complex128) - for i in range(nat): - for j in range(nat): - output_dyn[3*i : 3*(i+1), 3*j: 3*(j+1)]= new_dyn[:,:, i, j] - - return output_dyn - - - - -def get_dyn_from_ase_phonons(ase_ph, adjust_qstar = True): - """ - GET THE DYNAMICAL MATRIX FROM ASE - ================================= - - This function converts an ASE phonons object into the cellconstructor Phonons. - - Parameters - ---------- - ase_ph : ase.phonons.Phonons() - The ASE Phonons. It must be already computed - adjust_qstar : bool - If true the q points are ordered in star, preparing the dynamical matrix - for the symmetrization - - Results - ------- - dyn : CC.Phonons.Phonons() - The dynamical matrix - """ - - - FC = ase_ph.get_force_constant() - - supercell_size = ase_ph.N_c - - # Get the structure - structure = Structure.Structure() - structure.generate_from_ase_atoms(ase_ph.atoms) - - # Check if the structure has the unit cell - if np.linalg.det(structure.unit_cell) == 0: - ERROR_MSG = """ - Error, the ASE strucure passed to method 'get_dyn_from_ase_phonons' - does not have a valid unit cell. - If you are computing a isolated molecule, - you have to define an unit_cell that contains the molecule - (it will not affect the calculation) - """ - raise ValueError(ERROR_MSG) - - # Get the supercell structure and itau - nat_sc = structure.N_atoms * np.prod(supercell_size) - supercell_structure = structure.generate_supercell(supercell_size) - # Get the equivalent atom in the unit cell vs atoms in the supercell - itau = supercell_structure.get_itau(structure) - 1 # Fort -> Py (indexing) - - # Get the lattice vectors - R_cN = ase_ph.lattice_vectors() - R_cN = np.array(R_cN).T - - # Get the lattice in cartesian units - R_cN = R_cN.dot(structure.unit_cell) - - N_sup = np.prod(supercell_size) - - q_grid = symmetries.GetQGrid(structure.unit_cell, supercell_size) - - # Put gamma as the first vector - gamma_index = np.argmin(np.sum(np.array(q_grid)**2, axis = 1)) - q_grid[gamma_index] = q_grid[0].copy() - q_grid[0] = np.zeros(3, dtype = np.double) - - - # Prepare the dynamical matrix - dyn = Phonons(structure, len(q_grid)) - dyn.q_tot = q_grid - - # Each q point in a different star - dyn.q_stars = [ [q] for q in q_grid] - - # Generate the dynamical matrix in the supercell - fc_sup = np.zeros( (3*nat_sc, 3*nat_sc), dtype = np.double) - - # TODO: This can be slow - # It could be speeded up by getting directly the eigenmodes at the wanted q points. - for ia in range(nat_sc): - for ib in range(ia, nat_sc): - # lattice vector - R_1 = supercell_structure.coords[ia, :] - structure.coords[itau [ia], :] - R_2 = supercell_structure.coords[ib, :] - structure.coords[itau [ib], :] - delta_R = R_2 - R_1 - - i_block = Methods.identify_vector(supercell_structure.unit_cell, R_cN, delta_R) - - if i_block == None: - ERR_MSG=""" -ERROR, the ASE Phonons seems not to contain a supercell vector needed for the - force constant matrix: -Lattice vector needed : {:16.8f} {:16.8f} {:16.8f} -List of ASE vectors: {}""".format(delta_R[0], delta_R[1], delta_R[2], R_cN) - raise ValueError(ERR_MSG) - - for xa in range(3): - for xb in range(3): - # We found the block - fc_sup[3*ia + xa, 3*ib + xb] = FC[i_block, 3*itau[ia] + xa, 3*itau[ib] + xb] - fc_sup[3*ib + xb, 3*ia + xa] = fc_sup[3*ia + xa, 3*ib + xb] - - # Now get the dynamical matrix in the correct q point - dynq = GetDynQFromFCSupercell(fc_sup, np.array(dyn.q_tot), structure, supercell_structure) - - for iq in range(len(dyn.q_tot)): - # Convert from eV/A^2 into Ry/Bohr^2 - dyn.dynmats[iq] = dynq[iq, :, :] * BOHR_TO_ANGSTROM**2 / RY_TO_EV - - # Now adjust the q stars to match the symmetries - if adjust_qstar: - dyn.AdjustQStar() - - return dyn - - - - - -def compute_phonons_finite_displacements(structure, ase_calculator, epsilon = 0.05, supercell = (1,1,1), progress = -1, progress_bar = False): - """ - COMPUTE THE FORCE CONSTANT MATRIX - ================================= - - Use finite displacements to compute the force constant matrix. - (Works only at Gamma) - - Parameters - ---------- - structure : CC.Structure.Structure - The structure on the parameters - ase_calculator : ase.calculators.calculator - The ase calculator to compute energy and forces - epsilon : double - The finite displacement - progress : int - If positive, prints the status each tot structures - progress_bar : bool - If True, overwrite the progress line each structure - - Results - ------- - phonons : CC.Phonons.Phonons() - The dynamical matrix - """ - - - super_structure = structure.generate_supercell(supercell) - final_dyn = Phonons(super_structure) - - nat3 = 3 * super_structure.N_atoms - fc = np.zeros( (nat3, nat3), dtype = np.double) - - # Enable the parallel calculation - ase_calculator.directory = "calc_{}".format(Settings.get_rank()) - ase_calculator.set_label("label_{}".format(Settings.get_rank())) - - - #atm = structure.get_ase_atoms() - #atm.set_calculator(ase_calculator) - fc[:,:] = np.zeros((nat3, nat3), np.double) - if progress > 0: - print() - print("Computing phonons with finite differences.") - - - list_of_calculations = [] - - for i in range(super_structure.N_atoms): - for j in range(3): - list_of_calculations.append((i,j)) - - def compute_force(indices): - i, j = indices - #Settings.all_print("Computing indices:", i, j) - - if progress > 0: - if (3*i + j) % progress == 0: - if progress_bar and Settings.am_i_the_master(): - sys.stdout.write("\rProgress {:4.1f} % ... ".format(100 * (3*i + j + 1) / nat3)) - sys.stdout.flush() - else: - Settings.all_print("Finite displacement of structure {} / {}".format(3*i + j + 1, nat3)) - - - - s = super_structure.copy() - s.coords[i, j] += epsilon - - - ase_calculator.set_label("disp_{}".format(3*i + j)) - ase_calculator.directory = "disp_{}".format(3*i + j) - energy, forces = calculators.get_energy_forces(ase_calculator, s) - fc_tmp = np.zeros((nat3, nat3), dtype = np.double) - fc_tmp[3*i+j,:] -= forces.ravel() - - #print("FORCE ({}) = ".format(3*i+j), forces) - return fc_tmp - #atm = s.get_ase_atoms() - #atm.set_calculator(ase_calculator) - fc[3*i + j, :] -= forces.ravel() - - fc = Settings.GoParallel(compute_force, list_of_calculations, reduce_op='+') - - #if Settings.am_i_the_master(): - # np.savetxt("FC_before_subtraction.dat", fc) - - energy = None - forces = None - if Settings.am_i_the_master(): - energy, forces = calculators.get_energy_forces(ase_calculator, super_structure) - fc[:,:] += np.tile(forces.ravel(), (nat3, 1)) - Settings.barrier() - fc = Settings.broadcast(fc) - - #if Settings.am_i_the_master(): - # np.savetxt("FC_after_subtraction.dat", fc) - - if progress > 0: - print() - print("Done.") - - # Impose hermitianity - fc = .5 * (fc + fc.T) / epsilon - - # Convert to the correct units - final_dyn.dynmats[0] = fc / RY_TO_EV * BOHR_TO_ANGSTROM**2 - - - # Now we have the dynamical matrix in the supercell, get the dynamical matrix in the correct unit cell - if np.prod(supercell) > 1: - correct_dyn = Phonons(structure, nqirr = np.prod(supercell)) - q_tot = symmetries.GetQGrid(structure.unit_cell, supercell) - dynq = GetDynQFromFCSupercell(final_dyn.dynmats[0], np.array(q_tot), structure, super_structure) - for iq, q in enumerate(q_tot): - correct_dyn.dynmats[iq] = dynq[iq, :,:] - correct_dyn.q_tot[iq] = q - - correct_dyn.AdjustQStar() - final_dyn = correct_dyn - - return final_dyn diff --git a/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py deleted file mode 100644 index 5d3f8aee..00000000 --- a/cellconstructor/.ipynb_checkpoints/Settings-checkpoint.py +++ /dev/null @@ -1,346 +0,0 @@ -from __future__ import print_function -""" -This file keeps in mind common settings that needs to be initialized once. -""" -import numpy as np - -# The parallelization setup -__PARALLEL_TYPE__ = "serial" -try: - import mpi4py - import mpi4py.MPI - __PARALLEL_TYPE__ = "mpi4py" -except: - try: - __PARALLEL_TYPE__ = "serial" - import multiprocessing as mp - except: - __PARALLEL_TYPE__ = "serial" - - - -__SUPPORTED_LIBS__ = ["mp", "serial", "mpi4py"] -__MPI_LIBRARIES__ = ["mpi4py"] -__NPROC__ = 1 - -def ParallelPrint(*args, **kwargs): - """ - Print only if I am the master - """ - if am_i_the_master(): - print(*args, **kwargs) - -def all_print(*args, **kwargs): - """ - Print for all the processors - """ - print("[RANK {}] ".format(get_rank()), end = "") - print(*args, **kwargs) - - -def am_i_the_master(): - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - rank = comm.Get_rank() - if rank == 0: - return True - return False - else: - return True - -def get_rank(): - """ - Get the rank of the process - """ - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - return comm.Get_rank() - elif __PARALLEL_TYPE__ == "serial": - return 0 - else: - raise NotImplementedError("Error, I do not know what is the rank with the {} parallelization".format(__PARALLEL_TYPE__)) - -def broadcast(list_of_values, enforce_double = False, other_type = None): - """ - Broadcast the list to all the processors from the master. - It returns a list equal for all the processors from the master. - - If you are broadcasting a numpy array, use enforce_double. If the array is not a C double - type, specify the other_type (must be an MPI type). - - NOTE: Now enforce_double is just a dumb variable, as it is always overridded. - It seems that Bcast does not work as expected - """ - - # STRONG OVERRIDE OVER ENFORCE DOUBLE THAT IS NOT WORKING - enforce_double = False - - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - if comm.Get_size() == 1: - return list_of_values - - if not enforce_double: - return comm.bcast(list_of_values, root = 0) - else: - total_shape = list_of_values.shape - mpitype = mpi4py.MPI.DOUBLE - if other_type is not None: - mpitype = other_type - new_data = list_of_values.ravel() - comm.Bcast([new_data, np.prod(total_shape), mpitype], root = 0) - return new_data.reshape(total_shape) - elif __PARALLEL_TYPE__ == "serial": - return list_of_values - else: - raise NotImplementedError("broadcast not implemented for {} parallelization.".format(__PARALLEL_TYPE__)) - - -def barrier(): - """ - This function force the MPI processes to sync: - All the processes arrived at this function are stopped until all the others call the same method. - """ - - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - comm.barrier() - -def SetupParallel(n_processors=1): - """ - SETUP THE MODULE FOR PARALLEL EXECUTION - ======================================= - - This method initialize the parallelization of the module. - For serial execution use n_processors = 1. - Note that this kind of parallelization is implemented in single nodes. - - Parameters - ---------- - n_processors : int - The number of processors to be used for the heavy parallel - executions. If negative or zero, the system tries to determine - automatically the number of aveabile. - It is useless if the parallelization is done with MPI - """ - - global __NPROC__ - global __PARALLEL_TYPE__ - - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - __NPROC__ = comm.Get_size() - if __NPROC__ == 1: - __PARALLEL_TYPE__ = "serial" - - __NPROC__ = n_processors - if n_processors > 1 and __PARALLEL_TYPE__ == "serial": - raise ValueError("Error, trying to setup a parallel computation with a 'serial' library") - if n_processors < 1: - raise ValueError("Error, the number of processors must be 1 or higher") - -def GetNProc(): - """ - GET THE PROCESSORS FOR THE PARALLEL COMPUTATION - =============================================== - - This method returns the total number of processors currently setted up - for a parallel execution. If a serial algorithm is chosen, then 1 is returned. - You can modify it using the SetupParallel method. - """ - - if __PARALLEL_TYPE__ == "mpi4py": - return mpi4py.MPI.COMM_WORLD.Get_size() - - return __NPROC__ - -def GoParallel(function, list_of_inputs, reduce_op = None): - """ - GO PARALLEL - =========== - - Perform a parallel evaluation of the provided function with the spawned - list of inputs, and returns a list of output - - Parameters - ---------- - function : pointer to function - The function to be executed in parallel - list_of_inputs : list - A list containing the inputs to be passed to the function. - reduce_op : string - If a reduction must be performed on output, specify the operator, - accepted are "+", "*". For now this is implemented only with MPI - - """ - if not __PARALLEL_TYPE__ in __SUPPORTED_LIBS__: - raise ValueError("Error, wrong parallelization type: %s\nSupported types: %s" % (__PARALLEL_TYPE__, " ".join(__SUPPORTED_LIBS__))) - - - if __PARALLEL_TYPE__ in __MPI_LIBRARIES__ or __PARALLEL_TYPE__ == "serial": - if not reduce_op is None: - if not reduce_op in ["*", "+"]: - raise NotImplementedError("Error, reduction '{}' not implemented.".format(reduce_op)) - - # Here we create the poll manually - n_proc = GetNProc() - rank = get_rank() - - # broadcast the values - list_of_inputs = broadcast(list_of_inputs) - - # Prepare the work for the current processor - # TODO: Use a generator - computing_list = [] - for i in range(rank, len(list_of_inputs), n_proc): - computing_list.append(list_of_inputs[i]) - - #print("Rank {} is computing {} elements".format(rank, len(computing_list))) - #all_print("Computing:", computing_list) - - - # Perform the reduction - if reduce_op == "+": - result = 0 - for x in computing_list: - result += function(x) - - elif reduce_op == "*": - result = 1 - for x in computing_list: - result *= function(x) - - # If a reduction must be done, return - if not reduce_op is None: - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - results = comm.allgather(result) - elif __PARALLEL_TYPE__ == "serial": - return result - else: - raise NotImplementedError("Error, not implemented {}".format(__PARALLEL_TYPE__)) - - - #np.savetxt("result_{}.dat".format(rank), result) - result = results[0] - # Perform the last reduction - if reduce_op == "+": - for i in range(1,len(results)): - result+= results[i] - elif reduce_op == "*": - for i in range(1,len(results)): - result*= results[i] - - #np.savetxt("result_{}_total.dat".format(rank), result) - - return result - else: - raise NotImplementedError("Error, for now parallelization with MPI implemented only with reduction") - else: - raise NotImplementedError("Something went wrong: {}".format(__PARALLEL_TYPE__)) - - #elif __PARALLEL_TYPE__ == "mp": - #p = mp.Pool(__NPROC__) - #return p.map(function, list_of_inputs) - #elif __PARALLEL_TYPE__ == "serial": - #return map(function, list_of_inputs) - - -def GoParallelTuple(function, list_of_inputs, reduce_op = None): - """ - GO PARALLEL TUPLE - ================== - - Perform a parallel evaluation of the provided function with the spawned - list of inputs, and returns a list of output. It works well if function returns more than one result - - Parameters - ---------- - function : pointer to function - The function to be executed in parallel. It must return a tuple, and each element of the tuple must be defined - list_of_inputs : list - A list containing the inputs to be passed to the function. - reduce_op : string - If a reduction must be performed on output, specify the operator, - accepted are "+", "*". For now this is implemented only with MPI - - """ - if not __PARALLEL_TYPE__ in __SUPPORTED_LIBS__: - raise ValueError("Error, wrong parallelization type: %s\nSupported types: %s" % (__PARALLEL_TYPE__, " ".join(__SUPPORTED_LIBS__))) - - - if __PARALLEL_TYPE__ in __MPI_LIBRARIES__ or __PARALLEL_TYPE__ == "serial": - if not reduce_op is None: - if not reduce_op in ["*", "+"]: - raise NotImplementedError("Error, reduction '{}' not implemented.".format(reduce_op)) - - # Here we create the poll manually - n_proc = GetNProc() - rank = get_rank() - - # broadcast the values - list_of_inputs = broadcast(list_of_inputs) - - # Prepare the work for the current processor - # TODO: Use a generator - computing_list = [] - for i in range(rank, len(list_of_inputs), n_proc): - computing_list.append(list_of_inputs[i]) - - #print("Rank {} is computing {} elements".format(rank, len(computing_list))) - - # Work! TODO: THIS IS VERY MEMORY HEAVY - results = [function(x) for x in computing_list] - - - # Perform the reduction - if reduce_op == "+": - result = list(results[0]) - for i in range(1,len(results)): - for j in range(len(results[i])): - result[j] += results[i][j] - - - if reduce_op == "*": - result = list(results[0]) - for i in range(1,len(results)): - for j in range(len(results[i])): - result[j] *= results[i][j] - - - # If a reduction must be done, return - if not reduce_op is None: - if __PARALLEL_TYPE__ == "mpi4py": - comm = mpi4py.MPI.COMM_WORLD - results = [] - for i in range(len(result)): - results.append(comm.allgather(result[i])) - - elif __PARALLEL_TYPE__ == "serial": - return result - else: - raise NotImplementedError("Error, not implemented {}".format(__PARALLEL_TYPE__)) - - - result = results[0] - for j in range(len(results)): - # Perform the last reduction - if reduce_op == "+": - for i in range(1,len(results[j])): - result[j]+= results[j][i] - elif reduce_op == "*": - for i in range(1,len(results[j])): - result[j]*= results[j][i] - - return result - else: - raise NotImplementedError("Error, for now parallelization with MPI implemented only with reduction") - else: - raise NotImplementedError("Something went wrong: {}".format(__PARALLEL_TYPE__)) - - #elif __PARALLEL_TYPE__ == "mp": - #p = mp.Pool(__NPROC__) - #return p.map(function, list_of_inputs) - #elif __PARALLEL_TYPE__ == "serial": - #return map(function, list_of_inputs) - diff --git a/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py deleted file mode 100644 index 99493a88..00000000 --- a/cellconstructor/.ipynb_checkpoints/Spectral-checkpoint.py +++ /dev/null @@ -1,1984 +0,0 @@ -from __future__ import print_function -from __future__ import division - -import cellconstructor.Phonons as Phonons -import cellconstructor.Methods as Methods -import cellconstructor.symmetries as symmetries - -import numpy as np - -import itertools - -import symph -import thirdorder - -import cellconstructor as CC -import cellconstructor.symmetries -import cellconstructor.Settings -import cellconstructor.Methods as Methods - - -import time - -from cellconstructor.Settings import ParallelPrint as print - - -""" -In this module we compute the Spectral function -using the interpolation on the third order force constant matrix -""" - -# ========================== STATIC ================================================== - -def get_static_bubble(tensor2, tensor3, k_grid, q, T , verbose = False): - """ - COMPUTE THE STATIC BUBBLE - ========================= - - This function computes the static bubble for a given dynamical matrix, - the third order force constant tensor by using the Fourier interpolation - - - Parameters - ---------- - tensor2 : ForceTensor.Tensor2() - The second order force constant - tensor3 : ForceTensor.Tensor3() - The centered third order force costant - k_grid : (nk1, nk2, nk3) - The grid of k points to be used for the integration - q : ndarray(size = 3) - The q point at which compute the bubble. - T : float - The tempearture of the calculation (default 0 K) - verbose : bool - If true print debugging and timing info - - Results - ------- - dynq : ndarray( size = (3*nat, 3*nat), dtype = np.complex128) - The bubble matrix at the specified q point (only bubble). - """ - - structure = tensor2.unitcell_structure - - # Get the integration points - k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) - - - # Get the phi2 in q - phi2_q = tensor2.Interpolate(q, asr = False) - - - # dynamical matrix in q - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - mm_mat = np.sqrt(np.outer(m, m)) - mm_inv_mat = 1 / mm_mat - # - d2_q = phi2_q * mm_inv_mat - - # Diagonalize the dynamical matrix in q - w2_q, pols_q = np.linalg.eigh(d2_q) - - # Check if the q point is gamma - is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) - - if is_q_gamma: - w2_q[0:3]=0.0 - if not (w2_q >= 0.0).all(): - print('q= ',q, ' (2pi/A)') - print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_q=np.sqrt(w2_q) - - # Allocate the memory for the bubble - n_mod=3*structure.N_atoms - tmp_bubble = np.zeros((n_mod, n_mod), - dtype = np.complex128, order = "F") - - def compute_k(k): - - # phi3 in q, k, -q-k - t1 = time.time() - phi3=tensor3.Interpolate(k,-q-k, asr = False) - t2 = time.time() - - # phi2 in k - phi2_k = tensor2.Interpolate(k, asr = False) - - # phi2 in -q-k - phi2_mq_mk = tensor2.Interpolate(-q-k, asr = False) - - t3 = time.time() - - # dynamical matrices (divide by the masses) - d2_k = phi2_k * mm_inv_mat - d2_mq_mk = phi2_mq_mk * mm_inv_mat - - # Diagonalize the dynamical matrices - w2_k, pols_k = np.linalg.eigh(d2_k) - w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) - - is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) - is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) - - if is_k_gamma: - w2_k[0:3]=0.0 - if not (w2_k >= 0.0).all(): - print('k= ',k, ' (2pi/A)') - print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_k=np.sqrt(w2_k) - - if is_mq_mk_gamma: - w2_mq_mk[0:3]=0.0 - if not (w2_mq_mk >= 0.0).all(): - print('-q-k= ',-q-k, ' (2pi/A)') - print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_mq_mk=np.sqrt(w2_mq_mk) - - # Dividing the phi3 by the sqare root of masses - d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) - - # d3 in mode components - # d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) - d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) - d3_pols = np.einsum("ibc, bj -> ijc", d3_pols, pols_k) - d3_pols = np.einsum("ijc, ck -> ijk", d3_pols, pols_mq_mk) - - t4 = time.time() - - # Fortran duty ==== - - tmp_bubble = thirdorder.third_order_bubble.compute_static_bubble(T,np.array([w_q,w_k,w_mq_mk]).T, - np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), - d3_pols, - n_mod=n_mod) - - t5 = time.time() - - if verbose: - print("Time to interpolate the third order: {} s".format(t2 - t1)) - print("Time to interpolate the second order: {} s".format(t3 - t2)) - print("Time to transform the tensors: {} s".format(t4 - t3)) - print("Time to compute the bubble: {} s".format(t5 - t4)) - - return tmp_bubble - - - CC.Settings.SetupParallel() - tmp_bubble = CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") - # divide by the N_k factor - tmp_bubble /= len(k_points) - # bubble in cartesian - #d_bubble = np.einsum("ab, ia, jb -> ij", tmp_bubble, pols_q, np.conj(pols_q)) - - d_bubble = np.einsum("ij, ai -> aj", tmp_bubble, pols_q) - d_bubble = np.einsum("aj, bj -> ab", d_bubble, np.conj(pols_q)) - - # add to the SSCHA dynamical matrix in q - d2_final_q = d2_q + d_bubble - # and mutiply by the masses ( -> FC) - phi2_final_q = d2_final_q * mm_mat - - return phi2_final_q, w_q - -def get_static_correction(dyn, tensor3, k_grid, list_of_q_points, T): - """ - Get the dyn + static bubble correction for the list of q points - """ - dynq = np.zeros( (len(list_of_q_points), 3*dyn.structure.N_atoms, 3*dyn.structure.N_atoms), dtype = np.complex128 ) - - # Prepare the tensor2 - tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) - tensor2.SetupFromPhonons(dyn) - tensor2.Center() - - for iq, q in enumerate(list_of_q_points): - dynq[iq, :, :] = get_static_bubble(tensor2, tensor3, k_grid, np.array(q),T)[0] - - return dynq - -def get_static_correction_interpolated(dyn, tensor3, T, new_supercell, k_grid): - """ - Interpolate the dyn + the v3 tensor on a new supercell. - The dyn and the tensor3 can be defined on different supercells - - Parameters - ---------- - dyn : Phonons() - The harmonic / SSCHA dynamical matrix - tensor3 : Tensor3() - The third order force constant matrix - T : float - The temperature - new_supercell : list(len = 3) - The new supercell on which you want to interpolate the results - k_grid : list(len = 3) - The integration grid on the Brillouin zone - - Results - ------- - dyn_plus_odd : Phonons() - The dynamical matrix that includes the static bubble. - """ - - new_dyn = Phonons.Phonons(dyn.structure) - - q_tot = symmetries.GetQGrid(dyn.structure.unit_cell, new_supercell) - - # Prepare the q points for the new dynamical matrix - new_dyn.q_tot = q_tot - # For now we fill all the q point in the same star (we will adjust them later) - new_dyn.q_stars = [ [x.copy() for x in q_tot] ] - - # Get the dynamical matrix interpolated along each q point - dynq = get_static_correction(dyn, tensor3, k_grid, q_tot, T) - - # Add all the new computed dynamical matrix - for iq in range(len(q_tot)): - new_dyn.dynmats.append(dynq[iq, :, :]) - - # Adjust the dynamical matrix q points and the stars - new_dyn.AdjustQStar() - - return new_dyn - -def get_static_correction_along_path(dyn, - tensor3, - k_grid, - T=0, - q_path=[0.0,0.0,0.0], - q_path_file=None, - print_path = True, - filename_st="v2+d3static_freq.dat", - print_dyn = False, - name_dyn = "sscha_plus_odd_dyn", - d3_scale_factor = None, - tensor2 = None): - """ - Get the dyn + static bubble correction along a given path and prints the SSCHA and the - corrected frequencies in the file filename_st (path length in 2pi/Angstrom, SSCHA frequencies (cm-1), - SSCHA+static odd correction frequencies (cm-1)). - If print_dyn = True, the dyn+static bubble dynamical matrices are printed in QE format. - - Parameters - ---------- - - dyn : Phonons() - The harmonic / SSCHA dynamical matrix - tensor3 : Tensor3() - The third order force constant matrix - k_grid : list(len = 3) - The integration grid on the Brillouin zone - - Optional - -------- - - T : float - The temperature - (default: 0 K) - q_path : list of triplets - Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, - where the caculation is performed - (defualt: [0.0,0.0,0.0]) - q_path_file : string - Name of the file where the q_path can be read. - Format: column of triples, q points in 2pi/Angstrom - If the name of the file is present, it has the - priority over the q_path value - (default: None) - print_path : logical - If True (and the path is composed of more then one q-point), - a file 'path_len.dat' is printed. - Format: column of 4 values, coordinates of - the q-point and path length (in 2pi/Angstrom) . - (default: True) - filename_st : string - File where the result is written. - Format: length of the path (in 2pi/Alat), - SSCHA freq (cm-1),SSCHA+static bubble freq. (cm-1) - (default: "v2+d3static_freq.dat") - print_dyn : logical - If True, the dyn+odd dynamical matrices are printed - for the q-points of the path (in QE format) - (default: False) - name_dyn : string - Prefix of the name of the dyn+odd dynamical matrix printed - name: prefix#q(progressive_number) - (default: "sscha_plus_odd_dyn") - d3_scale_factor : float - If present, the 3rd order FC is multiplied by this factor - (e.g. it can be used to make tests about the perturbative limit) - (default: None) - tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) - If present, this 2nd order FC overwrites the one - obtained from dyn. - (default: None) - - """ - - print(" ") - print(" ====================================" ) - print(" Bubble static correction " ) - print(" ====================================" ) - print(" ") - print(" T= {:>5.1f} K".format(T)) - print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) - print(" ") - - if ( tensor2 == None ): - # Prepare the tensor2 - tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) - tensor2.SetupFromPhonons(dyn) - tensor2.Center() - - # Scale the FC3 =========================================================================== - if d3_scale_factor != None : - print(" ") - print(" d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) - print(" ") - tensor3.tensor=tensor3.tensor*d3_scale_factor - # ================================== q-PATH =============================================== - if q_path_file == None: - q_path=np.array(q_path) - else: - print(" ") - print(" q_path read from "+q_path_file) - print(" ") - q_path=np.loadtxt(q_path_file) - if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) - # Get the length of the q path - x_length = np.zeros(len(q_path)) - q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) - x_length[1:] = q_tot - x_length=np.cumsum(x_length) - x_length_exp=np.expand_dims(x_length,axis=0) - # print the path q-points and length - if print_path and (q_path.shape[0] > 1) : - fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] - result=np.hstack((q_path,x_length_exp.T)) - np.savetxt('path_len.dat',result,fmt=fmt_txt) - print(" ") - print(" Path printed in path_len.dat ") - print(" ") - # ========================================================================================== - - # Mass matrix - m = np.tile(dyn.structure.get_masses_array(), (3,1)).T.ravel() - mm_mat = np.sqrt(np.outer(m, m)) - - # Allocate frequencies array - nat=dyn.structure.N_atoms - n_mod=3 * nat - frequencies = np.zeros((len(q_path), n_mod), dtype = np.float64 ) # SSCHA+odd freq - v2_wq = np.zeros( (len(q_path), n_mod), dtype = np.float64 ) # pure SSCHA freq - - # =============== core calculation =========================================== - if print_dyn: - print(" ") - print(" dyn+odd dynamical matrices printed in "+name_dyn+"#q") - print(" ") - for iq, q in enumerate(q_path): - dynq, v2_wq[iq,:] = get_static_bubble(tensor2=tensor2, tensor3=tensor3, - k_grid=k_grid, q=np.array(q), - T=T, verbose = False) - - w2, pol = np.linalg.eigh(dynq / mm_mat) - frequencies[iq,:] = np.sign(w2)*np.sqrt(np.abs(w2)) - - if print_dyn: - Methods.save_qe(dyn,q,dynq,frequencies[iq,:],pol,fname=name_dyn+str(iq+1)) - # ============================================================================ - - # === print result ================================== - frequencies *= CC.Units.RY_TO_CM - v2_wq *= CC.Units.RY_TO_CM - result=np.hstack((x_length_exp.T,v2_wq,frequencies)) - fmt_txt='%10.6f\t\t'+n_mod*'%11.7f\t'+'\t'+n_mod*'%11.7f\t' - - print(" ") - print(" Results printed in "+filename_st) - print(" ") - head=("------------------------------------------------------------------------" - "\nlen (2pi/Angstrom), sscha freq (cm-1), sscha + static bubble freq (cm-1)" - "\n------------------------------------------------------------------------") - - np.savetxt(filename_st,result,fmt=fmt_txt,header=head) - # ================================================================================== - - - - - - - - - - - - - # ========================= FULL DYNAMIC ========================= - -def get_full_dynamic_bubble(tensor2, tensor3, k_grid, q, - smear_id, smear, energies, - T, - static_limit, - notransl, diag_approx, - verbose = False ): - - - """ - COMPUTE THE FULL DYNAMIC BUBBLE - ========================= - - This function computes the dynamic bubble for a given dynamical matrix, - the third order force constant tensor by using the Fourier interpolation - - - Parameters - ---------- - tensor2 : ForceTensor.Tensor2() - The second order force constant - tensor3 : ForceTensor.Tensor3() - The centered third order force costant - k_grid : (nk1, nk2, nk3) - The grid of k points to be used for the integration - q : ndarray(size = 3) - The q point at which compute the bubble. - T : float - The tempearture of the calculation (default 0 K) - asr : bool - If true, impose the acoustic sum rule during the Fourier transform - verbose : bool - If true print debugging and timing info - - Results - ------- - dynq : ndarray( size = (3*nat, 3*nat), dtype = np.complex128) - The bubble matrix at the specified q point (only bubble). - """ - - structure = tensor2.unitcell_structure - - # Get the integration points - k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) - - - # Get the phi2 in q - phi2_q = tensor2.Interpolate(q, asr = False) - - - # dynamical matrix in q - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - mm_mat = np.sqrt(np.outer(m, m)) - mm_inv_mat = 1 / mm_mat - # - d2_q = phi2_q * mm_inv_mat - - # Diagonalize the dynamical matrix in q - w2_q, pols_q = np.linalg.eigh(d2_q) - - # Check if the q point is gamma - is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) - - if is_q_gamma: - w2_q[0:3]=0.0 - if not (w2_q >= 0.0).all(): - print('q= ',q, ' (2pi/A)') - print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_q=np.sqrt(w2_q) - - # Allocate the memory for the bubble - ne=energies.shape[0] - nsm=smear.shape[0] - tmp_bubble = np.zeros((ne,nsm,3*structure.N_atoms, 3*structure.N_atoms), - dtype = np.complex128, order = "F") - - - def compute_k(k): - # phi3 in q, k, -q - k - t1 = time.time() - phi3=tensor3.Interpolate(k,-q-k, asr = False) - t2 = time.time() - # phi2 in k - phi2_k = tensor2.Interpolate(k, asr = False) - - # phi2 in -q-k - phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) - - t3 = time.time() - - # dynamical matrices (divide by the masses) - d2_k = phi2_k * mm_inv_mat - d2_mq_mk = phi2_mq_mk * mm_inv_mat - - # Diagonalize the dynamical matrices - w2_k, pols_k = np.linalg.eigh(d2_k) - w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) - - - is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) - is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) - - if is_k_gamma: - w2_k[0:3]=0.0 - if not (w2_k >= 0.0).all(): - print('k= ',k, ' (2pi/A)') - print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_k=np.sqrt(w2_k) - - if is_mq_mk_gamma: - w2_mq_mk[0:3]=0.0 - if not (w2_mq_mk >= 0.0).all(): - print('-q-k= ',-q-k, ' (2pi/A)') - print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_mq_mk=np.sqrt(w2_mq_mk) - - # Dividing the phi3 by the sqare root of masses - d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) - - # d3 in mode components - #d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) - d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) - d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) - d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) - - t4 = time.time() - - # Fortran duty ==== - tmp_bubble = thirdorder.third_order_bubble.compute_dynamic_bubble(energies,smear,static_limit,T, - np.array([w_q,w_k,w_mq_mk]).T, - np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), - d3_pols,diag_approx,ne,nsm,n_mod=3*structure.N_atoms) - - t5 = time.time() - - if verbose: - print("Time to interpolate the third order: {} s".format(t2 - t1)) - print("Time to interpolate the second order: {} s".format(t3 - t2)) - print("Time to transform the tensors: {} s".format(t4 - t3)) - print("Time to compute the bubble: {} s".format(t5 - t4)) - - return tmp_bubble - - - CC.Settings.SetupParallel() - d_bubble_mod = CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") - # divide by the N_k factor - d_bubble_mod /= len(k_points) # (ne,nsmear,3nat,3nat) - # the self-energy bubble in cartesian coord, divided by the sqare root of masses - d_bubble_cart = np.einsum("pqab, ia, jb -> pqij", d_bubble_mod, pols_q, np.conj(pols_q)) - # get the spectral function - no_gamma_pick=bool(is_q_gamma*notransl) - # - if no_gamma_pick : - print(" ") - print(" The acoustic pick in Gamma is discarded ") - print(" ") - # - # - spectral_func=thirdorder.third_order_bubble.compute_spectralf(smear_id, - energies, - d2_q, - d_bubble_cart, - no_gamma_pick, - structure.get_masses_array(), - structure.N_atoms,ne,nsm) - - return spectral_func - - -def get_full_dynamic_correction_along_path(dyn, - tensor3, - k_grid, - e1, de, e0, - sm1, sm0, - sm1_id, sm0_id, - nsm=1, - T=0, - q_path=[0.0,0.0,0.0], - q_path_file=None, - print_path = True, - static_limit = False, - notransl = True, - diag_approx = False, - filename_sp='full_spectral_func', - d3_scale_factor=None, - tensor2 = None): - - """ - Get the spectral function for a list of energies, and several q along a given path. - The calculations are performed for several values of smearings to calculate the self-energy - and the Green function. The resuls is printed in the file - filename_sp_[id_smear]_[smear].dat (path length in 2pi/Angstrom, energies (cm-1), - spectral function (1/cm-1)). - - Parameters - ---------- - - dyn : Phonons() - The harmonic / SSCHA dynamical matrix - tensor3 : Tensor3() - The third order force constant matrix - k_grid : list(len = 3) - The integration grid on the Brillouin zone - e1, de ,e0: float - The list of energies considered (cm-1), from e0 to e1, with interval de - sm0, sm1 : float - Minimum and maximum value of the smearing (cm-1) to compute the self-energy - sm0_id, sm1_id : float - Minimum and maximum value of the smearing (cm-1) for the term of the Green function - proportional to the identity - - Optional - -------- - - nsm : integer - Number of smearings to consider - (default = 1) - T : float - The temperature - (default: 0 K) - q_path : list of triplets - Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, - where the caculation is performed - (defualt: [0.0,0.0,0.0]) - q_path_file : string - Name of the file where the q_path can be read. - Format: column of triples, q points in 2pi/Angstrom - If the name of the file is present, it has the - priority over the q_path value - (default: None) - print_path : logical - If True (and the path is composed of more then one q-point), - a file 'path_len.dat' is printed. - Format: column of 4 values, coordinates of - the q-point and path length (in 2pi/Angstrom) . - (default: True) - static limit : logical - If True the self-energy is evaluated at E=0. - The spectral function is given by delta peaks in correspondence - of the frequencies of the sscha + static bubble correction - (default : False) - notransl : logical - If True, the contribution to the spectral function given by the acoustic - phonons in Gamma is discarded. - (defaul = True) - diag approx : logical - If True, the off-diagonal terms of the slef-energy are discarded - (the same result can be obtained in a cheaper way by using the - corresponding function) - (default : False) - filename_sp : string - filename_sp_[id_smear]_[smear].dat - is the file where the result is written. - Format: length of the path (in 2pi/Alat), - energy (cm-1),spectral function (1/cm-1) - (default: "full_spectral_func") - d3_scale_factor : float - If present, the 3rd order FC is multiplied by this factor - (e.g. it can be used to make tests about the perturbative limit) - (default: None) - tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) - If present, this 2nd order FC overwrites the one - obtained from dyn. - (default: None) - - """ - - print(" ") - print(" ===========================================" ) - print(" Bubble full dynamic correction " ) - print(" ===========================================" ) - print(" ") - print(" T= {:>5.1f} K".format(T)) - print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) - if static_limit : - print(" ") - print(" - The static limit is considered - ") - print(" ") - if diag_approx : - print(" ") - print(" - The off-diagonal terms of the self-energy are discarded - ") - print(" ") - - - if ( tensor2 == None ): - - # Prepare the tensor2 - tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) - tensor2.SetupFromPhonons(dyn) - tensor2.Center() - - # Scale the FC3 =========================================================================== - if d3_scale_factor != None : - print(" ") - print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) - print(" ") - tensor3.tensor=tensor3.tensor*d3_scale_factor - # ================================== q-PATH =============================================== - if q_path_file == None: - q_path=np.array(q_path) - else: - print(" ") - print(" q_path read from "+q_path_file) - print(" ") - q_path=np.loadtxt(q_path_file) - if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) - # Get the length of the q path - x_length = np.zeros(len(q_path)) - q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) - x_length[1:] = q_tot - x_length=np.cumsum(x_length) - x_length_exp=np.expand_dims(x_length,axis=0) - # print the path q-points and length - if print_path and (q_path.shape[0] > 1) : - fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] - result=np.hstack((q_path,x_length_exp.T)) - np.savetxt('path_len.dat',result,fmt=fmt_txt) - print(" ") - print(" Path printed in path_len.dat ") - print(" ") - # ========================================================================================== - - # ======================= Energy & Smearing ========================================== - # energy in input is in cm-1 - # smearing in input is in cm-1 - # converto to Ry - - # list of energies - energies=np.arange(e0,e1,de)/CC.Units.RY_TO_CM - ne=energies.shape[0] - # list of smearing - if nsm == 1 : - sm1=sm0 - sm1_id=sm0_id - smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM - smear_id=np.linspace(sm0_id,sm1_id,nsm)/CC.Units.RY_TO_CM - # ========================================================================================== - # - # - spectralf = np.zeros( (ne, nsm), dtype = np.float64 ) - # - smear_cm = smear * CC.Units.RY_TO_CM - smear_id_cm = smear_id * CC.Units.RY_TO_CM - energies_cm = energies * CC.Units.RY_TO_CM - for iq, q in enumerate(q_path): - spectralf[:, :] = get_full_dynamic_bubble(tensor2, tensor3, k_grid, np.array(q), - smear_id, smear, energies, T, - static_limit, notransl , - diag_approx, verbose=False ) - - # convert from 1/Ry to 1/cm-1 - spectralf /= CC.Units.RY_TO_CM - - - # ================================================================================== - - # print the result - for ism in range(nsm): - # - name="{:5.2f}".format(smear_id_cm[ism]).strip()+"_"+"{:6.1f}".format(smear_cm[ism]).strip() - # - filename_new=filename_sp+'_'+name+'.dat' - if iq == 0: - with open(filename_new,'w') as f: - f.write(" # ------------------------------------------------------------- \n") - f.write(" # len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1) \n") - f.write(" # ------------------------------------------------------------- \n") - for ie, ene in enumerate(energies_cm): - f.write("{:>10.6f}\t{:>11.7f}\t{:>11.7f}\n".format(x_length[iq],ene,spectralf[ie,ism])) - f.write("\n") - else: - with open(filename_new,'a') as f: - for ie, ene in enumerate(energies_cm): - f.write("{:>10.6f}\t{:>11.7f}\t{:>11.7f}\n".format(x_length[iq],ene,spectralf[ie,ism])) - f.write("\n") - - - - print(" ") - print(" Results printed in "+filename_sp+'_[id_smear]_[smear].dat') - print(" ") - - - - - - - -# ========================= DIAGONAL SELF-ENERGY DYNAMIC CORRECTION ========================= - -def get_diag_dynamic_bubble(tensor2, - tensor3, - k_grid, - q, - smear_id, - smear, - energies, - T, - verbose = False ): - - - structure = tensor2.unitcell_structure - - # Get the integration points - k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) - - - # Get the phi2 in q - phi2_q = tensor2.Interpolate(q, asr = False) - - - # dynamical matrix in q - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - mm_mat = np.sqrt(np.outer(m, m)) - mm_inv_mat = 1 / mm_mat - # - d2_q = phi2_q * mm_inv_mat - - # Diagonalize the dynamical matrix in q - w2_q, pols_q = np.linalg.eigh(d2_q) - - # Check if the q point is gamma - is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) - - if is_q_gamma: - w2_q[0:3]=0.0 - if not (w2_q >= 0.0).all(): - print('q= ',q, ' (2pi/A)') - print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_q=np.sqrt(w2_q) - - # Allocate the memory for the bubble - ne=energies.shape[0] - nsm=smear.shape[0] - nat=structure.N_atoms - n_mod=3*nat - tmp_bubble = np.zeros((ne,nsm,n_mod), - dtype = np.complex128, order = "F") - - - def compute_k(k): - # phi3 in q, k, -q - k - t1 = time.time() - phi3=tensor3.Interpolate(k,-q-k, asr = False) - t2 = time.time() - # phi2 in k - phi2_k = tensor2.Interpolate(k, asr = False) - - # phi2 in -q-k - phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) - - t3 = time.time() - - # dynamical matrices (divide by the masses) - d2_k = phi2_k * mm_inv_mat - d2_mq_mk = phi2_mq_mk * mm_inv_mat - - # Diagonalize the dynamical matrices - w2_k, pols_k = np.linalg.eigh(d2_k) - w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) - - - is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) - is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) - - if is_k_gamma: - w2_k[0:3]=0.0 - if not (w2_k >= 0.0).all(): - print('k= ',k, ' (2pi/A)') - print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_k=np.sqrt(w2_k) - - if is_mq_mk_gamma: - w2_mq_mk[0:3]=0.0 - if not (w2_mq_mk >= 0.0).all(): - print('-q-k= ',-q-k, ' (2pi/A)') - print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_mq_mk=np.sqrt(w2_mq_mk) - - # Dividing the phi3 by the sqare root of masses - d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) - - # d3 in mode components - #d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) - d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) - d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) - d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) - - t4 = time.time() - - - # Fortran duty ==== - - # - tmp_bubble = thirdorder.third_order_bubble.compute_diag_dynamic_bubble(energies,smear,T, - np.array([w_q,w_k,w_mq_mk]).T, - np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), - d3_pols,ne,nsm,n_mod=n_mod) - - t5 = time.time() - - if verbose: - print("Time to interpolate the third order: {} s".format(t2 - t1)) - print("Time to interpolate the second order: {} s".format(t3 - t2)) - print("Time to transform the tensors: {} s".format(t4 - t3)) - print("Time to compute the bubble: {} s".format(t5 - t4)) - - return tmp_bubble - - CC.Settings.SetupParallel() - - d_bubble_mod =CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") - - # divide by the N_k factor - d_bubble_mod /= len(k_points) # (ne,nsmear,n_mod) - # - np.savetxt("db_new",d_bubble_mod[:,-1,-1]) - # - spectralf=thirdorder.third_order_bubble.compute_spectralf_diag(smear_id,energies,w_q, - d_bubble_mod, - nat,ne,nsm) - # (ne, n_mod, nsmear) - - w2_q_ext=w2_q[None,None,...] - z=np.sqrt(d_bubble_mod + w2_q_ext) # (A20) PHYSICAL REVIEW B 97, 214101 (2018) - - w_q_ext=w_q[None,None,...] - z_pert=w_q_ext+np.divide(d_bubble_mod, 2*w_q_ext, out=np.zeros_like(d_bubble_mod), where=w_q_ext!=0) - - return spectralf, z, z_pert, w_q - - -def get_diag_dynamic_correction_along_path(dyn, tensor3, - k_grid, - e1, de, e0, - sm1, sm0, - sm1_id=None, sm0_id=None, - nsm=1, - q_path=[0.0,0.0,0.0], - q_path_file=None, - print_path = True, - T=0.0, - filename_sp = 'spectral_func', - filename_z = None, - filename_freq_dyn = 'freq_dynamic', - filename_shift_lw = 'v2_freq_shift_hwhm', - self_consist = False, - iterative=False, - numiter=200, - d3_scale_factor=None, - tensor2 = None): - - - """ - Get the spectral function for a list of energies, and several q along a given path, - in the diagonal approximation (off-diagonal terms of the self-energies are discarded). - The calculations are performed for several values of smearings to calculate the self-energy - and the Green function. The resuls is printed in the file - filename_sp_[id_smear]_[smear].dat (path length in 2pi/Angstrom, energies (cm-1), - spectral function (1/cm-1), mode components of the spectral function (1/cm-1) ). - The Z function [PRB 97 214101 (A20)] is also printed in filename_z_[id_smear]_[smear].dat. - The frequency shift (with respect to the SSCHA frequency) and linewidth are computed in three ways - (one optional). 1. One shot, evaluating the Z function in the SSCHA frequency value.2. Perturbative, - evaluating the perturbative correction. 3. (optional) solving the self-consistent relation (details - in [PRB 97 214101 (A21)]). The corresponding Lorenzian spectral functions are then printed. - - Parameters - ---------- - - dyn : Phonons() - The harmonic / SSCHA dynamical matrix - tensor3 : Tensor3() - The third order force constant matrix - k_grid : list(len = 3) - The integration grid on the Brillouin zone - e1, de ,e0: float - The list of energies considered (cm-1), from e0 to e1, with interval de - sm0, sm1 : float - Minimum and maximum value of the smearing (cm-1) to compute the self-energy - - Optional - -------- - - nsm : integer - Number of smearings to consider - (default = 1) - T : float - The temperature - (default: 0 K) - sm0_id, sm1_id : float - Minimum and maximum value of the smearing (cm-1) for the term of the Green function - proportional to the identity. If not present, it is sm0_id = sm1_id = 2.0 * de - (default: None) - q_path : list of triplets - Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, - where the caculation is performed - (defualt: [0.0,0.0,0.0]) - q_path_file : string - Name of the file where the q_path can be read. - Format: column of triples, q points in 2pi/Angstrom - If the name of the file is present, it has the - priority over the q_path value - (default: None) - print_path : logical - If True (and the path is composed of more then one q-point), - a file 'path_len.dat' is printed. - Format: column of 4 values, coordinates of - the q-point and path length (in 2pi/Angstrom) . - (default: True) - filename_sp : string - filename_sp_[smear].dat - is the file where the spectral function is written. - Format: length of the path (in 2pi/Alat), - energy (cm-1),spectral function (1/cm-1), - single mode contributions to spectral function (1/cm-1) - (default: "spectral_func") - filename_z : string - if present, the file - filename_z_[smear].dat - with the z function is written - Format: length of the path (in 2pi/Alat), - energy (cm-1), z function (cm-1), - (default: None) - filename_shift_lw : string - filename_shift_lw_[method]_[smear].dat - is the file where - len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) - are printed. [method] are "one shot", "perturb" and "self-consist" - (the last one optional) - (default: "v2_freq_shift_hwhm") - filename_freq_dyn : string - filename_freq_dyn_[method]_[smear].dat - is the file where - len (2pi/Angstrom), freq (cm-1) (sorted in ascending order), corresponding HWHM (cm-1) - are printed. [method] are "one shot", "perturb" and "self-consist" - (the last one optional) - (default: "freq_dynamic") - self_consist : Logical - If True, the dynamical frequency is found solving the self-consistent - relation [PRB 97 214101 (A21)] - (default: False) - iterative : Logical - If True, the self-consistent relation is found iteratively - (default: False) - numiter : integer - Number of maximum steps to find the self-consistency iteratively - (default : 200) - d3_scale_factor : float - If present, the 3rd order FC is multiplied by this factor - (e.g. it can be used to make tests about the perturbative limit) - (default: None) - tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) - If present, this 2nd order FC overwrites the one - obtained from dyn. - (default: None) - - """ - - - - print(" ") - print(" ===========================================" ) - print(" Bubble diagonal dynamic correction " ) - print(" ===========================================" ) - print(" ") - print(" T= {:>5.1f} K".format(T)) - print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) - print(" ") - print(" Smearing values: ") - for sm in np.linspace(sm0,sm1,nsm): - print(" sm= {:>6.2f} cm-1".format(sm)) - print(" ") - print(" ===========================================" ) - print(" " ) - - - if sm1_id != None and sm0_id != None: - for sm in np.linspace(sm0_id,sm1_id,nsm): - print(" sm_id= {:>6.2f} cm-1".format(sm)) - else: - sm1_id=de*2.0 - sm0_id=de*2.0 - - if ( tensor2 == None ): - - # Prepare the tensor2 - tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) - tensor2.SetupFromPhonons(dyn) - tensor2.Center() - structure = tensor2.unitcell_structure - - - - # Scale the FC3 =========================================================================== - if d3_scale_factor != None : - print(" ") - print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) - print(" ") - tensor3.tensor=tensor3.tensor*d3_scale_factor - # ================================== q-PATH =============================================== - if q_path_file == None: - q_path=np.array(q_path) - else: - print(" ") - print(" q_path read from "+q_path_file) - print(" ") - q_path=np.loadtxt(q_path_file) - if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) - # Get the length of the q path - x_length = np.zeros(len(q_path)) - q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) - x_length[1:] = q_tot - x_length=np.cumsum(x_length) - x_length_exp=np.expand_dims(x_length,axis=0) - # print the path q-points and length - if print_path and (q_path.shape[0] > 1) : - fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] - result=np.hstack((q_path,x_length_exp.T)) - np.savetxt('path_len.dat',result,fmt=fmt_txt) - print(" ") - print(" Path printed in path_len.dat ") - print(" ") - # ======================= Energy & Smearing ========================================== - # - # energy in input is in cm-1 - # smearing in input is in cm-1 - # converto to Ry - - # list of energies - energies=np.arange(e0,e1,de)/CC.Units.RY_TO_CM - ne=energies.shape[0] - # list of smearing - if nsm == 1 : - sm1=sm0 - sm1_id=sm0_id - smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM - smear_id=np.linspace(sm0_id,sm1_id,nsm)/CC.Units.RY_TO_CM - # - # ========================================================================================== - # - - def Lorenz(x,x0,G): - return G/((x-x0)**2+G**2)/np.pi/2.0 - def findne(val,e0,de): - #return int( round( ((val-e0)/de)+1 ) ) - return int(round( (val-e0)/de ) ) - # - print(" ") - #print(" Spectral function, in diagonal approximation, printed in "+filename_sp+"_[smear_id]_[smear].dat") - print(" Spectral function, in diagonal approximation, printed in "+filename_sp+"_[smear].dat") - print(" ") - if filename_z != None: - print(" ") - print(" Z function [PRB 97 214101 (A21)], printed in "+filename_z+"_[smear].dat") - print(" ") - - print(" ========================================= ") - print(" Frequncies shifts and widths calculations ") - print(" ========================================= ") - print(" ") - print(" Frequencies shifts and linewidths computed with perturbative approximation and one-shot calculation in: ") - print(" ") - #print(" "+filename_shift_lw +"_perturb_[smear_id]_[smear].dat") - #print(" "+filename_shift_lw +"_one_shot_[smear_id]_[smear].dat") - print(" "+filename_shift_lw +"_perturb_[smear].dat") - print(" "+filename_shift_lw +"_one_shot_[smear].dat") - print(" ") - print(" ") - print(" Dynamical frequencies sorted, with HWHM: ") - print(" ") - #print(" "+filename_freq_dyn +"_perturb_[smear_id]_[smear].dat") - #print(" "+filename_freq_dyn +"_one_shot_[smear_id]_[smear].dat") - print(" "+filename_freq_dyn +"_perturb_[smear].dat") - print(" "+filename_freq_dyn +"_one_shot_[smear].dat") - print(" ") - print(" ") - print(" Relative spectral functions in Lorentzian approximation: ") - print(" ") - #print(" "+filename_sp+"_lorentz_perturb_[smear_id]_[smear].dat") - #print(" "+filename_sp+"_one_shot_[smear_id]_[smear].dat") - print(" "+filename_sp+"_lorentz_perturb_[smear].dat") - print(" "+filename_sp+"_one_shot_[smear].dat") - print(" ") - if self_consist: - print(" ************************************************ ") - print(" Self-consistent search for dynamical frequencies ") - print(" ************************************************ ") - print(" ") - print(" Results printed in: ") - print(" ") - #print(" "+filename_shift_lw +"_[smear_id]_[smear].dat") - print(" "+filename_shift_lw +"_self-consist_[smear].dat") - print(" ") - print(" ") - #print(" "+filename_freq_dyn +"_[smear_id]_[smear].dat") - print(" "+filename_freq_dyn +"_self-consist_[smear].dat") - print(" ") - print(" ") - #print(" "+filename_sp+"_lorentz_[smear_id]_[smear].dat") - print(" "+filename_sp+"_lorentz_self-consist_[smear].dat") - print(" ") - print(" ") - # convert from Ry to cm-1 - smear_cm = smear * CC.Units.RY_TO_CM - smear_id_cm = smear_id * CC.Units.RY_TO_CM - energies_cm = energies * CC.Units.RY_TO_CM - - n_mod=3*dyn.structure.N_atoms - # - spectralf = np.zeros( (ne,n_mod,nsm), dtype=np.float64 ) - z = np.zeros( (ne,nsm,n_mod) , dtype=np.complex128 ) - z_pert = np.zeros( (ne,nsm,n_mod), dtype = np.complex128 ) - wq = np.zeros( n_mod, dtype = np.float64 ) - # - - for iq, q in enumerate(q_path): - - spectralf, z , z_pert, wq = get_diag_dynamic_bubble(tensor2, tensor3, - k_grid, np.array(q), - smear_id, smear, energies, - T, verbose=False ) - - - # - z *= CC.Units.RY_TO_CM - z_pert *= CC.Units.RY_TO_CM - wq *= CC.Units.RY_TO_CM - # convert from 1/Ry to 1/cm-1 - spectralf /= CC.Units.RY_TO_CM - - - - - - for ism in range(nsm): - # - # pre-name for writing data - # - #name="{:5.2f}".format(smear_id_cm[ism]).strip()+"_"+"{:6.1f}".format(smear_cm[ism]).strip()# - name="{:6.2f}".format(smear_cm[ism]).strip() - # - # write spectral and z function - # - # ======= - # spectral func - # ======= - filename_new=filename_sp+'_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" - - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ---------------------------------------------------------------------------------------------------------\n") - f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1)\n") - f.write("# ---------------------------------------------------------------------------------------------------------\n") - for ie, ene in enumerate(energies_cm): - out=spectralf[ie,:,ism] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - else: - with open(filename_new,'a') as f: - for ie, ene in enumerate(energies_cm): - out=spectralf[ie,:,ism] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - # ======= - # z func - # ======= - if filename_z != None: - filename_new=filename_z+'_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod)+"\n" - - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ---------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), energy (cm-1), z function (cm-1) \n") - f.write("# ---------------------------------------------------- \n") - for ie, ene in enumerate(energies_cm): - out=z[ie,ism,:] - f.write(fmt.format(x_length[iq],ene,*out)) - else: - with open(filename_new,'a') as f: - for ie, ene in enumerate(energies_cm): - out=z[ie,ism,:] - f.write(fmt.format(x_length[iq],ene,*out)) - - # ====================================== - # compute frequency shift and linewidth - # ====================================== - - if self_consist: - res=np.zeros((n_mod,2),dtype=np.float64) # self-consist shifted freq and linewidth - res_os=np.zeros((n_mod,2),dtype=np.float64) # one-shot shifted freq and linewidth - res_pert=np.zeros((n_mod,2),dtype=np.float64) # perturbative shifted freq and linewidth - - is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q_path[iq]) - for ifreq in range(n_mod): - done=False - if iterative : - # - freqold=wq[ifreq] - freqoldold=freqold - for i in range(numiter): - x=findne(freqold,e0,de) - if i==0: xtriv=x - freqshifted=np.real(z[x-1,ism,ifreq]) # Re(z) is the shifted freq - if abs(freqshifted-freqold)< 2*de: - done=True - break - else: - freqoldold=freqold - freqold=freqshifted - # - else: - xtriv=findne(wq[ifreq],e0,de) - osval=np.real(z[xtriv-1,ism,ifreq]) - diff=np.infty - for x in range(ne): - value=np.real(z[x,ism,ifreq])-energies_cm[x] - if( abs(value ) < 2*de) : - if ( 1.0 < abs(energies_cm[x]) or ( is_q_gamma and ifreq < 3 ) ): - done=True - if ( abs( energies_cm[x]-osval ) < diff ): - diff=abs( energies_cm[x]-osval ) - freqshifted=energies_cm[x] - - - # - if done: - # - res[ifreq,0]=freqshifted - x=findne(freqshifted,e0,de) - res[ifreq,1]=-np.imag(z[x-1,ism,ifreq]) - # - else: - # - print(" Self-consistency for the {:5d}-th mode of the {:5d}-th q-point not reached. " - "One-shot approx. value used".format(ifreq+1,iq+1)) - res[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) - res[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) - # - res_os[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) - res_os[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) - # - res_pert[ifreq,0]=np.real(z_pert[xtriv-1,ism,ifreq]) - res_pert[ifreq,1]=-np.imag(z_pert[xtriv-1,ism,ifreq]) - else: - res_os=np.zeros((n_mod,2),dtype=np.float64) # one-shot shifted freq and linewidth - res_pert=np.zeros((n_mod,2),dtype=np.float64) # perturbative shifted freq and linewidth - - for ifreq in range(n_mod): - xtriv=findne(wq[ifreq],e0,de) - # - res_os[ifreq,0]=np.real(z[xtriv-1,ism,ifreq]) - res_os[ifreq,1]=-np.imag(z[xtriv-1,ism,ifreq]) - # - res_pert[ifreq,0]=np.real(z_pert[xtriv-1,ism,ifreq]) - res_pert[ifreq,1]=-np.imag(z_pert[xtriv-1,ism,ifreq]) - - - - - # ======================= - # v2_freq, shift, hwhm - # ======================= - if self_consist: - filename_new=filename_shift_lw+'_self-consist_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ----------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") - f.write("# ----------------------------------------------------------------- \n") - out=np.concatenate((wq[:],res[:,0]-wq[:], res[:,1])) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq[:],res[:,0]-wq[:], res[:,1])) - f.write(fmt.format(x_length[iq],*out)) - - filename_new=filename_shift_lw+'_one_shot_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ----------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") - f.write("# ----------------------------------------------------------------- \n") - out=np.concatenate((wq[:],res_os[:,0]-wq[:], res_os[:,1])) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq[:],res_os[:,0]-wq[:], res_os[:,1])) - f.write(fmt.format(x_length[iq],*out)) - # - filename_new=filename_shift_lw+'_perturb_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ----------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) \n") - f.write("# ----------------------------------------------------------------- \n") - out=np.concatenate((wq[:],res_pert[:,0]-wq[:], res_pert[:,1])) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq[:],res_pert[:,0]-wq[:], res_pert[:,1])) - f.write(fmt.format(x_length[iq],*out)) - # ================================================ - # freq sorted, hwhm && Lorenztian spectral func - # ================================================ - - if self_consist: - - wq_shifted=res[:,0] - hwhm=res[:,1] - - sortidx=np.argsort(wq_shifted,axis=0) - - wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) - hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) - #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted - #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted - # - # freq, freq - # - filename_new=filename_freq_dyn+'_self-consist_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ------------------------------------------------------------ \n") - f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") - f.write("# ------------------------------------------------------------ \n") - #out=np.concatenate((wq_shifted_sorted, - #wq_shifted_sorted_plus, - #wq_shifted_sorted_minus)) - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - # - # Lorenztian spectral func - # - filename_new=filename_sp+'_lorentz_self-consist_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ---------------------------------------------------------------------------------------------------------\n") - f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1)\n") - f.write("# ---------------------------------------------------------------------------------------------------------\n") - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - else: - with open(filename_new,'a') as f: - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - # - wq_shifted=res_os[:,0] - hwhm=res_os[:,1] - - sortidx=np.argsort(wq_shifted,axis=0) - - wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) - hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) - #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted - #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted - # - # freq, freq +/- hwhm - # - filename_new=filename_freq_dyn+'_one_shot_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ------------------------------------------------------------ \n") - f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") - f.write("# ------------------------------------------------------------ \n") - #out=np.concatenate((wq_shifted_sorted, - #wq_shifted_sorted_plus, - # wq_shifted_sorted_minus)) - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - # - # Lorentzian spectral func - # - filename_new=filename_sp+'_lorentz_one_shot_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# --------------------------------------------------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1) \n") - f.write("# --------------------------------------------------------------------------------------------------------- \n") - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - else: - with open(filename_new,'a') as f: - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - - # - wq_shifted=res_pert[:,0] - hwhm=res_pert[:,1] - - sortidx=np.argsort(wq_shifted,axis=0) - - wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) - hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) - #wq_shifted_sorted_plus= wq_shifted_sorted+hwhm_sorted - #wq_shifted_sorted_minus= wq_shifted_sorted-hwhm_sorted - # - # freq, freq +/- hwhm - # - filename_new=filename_freq_dyn+'_perturb_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ------------------------------------------------------------ \n") - f.write("# len (2pi/Angstrom), SSCHA+shift (sorted) (cm-1), HWHM (cm-1) \n") - f.write("# ------------------------------------------------------------ \n") - #out=np.concatenate((wq_shifted_sorted, - #wq_shifted_sorted_plus, - #wq_shifted_sorted_minus)) - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq_shifted_sorted, - hwhm_sorted)) - f.write(fmt.format(x_length[iq],*out)) - # - # Lorenztian spectral func - # - filename_new=filename_sp+'_lorentz_perturb_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.3f}"+"\t{:>11.7f}"*(n_mod+1)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# --------------------------------------------------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), energy (cm-1), spectral function (1/cm-1), spectral function mode components (1/cm-1) \n") - f.write("# --------------------------------------------------------------------------------------------------------- \n") - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - else: - with open(filename_new,'a') as f: - Lor_spectralf=np.zeros((ne,n_mod),dtype=np.float64) - for ifreq in range(n_mod): - Lor_spectralf[:,ifreq]=Lorenz(energies_cm, - wq_shifted_sorted[ifreq], - hwhm_sorted[ifreq]+smear_id_cm[ism]) - for ie, ene in enumerate(energies_cm): - out=Lor_spectralf[ie,:] - f.write(fmt.format(x_length[iq],ene,np.sum(out),*out)) - f.write("\n") - # - - - - # ===== PERTURBATIVE CORRECTION TO SSCHA FREQUENCY (SHIFT and LINEWIDTH) ===================== - -def get_perturb_dynamic_selfnrg(tensor2, tensor3, - k_grid, q, - smear, - T, - verbose= False): - - structure = tensor2.unitcell_structure - - # Get the integration points - k_points = CC.symmetries.GetQGrid(structure.unit_cell, k_grid) - - - # Get the phi2 in q - phi2_q = tensor2.Interpolate(q, asr = False) - - # dynamical matrix in q - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - mm_mat = np.sqrt(np.outer(m, m)) - mm_inv_mat = 1 / mm_mat - # - d2_q = phi2_q * mm_inv_mat - - # Diagonalize the dynamical matrix in q - w2_q, pols_q = np.linalg.eigh(d2_q) - - # Check if the q point is gamma - is_q_gamma = CC.Methods.is_gamma(structure.unit_cell, q) - - if is_q_gamma: - w2_q[0:3]=0.0 - if not (w2_q >= 0.0).all(): - print('q= ',q, ' (2pi/A)') - print('w(q)= ',np.sign(w2_q)*np.sqrt(np.abs(w2_q))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_q=np.sqrt(w2_q) - - def compute_k(k): - # phi3 in q, k, -q - k - t1 = time.time() - phi3=tensor3.Interpolate(k,-q-k, asr = False) - t2 = time.time() - # phi2 in k - phi2_k = tensor2.Interpolate(k, asr = False) - - # phi2 in -q-k - phi2_mq_mk = tensor2.Interpolate(-q -k, asr = False) - - t3 = time.time() - - # dynamical matrices (divide by the masses) - d2_k = phi2_k * mm_inv_mat - d2_mq_mk = phi2_mq_mk * mm_inv_mat - - # Diagonalize the dynamical matrices - w2_k, pols_k = np.linalg.eigh(d2_k) - w2_mq_mk, pols_mq_mk = np.linalg.eigh(d2_mq_mk) - - - is_k_gamma = CC.Methods.is_gamma(structure.unit_cell, k) - is_mq_mk_gamma = CC.Methods.is_gamma(structure.unit_cell, -q-k) - - if is_k_gamma: - w2_k[0:3]=0.0 - if not (w2_k >= 0.0).all(): - print('k= ',k, ' (2pi/A)') - print('w(k)= ',np.sign(w2_k)*np.sqrt(np.abs(w2_k))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_k=np.sqrt(w2_k) - - if is_mq_mk_gamma: - w2_mq_mk[0:3]=0.0 - if not (w2_mq_mk >= 0.0).all(): - print('-q-k= ',-q-k, ' (2pi/A)') - print('w(-q-k)= ',np.sign(w2_mq_mk)*np.sqrt(np.abs(w2_mq_mk))*CC.Units.RY_TO_CM,' (cm-1)') - print('Cannot continue with SSCHA negative frequencies') - exit() - w_mq_mk=np.sqrt(w2_mq_mk) - - # Dividing the phi3 by the sqare root of masses - d3 = np.einsum("abc, a, b, c -> abc", phi3, 1/np.sqrt(m), 1/np.sqrt(m), 1/np.sqrt(m)) - - # d3 in mode components - # d3_pols = np.einsum("abc, ai, bj, ck -> ijk", d3, pols_mq, pols_k, pols_q_mk) - d3_pols = np.einsum("abc, ai -> ibc", d3, pols_q) - d3_pols = np.einsum("abc, bi -> aic", d3_pols, pols_k) - d3_pols = np.einsum("abc, ci -> abi", d3_pols, pols_mq_mk) - - t4 = time.time() - - - nsm=smear.shape[0] - n_mod=3*structure.N_atoms - # Fortran duty ==== - - selfnrg = thirdorder.third_order_bubble.compute_perturb_selfnrg(smear,T, - np.array([w_q,w_k,w_mq_mk]).T, - np.array([is_q_gamma,is_k_gamma,is_mq_mk_gamma]), - d3_pols,nsm,n_mod) - - t5 = time.time() - - if verbose: - print("Time to interpolate the third order: {} s".format(t2 - t1)) - print("Time to interpolate the second order: {} s".format(t3 - t2)) - print("Time to transform the tensors: {} s".format(t4 - t3)) - print("Time to compute the bubble: {} s".format(t5 - t4)) - - return selfnrg - - CC.Settings.SetupParallel() - - selfnrg =CC.Settings.GoParallel(compute_k, k_points, reduce_op = "+") - - # divide by the N_k factor - selfnrg /= len(k_points) # (n_mod,nsigma) - - w_q_ext=w_q[...,None] - - shift=np.divide(selfnrg.real, 2*w_q_ext, out=np.zeros_like(selfnrg.real), where=w_q_ext!=0) - hwhm=np.divide(-selfnrg.imag, 2*w_q_ext, out=np.zeros_like(selfnrg.imag), where=w_q_ext!=0) - - return w_q, shift,hwhm - - - -def get_perturb_dynamic_correction_along_path(dyn, tensor3, - k_grid, - sm1, sm0, - nsm=1, - q_path=[0.0,0.0,0.0], - q_path_file=None, - print_path = True, - T=0, - filename_shift_lw = 'v2_freq_shift_hwhm', - filename_freq_dyn = 'freq_dynamic', - d3_scale_factor=None, - tensor2= None): - - - """ - The frequency shift (with respect to the SSCHA frequency) and linewidth are computed with the perturbative - formula with respect to the SSCHA frequency. - - Parameters - ---------- - - dyn : Phonons() - The harmonic / SSCHA dynamical matrix - tensor3 : Tensor3() - The third order force constant matrix - k_grid : list(len = 3) - The integration grid on the Brillouin zone - sm0, sm1 : float - Minimum and maximum value of the smearing (cm-1) to compute the self-energy - - Optional - -------- - - nsm : integer - Number of smearings to consider - (default = 1) - T : float - The temperature - (default: 0 K) - q_path : list of triplets - Path of the q-points of the Brillouin Zone, in 2pi/Anstrom units, - where the caculation is performed - (defualt: [0.0,0.0,0.0]) - q_path_file : string - Name of the file where the q_path can be read. - Format: column of triples, q points in 2pi/Angstrom - If the name of the file is present, it has the - priority over the q_path value - (default: None) - print_path : logical - If True (and the path is composed of more then one q-point), - a file 'path_len.dat' is printed. - Format: column of 4 values, coordinates of - the q-point and path length (in 2pi/Angstrom) . - (default: True) - filename_shift_lw : string - filename_shift_lw_[id_smear]_[smear].dat - is the file where - len (2pi/Angstrom), SSCHA freq (cm-1), shift (cm-1) , HWHM (cm-1) - are printed. - (default: "v2_freq_shit_hwhm") - filename_freq_dyn : string - filename_freq_dyn_[id_smear]_[smear].dat - is the file where - len (2pi/Angstrom), freq (cm-1) (sorted in ascending order), HWHM (cm-1) - are printed. - (default: "freq_dynamic") - d3_scale_factor : float - If present, the 3rd order FC is multiplied by this factor - (e.g. it can be used to make tests about the perturbative limit) - (default: None) - tensor2 : ndarray( size = (3*nat, 3*nat), dtype = np.float) - If present, this 2nd order FC overwrites the one - obtained from dyn. - (default: None) - - """ - - - - print(" ") - print(" ===========================================" ) - print(" Bubble perturbative dynamic correction " ) - print(" ===========================================" ) - print(" ") - print(" T= {:>5.1f} K".format(T)) - print(" k grid= {} x {} x {} ".format(*tuple(k_grid))) - print(" ") - print(" Smearing values: ") - for sm in np.linspace(sm0,sm1,nsm): - print(" sm= {:>6.2f} cm-1".format(sm)) - print(" ") - print(" ===========================================" ) - print(" " ) - - if ( tensor2 == None ): - - # Prepare the tensor2 - tensor2 = CC.ForceTensor.Tensor2(dyn.structure, dyn.structure.generate_supercell(dyn.GetSupercell()), dyn.GetSupercell()) - tensor2.SetupFromPhonons(dyn) - tensor2.Center() - - # Scale the FC3 =========================================================================== - if d3_scale_factor != None : - print(" ") - print("d3 scaling : d3 -> d3 x {:>7.3f}".format(d3_scale_factor)) - print(" ") - tensor3.tensor=tensor3.tensor*d3_scale_factor - # ================================== q-PATH =============================================== - if q_path_file == None: - q_path=np.array(q_path) - else: - print(" ") - print(" q_path read from "+q_path_file) - print(" ") - q_path=np.loadtxt(q_path_file) - if len(q_path.shape) == 1 : q_path=np.expand_dims(q_path,axis=0) - # Get the length of the q path - x_length = np.zeros(len(q_path)) - q_tot = np.sqrt(np.sum(np.diff(np.array(q_path), axis = 0)**2, axis = 1)) - x_length[1:] = q_tot - x_length=np.cumsum(x_length) - x_length_exp=np.expand_dims(x_length,axis=0) - # print the path q-points and length - if print_path and (q_path.shape[0] > 1) : - fmt_txt=['%11.7f\t','%11.7f\t','%11.7f\t\t','%10.6f\t'] - result=np.hstack((q_path,x_length_exp.T)) - np.savetxt('path_len.dat',result,fmt=fmt_txt) - print(" ") - print(" Path printed in path_len.dat ") - print(" ") - # ======================= Smearing ========================================== - # smearing in input is in cm-1 - # converto to Ry - # list of smearing - # - if nsm == 1 : - sm1=sm0 - smear=np.linspace(sm0,sm1,nsm)/CC.Units.RY_TO_CM - # ======================== Calculation ========================================== - n_mod=3*dyn.structure.N_atoms - shift = np.zeros( ( n_mod, nsm), dtype = np.float64 ) # q-point,mode,smear - hwhm = np.zeros( ( n_mod, nsm), dtype = np.float64 ) # q-point,mode,smear - wq = np.zeros( ( n_mod), dtype = np.float64 ) # q-point,mode - # - smear_cm = smear * CC.Units.RY_TO_CM - for iq, q in enumerate(q_path): - wq[:],shift[:,:], hwhm[:,:] = get_perturb_dynamic_selfnrg(tensor2, tensor3, - k_grid, np.array(q), - smear, T, - verbose=False ) - - # print results - wq*=CC.Units.RY_TO_CM - shift*=CC.Units.RY_TO_CM - hwhm*=CC.Units.RY_TO_CM - # - #==================== SORTING =============================== - wq_shifted=wq[...,None]+shift - - sortidx=np.argsort(wq_shifted,axis=0) - - wq_shifted_sorted=np.take_along_axis(wq_shifted, sortidx, 0) - hwhm_sorted=np.take_along_axis(hwhm, sortidx, 0) - #=============== Print Results =============================== - - # - for ism in range(nsm): - # - name="{:6.2f}".format(smear_cm[ism]).strip() - # - # v2 freq, corresponding shift & hwhm - # - filename_new=filename_shift_lw+'_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(3*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# --------------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), sscha freq (cm-1), freq shift (cm-1), hwhm (cm-1) \n") - f.write("# --------------------------------------------------------------------- \n") - out=np.concatenate((wq[:],shift[:,ism], - hwhm[:,ism])) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq[:],shift[:,ism], - hwhm[:,ism])) - f.write(fmt.format(x_length[iq],*out)) - # - name="{:6.1f}".format(smear_cm[ism]).strip() - # - # shifted freq sorted, corresponding hwhm - # - filename_new=filename_freq_dyn+'_'+name+'.dat' - fmt="{:>10.6f}\t"+"\t{:>11.7f}"*(2*n_mod)+"\n" - if iq == 0: - with open(filename_new,'w') as f: - f.write("# ----------------------------------------------------------------- \n") - f.write("# len (2pi/Angstrom), sscha+shift freq (sorted) (cm-1), hwhm (cm-1) \n") - f.write("# ----------------------------------------------------------------- \n") - out=np.concatenate((wq_shifted_sorted[:,ism], - hwhm_sorted[:,ism])) - f.write(fmt.format(x_length[iq],*out)) - else: - with open(filename_new,'a') as f: - out=np.concatenate((wq_shifted_sorted[:,ism], - hwhm_sorted[:,ism])) - f.write(fmt.format(x_length[iq],*out)) - - - print(" ") - print(" Results printed in "+filename_shift_lw+'_'+'[smear].dat') - print(" ") - print(" ") - print(" Results printed in "+filename_freq_dyn+'_'+'[smear].dat') - print(" ") - - diff --git a/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py deleted file mode 100644 index ac4313c5..00000000 --- a/cellconstructor/.ipynb_checkpoints/Structure-checkpoint.py +++ /dev/null @@ -1,2319 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jun 6 10:44:27 2018 - -@author: pione -""" -from __future__ import print_function -from __future__ import absolute_import - -import numpy as np -try: - __ASE__ = True - import ase - import ase.io -except: - __ASE__ = False - -try: - import phonopy, phonopy.structure.atoms - __PHONOPY__ = True -except: - __PHONOPY__ = False - - -import sys, os - -import cellconstructor.Methods as Methods -import cellconstructor.symmetries as SYM -from cellconstructor.Units import * -import cellconstructor.Timer as Timer - -#import ase.visualize - -import symph -import warnings - - - - -__all__ = ["Structure"] -BOHR_TO_ANGSTROM=0.529177249 - -class Structure(object): - def __init__(self, nat=0): - self.N_atoms=nat - # Coordinates are always express in chartesian axis - self.coords = np.zeros((self.N_atoms, 3), dtype = np.float64) - self.atoms = ["H"] * nat - self.unit_cell = np.zeros((3,3)) # Note: setting the unit cell to something different from zero automatically put has_unit_cell to true - self.has_unit_cell = False - self.masses = {} - self.ita = 0 # Symmetry group in ITA standard - - # Setup the attribute control - self.__total_attributes__ = [item for item in self.__dict__.keys()] - self.fixed_attributes = True # This must be the last attribute to be setted - - - def __setattr__(self, name, value): - """ - This method is used to set an attribute. - It will raise an exception if the attribute does not exists (with a suggestion of similar entries) - """ - - - if "fixed_attributes" in self.__dict__: - if name in self.__total_attributes__: - super(Structure, self).__setattr__(name, value) - elif self.fixed_attributes: - similar_objects = str( difflib.get_close_matches(name, self.__total_attributes__)) - ERROR_MSG = """ - Error, the attribute '{}' is not a member of '{}'. - Suggested similar attributes: {} ? - """.format(name, type(self).__name__, similar_objects) - - raise AttributeError(ERROR_MSG) - else: - super(Structure, self).__setattr__(name, value) - - - # Here check the consistency of the input - if name == "unit_cell": - if np.abs(np.linalg.det(value)) > 1e-8: - self.has_unit_cell = True - - - def get_volume(self): - """ - Returns the volume of the unit cell - """ - ERR_MSG = """ -Error, to compute the volume the structure must have a unit cell initialized: -(i.e. the has_unit_cell attribute must be True).""" - - assert self.has_unit_cell, ERR_MSG - - return np.abs(np.linalg.det(self.unit_cell)) - - def generate_from_ase_atoms(self, atoms, get_masses = True): - """ - This subroutines generate the current structure - from the ASE Atoms object - - Parameters - ---------- - atoms : the ASE Atoms object - get_masses : bool - If true, also build the masses. - Note that massess are saved in Ry units (electron mass) - - """ - - self.unit_cell = atoms.get_cell() - self.has_unit_cell = True - self.atoms = atoms.get_chemical_symbols() - self.N_atoms = len(self.atoms) - self.coords = atoms.positions.copy() - - if get_masses: - self.masses = {} - mass = atoms.get_masses() - for i, ma in enumerate(mass): - if not self.atoms[i] in self.masses: - self.masses[self.atoms[i]] = ma / MASS_RY_TO_UMA - - - def build_masses(self): - """ - Use the ASE database to build the masses. - The masses will be in [Ry] units (the electron mass) - """ - - ase_struct = self.get_ase_atoms() - - - self.masses = {} - mass = ase_struct.get_masses() - for i, ma in enumerate(mass): - if not self.atoms[i] in self.masses: - self.masses[self.atoms[i]] = ma / MASS_RY_TO_UMA - - - def copy(self): - """ - This method simply returns a copy of the current structure - - Results - ------- - - aux : Structure - A copy of the self structure. - """ - - aux = Structure() - aux.N_atoms = self.N_atoms - aux.coords = self.coords.copy() - aux.atoms = [atm for atm in self.atoms] - aux.unit_cell = self.unit_cell.copy() - aux.has_unit_cell = self.has_unit_cell - - # Deep copy of the masses - aux.masses = {} - for k in self.masses.keys(): - aux.masses[k] = self.masses[k] - - aux.ita = self.ita - return aux - - def set_masses(self, masses): - """ - This method set up the masses of the system. It requires a dictionary containing the - symobl and the value of the masses in a.u. (mass of the electron) - - Parameters - ---------- - - masses : dictionary - A dictionary containing the label and the corresponding mass in a.u. - Ex. masses = {'H' : 918.68, 'O' : 14582.56} - """ - - self.masses = masses - - def get_masses_array(self): - """ - Convert the masses of the current structure - in a numpy array of size N_atoms - - NOTE: This method will rise an exception if the masses are not initialized well - - Results - ------- - masses : ndarray (size self.N_atoms) - The array containing the mass for each atom of the system. - """ - - masses = np.zeros( self.N_atoms) - for i in range(self.N_atoms): - masses[i] = self.masses[ self.atoms[i] ] - - return masses - - def get_ityp_from_species(self, species): - """ - Get the integer of the atomic type from the species (string) - """ - - ityp = self.get_ityp() - for i in range(self.N_atoms): - if self.atoms[i] == species: - return ityp[i] - - raise ValueError("Error, species {} not present in this structure.".format(species)) - - - def get_atomic_types(self): - """ - Get an array of integer, starting from 1, for each atom of the structure, - so that two equal atoms share the same index. - - This is how different types are stored in Quantum ESPRESSO and it - is usefull for the wrapping Fortran => Python. - - Result - ------ - ityp : ndarray dtype=(numpy.intc) - The type array - """ - - ityp = [] - dictionary = {} - count = 1 - for i, atm in enumerate(self.atoms): - #ityp.append(cont) - if not atm in dictionary: - dictionary[atm] = count - count += 1 - - ityp = [dictionary[x] for x in self.atoms] - - # For fortran and C compatibility parse the array - return np.array(ityp, dtype = np.intc) - - def read_xyz(self, filename, alat = False, epsilon = 1e-8, frame_id = 0): - """ - This function reads the atomic position from a xyz file format. - if the passed file contains an animation, only the frame specified by frame_id - will be processed. - - Parameters - ---------- - - filename : string - The path of the xyz file, read access is required - - alat : bool, optional - If true the coordinates will be rescaled with the loaded unit cell, otherwise - cartesian coordinates are supposed (default False). - - frame_id : int - The id of the frame to be processed in an animation. - - epsilon : double precision, optional - Each value below this is considered to be zero (defalut 1e-8) - """ - # Check if the input is consistent - if (alat and not self.has_unit_cell): - sys.stderr.write("ERROR, alat setted to true, but no unit cell initialized\n") - raise ValueError("Function read_xyz, alat = True but no unit cell.") - - ## Check if the unit cell can be used with alat (only for orthorombic diagonal unit cell) - # if (alat): - # if (sum(self.unit_cell ** 2) - sum(diag(self.unit_cell)**2) < epsilon): - # sys.stderr.write("ERROR, alat compatible only with diagonal unit cell.\n") - # raise ErrorInUnitCell("Function read_xyz, alat parameters not combatible with cell.") - - - # Open the file - xyz = open(filename, "r") - - # Jump to the correct frame id - for k in range(frame_id): - njump = int(xyz.readline()) + 1 - for jl in range(njump): - xyz.readline() - - - self.N_atoms = int(xyz.readline()) - self.coords = np.zeros((self.N_atoms, 3)) - - # Read the comment line - xyz.readline() - - for i in range(self.N_atoms): - line = xyz.readline() - atom, x, y, z = line.split() - - self.atoms.append(atom) - self.coords[i,0] = np.float64(x) - self.coords[i,1] = np.float64(y) - self.coords[i,2] = np.float64(z) - - # Rescale the coordinates with the unit cell if requested - if alat: - # Not shure if the dot product must be done with the transposed unit cell matrix - self.coords[i, :] = np.dot( np.transpose(self.unit_cell), self.coords[i, :]) - - # Close the xyz file - xyz.close() - - def read_scf(self, filename, alat=1, read_string = False, read_espresso = False): - """ - Read the given filename in the quantum espresso format. - Note: - The file must contain only the part reguarding ATOMIC POSITIONS. - - Parameters - ---------- - - filename : str - The filename containing the atomic positions - - alat : double - If present the system will convert both the cell and the atoms position - by this factor. If it is also specified in the CELL_PARAMETERS line, - the one specified in the file will be used. - - read_string : bool - If true the filename is interpreted directly as a scf string, not as a file to be read. - - read_espresso : bool - If true, the SCF data are expected in between of a quantum espresso PW input. - """ - - # Check if the specified filename exists - if not read_string: - if not os.path.exists(filename): - raise ValueError("File %s does not exist" % filename) - - # Read the input filename - fp = open(filename, "r") - lines = fp.readlines() - fp.close() - else: - lines = filename.split("\n") - - n_atoms = 0 - #good_lines = [] - - # First read - read_cell = False - cell_index = 0 - read_atoms = True - if read_espresso: - read_atoms = False - cell_present = False - - read_crystal = False - - #print "ALAT:", alat - - #atom_index = 0 - cell = np.zeros((3,3), dtype = np.float64) - tmp_coords = [] - for line in lines: - line = line.strip() - - #Skipp comments - if len(line) == 0: - continue - - if line[0] == "!": - continue - - # Split the line into the values - values = line.split() - if values[0] == "CELL_PARAMETERS": - read_cell = True - read_atoms = False - self.has_unit_cell = True - cell_present = True - - # Check if the alat value is specified here - if "alat=" in line.lower().replace(" ", ""): - value_alat = np.float64(line[ line.find("=") + 1:].strip().replace(")","")) - alat = value_alat * BOHR_TO_ANGSTROM - - continue - if values[0] == "ATOMIC_POSITIONS": - self.atoms = [] - read_cell = False - read_atoms = True - if "crystal" in values[1].lower(): - read_crystal = True - - continue - - - if read_cell and cell_index < 3: - cell[cell_index, :] = [np.float64(v)*alat for v in values] - cell_index += 1 - elif cell_index == 3: - read_cell = False - - if read_atoms: - self.atoms.append(values[0]) - if not read_crystal: - tmp_coords.append([np.float64(v)*alat for v in values[1:4]]) - else: - # Read the crystal coordinate without taking care of alat - tmp_coords.append([np.float64(v) for v in values[1:4]]) - - n_atoms += 1 - - - - # Initialize the structure - self.coords = np.zeros((n_atoms, 3), dtype = np.float64) - self.N_atoms = n_atoms - - if cell_present: - self.has_unit_cell = True - self.unit_cell = cell - - for i, coord in enumerate(tmp_coords): - self.coords[i,:] = np.array(coord, dtype = np.float64) - - # Transform the coordinates if crystal - if read_crystal: - if not cell_present: - raise ValueError("Error, read crystal coordinates but no cell given in %s" % filename) - - self.coords[i,:] = np.einsum("ij, i", self.unit_cell, self.coords[i,:]) - - #print "COORDS:", self.coords - - - def read_generic_file(self, filename): - """ - This reader use ASE to parse the input and build the appropriate structure. - Any ASE accepted file is welcome. - This very simple reader uses the ase environment. - """ - - if not __ASE__: - print("ASE library not found.") - raise ImportError("Error, ASE library is required to read generic file.") - - atoms = ase.io.read(filename) - - # Now obtain all the information - self.generate_from_ase_atoms(atoms) - - - def set_unit_cell(self, filename, delete_copies = False, rescale_coords = False): - """ - Read the unit cell from the filename. - The rows of the filename are the unit cell vectors! - - Parameters - ---------- - - filename : string - The path of the file that contains the unit cell, in the numpy datafile format (text) - - delete_copies : bool, optional - If true the delete_copies subroutine is lounched after the creation of the unit cell - (default False) - - rescale_coords : bool, optional - If true ths system will be multiplied rows by column by the unit cell (default False) - """ - - # Load the unit cell - self.unit_cell = np.loadtxt(filename) - self.has_unit_cell = True - - if delete_copies: - self.delete_copies(verbose = False) - - if rescale_coords: - for i in range(self.N_atoms): - self.coords[i,:] = self.unit_cell.dot(self.coords[i,:]) - - def change_unit_cell(self, unit_cell): - """ - This method change the unit cell of the structure keeping fixed the crystal coordinates. - - NOTE: the unit_cell argument will be copied, so if the unit_cell variable is modified, this will not - affect the unit cell of this structure. - - Parameters - ---------- - unit_cell : numpy ndarray (3x3) - The new unit cell - """ - if not self.has_unit_cell: - raise ValueError("Error, the structure must already have a unit cell initialized.") - - # Get the crystal coordinates - crys_coord = np.zeros(np.shape(self.coords)) - for i in range(self.N_atoms): - crys_coord[i,:] = Methods.covariant_coordinates(self.unit_cell, self.coords[i,:]) - - # Setup the new unit cell - self.unit_cell = unit_cell.copy() - - # Modify the coordinates - for i in range(self.N_atoms): - self.coords[i,:] = np.einsum("ij, i", self.unit_cell, crys_coord[i,:]) - - - def export_unit_cell(self, filename): - """ - This method save the unit cell on the given file. - The rows will be the direct lattice vectors. - - Parameters - ---------- - - filename : string - The filename in which to save the unit cell - - """ - - np.savetxt(filename, self.unit_cell, header = "Rows are the unit cell vectors") - - def get_reciprocal_vectors(self): - """ - RECIPROCAL LATTICE - ================== - - Get the vectors of the reciprocal lattice. The self structure - must have the unit cell initialized (A NoUnitCell exception will be reised otherwise). - - Results - ------- - - reciprocal_vectors : float ndarray 3x3 - A matrix whose rows are the vectors of the reciprocal lattice - """ - - if not self.has_unit_cell: - raise ValueError("Error: the specified structure has not the unit cell.") - - return Methods.get_reciprocal_vectors(self.unit_cell) * 2 * np.pi - #return np.transpose(np.linalg.inv(self.unit_cell)) * 2 * np.pi - - def strain(self, strain_tensor, voigt = False, fix_volume = False): - r""" - APPLY A STRAIN TO THE STRUCTURE - =============================== - - This method applies a strain tensor to the structure. - Note, it will not affect the current structure, - but it returns a new strained strcture. - - Note: in the voigt representation, the off-diagonal terms of the strain tensor are intended as the sum - of the two symmetric components of the tensor. - - .. math :: - - \begin{pmatrix} \epsilon_1 \\ \epsilon_2 \\ \epsilon_3 \\ - 2\epsilon_4 \\ 2\epsilon_5 \\ 2\epsilon_6 \end{pmatrix} = - \begin{pmatrix} \epsilon_1 & \epsilon_6 & \epsilon_5 \\ - \epsilon_6 & \epsilon_2 & \epsilon_4 \\ - \epsilon_5 & \epsilon_4 & \epsilon_3 \end{pmatrix} - - - - Parameters - ---------- - strain_tensor: ndarray (size = (3,3), dtype = np.double) - The strain tensor. It must be a 3x3 tensor. - If you want to use the Voigt notation, set voigt to true - voigt : bool - If true, the strain_tensor is assumed in the voigt notation - (a 6 element array). - fix_volume : bool - If true, impose a hard volume constrain, that prevents the train to - change the total volume of the system. - - Results - ------- - strained_structure : CC.Structure.Structure() - The result of the strain. - """ - - if voigt: - strain_tensor[3:] /= 2 - strain_tensor = Methods.transform_voigt(strain_tensor, voigt_to_mat = True) - - - I = np.eye(3) - unit_cell = self.unit_cell.dot( I + strain_tensor.transpose()) - - if fix_volume: - # Fix the volume - unit_cell[:,:] *= (self.get_volume() / np.abs(np.linalg.det(unit_cell)))**(1/np.float64(3)) - - strained_struct = self.copy() - strained_struct.change_unit_cell(unit_cell) - - return strained_struct - - - - def delete_copies(self, minimum_dist=1e-6, verbose=False): - """ - This method checks if double atoms are present in the structure, - and delete them. - - Parameters - ---------- - - minimum_dist : double precision, optional - the minimum distance between two atoms of the same type allowed in the structure. - - verbose : bool (logical), optional - if True print on stdout how many atoms have been deleted (default False) - """ - - list_pop = [] - for i in range(self.N_atoms-1): - # Avoid to consider replica atoms already found - if (i in list_pop): - continue - - # If the atom is not a replica, then found if there are its replica missing - for j in range(i+1, self.N_atoms): - if (self.atoms[i] != self.atoms[j]): - continue - if j in list_pop: - continue - - # Get the axis - v1 = self.coords[i, :] - v2 = self.coords[j, :] - - d = np.sqrt(np.sum( (v1-v2)**2)) - if (self.has_unit_cell): - d = Methods.get_min_dist_into_cell(self.unit_cell, v1, v2) - - # # Apply the unit cell if necessary - # distances = [] - # if (self.has_unit_cell): - # # For each vector in the unit cell, add a distance - # shifts = [-1,0,1] - # for i_x, x_u in enumerate(shifts): - # new_x = x1 + x_u * self.unit_cell[i_x, :] - # for i_y, y_u in enumerate(shifts): - # new_y = y1 + y_u * self.unit_cell[i_y, :] - # for i_z, z_u in enumerate(shifts): - # new_z = z1 + z_u * self.unit_cell[i_z, :] - - # # Add the transformed distance - # distances.append( np.sqrt((x-new_x)**2 + (y - new_y)**2 + (z - new_z)**2)) - # else: - # # Get the first distance between atoms - # distances.append(np.sqrt( (x-x1)**2 + (y-y1)**2 + (z-z1)**2 )) - - - if (d < minimum_dist): - # Add the atom as a replica - list_pop.append(j) - - - # Print how many replica have been found - N_rep = len(list_pop) - if verbose: - print("Found %d replica" % N_rep) - - # Delete the replica - #list_pop = list(set(list_pop)) # Avoid duplicate indices - list_pop.sort(reverse=True) - #print list_pop, self.N_atoms - for index in list_pop: - #print index - del self.atoms[index] - - self.coords = np.delete(self.coords, list_pop, axis = 0) - self.N_atoms -= N_rep - - def apply_symmetry(self, sym_mat, delete_original = False, thr = 1e-6, timer = Timer.Timer()): - """ - This function apply the symmetry operation to the atoms - of the current structure. - - Parameters - ---------- - - sym_mat : (matrix 3x4) - The matrix of the symemtri operation, the final column is the translation - - - delete_original : bool, default False - If true only the atoms after the symmetry application are left (good to force symmetry) - - - thr : float, optional - The threshold for two atoms to be considered the same in the reduction process - (must be smaller than the minimum distance between two generic atoms in the struct, - but bigger than the numerical error in the wyckoff positions of the structure). - """ - - if not self.has_unit_cell: - raise ValueError("The structure has no unit cell!") - - if delete_original: - #self.N_atoms *= 2 - new_atoms = np.zeros( (self.N_atoms, 3)) - timer.execute_timed_function(self.fix_coords_in_unit_cell, delete_copies = False) - - old_coords = timer.execute_timed_function(Methods.covariant_coordinates, self.unit_cell, self.coords) - new_coords = sym_mat[:, :3].dot(old_coords.T).T - new_coords += np.tile( sym_mat[:, 3], (self.N_atoms, 1)) - - self.coords = new_coords.dot(self.unit_cell) - - timer.execute_timed_function(self.fix_coords_in_unit_cell, delete_copies = False) - else: - - - for i in range(self.N_atoms): - # Convert the coordinates into covariant - old_coords = Methods.covariant_coordinates(self.unit_cell, self.coords[i, :]) - - # Apply the symmetry - new_coords = sym_mat[:, :3].dot(old_coords) - new_coords += sym_mat[:, 3] - - # Return into the cartesian coordinates - coords = np.dot( np.transpose(self.unit_cell), new_coords) - - # Put the atoms into the unit cell - new_atoms[i, :] = Methods.put_into_cell(self.unit_cell, coords) - - # Add also the atom type - if not delete_original: - self.atoms.append(self.atoms[i]) - - self.N_atoms *= 2 - self.coords = np.concatenate( (self.coords, new_atoms), axis = 0) - self.delete_copies(verbose = False, minimum_dist = thr) - - def check_symmetry(self, sym_mat, thr = 1e-6): - """ - This method check if the provided matrix is actually a symmetry for the given system - - Parameters - ---------- - - sym_mat: a 3 rows by 4 columns matrix (float) - It contains the rotation matrix (the first 3x3 block) - and the traslation vector (the last column) of the symmetry - - thr : float, optional - The threshold for two atoms to be considered the same. - - Results - ------- - - check : bool - It is true if the given matrix is a real symmetry of the system. - """ - - # Copy the struct - new_struct = self.copy() - - # Apply the symmetry - new_struct.apply_symmetry(sym_mat, delete_original=True) - - # Get the equivalence - eq_atoms = new_struct.get_equivalent_atoms(self) - - # Exchange the atoms - new_struct.coords[eq_atoms, :] = new_struct.coords.copy() - - # Fix the structure in the unit cell - new_struct.fix_coords_in_unit_cell() - - # Get the displacements - u_vect = self.get_displacement(new_struct) - - # Get the distance between the structures - dist = np.sqrt(np.sum(u_vect ** 2)) - - if dist > thr: - return False - return True - - - def set_ita_group(self, group): - """ - This function setup the ita group of the cell, - the unit cell must be initialized and the ITA group must be - inside the supported one. - All the symmetries of the specified group are applied. - - Parameters - ---------- - - group : int - The ITA identifier of the symmetry group. - """ - - # Apply all the symmetries - sym_mats = SYM.get_symmetries_from_ita(group) - self.ita = group - - for mat in sym_mats: - self.apply_symmetry(mat) - - def load_symmetries(self, filename, progress_bar=False, verbose = False): - """ - This function loads the symmetries operation from a specific file - and applies them to the system. - The file must init with the total number of symmetries, and followed by - N 3 rows x 4 columns matrices that represent the symmetry application. - - Parameters - ---------- - filename : string - The path in which the symmetries are stored, a text file. - progress_bar : bool - If true a progress bar on stderr is shown, usefull if the system is very large and - this function can take a while. - """ - - - # Get the number of symmetries - symfile = open(filename) - N_sym = int(symfile.readline().strip()) - symfile.close() - - # Get the symmetries - symdata = np.loadtxt(filename, skiprows = 1) - - if (progress_bar): print() - - for i in range(N_sym): - sym_mat = symdata[3*i:3*(i+1), :] - - self.apply_symmetry(sym_mat) - - if (progress_bar): - if not verbose: - sys.stderr.write("\rProgress computing symmetries... %d of %d %%" % (i, N_sym) ) - else: - sys.stderr.write("\rSymmetry %d out of %d, %d atoms" % (i, N_sym, self.N_atoms ) ) - - sys.stderr.flush() - - if (progress_bar): print() - - def impose_symmetries(self, symmetries, threshold = 1.0e-6, verbose = True): - """ - This methods impose the list of symmetries found in the given filename. - It solves a self-consistente equation: Sx = x. If this equation is not satisfied at precision - of the initial_threshold the method will raise an exception. - - Parameters - ---------- - - symmetries : list - The simmetries to be imposed as a list of 3x4 ndarray matrices. The last column is the - fractional translations - - threshold : float - The threshold for the self consistent equation. The algorithm stops when Sx = x is satisfied - up to the given threshold value for all the symmetries. - - verbose : bool - If true the system will print on stdout info about the self-consistent threshold - - """ - - # An array storing which symmetry operation has reached the threshold - aux_struct = self.copy() - - - # Start the self consistent algorithm - running = True - index = 0 - while running: - old_coords = np.zeros( np.shape(self.coords)) - - for sym in symmetries: - aux_struct = self.copy() - aux_struct.apply_symmetry(sym, delete_original = True) - aux_struct.fix_coords_in_unit_cell() - - # Get the equivalent atoms - eq_atoms = self.get_equivalent_atoms(aux_struct) - - #ase.visualize.view(self.get_ase_atoms()) - #ase.visualize.view(aux_struct.get_ase_atoms()) - - # Order the atoms - aux_struct.atoms = [aux_struct.atoms[item] for item in eq_atoms] - aux_struct.coords = aux_struct.coords[eq_atoms,:] - - # Get the displacements - old_coords += self.get_displacement(aux_struct) - - # Average - old_coords /= len(symmetries) - - r = np.max(np.sqrt(np.sum((old_coords)**2, axis = 1))) -# -# if verbose: -# print np.sqrt(np.sum((old_coords - self.coords)**2, axis = 1)) -# print "Self:" -# print self.coords -# print "New:" -# print old_coords - - - self.coords -= old_coords - if r < threshold: - running = False - - index += 1 - if (verbose): - print("Self-consistent iteration %d -> r = %.3e | threshold = %.3e" % (index, r, threshold)) - - if (verbose): - print("Symmetrization reached in %d steps." % index) - - - - def get_equivalent_atoms(self, target_structure, return_distances = False, debug = False): - """ - GET EQUIVALENT ATOMS BETWEEN TWO STRUCTURES - =========================================== - - - This function returns a list of the atom index in the target structure that - correspond to the current structure. - NOTE: This method assumes that the two structures are equal. - - - Parameters - ---------- - target_structure : Structure() - This is the target structure to be used to get the equivalent atoms. - return_distances : bool - If True it returns also the list of the distances between the atoms - - Results - ------- - list - list of int. Each integer is the atomic index of the target_structure equivalent to the i-th element - of the self structure. - """ - - # Check if the structures are compatible - if self.N_atoms != target_structure.N_atoms: - raise ValueError("Error, the target structure must be of the same type of the current one") - - for typ in self.atoms: - if not typ in target_structure.atoms: - raise ValueError("Error, the target structure must be of the same type of the current one") - if self.atoms.count(typ) != target_structure.atoms.count(typ): - raise ValueError("Error, the target structure must be of the same type of the current one") - - eq_atm = list(symph.get_equivalent_atoms(self.coords, target_structure.coords, self.unit_cell, self.get_ityp(), target_structure.get_ityp())) - - if debug or return_distances: - equiv_atoms = [] - effective_distances = [] - for i in range(self.N_atoms): - i_typ = self.atoms[i] - - # Select the possible equivalent atoms in the target structure - target_indices = [x for x in range(self.N_atoms) if target_structure.atoms[x] == i_typ and not (x in equiv_atoms)] - - # For each possible equivalent atoms get the minimum distance - d = [] - for j in target_indices: - #v = Methods.get_closest_vector(self.unit_cell, self.coords[i,:] - target_structure.coords[j, :]) - #d.append(np.sqrt(np.sum(v**2))) - d.append(Methods.get_min_dist_into_cell(self.unit_cell, self.coords[i,:], target_structure.coords[j, :])) - - # Pick the minimum - j_min = target_indices[ np.argmin(d) ] - effective_distances.append(np.min(d)) - - # Set the equivalent atom index - equiv_atoms.append(j_min) - - #print "Max distance:", np.max(effective_distances) - - assert all(eq_atm == equiv_atoms) - - if return_distances: - return equiv_atoms, effective_distances - - return eq_atm - - - def sort_molecules(self, distance = 1.3): - """ - This method sorts the atom lists to have the atoms in the same molecule written subsequentially. - - Parameters - ---------- - - distance : double precision, optional - The distance below wich two atoms are considered to be bounded. The unit is in Argstrom. - """ - - molecules = [] - pop_indices = [] - for i in range(self.N_atoms): - if i in pop_indices: continue - - molecule = [i] - - # Get the closest molecules - for j in range(i+1, self.N_atoms): - if j in pop_indices: continue - - if np.sqrt(np.sum( (self.coords[i,:] - self.coords[j,:])**2 )) < distance: - molecule.append(j) - pop_indices.append(j) - - molecules.append(molecule) - - # Resort the atoms - coords = np.zeros( (self.N_atoms, 3)) - atoms = ["X"] * self.N_atoms - - cont = 0 - for mol in molecules: - for index in mol: - atoms[cont] = self.atoms[index] - coords[cont, :] = self.coords[index,:] - cont += 1 - self.atoms = atoms - self.coords = coords - - def save_xyz(self, filename, comment="Generated with BUC", overwrite = True): - """ - This function write the structure on the given filename in the xyz file format - - Parameters - ---------- - filename : string - The path of the file in which to save the structure. The user must have write access - comment : string, optional - This line is written in the comment line of the xyz file. - NOTE: this string is followed by the unit cell info is present - overwrite : bool, optional - If true any precedent file will be erased, otherwise the structure is appended - on the bottom of the previous one. In this way it is possible to save videos. - - """ - - if overwrite: - xyz = open(filename, "w") - else: - xyz = open(filename, "a") - - # Write the number of atoms - xyz.write("%d\n" % self.N_atoms) - - # Write the comment line - unit_cell_string = "" - if self.has_unit_cell: - unit_cell_string = " cell: " - for i in range(3): - unit_cell_string += chr( ord('A') + i) + " ".join([str(x_val) for x_val in self.unit_cell[i,:]]) + " " - xyz.write("%s\n" % (comment + unit_cell_string)) - - # Write the strcture - lines = [] - for i in range(self.N_atoms): - label = self.atoms[i] - x, y, z = self.coords[i, :] - - line = " ".join([label, str(x), str(y), str(z)]) + "\n" - lines.append(line) - xyz.writelines(lines) - xyz.close() - - def save_bcs(self, filename, symmetry_file = ""): # STILL NOT WORKING - """ - Save the current structure in the Bilbao Crystallographic Server file format - This is very usefull since the BCS website provide a conversor between - BCS with most of widely used crystallographic file format. - - NOTE: - remember to specify the correct ITA group symmetry in the structure. - You can find more about ITA on BCS website. Otherwise you must specify a file with symmetries - - Parameters - ---------- - - filename : str - The path of the bcs file in which you want to save the structure - - symmetry_file : str, optional - The path to a file containing the symmetries operations of the group space - This is not needed if a ITA grup has been specified. - """ - - fp = open(filename, "w") - fp.write("# Space Group ITA number\n%d\n# Lattice parameters\n" % self.ita) - - # Convert the cell into the a,b,c,alpha,beta,gamma format - cellbcs = Methods.cell2abc_alphabetagamma(self.unit_cell) - fp.write("%.8f %.8f %.8f %3d %3d %3d\n" % (cellbcs[0], cellbcs[1], cellbcs[2], - cellbcs[3], cellbcs[4], cellbcs[5])) - - # Get the independent atoms - if symmetry_file != "": - syms = [] - #TODO !!!!!! - syms = SYM.get_symmetries_from_ita(self.ita, True) - - removing_struct = self.copy() - - running = True - while running: - # Try to remove an atoms - total_removed = 0 - for i in range(removing_struct.N_atoms): - tmp_struct = removing_struct.copy() - - # Delete the atom - tmp_struct.N_atoms -= 1 - tmp_struct.atoms.pop(i) - np.delete(tmp_struct.coords, i, axis = 0) - - # Apply all the symmetries - for ind, sym in enumerate(syms): - tmp_struct.apply_symmetry(sym) - print("atom %d, sym %d - NEW %d / %d" % (i, ind, tmp_struct.N_atoms, removing_struct.N_atoms)) - - if tmp_struct.N_atoms == removing_struct.N_atoms: - total_removed += 1 - removing_struct = tmp_struct.copy() - - # If no atoms can be removed, then we obtained the minimal structure - if not total_removed: - running = False - - - # Write the atoms - fp.write("# Number of independent atoms\n%d\n" % removing_struct.N_atoms) - fp.write("# [atom type] [number] [WP] [x] [y] [z]\n") - - for i in range(removing_struct.N_atoms): - cvect = Methods.covariant_coordinates(self.unit_cell, removing_struct.coords[i,:]) - vect_str = " ".join(["%.8f" % item for item in cvect]) - fp.write("%2s %3d - %s\n" % (removing_struct.atoms[i], i+1, vect_str)) - - fp.close() - - def get_xcoords(self): - """ - Returns the crystalline coordinates - """ - - assert self.has_unit_cell - - xcoords = np.zeros(self.coords) - for i in range(self.N_atoms): - xcoords[i,:] = Methods.covariant_coordinates(self.unit_cell, self.coords[i,:]) - - return xcoords - def set_from_xcoords(self, xcoords): - """ - Set the cartesian coordinates from crystalline - """ - - assert self.has_unit_cell - - for i in range(self.N_atoms): - self.coords[i,:] = self.unit_cell.T.dot(xcoords[i,:]) - - - def save_scf(self, filename, alat = 1, avoid_header=False, crystal = False, get_text = False): - """ - This methods export the phase in the quantum espresso readable format. - Of course, only the data reguarding the unit cell and the atomic position will be written. - The rest of the file must be edited by the user to start a calculation. - - Parameters - ---------- - filename : string - The name of the file that you want to save. If None, no file is generated (in that case, use get_text to get the string of the scf file) - alat : float, optional - If different from 1, both the cell and the coordinates are saved in alat units. - It must be in Angstrom. - avoid_header : bool, optional - If true nor the cell neither the ATOMIC_POSITION header is printed. - Usefull for the sscha.x code. - crystal : bool, optional - If true, the atomic coordinates are saved in crystal components - get_text : bool - If true, the scf file is returned as pure text. - """ - - if alat <= 0: - raise ValueError("Error, alat must be positive [Angstrom]") - - data = [] - if self.has_unit_cell and not avoid_header: - unit_cell = np.copy(self.unit_cell) - if alat == 1: - data.append("CELL_PARAMETERS angstrom\n") - else: - data.append("CELL_PARAMETERS alat\n") - - unit_cell /= alat - - for i in range(3): - data.append("%.16f %.16f %.16f\n" % (unit_cell[i, 0], - unit_cell[i, 1], - unit_cell[i, 2])) - data.append("\n") - - - if not avoid_header: - if crystal: - unit_type = "crystal" - else: - if alat == 1: - unit_type = "angstrom" - else: - data.append("ATOMIC_POSITIONS alat\n") - - data.append("ATOMIC_POSITIONS {}\n".format(unit_type)) - for i in range(self.N_atoms): - if not crystal: - coords = np.copy(self.coords) - coords /= alat - else: - coords = Methods.covariant_coordinates(self.unit_cell, self.coords) - - data.append("%s %.16f %.16f %.16f\n" % (self.atoms[i], - coords[i, 0], - coords[i, 1], - coords[i, 2])) - - # Write - if filename is not None: - fdata = open(filename, "w") - fdata.writelines(data) - fdata.close() - - if get_text: - return "".join(data) - - - def fix_coords_in_unit_cell(self, delete_copies = True, debug = False): - """ - This method fix the coordinates of the structure inside - the unit cell. It works only if the structure has - predefined unit cell. - """ - - if not self.has_unit_cell: - raise ValueError("Error, try to fix the coordinates without the unit cell") - - if not debug: - self.coords = symph.fix_coords_in_unit_cell(self.coords, self.unit_cell) - - if debug: - c1 = symph.fix_coords_in_unit_cell(self.coords, self.unit_cell) - coords = self.coords.copy() - for i in range(self.N_atoms): - coords[i,:] = Methods.put_into_cell(self.unit_cell, self.coords[i,:]) - - check = np.max(np.abs(coords - c1)) < 1e-7 - - - if not check: - print("Error in the check coordinates") - print(self.unit_cell) - - for i in range(self.N_atoms): - print("Atom {}:".format(i)) - print("original: {}".format(self.coords[i, :])) - print("new method: {}".format(c1[i,:])) - print("old method: {}".format(coords[i,:])) - print() - - self.coords = c1 - #ase.visualize.view(self.get_ase_atoms()) - - raise ValueError("Error, the two methods to fix the coordinates in the unit cell give different results.") - - - # Delete duplicate atoms - if delete_copies: - self.delete_copies() - - def fix_wigner_seitz(self): - """ - Atoms will be replaced in the periodic images inside the wigner_seitz cell - """ - - assert self.has_unit_cell, "Error, the wigner_seitz is defined for periodic boundary conditions" - - for i in range(self.N_atoms): - new_r = Methods.get_closest_vector(self.unit_cell, self.coords[i,:]) - self.coords[i, :] = new_r - - def get_strct_conventional_cell(self): - """ - This methods, starting from the primitive cell, returns the same structure - in the conventional cell. It picks the angle that mostly differs from 90 deg, - and transfrom the axis of the cell accordingly to obtain a bigger cell, but similar - to an orthorombic one. - The atoms are then replicated and correctly placed inside the new cell. - - If the structure does not have a unit cell, the method will raise an error. - - NOTE: The new structure will be returned, but this will not be modified - - Returns - ------- - Structure.Structure() - The structure with the conventional cell - """ - - - if not self.has_unit_cell: - raise ValueError("Error, the given structure does not have a valid unit cell.") - - # Compute the three angles - angls = np.zeros(3) - for i in range(3): - nexti = (i+1)%3 - otheri = (i+2)%3 - angls[otheri] = np.arccos( np.dot(self.unit_cell[i,:], self.unit_cell[nexti,:]) / - (np.sqrt(np.dot(self.unit_cell[i,:], self.unit_cell[i,:])) * - np.sqrt(np.dot(self.unit_cell[nexti,:], self.unit_cell[nexti,:])))) * 180 / np.pi - - # Pick the angle that differ the most from 90 - otheri = np.argmax( np.abs( angls - 90)) - #print angls, otheri - - # Now select the two vectors between this angle - vec1 = self.unit_cell[(otheri + 1) % 3,:].copy() - vec2 = self.unit_cell[(otheri + 2) % 3,:].copy() - - # Get the new system - vec1_prime = vec1 + vec2 - vec2_prime = vec1 - vec2 - - # Get the new structure - s_new = self.generate_supercell( (2,2,2) ) - s_new.unit_cell = self.unit_cell.copy() - s_new.unit_cell[(otheri+1)%3,:] = vec1_prime - s_new.unit_cell[(otheri+2)%3,:] = vec2_prime - - s_new.fix_coords_in_unit_cell() - - return s_new - - def get_ase_atoms(self): - """ - This method returns the ase atoms structure, ready for computations. - - Results - ------- - - atoms : ase.Atoms() - The ase.Atoms class containing the self structure. - """ - - if not __ASE__: - print ("ASE library not found") - raise ImportError("Error, ASE library not found") - - # Get thee atom list - atm_list = [] - for i in range(self.N_atoms): - atm_list.append(ase.Atom(self.atoms[i], self.coords[i,:])) - - atm = ase.Atoms(atm_list) - - if self.has_unit_cell: - atm.set_cell(self.unit_cell) - atm.pbc[:] = True - - - return atm - - def get_phonopy_calculation(self, supercell = [1,1,1]): - """ - Convert the CellConstructor structure to a phonopy object - for the calculation of phonons using finite differences. - - Note: while phonopy allows for nonconventional supercells, - this method is only interfaced to create supercell calculations which are finite multiple - of the unit cell defined by the self structure. - - Parameters - ---------- - supercell : list of 3 int - The supercell (how many times the unit cell vector). - """ - - if not __PHONOPY__: - raise ValueError("Error, to run 'get_phonopy_calculation' you need to have Phonopy installed.") - - if not self.has_unit_cell: - raise ValueError("Error, to run 'get_phonopy_calculation' the system has to have a defined unit cell") - - if self.get_volume() < 1e-8: - raise ValueError("Error, this structure has singular unit cell; I cannot initialize a phonopy object.") - - if len(supercell) != 3: - raise ValueError("Error, the given supercell must be a 3 element vector ({} elements given)".format(self.supercell)) - - - scaled_position = Methods.covariant_coordinates(self.unit_cell, self.coords) - unitcell = phonopy.structure.atoms.PhonopyAtoms(symbols = self.atoms, cell = self.unit_cell, scaled_positions = scaled_position) - - return phonopy.Phonopy(unitcell, np.eye(3).dot(np.array(supercell))) - - - def get_ityp(self): - """ - GET THE TYPE ATOMS - ================== - - This is for fortran compatibility. - Get the ityp array for the structure. - Pass it + 1 to the fortran subroutine to match also the difference - between python and fortran indices - - Results - ------- - ityp : ndarray of int - The type of the atom in integer (starting from 0) - """ - - if not self.masses: - atm_species = list(set(self.atoms)) - else: - atm_species = list(self.masses) - - ityp = np.zeros(self.N_atoms, dtype = np.intc) - - for i in range(self.N_atoms): - # Rank the atom number - - ityp[i] = atm_species.index(self.atoms[i]) - - return ityp - - def get_itau(self, unit_cell_structure): - """ - GET ITAU - ======== - - This subroutine (called by a supercell structure), returns the array - of the corrispondence between its atoms and those in the unit cell.s - - NOTE: The ITAU is returned in Fortran indexing, subtract by 1 if you want to use it in python - - Parameters - ---------- - - unit_cell_structure : Structure() - The structure of the unit cell used to generate this supercell structure. - - Results - ------- - - itau : ndarray (size = nat_sc, type = int) - For each atom in the supercell contains the index of the corrisponding - atom in the unit_cell, starting from 1 to unit_cell_structure.N_atoms (included) - """ - - itau = np.zeros( self.N_atoms, dtype = np.intc) - - for i in range(self.N_atoms): - - v1 = Methods.put_into_cell(unit_cell_structure.unit_cell, self.coords[i,:]) - d = np.zeros(unit_cell_structure.N_atoms) - for j in range(unit_cell_structure.N_atoms): - d[j] = Methods.get_min_dist_into_cell(unit_cell_structure.unit_cell, v1, unit_cell_structure.coords[j,:]) - - itau[i] = np.argmin(d) + 1 - - return itau - - - def get_sublattice_vectors(self, unit_cell_structure): - """ - Get the lattice vectors that connects the atom of this supercell structure to those of - the unit_cell structure. - """ - - itau = self.get_itau(unit_cell_structure) - 1 - return self.coords[:,:] - unit_cell_structure.coords[itau[:], :] - - def generate_supercell(self, dim, itau = None, QE_convention = True, get_itau = False): - """ - This method generate a supercell of specified dimension, replicating the system - on the n-th neighbours unit cells. - - Parameters - ---------- - - dim : list, size(3), integer - A list that specifies the number of cells for each dimension. - - itau : ndarray of int, size(Natoms * supercell_size) - An array of integer. If it is of the correct shape and type it will be filled - with the correspondance of each new vector to the corresponding one in the unit cell - - QE_convention : bool, optional - If true (default) the quantum espresso set_tau subroutine is used to determine - the order of how the atoms in the supercell are generated - - get_itau : bool - If true also the itau order is returned in output (python convention). - - Results - ------- - - supercell : Structure - This structure is the supercell of the system. - - itau : ndarray - For each atom in the supercell, the index of the corresponding atom - in the primitive cell. Only if get_itau = True - """ - - - if len(dim) != 3: - raise ValueError("ERROR, dim must have 3 integers.") - - if not self.has_unit_cell: - raise ValueError("ERROR, the specified system has not the unit cell.") - - total_dim = np.prod(dim) - - new_N_atoms = self.N_atoms * total_dim - new_coords = np.zeros( (new_N_atoms, 3)) - atoms = [None] * new_N_atoms # Create an empty list for the atom's label - - - # Get the new data - - - # Check if itau is passed - if itau is not None: - try: - itau[:] = np.zeros(new_N_atoms, dtype = np.intc) - except: - raise ValueError("Error, itau passed to generate_supercell does not match the required shape\nRequired %d, passed %d"% (new_N_atoms, len(itau))) - - # Start the generation of the new supercell - if not QE_convention: - for i_z in range(dim[2]): - for i_y in range(dim[1]): - for i_x in range(dim[0]): - basis_index = self.N_atoms * (i_x + dim[0] * i_y + dim[0]*dim[1] * i_z) - for i_atm in range(self.N_atoms): - new_coords[basis_index + i_atm, :] = self.coords[i_atm, :] + \ - i_z * self.unit_cell[2, :] + \ - i_y * self.unit_cell[1, :] + \ - i_x * self.unit_cell[0, :] - atoms[i_atm + basis_index] = self.atoms[i_atm] - if itau is not None: - itau[i_atm + basis_index] = i_atm - - # Define the new structure - supercell = Structure() - supercell.coords = new_coords - supercell.N_atoms = new_N_atoms - supercell.atoms = atoms - supercell.masses = self.masses.copy() - - # Define the supercell - supercell.has_unit_cell = True - - for i in range(3): - supercell.unit_cell[i, :] = self.unit_cell[i,:] * dim[i] - - - if QE_convention: - # Prepare the variables - tau = np.array(self.coords.transpose(), dtype = np.float64, order = "F") - tau_sc = np.zeros((3, new_N_atoms), dtype = np.float64, order = "F") - ityp_sc = np.zeros( new_N_atoms, dtype = np.intc) - ityp = self.get_atomic_types() - - at_sc = np.array( supercell.unit_cell.transpose(), dtype = np.float64, order = "F") - at = np.array( self.unit_cell.transpose(), dtype = np.float64, order = "F") - - itau = np.zeros(new_N_atoms, dtype = np.intc) -# -# print "AT SC:", at_sc -# print "AT:", at -# print "TAU SC:", tau_sc -# print "TAU:", tau -# - # Fill the atom - symph.set_tau(at_sc, at, tau_sc, tau, ityp_sc, ityp, itau, new_N_atoms, self.N_atoms) - - - supercell.coords[:,:] = tau_sc.transpose() - itau -= 1 # Fortran To Python indexing - supercell.atoms = [self.atoms[x] for x in itau] - - - - if get_itau: - return supercell, itau - return supercell - - def reorder_atoms_supercell(self, reference_structure): - """ - ORDER THE ATOMS - =============== - - This subroutines order the atoms to match the same order as in the - generate_supercell method. - The self structure is supposed to be a structure that belongs to a supercell - of the given unit_cell, then it is reordered so that each atom in any different - supercell are consequent and the order of the supercell matches the one - created by generate supercell. The code will work even if the structures - do not match exactly the supercell generation. In this case, the closest - unit cell atom of the correct type is used as reference. - - TODO: THIS DOES NOT WORK!!!! - - Parameters - ---------- - - reference_structure : Structure() - The cell and coordinates that must be used as a reference - to reorder the atoms - - Results - ------- - - itau : ndarray of int - The shuffling array to order any array of this list - - """ - #raise ValueError("Subroutine still not working...") - - if not reference_structure.has_unit_cell: - raise ValueError("Error, the reference structure must have a unit cell") - - if not self.has_unit_cell: - raise ValueError("Error, the self structure must have a unit cell") - - unit_cell = reference_structure.unit_cell - reference_coords = reference_structure.coords - - # Get the supercell size - sx = self.unit_cell[0,:].dot(unit_cell[0,:]) / unit_cell[0,:].dot(unit_cell[0,:]) - sy = self.unit_cell[1,:].dot(unit_cell[1,:]) / unit_cell[1,:].dot(unit_cell[1,:]) - sz = self.unit_cell[2,:].dot(unit_cell[2,:]) / unit_cell[2,:].dot(unit_cell[2,:]) - - supercell_size = (int(sx + .5), int(sy + .5), int(sz + .5)) - - print ("SUPERCELL:", supercell_size) - - # Atoms in the unit cell - nat_uc = np.shape(reference_coords)[0] - - # Check if they match the given structure - if self.N_atoms % nat_uc != 0: - raise ValueError("Error, the number of atoms in this structure %d is not a multiple of %d (the reference structure)" % (self.N_atoms, nat_uc)) - - # The shuffling array - itau = np.arange(self.N_atoms) - - # Get cristal coordinates - for i in range(self.N_atoms): - cov = Methods.covariant_coordinates(unit_cell, self.coords[i,:]) - - # Identify the cell - i_x = int(cov[0] + .5) - i_y = int(cov[1] + .5) - i_z = int(cov[2] + .5) - - print (cov[0], cov[1], cov[2], i_x, i_y, i_z) - - # Get the index of the cell - basis_index = nat_uc * (i_x + supercell_size[0] * i_y + supercell_size[0] * supercell_size[1] * i_z) - - # Identify the atom - d = np.zeros(nat_uc, dtype = np.float32) - mask_good = np.zeros(nat_uc, dtype = bool) - for j in range(nat_uc): - if self.atoms[i] == reference_structure.atoms[j]: - mask_good[j] = True - d[j] = Methods.get_min_dist_into_cell(unit_cell, self.coords[i,:], reference_coords[j]) - - # Avoid to pick a wrong atom type - d[~mask_good] = np.max(d) + 1 - - # Avoid that two atoms point to the same position - process = True - while process: - - # Get the atom corresponding to the minimum distance - atm_index = np.argmin(d) - index = basis_index + atm_index - - #print "Chosen %d -> %d" % (i, index), "ITAU:", itau[:i] - - # Check if another atom already matched this one - if index in itau[:i]: - d[atm_index] = np.max(d) + 1 - else: - process = False - - - itau[i] = index - - # Now shuffle the current structure - self.coords = self.coords[itau, :] - - # Now shuffle the atom types - new_atoms = [] - for i in range(self.N_atoms): - new_atoms.append(self.atoms[itau[i]]) - self.atoms = new_atoms - - # Return the shuffling array - return itau - - - - def get_min_dist(self, index_1, index_2): - """ - This method returns the minimum distance between atom index 1 and atom index 2. - It uses the unit cell to correctly take into account the atoms at the edge of the unit cell. - - Parameters - ---------- - - index_1 : int - The index of the first atom in the structure - - index_2 : int - The index of the second atom in the structure - - Results - ------- - - min_dist : float - The minimum distance between the chosen atoms, eventually traslated by the unit cell. - """ - - vector1 = self.coords[index_1, :] - vector2 = self.coords[index_2, :] - - if not self.has_unit_cell: - return np.sqrt( np.sum( (vector1 - vector2)**2)) - - # Get the covariant components - cell = self.unit_cell - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = cell[i,:].dot(cell[j, :]) - - imt = np.linalg.inv(metric_tensor) - - # Get contravariant components - contra_vect = np.zeros(3) - for i in range(3): - contra_vect[i] = vector1.dot(cell[i, :]) - - # Invert the metric tensor and obtain the covariant coordinates - covect1 = imt.dot(contra_vect) - - contra_vect = np.zeros(3) - for i in range(3): - contra_vect[i] = vector2.dot(cell[i, :]) - - # Invert the metric tensor and obtain the covariant coordinates - covect2 = imt.dot(contra_vect) - - covect_distance = covect1 - covect2 - - # Bring the distance as close as possible to zero - covect_distance -= (covect_distance + np.sign(covect_distance)*.5).astype(int) - - # Compute the distance using the metric tensor - return np.sqrt(covect_distance.dot(metric_tensor.dot(covect_distance))) - - - def get_brillouin_zone(self, ISO_MESH=10): # NOT WORKING ----- - """ - BRILLOUIN ZONE - ============== - - This function uses ase utilities to plot the Brillouin zone. - TODO: Z primitive cell must be perpendicular to the others (only few reticulus) - - NOT WORKING!!!! - - Parameters - ---------- - - ISO_MESH : int (default 100) - The number of points for the volume mesh (to the 3 power) - - Results - ------- - - BZone : array of 3D vectors - The points of the ISO_MESH inside the first brillouin zone - """ - - # Get the reciprocal lattice vectors - b_vectors = self.get_reciprocal_vectors() - - b_mod = np.sum( b_vectors**2, axis = 1) - - metric_tensor = np.zeros((3,3)) - for i in range(0, 3): - for j in range(i, 3): - metric_tensor[i, j] = metric_tensor[j,i] = b_vectors[i,:].dot(b_vectors[j, :]) - invmt = np.linalg.inv(metric_tensor) - - # Uniformly fill the Reciprocal Unit Cell - spacing = np.linspace(-.5, .5, ISO_MESH) - - vectors = [] - - # Create all the surface in the contravariant coordinates - for x in spacing: - - mask1 = (spacing - x <= 0.5) & (spacing -x >= -.5) - mask2 = (spacing + x <= .5) & (spacing + x >= -.5) - - for y in spacing[mask1 & mask2]: - # if abs(y) != 0.5 and abs(x) != 0.5 and abs(y+x) != 0.5 and abs(y-x) != 0.5: - # continue - - for z in [-0.5, 0.5]: - contravect = np.array([b_mod[0] * x, b_mod[1] * y, b_mod[2] * z]) - covect = invmt.dot(contravect) - vectors.append(covect) - - # TODO NOT WORKING - pass - - return np.array(vectors) - - def GetBiatomicMolecules(self, atoms, distance, tollerance=0.01, return_indices = False): - """ - GET BIATOMIC MOLECULE - ===================== - - This function allows one to extract from a structure all the biatomic - molecules that contains the two atoms specified and that are at the distance - with a given tollerance. - This is very usefull to compute some particular average bond length. - - Parameters - ---------- - - atoms : list (char) (size = 2) - The atomic symbols of the molecule - - distance : float - The average distance between the two atom in the molecule - - tollerance : float, default 0.01 - The tollerance on the distance after which the two atoms are - no more consider inside the same molecule. - - return_indices : bool, default false - If true, per each molecule is returned also the list of the - original indices inside the structure. - - Results - ------- - - Molecules : list - List of molecules (Structure) that matches the input. - If none is found an empty list is returned - """ - # Check if the atoms is a 2 char list - if len(atoms) != 2: - raise ValueError("Error, the molecule must be biatomic") - - for a in atoms: - if not a in self.atoms: - raise ValueError("Error, the atom %s is not into this structure" % a) - - # Scroll all the atoms in the list that match the first type. - molecules = [] - original_indices = [] - for index1 in range(self.N_atoms): - atm1 = self.atoms[index1] - if atm1 != atoms[0]: - continue - - # Avoid double counting if the molecule is omonuclear - starting_index = 0 - if atoms[0] == atoms[1]: - starting_index = index1 + 1 - - for index2 in range(starting_index, self.N_atoms): - atm2 = self.atoms[index2] - if atm2 != atoms[1]: - continue - - # Check if the distances between the two atoms matches - d = self.get_min_dist(index1, index2) - if d > distance - tollerance and d < distance + tollerance: - # Create the structure of the molecule - mol = Structure() - mol.N_atoms = 2 - mol.atoms = atoms - mol.coords = np.zeros((2, 3)) - - # Translate the molecule in the middle of the cell - if self.has_unit_cell: - for i in range(3): - mol.coords[0,:] += self.unit_cell[i,:] / 2. - mol.coords[1,:] += self.unit_cell[i,:] / 2. - - mol.coords[1,:] += self.coords[index2,:] - self.coords[index1,:] - - # If the system has a unit cell, put the second atom inside the cell - if self.has_unit_cell: - mol.coords[1,:] = Methods.put_into_cell(self.unit_cell, mol.coords[1,:]) - - # Append the molecule to the structure - molecules.append(mol) - original_indices.append( (index1, index2) ) - - if return_indices: - return molecules, original_indices - - return molecules - - def get_displacement(self, target, dtype = np.float64): - """ - GET THE DISPLACEMENT STRUCTURE - ============================== - - This function will return an array of displacement respect to the target - of the current structure. Note that the two structures must be compatible. - - - NOTE: if any the self unit_cell will be considered, otherwise the target one. - no unit cell is used only if neither the self nor the target have one. - - Parameters - ---------- - target : Structure.Structure() - The reference atomic positions (also this is a structure) - dtype : type - The type to be cast the result. By default is the double precision - - Results - ------- - ndarray N_atoms x 3 - The displacements (same shape as self.coords) - """ - - # Check if the two structures are compatible - if self.N_atoms != target.N_atoms: - raise ValueError("Error, the target must share the same number of atoms") - - unit_cell = np.zeros((3,3)) - easy = False - if not self.has_unit_cell: - if not target.has_unit_cell: - easy = True - else: - unit_cell = target.unit_cell - else: - unit_cell = self.unit_cell - - disp = np.zeros(np.shape(self.coords)) - disp = np.float64(self.coords - target.coords) - if easy: - return disp - - # Check that the cell is good - for i in range(self.N_atoms): - # Add half of the the unit cell - for j in range(3): - disp[i,:] += unit_cell[j,:] * .5 - - disp[i,:] = Methods.put_into_cell(unit_cell, disp[i,:]) - - # Remove again the half cell - for j in range(3): - disp[i,:] -= unit_cell[j,:] * .5 - - return disp - - - def get_angle(self, index1, index2, index3, rad = False): - """ - GET ANGLE BETWEEN THREE ATOMS - ============================= - - This function evaluate the angle between three atoms located - in the structure at the correct indices. The unit cell is centered around - the second atom to compute correctly the structure. - - - Parameters - ---------- - indexI : int - Index of the Ith atom. (The angle is the one between 1-2-3) - rad : bool, optional - If true, the angle is returned in radiants (otherwise in degrees) - - Return - ------ - angle : float - Value of the angle in degrees (unles rad is specified) between the index1-index2-index3 - atoms of the structure. - - """ - - if index1 >= self.N_atoms or index2 >= self.N_atoms or index3 >= self.N_atoms: - raise ValueError("Error, the indices must be lower than the number of atoms.") - - - # Get the three vectors - v1 = self.coords[index1,:].copy() - v2 = self.coords[index2,:].copy() - v3 = self.coords[index3,:].copy() - - # center with respect of v2 - v1 -= v2 - v2 -= v2 - v3 -= v2 - - # Manipulate them if there is an unitcell - if self.has_unit_cell: - # Sum half of the cell vectors - for i in range(3): - v1 += self.unit_cell[i,:] * .5 - v2 += self.unit_cell[i,:] * .5 - v3 += self.unit_cell[i,:] * .5 - - # Put the vectors in the unit cell - v1 = Methods.put_into_cell(self.unit_cell, v1) - v2 = Methods.put_into_cell(self.unit_cell, v2) - v3 = Methods.put_into_cell(self.unit_cell, v3) - - # Center again around v2 - for i in range(3): - v1 -= self.unit_cell[i,:] * .5 - v2 -= self.unit_cell[i,:] * .5 - v3 -= self.unit_cell[i,:] * .5 - - # Now we can measure the angle - angle = np.arccos(np.dot(v1, v3) / np.sqrt(np.dot(v1, v1) * np.dot(v3, v3))) - - # Degree conversion - if not rad: - angle *= 180 / np.pi - - return angle - - - - def GetTriatomicMolecules(self, atoms, distance1, distance2, angle, thr_dist=0.01, thr_ang = 1, return_indices = False): - """ - GET TRIATOMIC MOLECULE - ===================== - - This function allows one to extract from a structure all the triatomic - molecules that contains the atoms specified and that are at the distance and angle - with a given tollerance. - This is very usefull to compute some particular average bond length. - - The two distances are between the first-second and second-third atom, while the angle - is between first-second-third atom. - - Be carefull if the atoms are equal and the distance1 and distance2 are very similar - the algorithm can find twice the same molecules. - - Parameters - ---------- - - atoms : list (char) (size = 3) - The atomic symbols of the molecule - - distance1 : float - The average distance between the first two atom in the molecule - - distance2 : float - The average distance between the last two atom in the molecule - - angle : float - Angle (in degree) between the central atom and the other two. - - thr_dist: float, default 0.01 - The tollerance on the distance after which the two atoms are - no more consider inside the same molecule. - - thr_angle: float, default 1 - Tollerance for the angle - - return_indices : bool, default false - If true, per each molecule is returned also the list of the - original indices inside the structure. - - Results - ------- - - Molecules : list - List of molecules (Structure) that matches the input. - If none is found an empty list is returned - """ - # Check if the atoms is a 3 char list - if len(atoms) != 3: - raise ValueError("Error, the molecule must be triatomic") - - for a in atoms: - if not a in self.atoms: - raise ValueError("Error, the atom %s is not into this structure" % a) - - # Scroll all the atoms in the list that match the first type. - molecules = [] - original_indices = [] - for index1 in range(self.N_atoms): - atm1 = self.atoms[index1] - if atm1 != atoms[0]: - continue - - # Avoid double counting if the molecule is omonuclear - starting_index = 0 - if atoms[0] == atoms[1]: - starting_index = index1 + 1 - - for index2 in range(starting_index, self.N_atoms): - atm2 = self.atoms[index2] - if atm2 != atoms[1]: - continue - - if index2 == index1: - continue - - # Check if the distances between the two atoms matches - d = self.get_min_dist(index1, index2) - #print "1) Selected %d %d => d = %.3f" % (index1, index2, d) - if not (d > distance1 - thr_dist and d < distance1 + thr_dist): - continue - - # Accepted the first two atoms - for index3 in range(0, self.N_atoms): - if index3 in [index1, index2]: - continue - - d = self.get_min_dist(index2, index3) - #print "2) Selected %d %d => d = %.3f" % (index2, index3, d) - - if not (d > distance2 - thr_dist and d < distance2 + thr_dist): - continue - - # Ok accepted for distance - # Check also the angle - ang = self.get_angle(index1, index2, index3) - print ("A> %d %d %d = %.3f" % (index1, index2, index3, ang)) - - if not (ang > angle - thr_ang and ang < angle + thr_ang): - continue - - - # Create the structure of the molecule - mol = Structure() - mol.N_atoms = 3 - mol.atoms = atoms - mol.coords = np.zeros((3, 3)) - mol.unit_cell = self.unit_cell - mol.has_unit_cell = True - - # Translate the molecule in the middle of the cell - if self.has_unit_cell: - for i in range(3): - mol.coords[0,:] += self.unit_cell[i,:] / 2. - mol.coords[1,:] += self.unit_cell[i,:] / 2. - mol.coords[2,:] += self.unit_cell[i,:] / 2. - - - mol.coords[0,:] += self.coords[index1,:] - self.coords[index2,:] - mol.coords[2,:] += self.coords[index3,:] - self.coords[index2,:] - - print ("1-Accepted:", mol.get_min_dist(0,1), mol.get_min_dist(1,2), mol.get_angle(0, 1, 2)) - - # If the system has a unit cell, put the second atom inside the cell - if self.has_unit_cell: - for k in range(3): - mol.coords[k,:] = Methods.put_into_cell(self.unit_cell, mol.coords[k,:]) - - print ("2-Accepted:", mol.get_min_dist(0,1), mol.get_min_dist(1,2), mol.get_angle(0, 1, 2)) - - # Append the molecule to the structure - molecules.append(mol) - original_indices.append( (index1, index2, index3) ) - - if return_indices: - return molecules, original_indices - - return molecules - - def generate_espresso_input(self, flags): - """ - GENERATE ESPRESSO INPUT - ======================= - - This subroutine will generate the input for a quantum espresso calculation - """ - pass - - def IsolateAtoms(self, *args, **kwargs): - warnings.warn("This function is deprecated, use isolate_atoms instead.") - return self.isolate_atoms(*args, **kwargs) - - def isolate_atoms(self, atoms_indices): - """ - This subroutine returns a Structure() with only the atoms indices identified - by the provided list. - - Parameters - ---------- - atoms_indices : list of int - List of the atoms that you want to isolate - - Returns - ------- - new_structure : Structure() - A structure with only the isolated atoms. - """ - - - new_struct = self.copy() - nat = len(atoms_indices) - new_struct.N_atoms = nat - - new_struct.coords = np.zeros( (nat, 3), dtype = np.float64) - new_struct.atoms = [None] * nat - - for i, x in enumerate(atoms_indices): - new_struct.coords[i,:] = self.coords[x,:] - new_struct.atoms[i] = self.atoms[x] - - return new_struct - - def get_inertia_tensor(self): - """ - GET INERTIA TENSOR - ==================== - - This method get the intertial tensor of the current structure. - Note periodic boundary conditions will be ingored, - so take care that the atoms are correctly centered. - - The units will be the units given for the mass dot the position^2 - - Results - ------- - I : ndarray ( size = (3,3), dtype = np.double) - The inertia tensor - """ - - # Extract the masses - m = self.get_masses_array() - - I = np.zeros( (3,3), dtype = np.double) - E = np.eye(3, dtype = np.double) - - # Get the center of mass - r_cm = np.einsum("a, ab->b", m, self.coords) / np.sum(m) - - # Get the inertia tensor - for i in range(self.N_atoms): - r = self.coords[i, :] - r_cm - - I += m[i] * (E * r.dot(r) - np.outer(r,r)) - - return I - - def get_classical_rotational_free_energy(self, temperature, unit_mass = "Ry"): - """ - ROTATIONAL FREE ENERGY - ====================== - - Get the classical free energy of a rigid rotor. - - Parameters - ---------- - temperature : float - Temperature in K - unit_mass : string - The unit of measurement of the masses. - It can be one of: - - "uma" : the atomic mass unit (1/12 of the C12 nucleus) - - "Ry" : the rydberg mass (twice electron mass) - - "Ha" : the hartree mass (electron mass) - Results - ------- - free_energy : float - The rotational free energy in eV - """ - - # Get the inertia tensor - It = self.get_inertia_tensor() - - # convert the mass - if unit_mass.lower() == "ry": - It *= MASS_RY_TO_UMA - elif unit_mass.lower() == "ha": - It /= ELECTRON_MASS_UMA - elif unit_mass.lower() == "uma": - pass - else: - ERROR_MSG = """ - Error, unkwown unit type: {} -""" - raise ValueError(ERROR_MSG.format(unit_mass)) - - - Idiag, dumb = np.linalg.eigh(It) - - kbT = temperature* K_B - Z = np.sqrt((2 * np.pi* kbT)**3 * np.prod(Idiag)) - free_energy = - kbT * np.log(Z) - #free_energy = 3 * kbT * np.log(2*kbT)/2 + kbT / 2 * np.sum(np.log(Idiag)) - - return free_energy - - - - -def get_structures_from_phonopy_supercells(ph_supercells): - """ - Get a list of CellConstructor structures starting from the phonopy supercells_with_displacements - object. - """ - - if not __PHONOPY__: - raise ImportError("Error, to execute this method you must have Phonopy installed.") - - if not isinstance(ph_supercells, list): - raise ValueError("Error ph_supercells must be a list.") - - if not isinstance(ph_supercells[0], phonopy.structure.atoms.PhonopyAtoms): - raise ValueError("Error, ph_supercells must be a list of PhonopyAtoms object.") - - n_tot = len(ph_supercells) - atms = ph_supercells[0].get_chemical_symbols() - nat = len(atms) - - s_basis = Structure(nat) - s_basis.atoms = atms - s_basis.has_unit_cell = True - s_basis.unit_cell = ph_supercells[0].get_cell() - - all_structures = [] - for i in range(n_tot): - s = s_basis.copy() - s.coords = ph_supercells[0].get_positions().copy() - - all_structures.append(s) - - return all_structures \ No newline at end of file diff --git a/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py b/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py deleted file mode 100644 index 91d2a766..00000000 --- a/cellconstructor/.ipynb_checkpoints/Units-checkpoint.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import print_function -from __future__ import division - - -import numpy as np - -__INFO__ = """ -Here all the conversion between common units are stored. - -The default units are Angstrom, eV, and the atomic unit mass (1/12 of C12 nucleus) - -However, phonons are saved in Ry/bohr^2 (to mantain the quantum-espresso compatibility) -therefore some unsefull conversion with Ry and Ha atomic units are provided. -""" - -A_TO_BOHR = np.float64(1.889725989) -BOHR_TO_ANGSTROM = 1 / A_TO_BOHR -RY_TO_CM = np.float64(109737.36034769034314)#109691.40235 -ELECTRON_MASS_UMA = np.float64(1822.8885468045) -MASS_RY_TO_UMA = 2 / ELECTRON_MASS_UMA -HBAR = np.float64(0.06465415105180661) -K_B = np.float64(8.617330337217213e-05) -RY_TO_EV = np.float64(13.605693012183622) -RY_PER_BOHR3_TO_GPA = 14710.513242194795 -RY_PER_BOHR3_TO_EV_PER_A3 = 91.81576765360094 -RY_TO_KELVIN = RY_TO_EV / K_B -GPA_TO_EV_PER_A3 = RY_PER_BOHR3_TO_EV_PER_A3 / RY_PER_BOHR3_TO_GPA diff --git a/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py b/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py deleted file mode 100644 index 6aba1fc7..00000000 --- a/cellconstructor/.ipynb_checkpoints/calculators-checkpoint.py +++ /dev/null @@ -1,525 +0,0 @@ -from ast import Delete -import cellconstructor as CC -import cellconstructor.Structure -import cellconstructor.Methods - -import subprocess - -import ase, ase.io -import ase.calculators.calculator - -import cellconstructor.Settings as Settings - -import cellconstructor.Units -import copy - -import scipy, scipy.optimize - -import numpy as np -import copy -import sys, os - - - - -class Calculator: - def __init__(self): - """ - CELLCONSTRUCTOR CALCULATOR - =========================== - - This is an alternative to ASE calculators, which often do not work. - It is explicitely done for cellconstructor and python-sscha - - """ - - self.label = "label" - self.directory = None - self.command = None - self.results = {} - self.structure = None - - def set_directory(self): - pass - - - -def get_energy_forces(calculator, structure): - """ - Accepts both an ase calculaotr and a calculator of CellConstructor - """ - - if isinstance(calculator, ase.calculators.calculator.Calculator): - atm = structure.get_ase_atoms() - atm.set_calculator(calculator) - energy = atm.get_total_energy() - if isinstance(energy, np.ndarray): - energy = energy[0] - return energy, atm.get_forces() - elif isinstance(calculator, Calculator): - calculator.calculate(structure) - return calculator.results["energy"], calculator.results["forces"] - else: - raise ValueError("Error, unknown calculator type") - -def get_results(calculator, structure, get_stress = True): - """ - Accepts both an ASE calculator and a Calculator from Cellconstructor - and computes all the implemented properties (energy, forces and stress tensor). - """ - - results = {} - if isinstance(calculator, ase.calculators.calculator.Calculator): - atm = structure.get_ase_atoms() - atm.set_calculator(calculator) - results["energy"] = atm.get_total_energy() - results["forces"] = atm.get_forces() - if get_stress: - results["stress"] = atm.get_stress(voigt = False) - elif isinstance(calculator, Calculator): - calculator.calculate(structure) - results = calculator.results - if get_stress: - results["stress"] = CC.Methods.transform_voigt(results["stress"], voigt_to_mat = True) - else: - raise ValueError("Error, unknown calculator type") - - return results - - -class FileIOCalculator(Calculator): - def __init__(self): - Calculator.__init__(self) - self.structure = None - self.output_file = "PREFIX.pwo" - - def write_input(self, structure): - if self.directory is None: - self.directory = os.path.abspath(".") - - if not os.path.isdir(self.directory): - os.makedirs(self.directory) - - # This is not thread safe, as writing the input override the structure of the shared calculator object - # Which is then overridden by the read_results - #self.structure = structure.copy() - - def calculate(self, structure): - self.write_input(structure) - self.execute() - self.read_results() - - def set_label(self, lbl): - self.label = lbl - - def set_directory(self, directory): - self.directory = directory - - # Produce the directory if it does not exists - if not os.path.exists(directory): - os.makedirs(directory) - - def execute(self): - #cmd = "cd {} && {} && cd ..".format(self.directory, self.command.replace("PREFIX", self.label)) - cmd = self.command.replace("PREFIX", os.path.join(os.path.abspath(self.directory),self.label)) - outputfname = self.output_file.replace("PREFIX", os.path.join(os.path.abspath(self.directory),self.label)) - - - new_env = {k: v for k, v in os.environ.items() if "MPI" not in k if "PMI" not in k} - sys.stdout.flush() - with open(os.path.join(self.directory, outputfname), "w") as foutput: - proc = subprocess.Popen(cmd, shell = True, env = new_env, cwd = self.directory, stdout = foutput) - sys.stdout.flush() - errorcode = proc.wait() - sys.stdout.flush() - - - #os.system(cmd) - - def read_results(self): - pass - - -class Espresso(FileIOCalculator): - def __init__(self, input_data = {}, pseudopotentials = {}, masses = None, command = "pw.x -i PREFIX.pwi", kpts = (1,1,1), koffset = (0,0,0)): - """ - ESPRESSO CALCULATOR - =================== - - parameters - ---------- - data_input : dict - Dictionary of the Quantum Espresso PW input namespace - pseudopotentials : dict - Dictionary of the file names of the pseudopotentials - masses : dict - Dictionary of the masses (in UMA) of the specified atomic species - kpts : list - A list of the k points grid to sample the space. - If the calculation is given at gamma, use the gamma string. - Note gamma is incompatible with a koffset - """ - FileIOCalculator.__init__(self) - - self.command = command - self.kpts = kpts - self.koffset = koffset - self.input_data = copy.deepcopy(input_data) # Copy to avoid double modification - self.pseudopotentials = pseudopotentials - self.output_file = "PREFIX.pwo" - if masses is None: - masses = {} - for atm in pseudopotentials: - masses[atm] = 1.000 - self.masses = masses - - assert len(list(self.pseudopotentials)) == len(list(self.masses)), "Error, pseudopotential and masses must match" - - def copy(self): - """ - Return an identical instance, without inhering the info of the calculation. - """ - new_class = Espresso(self.input_data, self.pseudopotentials, self.masses, self.command, self.kpts, self.koffset) - return new_class - - - def set_label(self, lbl, override_prefix = True, *args, **kwargs): - FileIOCalculator.set_label(self, lbl, *args, **kwargs) - - # Enforce the override of the prefix - if override_prefix: - if "control" in self.input_data: - self.input_data = copy.deepcopy(self.input_data) - self.input_data["control"].update({"prefix" : lbl}) - - def setup_from_ase(self, ase_calc): - """ - Copy the parameters from the ASE calculator - """ - - for kwarg in ase_calc.parameters: - self.__setattr__(kwarg, copy.deepcopy(ase_calc.parameters[kwarg])) - - self.set_label(ase_calc.label) - - #self.input_data = copy.deepcopy(ase_calc.parameters["input_data"]) - #self.kpts = ase_calc.parameters["kpts"] - #self.koffset = ase_calc.parameters["koffset"] - #self.pseudopotentials = copy.deepcopy(ase_calc.parameters["pseudopotentials"]) - - def write_input(self, structure): - FileIOCalculator.write_input(self, structure) - - typs = np.unique(structure.atoms) - - total_input = copy.deepcopy(self.input_data) - total_input["system"].update({"nat" : structure.N_atoms, "ntyp" : len(typs), "ibrav" : 0}) - #total_input["control"].update({"outdir" : self.directory, "prefix" : self.label}) - if not "prefix" in total_input["control"]: - total_input["control"].update({"prefix" : self.label}) - - scf_text = "".join(CC.Methods.write_namelist(total_input)) - - print("TOTAL INPUT:") - print(total_input) - scf_text += """ -ATOMIC_SPECIES -""" - for atm in typs: - scf_text += "{} {} {}\n".format(atm, self.masses[atm], self.pseudopotentials[atm]) - - if isinstance(self.kpts, str): - if self.kpts.lower() == 'gamma': - scf_text += ''' -K_POINTS gamma -''' - else: - raise ValueError('Error, kpts msut be either list or gamma, {} not recognized'.format(self.kpts)) - elif len(np.shape(self.kpts)) == 2: - nkpts, _ = np.shape(self.kpts) - scf_text += ''' -K_POINTS crystal -{} -'''.format(nkpts) - for i in range(nkpts): - scf_text += '{:.16f} {:.16f} {:.16f} 1\n'.format(*list(self.kpts[i, :])) - elif len(self.kpts) == 3: - scf_text += """ -K_POINTS automatic -{} {} {} {} {} {} -""".format(self.kpts[0], self.kpts[1], self.kpts[2], - self.koffset[0], self.koffset[1], self.koffset[2]) - - - scf_text += structure.save_scf(None, get_text = True) - - filename = os.path.join(self.directory, self.label + ".pwi") - - with open(filename, "w") as fp: - fp.write(scf_text) - - - def read_results(self, override_structure = True): - FileIOCalculator.read_results(self) - - - filename = os.path.join(self.directory, self.label + ".pwo") - - print('READING RESULTS FROM FILE ', filename) - - # Settings.all_print("reading {}".format(filename)) - #atm = ase.io.read(filename) - - energy = 0 - read_forces = False - counter = 0 - stress = np.zeros((3,3), dtype = np.double) - - read_stress = False - got_stress = False - read_structure = override_structure - read_coords = False - alat = CC.Units.BOHR_TO_ANGSTROM - - # If we read until the stress - # Everything went correctly, otherwise check for the JOB DONE - job_done = False - - if self.structure is None: - read_structure = True - else: - forces = np.zeros_like(self.structure.coords) - - with open(filename, "r") as fp: - for line in fp.readlines(): - line = line.strip() - data = line.split() - - # Avoid white lines - if not line: - continue - - # Check if the script exited correctly - if "JOB DONE" in line: - job_done = True - - if read_structure: - new_data = line.replace("=", " ").split() - if new_data[0] == "celldm(1)": - alat *= float(new_data[1]) - - if "number of atoms/cell" in line: - nat = int(data[-1]) - self.structure = CC.Structure.Structure(nat) - self.structure.has_unit_cell = True - self.structure.unit_cell = np.eye(3) - forces = np.zeros_like(self.structure.coords) - - if data[0] == "a(1)": - self.structure.unit_cell[0,:] = [float(x) * alat for x in data[3:-1]] - if data[0] == "a(2)": - self.structure.unit_cell[1,:] = [float(x) * alat for x in data[3:-1]] - if data[0] == "a(3)": - self.structure.unit_cell[2,:] = [float(x) * alat for x in data[3:-1]] - - if "Cartesian axes" in line: - read_coords = True - - - if read_coords: - # Improve the split of the line to avoid merging numbers - data = line.replace("-", " -").replace("(", "( ").split() - if len(data) == 10: - i_atm = int(data[0]) - 1 - self.structure.coords[i_atm, :] = [float(x) * alat for x in data[6:9]] - self.structure.atoms[i_atm] = data[1] - if i_atm == self.structure.N_atoms - 1: - read_coords = False - read_structure = False - continue - - - - if line[0] == "!": - energy = float(data[4]) - - if "Forces acting on atoms" in line: - read_forces = True - read_stress = False - continue - - if "total stress" in line: - read_stress = True - read_forces = False - counter = 0 - continue - - if read_forces and len(data) == 9: - if data[0] == "atom": - counter += 1 - - at_index = int(data[1]) - 1 - forces[at_index, :] = [float(x) for x in data[6:]] - - if counter >= self.structure.N_atoms: - read_forces = False - - if read_stress and len(data) == 6: - stress[counter, :] = [float(x) for x in data[:3]] - counter += 1 - if counter == 3: - got_stress = True - read_stress = False - - - # Convert to match ASE conventions - energy *= CC.Units.RY_TO_EV - forces *= CC.Units.RY_TO_EV / CC.Units.BOHR_TO_ANGSTROM - stress *= CC.Units.RY_PER_BOHR3_TO_EV_PER_A3 - stress = CC.Methods.transform_voigt(stress) # To be consistent with ASE, use voigt notation - - print('READING RESULTS : energy = {} | job done = {}'.format(energy, job_done)) - - # Everything went on correctly, update the results - if job_done or got_stress: - self.results = {"energy" : energy, "forces" : forces} - if got_stress: - # Use voit - self.results.update({"stress" : - stress}) - else: - self.results = None - - - - -# Here the methods to minimize the structure with a standard calculator -class Relax: - def __init__(self, structure, calculator, method = "BFGS", verbose = True, store_trajectory = True): - """ - Class that perform the structure relaxation. - - Parameters - ---------- - structure : CC.Structure.Structure() - The atomic structure - calculator : CC.calculators.Calculator() - The CellConstructor (or ASE) calculator. - method : string - The algorithm for the minimization. Default BFGS - verbose : bool - If true, prints the current total energy and forces - store_trajector : bool - If true, the trajectory of the minimization is saved in self.trajectory - """ - self.structure = structure - self.calculator = calculator - self.method = method - self.verbose = verbose - self.store_trajectory = store_trajectory - - self.trajectory = [] - - # Usefull variables to track the energy and add a callback - self.last_eval = None - self.last_energy = None - self.last_force = None - self.iterations = 1 - - - def static_relax(self, **kwargs): - """ - RELAX THE STRUCTURE - ------------------- - - Relax the structure keeping fixed the lattice parameters using a BFGS algorithm. - - Parameters - ---------- - **kwargs : - Any optional arguments of scipy.optimize.minimize to control - the minimization. - - Results - ------- - optimized_structure : CC.Structure.Structure() - The structure after the optimization - """ - - if "method" in kwargs: - self.method = kwargs["method"] - - - # Parse the function to match the scipy minimizer - self.last_eval = np.zeros(self.structure.coords.ravel().shape, dtype = np.double) - self.last_energy = 0 - self.last_force = np.zeros_like(self.last_eval) - - def func(x): - if np.linalg.norm(x - self.last_eval) < 1e-16: - return self.last_energy, self.last_force - - struct = self.structure.copy() - struct.coords[:,:] = x.reshape(struct.coords.shape) - - energy, forces = get_energy_forces(self.calculator, struct) - - self.last_eval[:] = x.copy() - self.last_energy = energy - self.last_force[:] = -forces.ravel().copy() - - - return energy, -forces.ravel() - - def callback(xk): - - if self.verbose: - energy, force = func(xk) - #print('it:', self.iterations) - #print('energy:', energy) - #print('force:', force) - print("{:5d}) {:16.8f} eV {:16.8f} eV/A".format(self.iterations, energy, np.linalg.norm(force))) - self.iterations += 1 - - if self.store_trajectory: - struc = self.structure.copy() - struc.coords[:,:] = xk.reshape(struc.coords.shape) - self.trajectory.append(struc) - - if self.verbose: - print("STATIC STRUCTURE RELAX") - print() - print("{:5s} {:16s} {:16s} ".format("ITERS", "ENERGY", "FORCE GRAD")) - print("--------------------------------------------------") - - - res = scipy.optimize.minimize(func, self.structure.coords.ravel(), method = self.method, jac = True, callback = callback, **kwargs) - - if self.verbose: - print() - - final_struct = self.structure.copy() - final_struct.coords[:,:] = res.x.reshape(final_struct.coords.shape) - self.structure = final_struct - - return final_struct - - - - - - - - - - - - - - - - - - - - - diff --git a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py b/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py deleted file mode 100644 index 8d34ef0f..00000000 --- a/cellconstructor/.ipynb_checkpoints/symmetries-checkpoint.py +++ /dev/null @@ -1,3013 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 29 11:10:21 2017 - -@author: darth-vader -""" -from __future__ import print_function -from __future__ import absolute_import -from __future__ import division - -import time -import os -import numpy as np - -import scipy -import scipy.linalg - -import cellconstructor.Methods as Methods -from cellconstructor.Units import * -import cellconstructor.Timer as Timer -from cellconstructor.Settings import ParallelPrint as print - -# Load the fortran symmetry QE module -import symph - -# Load the LinAlgebra module in C -from cc_linalg import GramSchmidt - -import warnings - - -__SPGLIB__ = True -try: - import spglib -except: - __SPGLIB__ = False - - - - -CURRENT_PATH = os.path.realpath(__file__) -CURRENT_DIR = os.path.dirname(CURRENT_PATH) -__EPSILON__ = 1e-5 - -class QE_Symmetry: - def __init__(self, structure, threshold = 1e-5): - """ - Quantum ESPRESSO Symmetry class - =============================== - - This class contains all the info about Quantum ESPRESSO symmetry data. - It is used to wrap symmetries into the quantum espresso fortran subroutines. - - Starting from a set of symmetry operation and the structure of the system, it - builds all the QE symmetry operations. - - NOTE: - Use always the provided methods to change the self variables, as they are - handled to properly cast the fortran types and array alignment. - - Parameters - ---------- - structure : CC.Structure.Structure() - The structure to which the symmetries refer to. - threshold : float - The threshold of the symmetry operation. - - """ - - if not structure.has_unit_cell: - raise ValueError("Error, symmetry operation can be initialize only if the structure has a unit cell") - - self.structure = structure - self.threshold = np.float64(threshold) - - # Setup the threshold - symph.symm_base.set_accep_threshold(self.threshold) - - nat = structure.N_atoms - - # Define the quantum espresso symmetry variables in optimized way to work with Fortran90 - self.QE_nat = np.intc( nat ) - self.QE_s = np.zeros( (3, 3, 48) , dtype = np.intc, order = "F") - self.QE_irt = np.zeros( (48, nat), dtype = np.intc, order = "F") - self.QE_invs = np.zeros( (48), dtype = np.intc, order = "F") - self.QE_rtau = np.zeros( (3, 48, nat), dtype = np.float64, order = "F") - self.QE_ft = np.zeros( (3, 48), dtype = np.float64, order = "F") - - - self.QE_minus_q = np.bool( False ) - self.QE_irotmq = np.intc(0) - self.QE_nsymq = np.intc( 0 ) - self.QE_nsym = np.intc(0) - - # Prepare the QE structure - self.QE_tau = np.zeros((3, nat), dtype = np.float64, order = "F") - self.QE_ityp = np.zeros(nat, dtype = np.intc) - - symbs = {} - counter = 1 - for i in range(nat): - # Rank the atom number - atm = structure.atoms[i] - if not atm in symbs.keys(): - symbs[atm] = counter - counter += 1 - - self.QE_ityp[i] = symbs[atm] - # Convert in bohr - for j in range(3): - self.QE_tau[j, i] = structure.coords[i, j] - - - self.QE_at = np.zeros( (3,3), dtype = np.float64, order = "F") - self.QE_bg = np.zeros( (3,3), dtype = np.float64, order = "F") - - bg = structure.get_reciprocal_vectors() - for i in range(3): - for j in range(3): - self.QE_at[i,j] = structure.unit_cell[j,i] - self.QE_bg[i,j] = bg[j,i] / (2* np.pi) - - # Here we define the quantities required to symmetrize the supercells - self.QE_at_sc = self.QE_at.copy() - self.QE_bg_sc = self.QE_bg.copy() - self.QE_translation_nr = 1 # The supercell total dimension (Nx * Ny * Nz) - self.QE_translations = [] # The translations in crystal axes - - # After the translation, which vector is transformed in which one? - # This info is stored here as ndarray( size = (N_atoms, N_trans), dtype = np.intc, order = "F") - self.QE_translations_irt = [] - - def ForceSymmetry(self, structure): - """ - FORCE SYMMETRY - ============== - - Force the symmetries found at a given threshold to - be satisfied also in a lower threshold. - - This use the irt trick - """ - if self.QE_nsymq == 0: - raise ValueError("Error, initialize the symmetries with SetupQPoint.") - - coords = np.zeros( (3, structure.N_atoms), order = "F", dtype = np.float64) - coords[:,:] = structure.coords.transpose() - - # Transform in crystal coordinates - symph.cryst_to_cart(coords, self.QE_bg, -1) - - new_coords = np.zeros( (3, structure.N_atoms), order = "F", dtype = np.float64) - for s_i in range(self.QE_nsymq): - for i in range(structure.N_atoms): - new_coords[:, self.QE_irt[s_i, i]-1 ] += self.QE_s[:,:,s_i].dot(coords[:,i]) - new_coords[:, self.QE_irt[s_i, i]-1 ] += self.QE_ft[:, s_i] - - new_coords /= self.QE_nsymq - - # Transform back into cartesian coordinates - symph.cryst_to_cart(new_coords, self.QE_at, 1) - - # Save in the structure - structure.coords[:,:] = new_coords.transpose() - - def PrintSymmetries(self): - """ - This method just prints the symmetries on stdout. - """ - - print() - print("Number of symmetries: {}".format(self.QE_nsym)) - syms = self.GetSymmetries() - for i in range(self.QE_nsym): - print(" Symmetry {}".format(i+1)) - for j in range(3): - print(" {:3.0f}{:3.0f}{:3.0f} | {:6.3f}".format(*syms[i][j,:])) - print() - - def GetUniqueRotations(self): - """ - This subroutine returns an alternative symmetries - that contains only unique rotations (without fractional translations). - This is usefull if the peculiar cell is a supercell - and the symmetrization was performed with SPGLIB - - Returns - ------- - QE_s : ndarray(size = (3,3,48), dtype = np.intc) - The symmetries - QE_invs : ndarray(size = 48, dtype = np.intc) - The index of the inverse symmetry - QE_nsym : int - The number of symmetries - """ - - QE_s = np.zeros( self.QE_s.shape, dtype = np.intc, order = "F") - QE_invs = np.zeros(self.QE_invs.shape, dtype = np.intc, order = "F") - QE_nsym = 0 - - - for i in range(self.QE_nsym): - # Check if the same rotation was already added - skip = False - for j in range(QE_nsym): - # Check if the rotation occurred - if (QE_s[:,:,j] == self.QE_s[:,:,i]).all(): - skip = True - break - - if not skip: - # We did not find another equal rotation - # Lets add this one - QE_s[:,:, QE_nsym] = self.QE_s[:,:,i] - QE_nsym += 1 - - # Get the inverse - QE_invs[:] = get_invs(QE_s, QE_nsym) - - return QE_s, QE_invs, QE_nsym - - - - - - - - def SetupQStar(self, q_tot, supergroup = False): - """ - DIVIDE THE Q POINTS IN STARS - ============================ - - This method divides the given q point list into the star. - Remember, you need to pass the whole number of q points - - Parameters - ---------- - q_tot : list - List of q vectors to be divided into stars - supergroup : bool - If true then assume we have initialized a supercell bigger - Results - ------- - q_stars : list of lists - The list of q_star (list of q point in the same star). - sort_mask : ndarray(size=len(q_tot), dtype = int) - a mask to sort the q points in order to match the - same order than the q_star - """ - - # Setup the symmetries - #self.SetupQPoint() - - # Lets copy the q list (we are going to pop items from it) - q_list = q_tot[:] - q_stars = [] - - count_qstar = 0 - count_q = 0 - q_indices = np.zeros( len(q_tot), dtype = int) - while len(q_list) > 0: - q = q_list[0] - # Get the star of the current q point - _q_ = np.array(q, dtype = np.float64) # Fortran explicit conversion - - nq_new, sxq, isq, imq = symph.star_q(_q_, self.QE_at, self.QE_bg, - self.QE_nsym, self.QE_s, self.QE_invs, 0) - - # print ("START WITH Q:", q) - # print ("FOUND STAR:") - # for jq in range(nq_new): - # print (sxq[:, jq]) - # print () - - # print ("TELL ME THE BG:") - # print (self.QE_bg.transpose()) - - # print("Manual star:") - # for k in range(self.QE_nsym): - # trial_q = q.dot(self.QE_s[:,:, k]) - # distance_q = Methods.get_min_dist_into_cell(self.QE_bg.T, trial_q, q) - # distance_mq = Methods.get_min_dist_into_cell(self.QE_bg.T, trial_q, -q) - # print("trial_q : {} | DQ: {:.4f} | DMQ: {:.4f}".format(trial_q, distance_q, distance_mq )) - - # Prepare the star - q_star = [sxq[:, k] for k in range(nq_new)] - - # If imq is not zero (we do not have -q in the star) then add the -q for each in the star - if imq == 0: - old_q_star = q_star[:] - min_dist = 1 - - for q in old_q_star: - q_star.append(-q) - - - - q_stars.append(q_star) - - # Pop out the q_star from the q_list - for jq, q_instar in enumerate(q_star): - # Look for the q point in the star and pop them - #print("q_instar:", q_instar) - q_dist = [Methods.get_min_dist_into_cell(self.QE_bg.transpose(), - np.array(q_instar), q_point) for q_point in q_list] - - pop_index = np.argmin(q_dist) - q_list.pop(pop_index) - - # Use the same trick to identify the q point - q_dist = [Methods.get_min_dist_into_cell(self.QE_bg.transpose(), - np.array(q_instar), q_point) for q_point in q_tot] - - q_index = np.argmin(q_dist) - #print (q_indices, count_q, q_index) - q_indices[count_q] = q_index - - count_q += 1 - - - return q_stars, q_indices - - - def ApplySymmetryToTensor3(self, v3, initialize_symmetries = True): - """ - SYMMETRIZE A RANK-3 TENSOR - ========================== - - This subroutines uses the current symmetries to symmetrize - a rank-3 tensor. - This tensor must be in the supercell space. - - The v3 argument will be overwritten. - - NOTE: The symmetries must be initialized in the supercell using spglib - - - Parameters - ---------- - v3 : ndarray( size=(3*nat, 3*nat, 3*nat), dtype = np.double, order = "F") - The 3-rank tensor to be symmetrized. - It will be overwritten with the new symmetric one. - It is suggested to specify the order of the array to "F", as this will prevent - the parser to copy the matrix when doing the symmetrization in Fortran. - initialize_symmetries : bool - If True the symmetries will be initialized using spglib. Otherwise - the already present symmetries will be use. Use it False at your own risk! - (It can crash with seg fault if symmetries are not properly initialized) - """ - if initialize_symmetries: - self.SetupFromSPGLIB() - - # Apply the permutation symmetry - symph.permute_v3(v3) - - # Apply the translational symmetries - symph.trans_v3(v3, self.QE_translations_irt) - - # Apply all the symmetries at gamma - symph.sym_v3(v3, self.QE_at, self.QE_s, self.QE_irt, self.QE_nsymq) - - def ApplySymmetryToEffCharge(self, eff_charges): - """ - SYMMETRIZE EFFECTIVE CHARGES - ============================ - - This subroutine applies the symmetries to the effective charges. - - As always, the eff_charges will be modified by this subroutine. - - Parameters - ---------- - - eff_charges : ndarray (size = (nat, 3, 3)) - The effective charges tensor. - The first dimension is the index of the atom in the primitive cell - the second index is the electric field. - The third index is the cartesian axis. - """ - - nat, cart1, cart2 = np.shape(eff_charges) - - assert cart1 == cart2 - assert cart1 == 3 - assert nat == self.QE_nat, "Error, the structure and effective charges are not compatible" - - - # Apply the sum rule - tot_sum = np.sum(eff_charges, axis = 0) - eff_charges -= np.tile(tot_sum, (nat, 1)).reshape((nat, 3,3 )) / nat - - new_eff_charges = np.zeros((nat, cart1, cart2), dtype = np.double) - - # Get the effective charges in crystal components - for i in range(nat): - eff_charges[i, :, :] = Methods.convert_matrix_cart_cryst(eff_charges[i, :, :], self.QE_at.T) - - # Apply translations - if self.QE_translation_nr > 1: - for i in range(self.QE_translation_nr): - irt = self.QE_translations_irt[:, i] - 1 - for j in range(nat): - new_mat = eff_charges[irt[j], :, :] - new_eff_charges[j, :, :] += new_mat - - eff_charges[:,:,:] = new_eff_charges / self.QE_translation_nr - new_eff_charges[:,:,:] = 0. - - # Apply rotations - for i in range(self.QE_nsym): - irt = self.QE_irt[i, :] - 1 - - for j in range(nat): - new_mat = self.QE_s[:,:, i].dot( eff_charges[irt[j], :, :].dot(self.QE_s[:,:,i].T)) - new_eff_charges[j, :, :] += new_mat - new_eff_charges /= self.QE_nsym - - # Convert back into cartesian - for i in range(nat): - eff_charges[i, :, :] = Methods.convert_matrix_cart_cryst(new_eff_charges[i, :, :], self.QE_at.T, True) - - def ApplySymmetryToRamanTensor(self, raman_tensor): - """ - SYMMETRIZE RAMAN TENSOR - ============================ - - This subroutine applies the symmetries to the raman tensor - - As always, the raman_tensor will be modified by this subroutine. - - Parameters - ---------- - - raman_tensor : ndarray (size = (3, 3, 3 * nat)) - The raman tensor. The first two indices indicate - the polarization of the incoming/outcoming field, while the last one - is the atomic/cartesian coordinate - """ - - pol1, pol2, at_cart = np.shape(raman_tensor) - - assert pol1 == pol2 - assert pol2 == 3 - assert at_cart == 3*self.QE_nat, "Error, the structure and effective charges are not compatible" - - # Apply the permutation on the electric fields - raman_tensor += np.einsum("abc->bac", raman_tensor) - raman_tensor /= 2 - - # Apply the sum rule - # The sum over all the atom for each cartesian coordinate should be zero. - rt_reshaped = raman_tensor.reshape((3,3,self.QE_nat, 3)) - - # Sum over all the atomic indices - tot_sum = np.sum(rt_reshaped, axis = 2) - - # Rebuild the shift to the tensor of the correct shape - shift = np.tile(tot_sum, (self.QE_nat, 1, 1, 1)) - - # Place the number of atoms at the correct position - # From the first to the third - shift = np.einsum("abcd->bcad", shift) - - # Now we apply the sum rule - rt_reshaped -= shift / self.QE_nat - - # Auxiliary variable - new_tensor = np.zeros(np.shape(rt_reshaped), dtype = np.double) - - # Get the raman tensor in crystal components - for i in range(self.QE_nat): - rt_reshaped[:,:, i, :] = Methods.convert_3tensor_to_cryst(rt_reshaped[:,:, i, :], self.QE_at.T) - - # Apply translations - if self.QE_translation_nr > 1: - for i in range(self.QE_translation_nr): - irt = self.QE_translations_irt[:, i] - 1 - for j in range(self.QE_nat): - new_mat = rt_reshaped[:,:, irt[j], :] - new_tensor[:,:,j,:] += new_mat[:,:,:] - - rt_reshaped = new_tensor / self.QE_translation_nr - new_tensor[:,:,:,:] = 0. - - # Apply rotations - for i in range(self.QE_nsym): - irt = self.QE_irt[i, :] - 1 - - for j in range(self.QE_nat): - # Apply the symmetry to the 3 order tensor - new_mat = np.einsum("ai, bj, ck, ijk -> abc", self.QE_s[:,:,i], self.QE_s[:,:,i], self.QE_s[:,:,i], rt_reshaped[:,:, irt[j], :]) - #new_mat = self.QE_s[:,:, i].dot( eff_charges[irt[j], :, :].dot(self.QE_s[:,:,i].T)) - new_tensor[:,:,j,:] += new_mat - - new_tensor /= self.QE_nsym - - # Convert back into cartesian - for i in range(self.QE_nat): - rt_reshaped[:, :, i, :] = Methods.convert_3tensor_to_cryst(new_tensor[:,:,i,:], self.QE_at.T, True) - - # Compress again the notation - raman_tensor[:,:,:] = rt_reshaped.reshape((3,3, 3*self.QE_nat)) - - - def ApplySymmetryToSecondOrderEffCharge(self, dM_drdr, apply_asr = True): - """ - SYMMETRIZE TWO PHONON EFFECTIVE CHARGES - ======================================= - - This subroutine applies simmetries to the two phonon - effective charges. - - Note, to symmetrize this tensor, symmetries must be imposed - on the supercell. - - Parameters - ---------- - dM_drdr : ndarray (size = (3 nat_sc, 3 nat_sc, 3)) - The derivative of effective charges. - The last index refers to electric field - apply_asr : bool - If True the sum rule is applied. - The sum rule is the 'custom' one where translations are projected - out from the space for each polarization components. - """ - - nat3, nat3_, cart = np.shape(dM_drdr) - - assert nat3 == nat3_, "Error on the shape of the argument" - assert nat3 == 3 * self.QE_nat, "Wrong number of atoms (Symmetries must be setup in the supercell)" - assert cart == 3 - - nat = int(nat3 / 3) - - dM_drdr += np.einsum("abc -> bac", dM_drdr) - dM_drdr /= 2 - - # Apply the Sum Rule - if apply_asr: - for pol in range(3): - CustomASR(dM_drdr[:,:,pol]) - - # Convert in crystal coordinates MAKE SURE THAT THIS IS CONVERTED BACK IN CARTESIAN COORDINATES - for i in range(nat): - for j in range(nat): - dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T) - - - # Apply translations - new_dM = np.zeros(np.shape(dM_drdr), dtype = np.double) - if self.QE_translation_nr > 1: - for i in range(self.QE_translation_nr): - irt = self.QE_translations_irt[:, i] - 1 - for jat in range(nat): - for kat in range(nat): - new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] - new_dM[3*jat: 3*jat+3, 3*kat:3*kat+3, :] += new_mat - - dM_drdr[:,:,:] = new_dM / self.QE_translation_nr - new_dM[:,:,:] = 0 - - # # DEBUG VARIABLE - # debug = np.zeros(np.shape(new_dM)) - - # Apply rotations - for i in range(self.QE_nsym): - irt = self.QE_irt[i, :] - 1 - for jat in range(nat): - for kat in range(nat): - new_mat = dM_drdr[3*irt[jat]: 3*irt[jat]+3, 3*irt[kat]:3*irt[kat] + 3,:] - # Apply the symmetries - - new_mat = np.einsum("ck, ijk -> ijc", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("bj, ijc -> ibc", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("ai, ibc -> abc", self.QE_s[:,:,i], new_mat) - new_dM[3*jat:3*jat+3, 3*kat:3*kat+3,:] += new_mat - - dM_drdr[:,:,:] = new_dM / self.QE_nsym - - # # CONVERT IN CARTESIAN COORDINATES TO DEBUG - # for _i_ in range(nat): - # for _j_ in range(nat): - # debug[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :] = Methods.convert_3tensor_to_cryst(new_dM[3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3, :],\ - # self.QE_at.T, cryst_to_cart = True) - # np.save('CC_new_{}'.format(i), debug) - - - # Convert in cartesian coordinates - for i in range(nat): - for j in range(nat): - dM_drdr[3*i : 3*i + 3, 3*j: 3*j+3, :] = Methods.convert_3tensor_to_cryst(dM_drdr[3*i:3*i+3, 3*j:3*j+3,:], self.QE_at.T, True) - - - - def ApplySymmetryToSecondOrderRamanTensor(self, dalpha_drdr, apply_asr = True): - """ - SYMMETRIZE TWO PHONON EFFECTIVE CHARGES - ======================================= - - This subroutine applies simmetries to the second order Raman tensor. - - Note, to symmetrize this tensor, symmetries must be imposed on the supercell. - - Parameters - ---------- - dalpha_drdr : ndarray (size = (3, 3, 3 nat_sc, 3 nat_sc)) - The second derivative of polarizability. - apply_asr : bool - If True the sum rule is applied. - The sum rule is the 'custom' one where translations are projected - out from the space for each polarization components. - """ - # Check the shape of the tensor - E1, E2, nat3, nat3_ = np.shape(dalpha_drdr) - - assert nat3 == nat3_, "Error on the shape of the argument for the atomic indices" - assert nat3 == 3 * self.QE_nat, "Wrong number of atoms (Symmetries must be setup in the supercell)" - assert E1 == E2, "Error on the shape of the argument electric field" - assert E1 == 3, "The first two entries are assosciated with electric field" - - # Get the number of atoms in the supercell - nat = int(nat3 /3) - - # Apply hermitianity on the atomic indices - dalpha_drdr += np.einsum("abcd->abdc", dalpha_drdr) - dalpha_drdr /= 2 - - # Apply hermitianity on the electric field - dalpha_drdr += np.einsum("abcd->bacd", dalpha_drdr) - dalpha_drdr /= 2 - - # SUM RULE ranging on the electric field components - if apply_asr: - for pol1 in range(3): - for pol2 in range(3): - CustomASR(dalpha_drdr[pol1, pol2, :, :]) - - # CONVERT TO CRYSTAL COORDINATES - for i in range(nat): - for j in range(nat): - dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j + 3], self.QE_at.T) - - - # Get a ZERO second order Raman tensor (AUXILIARY) - new_dalpha_drdr = np.zeros(np.shape(dalpha_drdr), dtype = np.double) - - # TRANSLATIONS - if self.QE_translation_nr > 1: - for i in range(self.QE_translation_nr): - # irt[at1] is the atom on which the translation i maps at1 - irt = self.QE_translations_irt[:, i] - 1 - for at1 in range(nat): - for at2 in range(nat): - # Get the part of the tensor that is equivalent by translations of atom at1 at2 - new_mat = dalpha_drdr[:, :, 3*irt[at1]: 3*irt[at1] + 3, 3 * irt[at2]: 3*irt[at2] + 3] - # Fill with the symmetric counterparts - new_dalpha_drdr[:, :, 3*at1: 3*at1+3, 3*at2:3*at2+3] += new_mat - - # OVERWRITE the second order Raman tensor - dalpha_drdr[:,:,:,:] = new_dalpha_drdr / self.QE_translation_nr - # SET TO ZERO THE AUXILIARY VARIABLE - new_dalpha_drdr[:,:,:,:] = 0 - - # # DEBUG VARIABLE - # debug = np.zeros(np.shape(new_dalpha_drdr)) - - # ROTATIONS - for i in range(self.QE_nsym): - # the symmetry applied on irt[at] gives the atom at - irt = self.QE_irt[i, :] - 1 - - for at1 in range(nat): - for at2 in range(nat): - # Get the part of the tensor that is equivalent by rotations of atom at1 at2 - # This has shape = (3, 3, 3, 3) - new_mat = dalpha_drdr[:, :, 3*irt[at1] : 3*irt[at1] + 3, 3*irt[at2] : 3*irt[at2] + 3] - - # Apply the symmetries - new_mat = np.einsum("dl, ijkl -> ijkd", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("ck, ijkd -> ijcd", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("bj, ijcd -> ibcd", self.QE_s[:,:,i], new_mat) - new_mat = np.einsum("ai, ibcd -> abcd", self.QE_s[:,:,i], new_mat) - - new_dalpha_drdr[:, :, 3*at1: 3*at1 + 3, 3*at2: 3*at2 + 3] += new_mat - - # # CONVERT IN CARTESIAN COORDINATES TO DEBUG - # for _i_ in range(nat): - # for _j_ in range(nat): - # debug[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3] = Methods.convert_4tensor_to_cryst(new_dalpha_drdr[:, :, 3*_i_ : 3*_i_ + 3, 3*_j_ : 3*_j_ + 3],\ - # self.QE_at.T, cryst_to_cart = True) - # np.save('CC_new_{}'.format(i), debug) - - # OVERWRITE THE second order Raman tensor - dalpha_drdr[:,:,:,:] = new_dalpha_drdr /self.QE_nsym - - - # Convert BACK in crystal coordinates - for i in range(nat): - for j in range(nat): - dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j: 3*j+3] = Methods.convert_4tensor_to_cryst(dalpha_drdr[:, :, 3*i : 3*i + 3, 3*j : 3*j + 3],\ - self.QE_at.T, cryst_to_cart = True) - - # # TO DEBUG - # np.save('CC_raman', dalpha_drdr) - - return - - - - - - def ApplySymmetryToTensor4(self, v4, initialize_symmetries = True): - """ - SYMMETRIZE A RANK-4 TENSOR - ========================== - - This subroutines uses the current symmetries to symmetrize - a rank-4 tensor. - This tensor must be in the supercell space. - - The v4 argument will be overwritten. - - NOTE: The symmetries must be initialized in the supercell using spglib - - - Parameters - ---------- - v4 : ndarray( size=(3*nat, 3*nat, 3*nat, 3*nat), dtype = np.double, order = "F") - The 4-rank tensor to be symmetrized. - It will be overwritten with the new symmetric one. - It is suggested to specify the order of the array to "F", as this will prevent - the parser to copy the matrix when doing the symmetrization in Fortran. - initialize_symmetries : bool - If True the symmetries will be initialized using spglib. Otherwise - the already present symmetries will be use. Use it False at your own risk! - (It can crash with seg fault if symmetries are not properly initialized) - """ - if initialize_symmetries: - self.SetupFromSPGLIB() - - # Apply the permutation symmetry - symph.permute_v4(v4) - - # Apply the translational symmetries - symph.trans_v4(v4, self.QE_translations_irt) - - # Apply all the symmetries at gamma - symph.sym_v4(v4, self.QE_at, self.QE_s, self.QE_irt, self.QE_nsymq) - - def ApplyQStar(self, fcq, q_point_group): - """ - APPLY THE Q STAR SYMMETRY - ========================= - - Given the fc matrix at each q in the star, it applies the symmetries in between them. - - Parameters - ---------- - - fcq : ndarray(nq, 3xnat, 3xnat) - The dynamical matrices for each q point in the star - - q_point_group : ndarray(nq, 3) - The q vectors that belongs to the same star - """ - - nq = np.shape(q_point_group)[0] - final_fc = np.zeros(np.shape(fcq), dtype = np.complex128) - - # Setup all the symmetries - self.SetupQPoint() - - new_dyn = np.zeros( (3 * self.QE_nat, 3*self.QE_nat), dtype = np.complex128, order = "F") - - dyn_star = np.zeros( (nq, 3, 3, self.QE_nat, self.QE_nat), dtype = np.complex128, order = "F") - - for i in range(nq): - # Get the q points order - nq_new, sxq, isq, imq = symph.star_q(q_point_group[i,:], self.QE_at, self.QE_bg, - self.QE_nsymq, self.QE_s, self.QE_invs, 0) - - - #print "Found nq:", nq_new - #print "IMQ?", imq - - # Check if the q star is correct - if nq_new != nq and imq != 0: - print ("Reciprocal lattice vectors:") - print (self.QE_bg.transpose() ) - print ("Passed q star:") - print (q_point_group) - print ("QE q star:") - print (sxq[:, :nq_new].transpose()) - raise ValueError("Error, the passed q star does not match the one computed by QE") -# -# # Print the star -# print "q point:", q_point_group[i,:] -# print "Point in the stars:", nq_new -# print "Star of q:" -# print sxq[:, :nq_new].transpose() -# -# print "NEW_DYN:", np.shape(new_dyn) -# print "AT:", np.shape(self.QE_at) -# print "BG:", np.shape(self.QE_bg) -# print "N SYM:", self.QE_nsymq -# print "S:", np.shape(self.QE_s) -# print "QE_INVS:", np.shape(self.QE_invs) -# print "IRT:", np.shape(self.QE_irt) -# print "RTAU:", np.shape(self.QE_rtau) -# print "NQ_NEW:", nq_new -# print "SXQ:", np.shape(sxq) -# print "ISQ:", np.shape(isq) -# print "IMQ:", imq -# print "NAT:", self.QE_nat - - new_dyn[:,:] = fcq[i,:,:] - #print "new dyn ready" - - # Get the new matrix - dyn_star = symph.q2qstar_out(new_dyn, self.QE_at, self.QE_bg, self.QE_nsymq, - self.QE_s, self.QE_invs, self.QE_irt, self.QE_rtau, - nq_new, sxq, isq, imq, nq, self.QE_nat) - #print "Fake" - - #print "XQ:", q_point_group[i, :], "NQ_NEW:", nq_new - - # Now to perform the match bring the star in the same BZ as the q point - # This facilitate the comparison between q points - current_q = q_point_group.copy() - #print "Fake2" -# for xq in range(nq): -# tmp = Methods.put_into_cell(self.QE_bg, sxq[:, xq]) -# sxq[:, xq] = tmp -# current_q[xq,:] = Methods.put_into_cell(self.QE_bg, current_q [xq,:]) -# - # Print the order of the q star - sorting_q = np.arange(nq) - for xq in range(nq): - count = 0 # Debug (avoid no or more than one identification) - for yq in range(nq): - real_y = yq - dot_f = 1 - if imq == 0 and yq >= nq_new: - real_y -= nq_new - dot_f = -1 - if Methods.get_min_dist_into_cell(self.QE_bg.transpose(), dot_f* sxq[:, real_y], current_q[xq,:]) < __EPSILON__: - sorting_q[xq] = yq - count += 1 - - if count != 1: - print ("Original star:") - print (q_point_group) - print ("Reshaped star:") - print (current_q) - print ("Reciprocal lattice vectors:") - print (self.QE_bg.transpose() ) - print ("STAR:") - print (sxq[:, :nq_new].transpose() ) - pta = (current_q[xq,:]) - print ("Distances of xq in the QE star:") - for yq in range(nq_new): - print ("%.4f %.4f %.4f => " % (sxq[0, yq], sxq[1, yq], sxq[2, yq]), Methods.get_min_dist_into_cell(self.QE_bg.transpose(), sxq[:, yq], current_q[xq,:])) - raise ValueError("Error, the vector (%.3f, %.3f, %.3f) has %d identification in the star" % (pta[0], pta[1], pta[2], - count)) - #print "Sorting array:" - #print sorting_q - - - # Copy the matrix in the new one - for xq in range(nq): - for xat in range(self.QE_nat): - for yat in range(self.QE_nat): - final_fc[xq, 3*xat: 3*xat + 3, 3*yat : 3*yat + 3] += dyn_star[sorting_q[xq], :,:, xat, yat] - - - # Now divide the matrix per the xq value - final_fc /= nq - - # Overwrite the matrix - fcq[:,:,:] = final_fc - - def ApplySymmetryToMatrix(self, matrix, err = None): - """ - Apply the symmetries to the 3x3 matrix. - It can be a stress tensor, a dielectric tensor and so on. - - Parameters - ---------- - matrix : a 3x3 matrix - The matrix to which you want to apply the symmetrization. - The matrix is overwritten with the output. - """ - - # Setup the symmetries in the Gamma point - #self.SetupQPoint() - - # Perform the symmetrization - mat_f = np.array(matrix, order = "F", dtype = np.float64) - - symph.symmatrix(mat_f, self.QE_s, self.QE_nsymq, self.QE_at, self.QE_bg) - - # To compute the error we count which element - # of the stress tensor are summed togheter to obtain any element. - # Then we propagate the error only on these. - if err is not None: - err_new = err.copy() - for i in range(3): - for j in range(3): - work = np.zeros( (3,3), dtype = np.float64, order = "F") - work[i,j] = np.float64(1) - - # Apply the symmetry - symph.symmatrix(work, self.QE_s, self.QE_nsymq, self.QE_at, self.QE_bg) - mask = (np.abs(work) > __EPSILON__) - naverage = np.sum( mask.astype(int)) - - if naverage == 0: - err_new[i,j] = 0 - else: - err_new[i,j] = np.sqrt(np.sum( err[mask]**2)) / naverage - err[:,:] = err_new - matrix[:,:] = mat_f - - - - def SymmetrizeFCQ(self, fcq, q_stars, verbose = False, asr = "simple"): - """ - Use the current structure to impose symmetries on a complete dynamical matrix - in q space. Also the simple sum rule at Gamma is imposed - - Parameters - ---------- - - fcq : ndarray(nq, 3xnat, 3xnat) - The q space force constant matrix to be symmetrized (it will be overwritten) - - q_stars : list of list of q points - The list of q points divided by stars, the fcq must follow the order - of the q points in the q_stars array - """ - - nqirr = len(q_stars) - nq = np.sum([len(x) for x in q_stars]) - - # Get the q_points vector - q_points = np.zeros( (nq, 3), dtype = np.float64) - sigma = 0 - for i in range(nqirr): - for q_vec in q_stars[i]: - q_points[sigma, :] = q_vec - sigma += 1 - - if nq != np.shape(fcq)[0]: - raise ValueError("Error, the force constant number of q point %d does not match with the %d given q_points" % (np.shape(fcq)[0], nq)) - - - for iq in range(nq): - # Prepare the symmetrization - if verbose: - print ("Symmetries in q = ", q_points[iq, :]) - t1 = time.time() - self.SetupQPoint(q_points[iq,:], verbose) - t2 = time.time() - if verbose: - print (" [SYMMETRIZEFCQ] Time to setup the q point %d" % iq, t2-t1, "s") - - # Proceed with the sum rule if we are at Gamma - - if asr == "simple" or asr == "custom": - if np.sqrt(np.sum(q_points[iq,:]**2)) < __EPSILON__: - if verbose: - print ("q_point:", q_points[iq,:]) - print ("Applying sum rule") - self.ImposeSumRule(fcq[iq,:,:], asr) - elif asr == "crystal": - self.ImposeSumRule(fcq[iq, :,:], asr = asr) - elif asr == "no": - pass - else: - raise ValueError("Error, only 'simple', 'crystal', 'custom' or 'no' asr are supported, given %s" % asr) - - t1 = time.time() - if verbose: - print (" [SYMMETRIZEFCQ] Time to apply the sum rule:", t1-t2, "s") - - # # Symmetrize the matrix - if verbose: - old_fcq = fcq[iq, :,:].copy() - w_old = np.linalg.eigvals(fcq[iq, :, :]) - print ("FREQ BEFORE SYM:", w_old ) - self.SymmetrizeDynQ(fcq[iq, :,:], q_points[iq,:]) - t2 = time.time() - if verbose: - print (" [SYMMETRIZEFCQ] Time to symmetrize the %d dynamical matrix:" % iq, t2 -t1, "s" ) - print (" [SYMMETRIZEFCQ] Difference before the symmetrization:", np.sqrt(np.sum(np.abs(old_fcq - fcq[iq, :,:])**2))) - w_new = np.linalg.eigvals(fcq[iq, :, :]) - print ("FREQ AFTER SYM:", w_new) - - # For each star perform the symmetrization over that star - q0_index = 0 - for i in range(nqirr): - q_len = len(q_stars[i]) - t1 = time.time() - if verbose: - print ("Applying the q star symmetrization on:") - print (np.array(q_stars[i])) - self.ApplyQStar(fcq[q0_index : q0_index + q_len, :,:], np.array(q_stars[i])) - t2 = time.time() - if verbose: - print (" [SYMMETRIZEFCQ] Time to apply the star q_irr = %d:" % i, t2 - t1, "s") - q0_index += q_len - - - def ChangeThreshold(self, threshold): - """ - Change the symmetry threshold sensibility - """ - self.threshold = np.float64(threshold) - symph.symm_base.set_accep_threshold(self.threshold) - - - def ImposeSumRule(self, force_constant, asr = "simple", axis = 1, zeu = None): - """ - QE SUM RULE - =========== - - This subroutine imposes on the given force constant matrix the acustic sum rule - - Parameters - ---------- - force_constnat : 3xnat , 3xnat - The force constant matrix, it is overwritten with the new one - after the sum rule has been applied. - asr : string, optional, default = 'custom' - One of 'custom', 'simple', 'crystal', 'one-dim' or 'zero-dim'. For a detailed - explanation look at the Quantum ESPRESSO documentation. - The custom one, default, is implemented in python as CustomASR. - No ASR is imposed on the effective charges in this case. - axis : int, optional - If asr = 'one-dim' you must set the rotational axis: 1 for x, 2 for - y and 3 for z. Ohterwise it is unused. - zeu : ndarray (N_atoms, 3, 3), optional - If different from None, it is the effective charge array. - As the force_constant, it is updated. - - """ - - QE_fc = np.zeros( (3, 3, self.QE_nat, self.QE_nat), order ="F", dtype = np.complex128) - - # Fill the effective charges if required - if zeu is not None: - # Convert in the correct indexing and use the fortran order - f_zeu = np.einsum("ijk -> kji", zeu, order = "F", dtype = np.float64) - else: - f_zeu = np.zeros( (3, 3, self.QE_nat), order = "F", dtype = np.float64) - - # Prepare the force constant - if asr != "custom": - for na in range(self.QE_nat): - for nb in range(self.QE_nat): - QE_fc[:, :, na, nb] = force_constant[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] - # -# print "ASR:", asr -# print "AXIS:", axis -# print "NAT:", self.QE_nat -# print "TAU SHAPE:", np.shape(self.QE_tau) -# print "QE_FC SHAPE:", np.shape(self.QE_fc) - - - symph.set_asr(asr, axis, self.QE_tau, QE_fc, f_zeu) - - # Copy the new value on output - for na in range(self.QE_nat): - if zeu is not None: - zeu[na, :,:] = f_zeu[:,:, na] - - for nb in range(self.QE_nat): - force_constant[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] = QE_fc[:,:, na, nb] - else: - CustomASR(force_constant) - - - - - def SetupQPoint(self, q_point = np.zeros(3), verbose = False): - """ - Get symmetries of the small group of q - - Setup the symmetries in the small group of Q. - - Parameters - ---------- - q_point : ndarray - The q vector in reciprocal space (NOT in crystal axes) - verbose : bool - If true the number of symmetries found for the bravais lattice, - the crystal and the small group of q are written in stdout - """ - # Convert the q point in Fortran - if len(q_point) != 3: - raise ValueError("Error, the q point must be a 3d vector") - - aq = np.zeros(3, dtype = np.float64) - aq[:] = Methods.covariant_coordinates(self.QE_bg.transpose(), q_point) - - # Setup the bravais lattice - symph.symm_base.set_at_bg(self.QE_at, self.QE_bg) - - # Prepare the symmetries - symph.symm_base.set_sym_bl() - - if verbose: - print ("Symmetries of the bravais lattice:", symph.symm_base.nrot) - - - # Now copy all the work initialized on the symmetries inside python - self.QE_s = np.copy(symph.symm_base.s) - self.QE_ft = np.copy(symph.symm_base.ft) - self.QE_nsym = symph.symm_base.nrot - - # Prepare a dummy variable for magnetic spin - m_loc = np.zeros( (3, self.QE_nat), dtype = np.float64, order = "F") - - # Find the symmetries of the crystal - #print "TAU:", np.shape(self.QE_tau) - symph.symm_base.find_sym(self.QE_tau, self.QE_ityp, 6, 6, 6, False, m_loc) - #print "IRT NOW:", np.shape(symph.symm_base.irt) - - if verbose: - print ("Symmetries of the crystal:", symph.symm_base.nsym) - - - - # Now copy all the work initialized on the symmetries inside python - self.QE_s = np.copy(symph.symm_base.s) - self.QE_ft = np.copy(symph.symm_base.ft) - - - # Prepare the symmetries of the small group of q - syms = np.zeros( (48), dtype = np.intc) - - # Initialize to true the symmetry of the crystal - syms[:symph.symm_base.nsym] = np.intc(1) - - self.QE_minus_q = symph.symm_base.smallg_q(aq, 0, syms) - self.QE_nsymq = symph.symm_base.copy_sym(symph.symm_base.nsym, syms) - self.QE_nsym = symph.symm_base.nsym - - - # Recompute the inverses - symph.symm_base.inverse_s() - - if verbose: - print ("Symmetries of the small group of q:", self.QE_nsymq) - - # Assign symmetries - self.QE_s = np.copy(symph.symm_base.s) - self.QE_invs = np.copy(symph.symm_base.invs) - self.QE_ft = np.copy(symph.symm_base.ft) - self.QE_irt = np.copy(symph.symm_base.irt) - - #print np.shape(self.QE_irt) - - # Compute the additional shift caused by fractional translations - self.QE_rtau = symph.sgam_ph_new(self.QE_at, self.QE_bg, symph.symm_base.nsym, self.QE_s, - self.QE_irt, self.QE_tau, self.QE_nat) - - lgamma = 0 - if np.sqrt(np.sum(q_point**2)) > 0.0001: - lgamma = 1 - -# self.QE_irotmq = symph.set_irotmq(q_point, self.QE_s, self.QE_nsymq, -# self.QE_nsym, self.QE_minus_q, -# self.QE_bg, self.QE_at, lgamma) - # If minus q check which is the symmetry -# - #syms = self.GetSymmetries() - self.QE_irotmq = 0 - if self.QE_minus_q: - # Fix in the Same BZ - #aq = aq - np.floor(aq) - - - #print "VECTOR AQ:", aq - - # Get the first symmetry: - for k in range(self.QE_nsym): - # Skip the identity - #if k == 0: - # continue - - # Position feels the symmetries with S (fortran S is transposed) - # While q vector feels the symmetries with S^t (so no .T required for fortran matrix) - new_q = self.QE_s[:,:, k].dot(aq) - # Compare new_q with aq - dmin = Methods.get_min_dist_into_cell(np.eye(3), -new_q, aq) - #print "Applying %d sym we transform " % (k+1), aq, "into", new_q, "dmin:", dmin - #print "Vector in cart: ", q_point, "We used symmetry:" - #print self.QE_s[:, :, k] - #print "" - #dmin = np.sqrt(np.sum( ((new_q + aq) % 1)**2)) -# -# print "Symmetry number ", k+1 -# print sym[:, :3] -# print "q cryst:", aq -# print "new_q_cryst:", new_q -# - #print "SYM NUMBER %d, NEWQ:" % (k+1), new_q - #print "Distance:", dmin - if dmin < __EPSILON__: - #print "CORRECT FOR IROTMQ" - self.QE_irotmq = k + 1 - break - if self.QE_irotmq == 0: - print ("Error, the fortran code tells me there is S so that Sq = -q + G") - print ("But I did not find such a symmetry!") - raise ValueError("Error in the symmetrization. See stdout") - - def SetupFromSPGLIB(self): - """ - USE SPGLIB TO SETUP THE SYMMETRIZATION - ====================================== - - This function uses spglib to find symmetries, recognize the supercell - and setup all the variables to perform the symmetrization inside the supercell. - - NOTE: If spglib cannot be imported, an ImportError will be raised - """ - if not __SPGLIB__: - raise ImportError("Error, this function works only if spglib is available") - - # Get the symmetries - spg_syms = spglib.get_symmetry(self.structure.get_ase_atoms(), symprec = self.threshold) - symmetries = GetSymmetriesFromSPGLIB(spg_syms, regolarize= False) - - trans_irt = 0 - self.QE_s[:,:,:] = 0 - - - # Check how many point group symmetries do we have - n_syms = 0 - for i, sym in enumerate(symmetries): - # Extract the rotation and the fractional translation - rot = sym[:,:3] - - # Check if the rotation is equal to the first one - if np.sum( (rot - symmetries[0][:,:3])**2 ) < 0.1 and n_syms == 0 and i > 0: - # We got all the rotations - n_syms = i - break - - # Extract the point group - if n_syms == 0: - self.QE_s[:,:, i] = rot.T - - # Get the IRT (Atoms mapping using symmetries) - irt = GetIRT(self.structure, sym) - self.QE_irt[i, :] = irt + 1 #Py to Fort - - - if n_syms == 0: - n_syms = len(symmetries) - - # From the point group symmetries, get the supercell - n_supercell = len(symmetries) // n_syms - self.QE_translation_nr = n_supercell - self.QE_nsymq = n_syms - self.QE_nsym = n_syms - - self.QE_translations_irt = np.zeros( (self.structure.N_atoms, n_supercell), dtype = np.intc, order = "F") - self.QE_translations = np.zeros( (3, n_supercell), dtype = np.double, order = "F") - - # Now extract the translations - for i in range(n_supercell): - sym = symmetries[i * n_syms] - # Check if the symmetries are correctly setup - - I = np.eye(3) - ERROR_MSG=""" - Error, symmetries are not correctly ordered. - They must always start with the identity. - - N_syms = {}; N = {}; SYM = {} - """.format(n_syms,i*n_syms, sym) - assert np.sum( (I - sym[:,:3])**2) < 0.5, ERROR_MSG - - # Get the irt for the translation (and the translation) - irt = GetIRT(self.structure, sym) - self.QE_translations_irt[:, i] = irt + 1 - self.QE_translations[:, i] = sym[:,3] - - # For each symmetry operation, assign the inverse - self.QE_invs[:] = get_invs(self.QE_s, self.QE_nsym) - - - - def ApplyTranslationsToVector(self, vector): - """ - This subroutine applies the translations to the given vector. - To be used only if the structure is a supercell structure - and the symmetries have been initialized with SPGLIB - - Parameters - ---------- - vector : size (nat, 3) - A vector that must be symmetrized. It will be overwritten. - """ - - nat = self.QE_nat - - assert vector.shape[0] == nat - assert vector.shape[1] == 3 - - # Ignore if no translations are presents - if self.QE_translation_nr <= 1: - return - - sum_all = np.zeros((nat, 3), dtype = type(vector[0,0])) - - for i in range(self.QE_translation_nr): - n_supercell = np.shape(self.QE_translations_irt)[1] - - sum_all += vector[self.QE_translations_irt[:, i] - 1, :] - sum_all /= self.QE_translation_nr - vector[:,:] = sum_all - - - - - def InitFromSymmetries(self, symmetries, q_point = np.array([0,0,0])): - """ - This function initialize the QE symmetries from the symmetries expressed in the - Cellconstructor format, i.e. a list of numpy array 3x4 where the last column is - the fractional translation. - - TODO: add the q_point preparation by limitng the symmetries only to - those that satisfies the specified q_point - """ - - nsym = len(symmetries) - - self.QE_nsymq = np.intc(nsym) - self.QE_nsym = self.QE_nsymq - - - for i, sym in enumerate(symmetries): - self.QE_s[:,:, i] = np.transpose(sym[:, :3]) - - # Get the atoms correspondence - eq_atoms = GetIRT(self.structure, sym) - - self.QE_irt[i, :] = eq_atoms + 1 - - # Get the inverse symmetry - inv_sym = np.linalg.inv(sym[:, :3]) - for k, other_sym in enumerate(symmetries): - if np.sum( (inv_sym - other_sym[:, :3])**2) < __EPSILON__: - break - - self.QE_invs[i] = k + 1 - - # Setup the position after the symmetry application - for k in range(self.QE_nat): - self.QE_rtau[:, i, k] = self.structure.coords[eq_atoms[k], :].astype(np.float64) - - - # Get the reciprocal lattice vectors - b_vectors = self.structure.get_reciprocal_vectors() - - # Get the minus_q operation - self.QE_minusq = False - - # NOTE: HERE THERE COULD BE A BUG - - # q != -q - # Get the q vectors in crystal coordinates - q = Methods.covariant_coordinates(b_vectors, q_point) - for k, sym in enumerate(self.QE_s): - new_q = self.QE_s[:,:, k].dot(q) - if np.sum( (Methods.put_into_cell(b_vectors, -q_point) - new_q)**2) < __EPSILON__: - self.QE_minus_q = True - self.QE_irotmq = k + 1 - break - - def GetSymmetries(self, get_irt=False): - """ - GET SYMMETRIES FROM QE - ====================== - - This method returns the symmetries in the CellConstructor format from - the ones elaborated here. - - - Parameters - ---------- - get_irt : bool - If true (default false) also the irt are returned. - They are the corrispondance between atoms for each symmetry operation. - Results - ------- - list : - List of 3x4 ndarray representing all the symmetry operations - irt : ndarray(size=(nsym, nat), dtype = int), optional - Returned only if get_irt = True. - It is the corrispondance between atoms after the symmetry operation is applied. - irt[x, y] is the atom mapped into y by the x symmetry. - """ - - syms = [] - for i in range(self.QE_nsym): - s_rot = np.zeros( (3, 4)) - s_rot[:, :3] = np.transpose(self.QE_s[:, :, i]) - s_rot[:, 3] = self.QE_ft[:, i] - - syms.append(s_rot) - - if not get_irt: - return syms - return syms, self.QE_irt[:self.QE_nsym, :].copy() - 1 - - - - - def SymmetrizeVector(self, vector): - """ - SYMMETRIZE A VECTOR - =================== - - This is the easier symmetrization of a generic vector. - Note, fractional translation and generic translations are not imposed. - This is because this simmetrization acts on displacements and forces. - - Parameters - ---------- - vector : ndarray(natoms, 3) - This is the vector to be symmetrized, it will be overwritten - with the symmetrized version - """ - - # Apply Translations if any - self.ApplyTranslationsToVector(vector) - - # Prepare the real vector - tmp_vector = np.zeros( (3, self.QE_nat), dtype = np.float64, order = "F") - - for i in range(self.QE_nat): - tmp_vector[0, i] = vector[i,0] - tmp_vector[1, i] = vector[i,1] - tmp_vector[2,i] = vector[i,2] - - symph.symvector(self.QE_nsymq, self.QE_irt, self.QE_s, self.QE_at, self.QE_bg, - tmp_vector, self.QE_nat) - - - for i in range(self.QE_nat): - vector[i, :] = tmp_vector[:,i] - - - def SymmetrizeDynQ(self, dyn_matrix, q_point): - """ - DYNAMICAL MATRIX SYMMETRIZATION - =============================== - - Use the Quantum ESPRESSO fortran code to symmetrize the dynamical matrix - at the given q point. - - NOTE: the symmetries must be already initialized. - - Parameters - ---------- - dyn_matrix : ndarray (3nat x 3nat) - The dynamical matrix associated to the specific q point (cartesian coordinates) - q_point : ndarray 3 - The q point related to the dyn_matrix. - - The input dynamical matrix will be modified by the current code. - """ - - # TODO: implement hermitianity to speedup the conversion - - #Prepare the array to be passed to the fortran code - QE_dyn = np.zeros( (3, 3, self.QE_nat, self.QE_nat), dtype = np.complex128, order = "F") - - # Get the crystal coordinates for the matrix - for na in range(self.QE_nat): - for nb in range(self.QE_nat): - fc = dyn_matrix[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] - QE_dyn[:, :, na, nb] = Methods.convert_matrix_cart_cryst(fc, self.structure.unit_cell, False) - - # Prepare the xq variable - #xq = np.ones(3, dtype = np.float64) - xq = np.array(q_point, dtype = np.float64) - # print "XQ:", xq - # print "XQ_CRYST:", Methods.covariant_coordinates(self.QE_bg.T, xq) - # print "NSYMQ:", self.QE_nsymq, "NSYM:", self.QE_nsym - # print "QE SYM:" - # print np.einsum("abc->cba", self.QE_s[:, :, :self.QE_nsymq]) - # print "Other syms:" - # print np.einsum("abc->cba", self.QE_s[:, :, self.QE_nsymq: self.QE_nsym]) - # print "QE INVS:" - # print self.QE_invs[:self.QE_nsymq] - # #print "QE RTAU:" - # #print np.einsum("abc->bca", self.QE_rtau[:, :self.QE_nsymq, :]) - # print "IROTMQ:", self.QE_irotmq - # print "MINUS Q:", self.QE_minus_q - # print "IRT:" - # print self.QE_irt[:self.QE_nsymq, :] - # print "NAT:", self.QE_nat - - # Inibhit minus q - #self.QE_minus_q = 0 - - - # USE THE QE library to perform the symmetrization - symph.symdynph_gq_new( xq, QE_dyn, self.QE_s, self.QE_invs, self.QE_rtau, - self.QE_irt, self.QE_irotmq, self.QE_minus_q, self.QE_nsymq, self.QE_nat) - - # Return to cartesian coordinates - for na in range(self.QE_nat): - for nb in range(self.QE_nat): - fc = QE_dyn[:, :, na, nb] - dyn_matrix[3 * na : 3* na + 3, 3*nb: 3 * nb + 3] = Methods.convert_matrix_cart_cryst(fc, self.structure.unit_cell, True) - - def GetQStar(self, q_vector): - """ - GET THE Q STAR - ============== - - Given a vector in q space, get the whole star. - We use the quantum espresso subrouitine. - - Parameters - ---------- - q_vector : ndarray(size= 3, dtype = np.float64) - The q vector - - Results - ------- - q_star : ndarray(size = (nq_star, 3), dtype = np.float64) - The complete q star - """ - self.SetupQPoint() - nq_new, sxq, isq, imq = symph.star_q(q_vector, self.QE_at, self.QE_bg, - self.QE_nsymq, self.QE_s, self.QE_invs, 0) - - #print ("STAR IMQ:", imq) - if imq != 0: - total_star = np.zeros( (nq_new, 3), dtype = np.float64) - else: - total_star = np.zeros( (2*nq_new, 3), dtype = np.float64) - - total_star[:nq_new, :] = sxq[:, :nq_new].transpose() - - if imq == 0: - total_star[nq_new:, :] = -sxq[:, :nq_new].transpose() - - return total_star - - def SelectIrreducibleQ(self, q_vectors): - """ - GET ONLY THE IRREDUCIBLE Q POINTS - ================================= - - This methods selects only the irreducible q points - given a list of total q points for the structure. - - Parameters - ---------- - q_vectors : list of q points - The list of q points to be polished fromt he irreducible - - Results - ------- - q_irr : list of q points - The q_vectors without the copies by symmetry of the dynamical matrix. - """ - - qs = np.array(q_vectors) - nq = np.shape(qs)[0] - - q_irr = [qs[x, :].copy() for x in range(nq)] - for i in range(nq): - if i >= len(q_irr): - break - - q_stars = self.GetQStar(q_irr[i]) - n_star = np.shape(q_stars)[0] - - # Look if the list contains point in the star - for j in range(n_star): - q_in_star = q_stars[j,:] - # Go reverse, in this way if we pop an element we do not have to worry about indices - for k in range(len(q_irr)-1, i, -1): - if Methods.get_min_dist_into_cell(self.QE_bg.transpose(), q_in_star, q_irr[k]) < __EPSILON__: - q_irr.pop(k) # Delete the k element - - return q_irr - - def GetQIrr(self, supercell): - """ - GET THE LIST OF IRREDUCIBLE Q POINTS - ==================================== - - This method returns a list of irreducible q points given the supercell size. - - Parameters - ---------- - supercell : (X, Y, Z) where XYZ are int - The supercell size along each unit cell vector. - - Returns - ------- - q_irr_list : list of q vectors - The list of irreducible q points in the brilluin zone. - """ - - # Get all the q points - q_points = GetQGrid(self.QE_at.T, supercell) - - # Delete the irreducible ones - q_irr = self.SelectIrreducibleQ(q_points) - - return q_irr - - def ApplySymmetriesToV2(self, v2, apply_translations = True): - """ - APPLY THE SYMMETRIES TO A 2-RANK TENSOR - ======================================= - - This subroutines applies the symmetries to a 2-rank - tensor. Usefull to work with supercells. - - Parameters - ---------- - v2 : ndarray (size = (3*nat, 3*nat), dtype = np.double) - The 2-rank tensor to be symmetrized. - It is directly modified - apply_translation : bool - If false pure translations are neglected. - """ - - # Apply the Permutation symmetry - v2[:,:] = 0.5 * (v2 + v2.T) - - # First lets recall that the fortran subroutines - # Takes the input as (3,3,nat,nat) - new_v2 = np.zeros( (3,3, self.QE_nat, self.QE_nat), dtype = np.double, order ="F") - for i in range(self.QE_nat): - for j in range(self.QE_nat): - new_v2[:, :, i, j] = v2[3*i : 3*(i+1), 3*j : 3*(j+1)] - - # Apply the translations - if apply_translations: - # Check that the translations have been setted up - assert len(np.shape(self.QE_translations_irt)) == 2, "Error, symmetries not setted up to work in the supercell" - symph.trans_v2(new_v2, self.QE_translations_irt) - - # Apply the symmetrization - symph.sym_v2(new_v2, self.QE_at, self.QE_bg, self.QE_s, self.QE_irt, self.QE_nsym, self.QE_nat) - - # Return back - for i in range(self.QE_nat): - for j in range(self.QE_nat): - v2[3*i : 3*(i+1), 3*j : 3*(j+1)] = new_v2[:, :, i, j] - - - -def get_symmetries_from_ita(ita, red=False): - """ - This function returns a matrix containing the symmetries from the given ITA code of the Group. - The corresponding ITA/group label can be found on the Bilbao Crystallographic Server. - - Parameters - ---------- - - ita : int - The ITA code that identifies the group symmetry. - - red : bool (default = False) - If red is True then load the symmetries only in the smallest unit cell (orthorombic) - Results - ------- - - symmetries : list - A list of 3 rows x 4 columns matrices (ndarray), containing the symmetry operations - of the chosen group. - """ - - if ita <= 0: - raise ValueError("Error, ITA group %d is not valid." % ita) - - filename="%s/SymData/%d.dat" % (CURRENT_DIR, ita) - if red: - filename="%s/SymData/%d_red.dat" % (CURRENT_DIR, ita) - - - if not os.path.exists(filename): - print ("Error, ITA group not yet implemented.") - print ("You can download the symmetries for this group from the Bilbao Crystallographic Server") - print ("And just add the %d.dat file into the SymData folder of the current program." % ita) - print ("It should take less than five minutes.") - - raise ValueError("Error, ITA group %d not yet implemented. Check stdout on how to solve this problem." % ita) - - fp = open(filename, "r") - - # Get the number of symemtries - n_sym = int(fp.readline().strip()) - fp.close() - - symdata = np.loadtxt(filename, skiprows = 1) - symmetries = [] - - for i in range(n_sym): - symmetries.append(symdata[3*i:3*(i+1), :]) - - return symmetries - - -def GetSymmetriesFromSPGLIB(spglib_sym, regolarize = False): - """ - CONVERT THE SYMMETRIES - ====================== - - This module comvert the symmetry fynction from the spglib format. - - - Parameters - ---------- - spglib_sym : dict - Result of spglib.get_symmetry( ... ) function - regolarize : bool, optional - If True it rewrites the translation to be exact. Usefull if you want to - constrain the symmetry exactly - - Returns - ------- - symmetries : list - A list of 4x3 matrices containing the symmetry operation - """ - - # Check if the type is correct - if not "translations" in spglib_sym: - raise ValueError("Error, your symmetry dict has no 'translations' key.") - - if not "rotations" in spglib_sym: - raise ValueError("Error, your symmetry dict has no 'rotations' key.") - - # Get the number of symmetries - out_sym = [] - n_sym = np.shape(spglib_sym["translations"])[0] - - translations = spglib_sym["translations"] - rotations = spglib_sym["rotations"] - - for i in range(n_sym): - # Create the symmetry - sym = np.zeros((3,4)) - sym[:,:3] = rotations[i, :, :] - sym[:, 3] = translations[i,:] - - # Edit the translation - if regolarize: - sym[:, 3] *= 2 - sym[:, 3] = np.floor(sym[:, 3] + .5) - sym[:, 3] *= .5 - sym[:, 3] = sym[:,3] % 1 - - out_sym.append(sym) - - return out_sym - -def CustomASR(fc_matrix): - """ - APPLY THE SUM RULE - ================== - - This function applies a particular sum rule. It projects out the translations - exactly. - - Parameters - ---------- - fc_matrix : ndarray(3nat x 3nat) - The force constant matrix. The sum rule is applied on that. - """ - - shape = np.shape(fc_matrix) - if shape[0] != shape[1]: - raise ValueError("Error, the provided matrix is not square: (%d, %d)" % (shape[0], shape[1])) - - nat = np.shape(fc_matrix)[0] // 3 - if nat*3 != shape[0]: - raise ValueError("Error, the matrix must have a dimension divisible by 3: %d" % shape[0]) - - - dtype = type(fc_matrix[0,0]) - - trans = np.eye(3*nat, dtype = dtype) - for i in range(3): - v1 = np.zeros(nat*3, dtype = dtype) - v1[3*np.arange(nat) + i] = 1 - v1 /= np.sqrt(v1.dot(v1)) - - trans -= np.outer(v1, v1) - - #print trans - - fc_matrix[:,:] = trans.dot(fc_matrix.dot(trans)) - - -def ExcludeRotations(fc_matrix, structure): - """ - APPLY THE ROTATION SUM RULE - =========================== - - We exclude the rotations from the force constant matrix. - - Parameters - ---------- - fc_matrix : ndarray(3*nat, 3*nat) - The force constant matrix - structure : Structure() - The structure that is identified by the force constant matrix - - """ - - nat = structure.N_atoms - dtype = type(fc_matrix[0,0]) - - # Get the center of the structure - r_cm = np.sum(structure.coords, axis = 0) / nat - r = structure.coords - r_cm - - v_rots = np.zeros((3, 3*nat), dtype = dtype) - projector = np.eye(3*nat, dtype = dtype) - counter = 0 - for i in range(3): - for j in range(i+1,3): - v = np.zeros(3*nat, dtype = dtype) - v_i = r[:, j] - v_j = -r[:, i] - - v[3*np.arange(nat) + i] = v_i - v[3*np.arange(nat) + j] = v_j - - - # orthonormalize - for k in range(counter): - v -= v_rots[k, :].dot(v) * v_rots[k, :] - - # Normalize - norm = np.sqrt(v.dot(v)) - v /= norm - - v_rots[counter, :] = v - projector -= np.outer(v,v) - counter += 1 - - - - fc_matrix[:,:] = projector.dot(fc_matrix.dot(projector)) - - -def GetIRT(structure, symmetry, timer = Timer.Timer(), debug = False): - """ - GET IRT - ======= - - Get the irt array. It is the array of the atom index that the symmetry operation - swaps. - - the y-th element of the array (irt[y]) is the index of the original structure, while - y is the index of the equivalent atom after the symmetry is applied. - - Parameters - ---------- - structure: Structure.Structure() - The unit cell structure - symmetry: list of 3x4 matrices - symmetries with frac translations - timer : Timer class - The functions will be timed using the timer object. - - """ - - - new_struct = structure.copy() - if timer is None: - new_struct.fix_coords_in_unit_cell(delete_copies = False, debug = debug) - else: - timer.execute_timed_function(new_struct.fix_coords_in_unit_cell, delete_copies = False, debug = debug) - n_struct_2 = new_struct.copy() - - if timer is None: - new_struct.apply_symmetry(symmetry, True) - irt = np.array(new_struct.get_equivalent_atoms(n_struct_2), dtype =np.intc) - else: - timer.execute_timed_function(new_struct.apply_symmetry, symmetry, True, timer = timer) - irt = np.array( timer.execute_timed_function(new_struct.get_equivalent_atoms, n_struct_2), dtype =np.intc) - - return irt - -def ApplySymmetryToVector(symmetry, vector, unit_cell, irt): - """ - APPLY SYMMETRY - ============== - - Apply the symmetry to the given vector of displacements. - Translations are neglected. - - .. math:: - - \\vec {v'}[irt] = S \\vec v - - - Parameters - ---------- - symmetry: ndarray(size = (3,4)) - The symmetry operation (crystalline coordinates) - vector: ndarray(size = (nat, 3)) - The vector to which apply the symmetry. - In cartesian coordinates - unit_cell : ndarray( size = (3,3)) - The unit cell in which the structure is defined - irt : ndarray(nat, dtype = int) - The index of how the symmetry exchanges the atom. - - """ - - # Get the vector in crystalline coordinate - nat, dumb = np.shape(vector) - work = np.zeros( (nat, 3)) - sym = symmetry[:, :3] - - for i in range(nat): - # Pass to crystalline coordinates - v1 = Methods.covariant_coordinates(unit_cell, vector[i, :]) - # Apply the symmetry - w1 = sym.dot(v1) - # Return in cartesian coordinates - work[irt[i], :] = np.einsum("ab,a", unit_cell, w1) - - return work - -def ApplySymmetriesToVector(symmetries, vector, unit_cell, irts): - """ - APPLY SYMMETRY - ============== - - Apply the symmetry to the given vector of displacements. - Translations are neglected. - - .. math:: - - \\vec {v'}[irt] = S \\vec v - - - Parameters - ---------- - symmetries: list of ndarray(size = (3,4)) - The symmetries operation (crystalline coordinates) - vector: ndarray(size = (nat, 3)) - The vector to which apply the symmetry. - In cartesian coordinates - unit_cell : ndarray( size = (3,3)) - The unit cell in which the structure is defined - irts : list of ndarray(nat, dtype = int) - The index of how the symmetry exchanges the atom. - - """ - - # Get the vector in crystalline coordinate - nat, dumb = np.shape(vector) - n_sym = len(symmetries) - - assert n_sym == len(irts) - - work = np.zeros( (n_sym, nat, 3), dtype = np.double, order = "C") - - # Pass to crystalline coordinates - v1 = Methods.covariant_coordinates(unit_cell, vector) - - # Apply the symmetry - for j, symmetry in enumerate(symmetries): - sym = symmetry[:, :3] - w1 = sym.dot(v1.T).T - - # Return in cartesian coordinates - work[j, irts[j][:], :] = w1.dot(unit_cell)# unit_cell.T.dot(w1) #np.einsum("ab,a", unit_cell, w1) - - return work - - -def PrepareISOTROPYFindSymInput(structure, path_to_file = "findsym.in", - title = "Prepared with Cellconstructor", - latticeTolerance = 1e-5, atomicPositionTolerance = 0.001): - """ - Prepare a FIND SYM input file - ============================= - - This method can be used to prepare a suitable input file for the ISOTROPY findsym program. - - Parameters - ---------- - path_to_file : string - A valid path to write the findsym input. - title : string, optional - The title of the job - """ - - lines = GetISOTROPYFindSymInput(structure, title, latticeTolerance, atomicPositionTolerance) - - fp = open(path_to_file, "w") - fp.writelines(lines) - fp.close() - - -def GetISOTROPYFindSymInput(structure, title = "Prepared with Cellconstructor", - latticeTolerance = 1e-5, atomicPositionTolerance = 0.001): - """ - As the method PrepareISOTROPYFindSymInput, but the input is returned as a list of string (lines). - - """ - # Check if the structure has a unit cell - if not structure.has_unit_cell: - raise ValueError("Error, the given structure has not a valid unit cell.") - - # Prepare the standard input - lines = [] - lines.append("!useKeyWords\n") - lines.append("!title\n") - lines.append(title + "\n") - lines.append("!latticeTolerance\n") - lines.append("%.8f\n" % latticeTolerance) - lines.append("!atomicPositionTolerance\n") - lines.append("%.8f\n" % atomicPositionTolerance) - lines.append("!latticeBasisVectors\n") - for i in range(3): - lines.append("%16.8f %16.8f %16.8f\n" % (structure.unit_cell[i, 0], - structure.unit_cell[i, 1], - structure.unit_cell[i, 2])) - - lines.append("!atomCount\n") - lines.append("%d\n" % structure.N_atoms) - lines.append("!atomType\n") - lines.append(" ".join(structure.atoms) + "\n") - lines.append("!atomPosition\n") - for i in range(structure.N_atoms): - # Get the crystal coordinate - new_vect = Methods.covariant_coordinates(structure.unit_cell, structure.coords[i, :]) - lines.append("%16.8f %16.8f %16.8f\n" % (new_vect[0], - new_vect[1], - new_vect[2])) - - return lines - - -def GetQGrid(unit_cell, supercell_size, enforce_gamma_first = True): - """ - GET THE Q GRID - ============== - - This method gives back a list of q points given the - reciprocal lattice vectors and the supercell size. - - Parameters - ---------- - unit_cell : ndarray(size=(3,3), dtype = np.float64) - The unit cell, rows are the vectors - supercell_size : ndarray(size=3, dtype = int) - The dimension of the supercell along each unit cell vector. - enforce_gamma_first : bool - If true, the Gamma point is the first one of the list. - - Returns - ------- - q_list : list - The list of q points, of type ndarray(size = 3, dtype = np.float64) - - """ - bg = Methods.get_reciprocal_vectors(unit_cell) - - n_vects = int(np.prod(supercell_size)) - q_final = np.zeros((3, n_vects), dtype = np.double, order = "F") - q_final[:,:] = symph.get_q_grid(bg.T, supercell_size, n_vects) - - # Get the list of the closest vectors - q_list = [Methods.get_closest_vector(bg, q_final[:, i]) for i in range(n_vects)] - - # Setup Gamma as the first vector - if enforce_gamma_first: - for i, q in enumerate(q_list): - if np.abs(np.sum(q)) < __EPSILON__: - tmp = q_list[0].copy() - q_list[0] = q.copy() - q_list[i] = tmp - break - - - return q_list - -def GetQGrid_old(unit_cell, supercell_size): - """ - GET THE Q GRID - ============== - - This method gives back a list of q points given the - reciprocal lattice vectors and the supercell size. - - Parameters - ---------- - unit_cell : ndarray(size=(3,3), dtype = np.float64) - The unit cell, rows are the vectors - supercell_size : ndarray(size=3, dtype = int) - The dimension of the supercell along each unit cell vector. - - Returns - ------- - q_list : list - The list of q points, of type ndarray(size = 3, dtype = np.float64) - - """ - - q_list = [] - # Get the recirpocal lattice vectors - bg = Methods.get_reciprocal_vectors(unit_cell) - - # Get the supercell - supercell = np.tile(supercell_size, (3, 1)).transpose() * unit_cell - - # Get the lattice vectors of the supercell - bg_s = Methods.get_reciprocal_vectors(supercell) - - #print "SUPERCELL:", supercell_size - - for ix in range(supercell_size[0]): - for iy in range(supercell_size[1]): - for iz in range(supercell_size[2]): - n_s = np.array( [ix, iy, iz], dtype = np.float64) - q_vect = n_s.dot(bg_s) - #q_vect = Methods.get_closest_vector(bg, q_vect) - - # Check if q is in the listcount = 0 - count = 0 - for q in q_list: - if Methods.get_min_dist_into_cell(bg, -q_vect, q) < __EPSILON__: - count += 1 - break - if count > 0: - continue - - # Add the q point - q_list.append(q_vect) - - # Check if -q and q are different - if Methods.get_min_dist_into_cell(bg, -q_vect, q_vect) > __EPSILON__: - q_list.append(-q_vect) - - - - return q_list - - - -def CheckSupercellQ(unit_cell, supercell_size, q_list): - """ - CHECK THE Q POINTS - ================== - - This subroutine checks that the given q points of a dynamical matrix - matches the desidered supercell. - It is usefull to spot bugs like the wrong definitions of alat units, - or error not spotted just by the number of q points (confusion between 1,2,2 or 2,1,2 supercell). - - Parameters - ---------- - unit_cell : ndarray(size=(3,3), dtype = np.float64) - The unit cell, rows are the vectors - supercell_size : ndarray(size=3, dtype = int) - The dimension of the supercell along each unit cell vector. - q_list : list of vectors - The total q point list. - Returns - ------- - is_ok : bool - True => No error - False => Error - """ - # Get the q point list for the given supercell - correct_q = GetQGrid(unit_cell, supercell_size) - - # Get the reciprocal lattice vectors - bg = Methods.get_reciprocal_vectors(unit_cell) - - # Check if the vectors are equivalent or not - for iq, q in enumerate(q_list): - for jq, qnew in enumerate(correct_q): - if Methods.get_min_dist_into_cell(bg, q, qnew) < __EPSILON__: - correct_q.pop(jq) - break - - if len(correct_q) > 0: - print ("[CHECK SUPERCELL]") - print (" MISSING Q ARE ") - print ("\n".join([" q =%16.8f%16.8f%16.8f " % (q[0], q[1], q[2]) for q in correct_q])) - return False - return True - -def GetNewQFromUnitCell(old_cell, new_cell, old_qs): - """ - GET NEW Q POINTS AFTER A CELL STRAIN - ==================================== - - This method returns the new q points after the unit cell is changed. - Remember, when changing the cell to mantain the same kind (cubic, orthorombic, hexagonal...) - otherwise the star identification will fail. - - The q point are passed (and returned) in cartesian coordinates. - - Parameters - ---------- - structure : Structure.Structure() - The structure to be changed (with the old unit celll) - new_cell : ndarray(size=(3,3), dtype = np.float64) - The new unit cell. - old_qs : list of ndarray(size=3, dtype = np.float64) - The list of q points to be converted - - Returns - ------- - new_qs : list of ndarray(size=3, dtype = np.float64) - The list of the new q points adapted in the new cell. - """ - - bg = Methods.get_reciprocal_vectors(old_cell) #/ (2 * np.pi) - new_bg = Methods.get_reciprocal_vectors(new_cell)# / (2 * np.pi) - - new_qs = [] - for iq, q in enumerate(old_qs): - # Get the q point in crystal coordinates - new_qprime = Methods.covariant_coordinates(bg, q) - - # Convert the crystal coordinates in the new reciprocal lattice vectors - new_q = np.einsum("ji, j", new_bg, new_qprime) - new_qs.append(new_q) - - return new_qs - -def GetSupercellFromQlist(q_list, unit_cell): - """ - GET THE SUPERCELL FROM THE LIST OF Q POINTS - =========================================== - - This method returns the supercell size from the list of q points - and the unit cell of the structure. - - Parameters - ---------- - q_list : list - List of the q points in cartesian coordinates - unit_cell : ndarray(3,3) - Unit cell of the structure (rows are the unit cell vectors) - - Results - ------- - supercell_size : list of 3 integers - The supercell dimension along each unit cell vector. - """ - - # Get the bravais lattice - bg = Methods.get_reciprocal_vectors(unit_cell) - - # Convert the q points in crystalline units - supercell = [1,1,1] - - for q in q_list: - qprime = Methods.covariant_coordinates(bg, q) - qprime -= np.floor(qprime) - qprime[np.abs(qprime) < __EPSILON__] = 1 - - rmax = 1/np.abs(qprime) - for j in range(3): - if supercell[j] < int(rmax[j] + .5): - supercell[j] = int(rmax[j] + .5) - - return supercell - - -# def GetSymmetriesOnModes(symmetries, structure, pol_vects): -# """ -# GET SYMMETRIES ON MODES -# ======================= - -# This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them -# through any symmetry operation. - -# Parameters -# ---------- -# symmetries : list -# The list of 3x4 matrices representing the symmetries. -# structure : Structure.Structure() -# The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one -# in each other. -# pol_vects : ndarray(size = (n_dim, n_modes)) -# The array of the polarization vectors (must be real) - - -# Results -# ------- -# pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) -# The symmetry operation between the modes. This allow to identify which mode -# will be degenerate, and which will not interact. -# """ - -# # Get the vector of the displacement in the polarization -# m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() -# disp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) - -# n_dim, n_modes = np.shape(pol_vects) - -# n_sym = len(symmetries) -# nat = structure.N_atoms - -# # For each symmetry operation apply the -# pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) -# for i, sym_mat in enumerate(symmetries): -# irt = GetIRT(structure, sym_mat) - -# for j in range(n_modes): -# # Apply the i-th symmetry to the j-th mode -# new_vector = ApplySymmetryToVector(sym_mat, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irt).ravel() -# new_coords = Methods.covariant_coordinates(disp_v, new_vector) -# pol_symmetries[i, j, :] = new_coords - -# return pol_symmetries - - -def _GetSymmetriesOnModes(symmetries, structure, pol_vects): - """ - GET SYMMETRIES ON MODES - ======================= - - This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them - through any symmetry operation. - - Parameters - ---------- - symmetries : list - The list of 3x4 matrices representing the symmetries. - structure : Structure.Structure() - The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one - in each other. - pol_vects : ndarray(size = (n_dim, n_modes)) - The array of the polarization vectors (must be real) - - - Results - ------- - pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) - The symmetry operation between the modes. This allow to identify which mode - will be degenerate, and which will not interact. - """ - - # Get the vector of the displacement in the polarization - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - disp_v = np.einsum("im,i->mi", pol_vects, 1 / np.sqrt(m)) - underdisp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) - - n_dim, n_modes = np.shape(pol_vects) - - n_sym = len(symmetries) - nat = structure.N_atoms - - # For each symmetry operation apply the - pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) - for i, sym_mat in enumerate(symmetries): - irt = GetIRT(structure, sym_mat) - - for j in range(n_modes): - # Apply the i-th symmetry to the j-th mode - new_vector = ApplySymmetryToVector(sym_mat, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irt).ravel() - pol_symmetries[i, :, j] = underdisp_v.dot(new_vector.ravel()) - - return pol_symmetries - -def GetSymmetriesOnModes(symmetries, structure, pol_vects, irts = [], timer = None, debug = False): - """ - GET SYMMETRIES ON MODES - ======================= - - This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them - through any symmetry operation. - - Parameters - ---------- - symmetries : list - The list of 3x4 matrices representing the symmetries. - structure : Structure.Structure() - The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one - in each other. - pol_vects : ndarray(size = (n_dim, n_modes)) - The array of the polarization vectors (must be real) - irts : list - The list of equivalent atoms for each symmetry - - - Results - ------- - pol_symmetries : ndarray( size=(n_sym, n_modes, n_modes)) - The symmetry operation between the modes. This allow to identify which mode - will be degenerate, and which will not interact. - """ - - # Get the vector of the displacement in the polarization - m = np.tile(structure.get_masses_array(), (3,1)).T.ravel() - disp_v = np.einsum("im,i->mi", pol_vects, 1 / np.sqrt(m)) - underdisp_v = np.einsum("im,i->mi", pol_vects, np.sqrt(m)) - - n_dim, n_modes = np.shape(pol_vects) - - n_sym = len(symmetries) - nat = structure.N_atoms - - # For each symmetry operation apply the - pol_symmetries = np.zeros((n_sym, n_modes, n_modes), dtype = np.float64) - - # Get the irt for all the symmetries (if needed) - if len(irts) == 0: - for i, sym_mat in enumerate(symmetries): - irts.append(GetIRT(structure, sym_mat, timer, debug = debug)) - - - - for j in range(n_modes): - # Apply the i-th symmetry to the j-th mode - t1 = time.time() - new_vectors = ApplySymmetriesToVector( symmetries, disp_v[j, :].reshape((nat, 3)), structure.unit_cell, irts).reshape((n_sym, 3 * nat)) - t2 = time.time() - - if timer is not None: - timer.add_timer(ApplySymmetriesToVector.__name__, t2-t1) - pol_symmetries[:, :, j] = underdisp_v.dot(new_vectors.T).T - - return pol_symmetries - - -def GetSymmetriesOnModesDeg(symmetries, structure, pol_vects, w_freq, timer = None, debug = False): - """ - GET SYMMETRIES ON MODES - ======================= - - This methods returns a set of symmetry matrices that explains how polarization vectors interacts between them - through any symmetry operation. - Differently from the previous subroutine GetSymmetriesOnModes, - which returns a tensor of the size (n_sym, n_modes, n_modes), this subroutine returns a list of lenght n_deg as - [(n_sym, ni, ni)] - where n_sym is the number of symmetries, n_deg the number of different non-degenerate modes, - and ni is the dimension of the degeneracy of the i-th group of modes. - This allows for a much lower memory consumption for symmetries - - Parameters - ---------- - symmetries : list - The list of 3x4 matrices representing the symmetries. - structure : Structure.Structure() - The structure (supercell) to allow the symmetry to correctly identify the atoms that transforms one - in each other. - pol_vects : ndarray(size = (n_dim, n_modes)) - The array of the polarization vectors (must be real) - - - Results - ------- - pol_symmetries : list - List of all the symmetries expressed in blocks. - pol_symmetries[a][k, x, y] = block of degenerate modes a, symmetry id k, modes x and y of the block - basis : list - basis[a] is the id of the modes inside the block a (the one corresponding to x, y indices) - """ - - - Ns = len(symmetries) - - # Now we can pull out the translations - pols = pol_vects - w = w_freq - #trans_mask = Methods.get_translations(pol_vects, structure.get_masses_array()) - - # Exclude degeneracies - #w = w_freq[~trans_mask] - #pols = pol_vects[:, ~trans_mask] - - - # Get the degeneracy - n_modes = len(w) - N_deg = np.ones(len(w), dtype = np.intc) - n_blocks = min(len(w), 1) # Counter of the different non-degenerate modes - start_deg = -1 - deg_space = [ [x] for x in range(n_modes)] - final_space = [] - - threshold = 1e-8 - - # Compute irts once for all - irts = [] - for i, sym_mat in enumerate(symmetries): - irts.append(GetIRT(structure, sym_mat, timer, debug = debug)) - - for i in range(1, len(w)): - if np.abs(w[i-1] - w[i]) < threshold : - N_deg[i] = N_deg[i-1] + 1 - - if start_deg == -1: - start_deg = i - 1 - - for j in range(start_deg, i): - N_deg[j] = N_deg[i] - deg_space[j].append(i) - deg_space[i].append(j) - - else: - start_deg = -1 - n_blocks += 1 - deg_space[i-1].sort() - final_space.append(deg_space[i-1]) - - deg_space[-1].sort() - final_space.append(deg_space[-1]) - - assert len(final_space) == n_blocks - - - # Now compute the symmetries only in the correct blocks - i_mode = 0 - result_list = [] - for i in range(n_blocks): # TODO ADD MPI PARALLELIZATION - mode_mask = np.zeros(n_modes, dtype = bool) - - for k in final_space[i]: - mode_mask[k] = True - - - - #assert np.sum(mode_mask.astype(int)) == N_deg[i_mode], "Error, something went wrong while computing the degeneracies." - - select_pols = pols[:, mode_mask] - pol_syms = GetSymmetriesOnModes(symmetries, structure, select_pols, irts, timer, debug) - - i_mode += len(deg_space[i_mode]) - - result_list.append(pol_syms) - - return result_list, final_space - - - - -def get_degeneracies(w): - """ - GET THE SUBSPACES OF DEGENERACIES - ================================= - - From the given frequencies, for each mode returns a list of the indices of the modes of degeneracies. - - Parameters - ---------- - w : ndarray(n_modes) - Frequencies - - Results - ------- - deg_list : list of lists - A list that contains, for each mode, the list of the modes (indices) that are degenerate with the latter one - """ - - - n_modes = len(w) - - ret_list = [] - for i in range(n_modes): - deg_list = np.arange(n_modes)[np.abs(w - w[i]) < 1e-8] - ret_list.append(deg_list) - return ret_list - -def get_diagonal_symmetry_polarization_vectors(pol_sc, w, pol_symmetries): - """ - GET THE POLARIZATION VECTORS THAT DIAGONALIZES THE SYMMETRIES - ============================================================= - - This function is very usefull to have a complex basis in which the application of symmetries - is trivial. - - In this basis, each symmetry is diagonal. - Indeed this forces the polarization vectors to be complex in the most general case. - - NOTE: To be tested, do not use for production run - It seems to be impossible to correctly decompose simmetries when we have multiple rotations. - - If the symmetries are not unitary, an exception will be raised. - - Parameters - ---------- - pol_sc : ndarray(3*nat, n_modes) - The polarizaiton vectors in the supercell (obtained by DiagonalizeSupercell of the Phonon class) - w : ndarray(n_modes) - The frequency for each polarization vectors - pol_symmetries : ndarray(N_sym, n_modes, n_modes) - The Symmetry operator that acts on the polarization vector - - - Results - ------- - pol_vects : ndarray(3*nat, n_modes) - The new (complex) polarization vectors that diagonalizes all the symmetries. - syms_values : ndarray(n_modes, n_sym) - The (complex) unitary eigenvalues of each symmetry operation along the given mode. - """ - raise NotImplementedError("Error, this subroutine has not been implemented.") - - # First we must get the degeneracies - deg_list = get_degeneracies(w) - - # Now perform the diagonalization on each degeneracies - final_vectors = np.zeros( pol_sc.shape, dtype = np.complex128) - final_vectors[:,:] = pol_sc.copy() - - n_modes = len(w) - n_syms = pol_symmetries.shape[0] - skip_list = [] - - syms_values = np.zeros((n_modes, n_syms), dtype = np.complex128) - - print("All modes:") - for i in range(n_modes): - print("Mode {} = {} cm-1 => ".format(i, w[i] * RY_TO_CM), deg_list[i]) - - print() - for i in range(n_modes): - if i in skip_list: - continue - - # If we have no degeneracies, we can ignore it - if len(deg_list[i]) == 1: - continue - - partial_modes = np.zeros((len(deg_list[i]), len(deg_list[i])), dtype = np.complex128) - partial_modes[:,:] = np.eye(len(deg_list[i])) # identity matrix - - mask_final = np.array([x in deg_list[i] for x in range(n_modes)]) - - # If we have degeneracies, lets diagonalize all the symmetries - for i_sym in range(n_syms): - skip_j = [] - diagonalized = False - np.savetxt("sym_{}.dat".format(i_sym), pol_symmetries[i_sym, :,:]) - - - # Get the symmetry matrix in the mode space (this could generate a problem with masses) - ps = pol_symmetries[i_sym, :, :] - sym_mat_origin = ps[np.outer(mask_final, mask_final)].reshape((len(deg_list[i]), len(deg_list[i]))) - - for j_mode in deg_list[i]: - if j_mode in skip_j: - continue - - # Get the modes that can be still degenerate by symmetries - mode_dna = syms_values[j_mode, : i_sym] - - # Avoid a bad error if i_sym = 0 - if len(mode_dna) > 0: - mode_space = [x for x in deg_list[i] if np.max(np.abs(syms_values[x, :i_sym] - mode_dna)) < 1e-3] - else: - mode_space = [x for x in deg_list[i]] - - # The mask for the whole symmetry and the partial_modes - mask_all = np.array([x in mode_space for x in np.arange(n_modes)]) - mask_partial_mode = np.array([x in mode_space for x in deg_list[i]]) - n_deg_new = np.sum(mask_all.astype(int)) - - if len(mode_space) == 1: - continue - - p_modes_new = partial_modes[:, mask_partial_mode] - - - print() - print("SYMMETRY_INDEX:", i_sym) - print("SHAPE sym_mat_origin:", sym_mat_origin.shape) - print("MODES: {} | DEG: {}".format(mode_space, deg_list[i])) - print("SHAPE P_MODES_NEW:", p_modes_new.shape) - sym_mat = np.conj(p_modes_new.T).dot(sym_mat_origin.dot(p_modes_new)) - - # Decompose in upper triangular (assures that eigenvectors are orthogonal) - s_eigvals_mat, s_eigvects = scipy.linalg.schur(sym_mat, output = "complex") - s_eigvals = np.diag(s_eigvals_mat) - - # Check if the s_eigvals confirm the unitary of sym_mat - # TODO: Check if some mass must be accounted or not... - print("SYM_MAT") - print(sym_mat) - print("Eigvals:") - print(s_eigvals) - print("Eigval_mat:") - print(s_eigvals_mat) - print("Eigvects:") - print(s_eigvects) - assert np.max(np.abs(np.abs(s_eigvals) - 1)) < 1e-5, "Error, it seems that the {}-th matrix is not a rotation.".format(i_sym).format(sym_mat) - - # Update the polarization vectors to account this diagonalization - partial_modes[:, mask_partial_mode] = p_modes_new.dot(s_eigvects) - - # Add the symmetry character on the new eigen modes - for k_i, k in enumerate(mode_space): - syms_values[k, i_sym] = s_eigvals[k_i] - - # Now add the modes analyzed up to know to the skip - for x in mode_space: - skip_j.append(x) - - diagonalized = True - - - # Now we diagonalized the space - # Apply the symmetries if we did not perform the diagonalization - if not diagonalized: - # Get the symmetrized matrix in the partial mode list: - sym_mat = np.conj(partial_modes.T).dot(sym_mat_origin.dot(partial_modes)) - - # Check that it is diagonal - s_eigvals = np.diag(sym_mat) - disp = sym_mat - np.diag( s_eigvals) - if np.max(np.abs(disp)) > 1e-4: - print("Matrix {}:".format(i_sym)) - print(sym_mat) - raise ValueError("Error, I expect the symmetry {} to be diagonal".format(i_sym)) - - syms_values[k, i_sym] = s_eigvals[k_i] - - # Add the symmetry character on the new eigen modes - for k_i, k in enumerate(deg_list[i]): - syms_values[k, i_sym] = s_eigvals[k_i] - - - # Now we solved our polarization vectors, add them to the final ones - final_vectors[:, mask_final] = pol_sc[:, mask_final].dot(partial_modes) - - # Do not further process the modes we used in this iteration - for mode in deg_list[i]: - skip_list.append(mode) - - - return final_vectors, syms_values - - - - - -def GetQForEachMode(pols_sc, unit_cell_structure, supercell_structure, \ - supercell_size, crystal = True): - """ - GET THE Q VECTOR - ================ - - For each polarization mode in the supercell computes the - corresponding q vector. - - Indeed the polarization vector will be a have components both at q and at -q. - - If a polarization vector mixes two q an error will be raised. - - NOTE: use DiagonalizeSupercell of Phonons to avoid mixing q. - - - Parameters - ---------- - pols_sc : ndarray ( size = (3*nat_sc, n_modes), dtype = np.float64) - The polarization vector of the supercell (real) - unit_cell_structure : Structure() - The structure in the unit cell - supercell_structure: Structure() - The structure in the super cell - supercell_size : list of 3 int - The supercell - crystal : bool - If True, q points are returned in cristal coordinates. - - - Results - ------- - q_list : ndarray(size = (n_modes, 3), dtype = np.float, order = "C") - The list of q points associated with each polarization mode. - If crystal is true, they will be in crystal coordinates. - """ - - # Check the supercell - n_cell = np.prod(supercell_size) - - nat = unit_cell_structure.N_atoms - nat_sc = np.shape(pols_sc)[0] / 3 - n_modes = np.shape(pols_sc)[1] - - ERR_MSG = """ - Error, the supercell {} is not commensurate with the polarization vector given. - nat = {}, nat_sc = {} - """ - assert n_cell * nat == nat_sc, ERR_MSG.format(supercell_size, nat, nat_sc) - assert nat_sc == supercell_structure.N_atoms - - # Get the reciprocal lattice - bg = Methods.get_reciprocal_vectors(unit_cell_structure.unit_cell) / (2 * np.pi) - - # Get the possible Q list - q_grid = GetQGrid(unit_cell_structure.unit_cell, supercell_size) - - # Allocate the output variable - q_list = np.zeros( (n_modes, 3), dtype = np.double, order = "C") - - # Get the correspondance between the unit cell and the super cell atoms - itau = supercell_structure.get_itau(unit_cell_structure) - 1 #Fort2Py - - # Get the translational vectors - R_vects = np.zeros( (nat_sc, 3), dtype = np.double) - for i in range(nat_sc): - R_vects[i, :] = unit_cell_structure.coords[itau[i],:] - supercell_structure.coords[i,:] - - R_vects = R_vects.ravel() - __thr__ = 1e-6 - - for imu in range(n_modes): - pol_v = pols_sc[:, imu] - - nq = 0 - for q in q_grid: - q_vec = np.tile(q, nat_sc) - q_cos = np.cos(2*np.pi * q_vec * R_vects) - q_cos /= np.sqrt(q_cos.dot(q_cos)) - q_sin = np.sin(2*np.pi * q_vec * R_vects) - q_sin /= np.sqrt(q_cos.dot(q_cos)) - - cos_proj = q_cos.dot(pol_v) - sin_proj = q_sin.dot(pol_v) - # Wrong, this select only a translational mode - - if np.abs(cos_proj**2 + sin_proj**2 -1) < __thr__: - new_q = q - if crystal: - new_q = Methods.covariant_coordinates(bg, q) - q_list[imu, :] = new_q - break - elif cos_proj**2 + sin_proj**2 > __thr__: - print (q_cos) - ERROR_MSG = """ - Error, mixing between two |q|. - Please provide polarization vectors that are well defined in |q|. - This can be reached using the subroutine Phonons.Phonons.DiagonalizeSupercell. - q = {} - i_mode = {} - - cos_proj = {} | sin_proj = {} - """ - raise ValueError(ERROR_MSG.format(q, imu, cos_proj, sin_proj)) - else: - nq += 1 - - - # If we are here not q has been found - if nq == len(q_grid): - ERROR_MSG = """ - Error, the polarization vector {} cannot be identified! - No q found in this supercell! - """ - raise ValueError(ERROR_MSG.format(imu)) - - - return q_list - - -def ApplyTranslationsToSupercell(fc_matrix, super_cell_structure, supercell): - """ - Impose the translational symmetry directly on the supercell - matrix. - - Parameters - ---------- - - fc_matrix : ndarray(size=(3*natsc, 3*natsc)) - The matrix in the supercell. In output will be - modified - - super_cell_structure : Structure() - The structure of the super cell - - supercell : (nx,ny,nz) - The dimension of the supercell. - """ - - natsc = super_cell_structure.N_atoms - - # Check the consistency of the passed options - natsc3, _ = np.shape(fc_matrix) - assert natsc == int(natsc3 / 3), "Error, wrong number of atoms in the supercell structure" - assert natsc3 == _, "Error, the matrix passed has a wrong shape" - assert natsc % np.prod(supercell) == 0, "Error, the given supercell is impossible with the number of atoms" - - # Fill the auxiliary matrix - new_v2 = np.zeros( (3,3, natsc, natsc), dtype = np.double, order ="F") - for i in range(natsc): - for j in range(natsc): - new_v2[:, :, i, j] = fc_matrix[3*i : 3*(i+1), 3*j : 3*(j+1)] - - - # The number of translations - n_trans = np.prod(supercell) - trans_irt = np.zeros((natsc, n_trans), dtype = np.double, order = "F") - - # Setup the translational symmetries - for nx in range(supercell[0]): - for ny in range(supercell[1]): - for nz in range(supercell[2]): - # Build the translational symmetry - symmat = np.zeros((3,4)) - symmat[:3,:3] = np.eye(3) - symmat[:, 3] = np.array([nx, ny, nz], dtype = float) / np.array(supercell) - - - nindex = supercell[2] * supercell[1] *nx - nindex += supercell[2] * ny - nindex += nz - - # Get the IRT for this symmetry operation in the supercell - trans_irt[:, nindex] = GetIRT(super_cell_structure, symmat) + 1 - - - - - # Apply the translations - symph.trans_v2(new_v2, trans_irt) - - # Return back to the fc_matrix - for i in range(natsc): - for j in range(natsc): - fc_matrix[3*i : 3*(i+1), 3*j : 3*(j+1)] = new_v2[:, :, i, j] - - - -def get_invs(QE_s, QE_nsym): - """ - GET INVERSION SYMMETRY - ====================== - - For each symmetry operation, get an index that its inverse - Note, the array must be in Fortran indexing (starts from 1) - - Parameters - ---------- - QE_s : ndarray(size = (3,3,48), dtype = np.intc) - The symmetries - QE_nsym : int - The number of symmetries - - Results - ------- - QE_invs : ndarray(size = 48, dtype = np.intc) - The index of the inverse symmetry. - In fortran indexing (1 => index 0) - """ - QE_invs = np.zeros(48, dtype = np.intc) - for i in range(QE_nsym): - found = False - for j in range(QE_nsym): - if (QE_s[:,:,i].dot(QE_s[:,:,j]) == QE_s[:,:,0]).all(): - QE_invs[i] = j + 1 # Fortran index - found = True - - if not found: - warnings.warn("This is not a group, some features like Q star division may fail.") - - return QE_invs - - -def GetSymmetryMatrix(sym, structure, crystal = False): - """ - GET THE SYMMETRY MATRIX - ======================= - - This subroutine converts the 3x4 symmetry matrix to a 3N x 3N matrix. - It also transform the symmetry to be used directly in cartesian space. - However, take care, it could be a very big matrix, so it is preverred to work with the small matrix, - and maybe use a fortran wrapper if you want speed. - - NOTE: The passe structure must already satisfy the symmetry - - Parameters - ---------- - sym : ndarray(size = (3, 4)) - The symmetry and translations - structure : CC.Structure.Structure() - The structure on which the symmetry is applied (The structure must satisfy the symmetry already) - crystal : bool - If true, the symmetry is returned in crystal coordinate (default false) - - Results - ------- - sym_mat : ndarray(size = (3*structure.N_atoms, 3*structure.N_atoms)) - """ - - # Get the IRT array - irt = GetIRT(structure, sym) - - nat = structure.N_atoms - sym_mat = np.zeros((3 * nat, 3*nat), dtype = np.double) - - # Comvert the symmetry matrix in cartesian - if not crystal: - sym_cryst = Methods.convert_matrix_cart_cryst2(sym[:,:3], structure.unit_cell, cryst_to_cart = True) - else: - sym_cryst = sym[:,:3] - - # Correctly fill the atomic position of sym_mat - for i in range(nat): - i_irt = irt[i] - sym_mat[3 * i_irt : 3*i_irt+3, 3*i : 3*i+ 3] = sym_cryst - - return sym_mat \ No newline at end of file From cf9331cb33501cb443eca928a8265cbd5d9e1a57 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:33:56 +0100 Subject: [PATCH 09/18] Delete tests/TestASR/.ipynb_checkpoints directory --- .../.ipynb_checkpoints/asr_scha-checkpoint.py | 126 ------------------ 1 file changed, 126 deletions(-) delete mode 100644 tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py diff --git a/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py b/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py deleted file mode 100644 index 6298fc3e..00000000 --- a/tests/TestASR/.ipynb_checkpoints/asr_scha-checkpoint.py +++ /dev/null @@ -1,126 +0,0 @@ -import cellconstructor as CC -import cellconstructor.Phonons -import cellconstructor.Structure -import cellconstructor.Units -import os -import numpy as np -import sys - -if __name__ == "__main__": - """ - THE MEANING OF THE ASR IN THE SCHA - """ - struct = CC.Structure.Structure(2) - struct.atoms = ['H', 'C'] - struct.coords = np.array([[-1, 0., 0.], - [+1, 0., 0.]]) - struct.masses = {'H' : 1837, 'C' : 1837 * 12} - struct.unit_cell = np.eye(3) * 100 - dyn = CC.Phonons.Phonons(struct) - dyn.dynmats[0] = np.random.uniform(size = (6,6)) - dyn.dynmats[0] += dyn.dynmats[0] - dyn.ForcePositiveDefinite() - dyn.ApplySumRule() - Temp = 100 # Kelvin - masses = np.tile(struct.get_masses_array(), (3, 1)).T.ravel() - - T = np.zeros((3, 6)) - for i in range(3): - v = np.zeros(3) - v[i] = 1 - T[i, :] = np.tile(v, 2) - - Y = dyn.GetUpsilonMatrix(Temp) - - # Multiply by a translation - FC_T = np.einsum('ab, ib -> ia', dyn.dynmats[0], T) - - Y_T = np.einsum('ab, ib -> ia', Y, T) - - print('\n### TEST ###') - print('Phi dot T') - for i in range(3): - print('trasl {}'.format(i)) - print(FC_T[i,:]) - if np.any(np.abs(FC_T[i, :]) > 1e-3): - raise ValueError('The traslation of FC does not work') - - print('\n### TEST ###') - print('Y dot T') - for i in range(3): - print('trasl {}'.format(i)) - print(Y_T[i,:]) - if np.any(np.abs(Y_T[i, :]) > 1e-3): - raise ValueError('The traslation of Y does not work') - - - # The matrix A of SCHA - def get_A(T, w, pols, masses): - A = np.zeros((6,6)) - if T < 1e-3: - return A - - a_mu = np.zeros(6) - mask = np.abs(w) < 1e-4 - a_mu[mask] = T * (CC.Units.RY_TO_KELVIN**-1) # beta^-1 - # The BE occupations - n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 - # The other eigenvalues - a_mu[~mask] = 2 * w[~mask] * n * (1 + n) /(1 + 2 * n) - - A = np.einsum('m, am, bm -> ab', a_mu, pols, pols) - # Multiply by the masses - return np.einsum('a, ab, b -> ab', np.sqrt(masses), A, np.sqrt(masses)) - - # The matrix Y of SCHA - def get_Y(T, w, pols, masses): - Y = np.zeros((6,6)) - - if T < 1e-3: - return A - - y_mu = np.zeros(6) - mask = np.abs(w) < 1e-4 - y_mu[mask] = 0. - n = (np.exp(w[~mask] * CC.Units.RY_TO_KELVIN /T) - 1)**-1 - y_mu[~mask] = 2 * w[~mask] /(1 + 2 * n) - - Y = np.einsum('m, am, bm -> ab', y_mu, pols, pols) - - return np.einsum('a, ab, b -> ab', np.sqrt(masses), Y, np.sqrt(masses)) - - - - w, pols = dyn.DyagDinQ(0) - A = get_A(Temp, w, pols, masses) - print('\nSCHA frequencies cm-1') - print(w[np.abs(w) > 1e-3] * CC.Units.RY_TO_CM) - - print('\n\nTest of the COM kinetic energy @ {} K'.format(Temp)) - # Check the sum in the COM kinetic energy - totA = 0. - _A_ = A.reshape((2, 3, 2, 3)) - for i in range(2): - for j in range(2): - for alpha in range(3): - totA += _A_[i, alpha, j, alpha] - - print('>The A contribution') - print( totA) - - - # Check the sum in the COM kinetic energy - totY = 0. - _Y_ = Y.reshape((2, 3, 2, 3)) - for i in range(2): - for j in range(2): - for alpha in range(3): - totY += _Y_[i, alpha, j, alpha] - - print(' >The Y contribution') - print( totY) - - print(np.einsum('am, bm -> ab', pols, pols)) - - # print(np.abs(Y - get_Y(Temp, w, pols, masses)).max()) - # print(np.abs(Y - get_Y(Temp, w, pols, masses)).min()) \ No newline at end of file From 405ec81be64035b19bb3849f16d28196a322875f Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:34:17 +0100 Subject: [PATCH 10/18] Delete tests/TestDiagonalizeSymmetries/.ipynb_checkpoints directory --- .../test_diagsymmetries-checkpoint.py | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py diff --git a/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py b/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py deleted file mode 100644 index 547f2b72..00000000 --- a/tests/TestDiagonalizeSymmetries/.ipynb_checkpoints/test_diagsymmetries-checkpoint.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import print_function -from __future__ import division - - -import cellconstructor as CC -import cellconstructor.Phonons -import cellconstructor.symmetries - -import spglib -import sys, os - -import numpy as np - -import ase -from ase.visualize import view - -import pytest - -@pytest.mark.skip(reason="Function not implemented") -def test_diag_symmetries(): - total_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(total_path) - - # Diagonalize the dynamical matrix in the supercell - dyn = CC.Phonons.Phonons("../TestDiagonalizeSupercell/prova", 4) - w, p = dyn.DiagonalizeSupercell() - - view(dyn.structure.get_ase_atoms()) - - # Get the symmetries - supercell_s = dyn.structure.generate_supercell(dyn.GetSupercell()) - spglib_syms = spglib.get_symmetry(dyn.structure.get_ase_atoms()) - syms = CC.symmetries.GetSymmetriesFromSPGLIB(spglib_syms) - - # Get the symmetries on the polarization vectors - pols_syms = CC.symmetries.GetSymmetriesOnModes(syms, supercell_s, p) - - # Now complete the diagonalization of the polarization vectors - # To fully exploit symmetries - new_pols, syms_character = CC.symmetries.get_diagonal_symmetry_polarization_vectors(p, w, pols_syms) - - # TODO: Test if these new polarization vectors really rebuild the dynamical matrix - - # write the symmetry character - n_modes, n_syms = syms_character.shape - - for i in range(n_modes): - print("Mode {} | ".format(i), np.angle(syms_character[i,:], deg = True)) - -if __name__ == "__main__": - test_diag_symmetries() From 6ede98fb65976fa9e89233e2516666a8971dc03f Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:34:45 +0100 Subject: [PATCH 11/18] Delete tests/TestSupercellRealSpace/.ipynb_checkpoints directory --- .../test_supercell_fourier-checkpoint.py | 85 ------------------- 1 file changed, 85 deletions(-) delete mode 100644 tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py diff --git a/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py b/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py deleted file mode 100644 index 4168a806..00000000 --- a/tests/TestSupercellRealSpace/.ipynb_checkpoints/test_supercell_fourier-checkpoint.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function -import numpy as np - -import cellconstructor as CC -import cellconstructor.Phonons - - -import sys, os - -def test_supercell_fourier(): - - total_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(total_path) - - - SUPER_DYN = "../TestPhononSupercell/dynmat" - NQIRR = 8 - SUPERCELL = (3, 3, 2) - - - dyn = CC.Phonons.Phonons(SUPER_DYN, NQIRR) - - - fc = dyn.GetRealSpaceFC(SUPERCELL) - fc_new = fc.copy() - - - print("Real space:") - print(fc[:6, :6]) - - print("First one:") - print(dyn.dynmats[0]) - - - print ("Distances") - super_structure = dyn.structure.generate_supercell(SUPERCELL) - m =super_structure.get_masses_array() - nq = np.prod(SUPERCELL) - nat_sc = dyn.structure.N_atoms *nq - - _m_ = np.zeros(3*nat_sc) - for i in range(nat_sc): - _m_[3 * i : 3*i + 3] = m[i] - - m_mat = np.outer(1 / np.sqrt(_m_), 1 / np.sqrt(_m_)) - - fc *= m_mat - - w_tot = np.sqrt(np.abs(np.real(np.linalg.eigvals(fc)))) - w_tot.sort() - - w_old = np.zeros(len(w_tot)) - - for i in range(nq): - w,p = dyn.DyagDinQ(i) - w_old[ i * len(w) : (i+1) * len(w)] = w - - w_old.sort() - print ("Freq:") - print ("\n".join ( [" %.5f vs %.5f" % (w_tot[i] * CC.Phonons.RY_TO_CM, w_old[i] * CC.Phonons.RY_TO_CM) for i in range (len(w_tot))])) - - - # Try to revert the code - - dynmats_new = CC.Phonons.GetDynQFromFCSupercell(fc_new, np.array(dyn.q_tot), dyn.structure, super_structure) - d2 = CC.Phonons.GetDynQFromFCSupercell_parallel(fc_new, np.array(dyn.q_tot), dyn.structure, super_structure) - - - dyn_sc_new = CC.Phonons.GetSupercellFCFromDyn(dynmats_new, np.array(dyn.q_tot), dyn.structure, super_structure) - dyn_sc_new2 = CC.Phonons.GetSupercellFCFromDyn(d2, np.array(dyn.q_tot), dyn.structure, super_structure) - - dist1 = np.max(np.abs(dyn_sc_new - fc_new)) - dist2 = np.max(np.abs(dyn_sc_new2 - fc_new)) - print ("Distance reverted:", dist1) - print ("Distance reverted:", dist2) - - assert dist1 < 1e-10, 'Error in the fourier transform' - assert dist2 < 1e-10, 'Error in the parallel fourier transform' - - #print "\n".join ( ["RATIO: %.5f " % (w_tot[i] / w_old[i] ) for i in range (len(w_tot))]) - - -if __name__ == "__main__": - test_supercell_fourier() From ad53ae64c937cddf5458973e19743c8796bfc1bc Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:35:01 +0100 Subject: [PATCH 12/18] Delete tests/TestSymmetriesSupercell/.ipynb_checkpoints directory --- .../test_spglib_symmetrization-checkpoint.py | 58 ---------- .../test_symmetries_supercell-checkpoint.py | 105 ------------------ 2 files changed, 163 deletions(-) delete mode 100644 tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py delete mode 100644 tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py diff --git a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py deleted file mode 100644 index 5661192e..00000000 --- a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_spglib_symmetrization-checkpoint.py +++ /dev/null @@ -1,58 +0,0 @@ -import sys, os -import cellconstructor as CC -import cellconstructor.Phonons -import pytest - -import numpy as np - -try: - __SPGLIB__ = True - import spglib -except: - __SPGLIB__ = False - -def test_spglib_symmetrization(): - total_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(total_path) - - # Skip the test if spglib is not installed - if not __SPGLIB__: - pytest.skip("This test requires SPGLIB installed") - - - # Load the dyn - dyn = CC.Phonons.Phonons("SnTe_sscha", 3) - - # Symmetrize with quantum espresso - dyn.Symmetrize() - - w, pols = dyn.DiagonalizeSupercell() - - # Check identity on the polarization vectors - identity = np.einsum("ai, bi", pols, pols) - I = np.eye(identity.shape[0]) - assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with QE symmetrization failed" - - # Generate the supercell and symmetrize with spglib - new_dyn = dyn.GenerateSupercellDyn(dyn.GetSupercell()) - new_dyn.Symmetrize(use_spglib = True) - w2, pols = new_dyn.DiagonalizeSupercell() - - identity = np.einsum("ai, bi", pols, pols) - I = np.eye(identity.shape[0]) - assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with SPGLIB symmetrization (supercell) failed" - - - # Symmetrize with spglib (nothing should happen) - dyn.Symmetrize(use_spglib = True) - - w3, pols = dyn.DiagonalizeSupercell() - - # Check identity on the polarization vectors - identity = np.einsum("ai, bi", pols, pols) - I = np.eye(identity.shape[0]) - assert np.max(np.abs(identity - I)) < 1e-10, "Test identity on polarization with SPGLIB symmetrization (unit_cell) failed" - - -if __name__ == "__main__": - test_spglib_symmetrization() diff --git a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py b/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py deleted file mode 100644 index 5bcc2671..00000000 --- a/tests/TestSymmetriesSupercell/.ipynb_checkpoints/test_symmetries_supercell-checkpoint.py +++ /dev/null @@ -1,105 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function -import numpy as np - -import cellconstructor as CC -import cellconstructor.Phonons -import cellconstructor.symmetries - -import sys, os -import pytest - -@pytest.mark.parametrize("FILDYN, NQIRR", [("Sym.dyn.", 3), ("skydyn_", 4)]) -def test_symmetries_supercell(FILDYN, NQIRR): - - total_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(total_path) - - - dynmat = CC.Phonons.Phonons(FILDYN, NQIRR) - SUPERCELL = dynmat.GetSupercell() - - # Compute the frequencies - supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) - w1, pols = supercell_dyn.DyagDinQ(0) - - # Show the modes for each q point - for i,q in enumerate(dynmat.q_tot): - print ("Dyagonalizing:", q) - w, p = dynmat.DyagDinQ(i) - print (" ".join(["%.4f cm-1 " % (x * CC.Phonons.RY_TO_CM) for x in w])) - - #dynmat.Symmetrize() - # # Test the symmetrization - qe_sym = CC.symmetries.QE_Symmetry(dynmat.structure) - - fc_dynmat_start = np.array(dynmat.dynmats) - - - after_sym = fc_dynmat_start.copy() - qe_sym.SymmetrizeFCQ(after_sym, np.array(dynmat.q_stars), verbose = True) - for i,q in enumerate(dynmat.q_tot): - dynmat.dynmats[i] = after_sym[i,:,:] - - # Show the modes for each q point - for i,q in enumerate(dynmat.q_tot): - print ("After Dyagonalizing:", q) - w, p = dynmat.DyagDinQ(i) - print (" ".join(["%.4f cm-1 " % (x * CC.Phonons.RY_TO_CM) for x in w])) - - # Print the difference between before and after the symmetrization - print () - print ("Difference of the symmetrization:") - print (np.sqrt( np.sum( (after_sym - fc_dynmat_start)**2 ) / np.sum(after_sym*fc_dynmat_start))) - - # print "" - - # Now lets try to randomize the matrix - #new_random = np.random.uniform( size = np.shape(fc_dynmat_start)) + 1j*np.random.uniform( size = np.shape(fc_dynmat_start)) - - # print "Saving a not symmetrized random matrix to Random.dyn.IQ, where IQ is the q index" - # # Lets save the new matrix in QE format - # for i, q in enumerate(dynmat.q_tot): - # dynmat.dynmats[i] = new_random[i, :, :] - # dynmat.save_qe("Random.dyn.") - - # # Lets constrain the symmetries - # # We use asr = crystal to force the existence of the acustic modes in Gamma - # qe_sym.SymmetrizeFCQ(new_random, np.array(dynmat.q_stars), asr = "no") - - # # Lets save the new matrix in QE format - # for i, q in enumerate(dynmat.q_tot): - # dynmat.dynmats[i] = new_random[i, :, :] - - # print "Saving a symmetrized random matrix to Sym.dyn.IQ, where IQ is the q index" - # dynmat.save_qe("Sym.dyn.") - # print "" - - # Compute the frequencies - supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) - w, pols = supercell_dyn.DyagDinQ(0) - # Get the translations - t = CC.Methods.get_translations(pols, supercell_dyn.structure.get_masses_array()) - - dynmat.Symmetrize() - # Compute the frequencies - supercell_dyn = dynmat.GenerateSupercellDyn(SUPERCELL) - w3, pols = supercell_dyn.DyagDinQ(0) - # Get the translations - t = CC.Methods.get_translations(pols, supercell_dyn.structure.get_masses_array()) - - - # Make the assert test - for i, _w_ in enumerate(w): - w2 = w3[i] - - assert np.abs(_w_ - w2) < 1e-8 - - # print "Frequencies:" - # print "\n".join(["%.4f cm-1 | %.4f cm-1 | %.4f cm-1 T: %d" % (w1[i]*CC.Phonons.RY_TO_CM, w[i]*CC.Phonons.RY_TO_CM, w3[i]*CC.Phonons.RY_TO_CM, t[i]) for i in range(len(w))]) - # print "" - # print "Done." - - -if __name__ == "__main__": - test_symmetries_supercell("Sym.dyn.", 3) From e895d10f0f2ab4955c54241a12b3a51737cd62ab Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:35:21 +0100 Subject: [PATCH 13/18] Delete tutorials/PlotPhononDispersion/.ipynb_checkpoints directory --- .../plot_dispersion-checkpoint.py | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py diff --git a/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py b/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py deleted file mode 100644 index fd836b85..00000000 --- a/tutorials/PlotPhononDispersion/.ipynb_checkpoints/plot_dispersion-checkpoint.py +++ /dev/null @@ -1,71 +0,0 @@ -import cellconstructor as CC -import cellconstructor.Phonons -import cellconstructor.ForceTensor - -import ase, ase.dft.kpoints -import sys, os - -import numpy as np -import matplotlib.pyplot as plt - -# The dynamical matrix -PATH_TO_DYN="../QuasiHarmonicApproximation/V804/dynmat" -NQIRR = 8 - -# Load the dynamical matrix -dyn = CC.Phonons.Phonons(PATH_TO_DYN, NQIRR) - -# Optionally you can display the BZ -# With the standard paths -# to choose the path -# Just uncomment the following three lines -""" SHOW THE BRILLUIN ZONE -ase_atoms = dyn.structure.get_ase_atoms() -lattice = ase_atoms.cell.get_bravais_lattice() -lattice.plot_bz(show = True) -""" - -# Select the path -PATH = "GYTAZG" -N_POINTS = 1000 - - - -# -------- HERE THE CORE SCRIPT ------------ -band_path = ase.dft.kpoints.bandpath(PATH, - dyn.structure.unit_cell, - N_POINTS) - -# Get the q points of the path -q_path = band_path.cartesian_kpts() - -# Get the values of x axis for plotting the band path -x_axis, xticks, xlabels = band_path.get_linear_kpoint_axis() - - -# Perform the interpolation -frequencies = CC.ForceTensor.get_phonons_in_qpath(dyn, q_path) - -# ============= PLOT THE FIGURE ================= -fig = plt.figure(dpi = 200) -ax = plt.gca() - -# Plot all the modes -for i in range(frequencies.shape[-1]): - ax.plot(x_axis, frequencies[:,i]) - -# Plot vertical lines for each high symmetry points -for x in xticks: - ax.axvline(x, 0, 1, color = "k", lw = 0.4) - -# Set the x labels to the high symmetry points -ax.set_xticks(xticks) -ax.set_xticklabels(xlabels) - -ax.set_ylabel("Energy [cm-1]") -ax.set_xlabel("q path") - -fig.tight_layout() -fig.savefig("dispersion.png") -fig.savefig("dispersion.eps") -plt.show() From d4b0401884cf6db77371b0033225b8bc44dfd458 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:35:37 +0100 Subject: [PATCH 14/18] Delete tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints directory --- .../.ipynb_checkpoints/QHA-checkpoint.py | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py diff --git a/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py b/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py deleted file mode 100644 index 30ee69ca..00000000 --- a/tutorials/QuasiHarmonicApproximation/.ipynb_checkpoints/QHA-checkpoint.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function -import cellconstructor as CC -import cellconstructor.Manipulate -import cellconstructor.Phonons -import numpy as np -import matplotlib.pyplot as plt - - - -""" -This example file provide the quasi harmonic approximation to compute the pressure -by interpolating two different dynamical matrix on a supercell 3x3x2 of common ice at -different volumes. -""" - -RyToEv=13.605698 -Ev_AngToGPa=160.21766208 - -# Import the two phonons -ph1 = CC.Phonons.Phonons("V804/dynmat", nqirr = 8) -ph2 = CC.Phonons.Phonons("V907/dynmat", nqirr = 8) - - -# Interpolate the dynamical matrices between the two volumes -N_points = 100 # How many points to interpolate -N_T = 100 -T = np.linspace(0, 300, N_T) -free_energy = CC.Manipulate.QHA_FreeEnergy(ph1, ph2, T, N_points) - - -# Get the volumes (The determinant of the unit cell vectors) -V0 = np.linalg.det(ph1.structure.unit_cell) -V1 = np.linalg.det(ph2.structure.unit_cell) - -print ("The two volumes are:", V0, "Angstrom^3 and", V1, "Angstrom^3") - -# Take the derivative and compute the pressure [Ry/angstrom^3] -pressure = np.diff(free_energy, axis = 0) / ((V0 - V1)/(N_points - 1)) -pressure *= RyToEv*Ev_AngToGPa * 10 # kbar - - -# Plot the free energy -plt.figure() -plt.imshow(free_energy, aspect = "auto") -plt.colorbar() - - -# Plot a single graf of the pressure -plt.figure() -plt.title("QHA Pressure contribution") -plt.plot(T, pressure[0,:]) -plt.xlabel("T [K]") -plt.ylabel("P [kbar]") -plt.show() From 6a20c125f5b66155fc7d2e78f00485144bea1f85 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:35:51 +0100 Subject: [PATCH 15/18] Delete tutorials/RadialDistributionFunction/.ipynb_checkpoints directory --- .../.ipynb_checkpoints/h2o-checkpoint.dyn | 1077 ----------------- 1 file changed, 1077 deletions(-) delete mode 100644 tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn diff --git a/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn b/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn deleted file mode 100644 index 376330ed..00000000 --- a/tutorials/RadialDistributionFunction/.ipynb_checkpoints/h2o-checkpoint.dyn +++ /dev/null @@ -1,1077 +0,0 @@ -Dynamical matrix file -default - 2 12 0 8.5037676 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 -Basis vectors - 0.973217108 0.000000000 0.000000000 - 0.486608554 0.845319362 0.000000000 - 0.000000000 0.000000000 1.590192051 - 1 'H ' 918.836054935965 - 2 'O ' 14582.1964298742 - 1 2 0.4866085540 0.2817095890 0.0949782740 - 2 1 0.4866085540 0.2787508620 0.3180055250 - 3 1 0.4866085540 0.0689293120 0.0280554270 - 4 2 0.9732171080 0.5643454260 1.4868195660 - 5 1 1.1518511060 0.4545173740 1.5628857730 - 6 1 0.7945831110 0.4545173740 1.5628857730 - 7 2 0.9732171080 0.5636097740 0.8900742990 - 8 1 0.9732171080 0.5665685010 1.1131015510 - 9 1 0.9732171080 0.7763900520 0.8231514520 - 10 2 0.4866085540 0.2809739370 0.6917235400 - 11 1 0.3079745570 0.3908019890 0.7677897470 - 12 1 0.6652425520 0.3908019890 0.7677897470 - - Dynamical Matrix in cartesian axes - - q = ( 0.000000000 0.000000000 0.000000000 ) - - 1 1 - 0.11584946 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.82182268 0.00000000 0.16886655 0.00000000 - 0.00000000 0.00000000 0.16886655 0.00000000 0.95237180 0.00000000 - 1 2 - -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.10095398 0.00000000 -0.01416349 0.00000000 - 0.00000000 0.00000000 0.05891446 0.00000000 -0.69413923 0.00000000 - 1 3 - -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.62884967 0.00000000 -0.11827583 0.00000000 - 0.00000000 0.00000000 -0.19762180 0.00000000 -0.14963933 0.00000000 - 1 4 - -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.08360949 0.00000000 0.00422445 0.00000000 - 0.00000000 0.00000000 0.00103466 0.00000000 0.00373450 0.00000000 - 1 5 - 0.01925038 0.00000000 -0.03312385 0.00000000 0.01956453 0.00000000 - -0.03167955 0.00000000 -0.00978269 0.00000000 -0.01830764 0.00000000 - 0.00867241 0.00000000 -0.01293408 0.00000000 -0.02439769 0.00000000 - 1 6 - 0.01925038 0.00000000 0.03312385 0.00000000 -0.01956453 0.00000000 - 0.03167955 0.00000000 -0.00978269 0.00000000 -0.01830764 0.00000000 - -0.00867241 0.00000000 -0.01293408 0.00000000 -0.02439769 0.00000000 - 1 7 - 0.00130736 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00178111 0.00000000 -0.00131520 0.00000000 - 0.00000000 0.00000000 0.00131520 0.00000000 -0.01847965 0.00000000 - 1 8 - -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00205108 0.00000000 0.00204797 0.00000000 - 0.00000000 0.00000000 -0.00116139 0.00000000 0.02213726 0.00000000 - 1 9 - -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00154776 0.00000000 -0.00040028 0.00000000 - 0.00000000 0.00000000 -0.01119291 0.00000000 0.00468808 0.00000000 - 1 10 - 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.01210823 0.00000000 -0.00907041 0.00000000 - 0.00000000 0.00000000 -0.00740392 0.00000000 -0.07314055 0.00000000 - 1 11 - -0.00104804 0.00000000 0.00045496 0.00000000 -0.00261184 0.00000000 - -0.00025909 0.00000000 0.00043365 0.00000000 0.00236601 0.00000000 - -0.01275350 0.00000000 0.00656946 0.00000000 0.00053849 0.00000000 - 1 12 - -0.00104804 0.00000000 -0.00045496 0.00000000 0.00261184 0.00000000 - 0.00025909 0.00000000 0.00043365 0.00000000 0.00236601 0.00000000 - 0.01275350 0.00000000 0.00656946 0.00000000 0.00053849 0.00000000 - 2 1 - -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.10095398 0.00000000 0.05891446 0.00000000 - 0.00000000 0.00000000 -0.01416349 0.00000000 -0.69413923 0.00000000 - 2 2 - 0.06838210 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.11880890 0.00000000 0.00471634 0.00000000 - 0.00000000 0.00000000 0.00471634 0.00000000 0.69459118 0.00000000 - 2 3 - -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00704542 0.00000000 -0.07432155 0.00000000 - 0.00000000 0.00000000 -0.00889208 0.00000000 -0.02118800 0.00000000 - 2 4 - -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00784790 0.00000000 0.00095621 0.00000000 - 0.00000000 0.00000000 -0.00218866 0.00000000 0.02355706 0.00000000 - 2 5 - 0.00449813 0.00000000 -0.00402947 0.00000000 -0.00825895 0.00000000 - -0.00587673 0.00000000 0.00151429 0.00000000 0.00440330 0.00000000 - -0.01487396 0.00000000 0.00942971 0.00000000 -0.00713054 0.00000000 - 2 6 - 0.00449813 0.00000000 0.00402947 0.00000000 0.00825895 0.00000000 - 0.00587673 0.00000000 0.00151429 0.00000000 0.00440330 0.00000000 - 0.01487396 0.00000000 0.00942971 0.00000000 -0.00713054 0.00000000 - 2 7 - -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00205108 0.00000000 0.00116139 0.00000000 - 0.00000000 0.00000000 -0.00204797 0.00000000 0.02213726 0.00000000 - 2 8 - 0.00053534 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00061854 0.00000000 -0.00124916 0.00000000 - 0.00000000 0.00000000 0.00124916 0.00000000 -0.02752885 0.00000000 - 2 9 - -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00214572 0.00000000 0.00215791 0.00000000 - 0.00000000 0.00000000 0.01655552 0.00000000 -0.00464964 0.00000000 - 2 10 - -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.01939748 0.00000000 0.01170030 0.00000000 - 0.00000000 0.00000000 0.00298573 0.00000000 0.02668255 0.00000000 - 2 11 - 0.00260018 0.00000000 -0.00158705 0.00000000 0.01000915 0.00000000 - -0.00030567 0.00000000 -0.00069963 0.00000000 -0.00642102 0.00000000 - 0.01778799 0.00000000 -0.00853623 0.00000000 -0.00260117 0.00000000 - 2 12 - 0.00260018 0.00000000 0.00158705 0.00000000 -0.01000915 0.00000000 - 0.00030567 0.00000000 -0.00069963 0.00000000 -0.00642102 0.00000000 - -0.01778799 0.00000000 -0.00853623 0.00000000 -0.00260117 0.00000000 - 3 1 - -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.62884967 0.00000000 -0.19762180 0.00000000 - 0.00000000 0.00000000 -0.11827583 0.00000000 -0.14963933 0.00000000 - 3 2 - -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00704542 0.00000000 -0.00889208 0.00000000 - 0.00000000 0.00000000 -0.07432155 0.00000000 -0.02118800 0.00000000 - 3 3 - 0.06917921 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.61325234 0.00000000 0.16954008 0.00000000 - 0.00000000 0.00000000 0.16954008 0.00000000 0.17821400 0.00000000 - 3 4 - -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.04589381 0.00000000 0.00752648 0.00000000 - 0.00000000 0.00000000 0.01499647 0.00000000 -0.02510867 0.00000000 - 3 5 - 0.00753674 0.00000000 0.01530146 0.00000000 0.01171428 0.00000000 - 0.03191275 0.00000000 -0.01897960 0.00000000 0.01331570 0.00000000 - 0.01870442 0.00000000 0.00338128 0.00000000 0.01172827 0.00000000 - 3 6 - 0.00753674 0.00000000 -0.01530146 0.00000000 -0.01171428 0.00000000 - -0.03191275 0.00000000 -0.01897960 0.00000000 0.01331570 0.00000000 - -0.01870442 0.00000000 0.00338128 0.00000000 0.01172827 0.00000000 - 3 7 - -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00154776 0.00000000 0.01119291 0.00000000 - 0.00000000 0.00000000 0.00040028 0.00000000 0.00468808 0.00000000 - 3 8 - -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00214572 0.00000000 -0.01655552 0.00000000 - 0.00000000 0.00000000 -0.00215791 0.00000000 -0.00464964 0.00000000 - 3 9 - 0.00051766 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00395634 0.00000000 -0.00121369 0.00000000 - 0.00000000 0.00000000 0.00121369 0.00000000 -0.00338285 0.00000000 - 3 10 - 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00051211 0.00000000 0.01287658 0.00000000 - 0.00000000 0.00000000 0.00231581 0.00000000 0.00110165 0.00000000 - 3 11 - 0.00016512 0.00000000 0.00030688 0.00000000 -0.00037550 0.00000000 - 0.00326324 0.00000000 -0.00171497 0.00000000 -0.00174310 0.00000000 - 0.00144169 0.00000000 -0.00023793 0.00000000 -0.00174915 0.00000000 - 3 12 - 0.00016512 0.00000000 -0.00030688 0.00000000 0.00037550 0.00000000 - -0.00326324 0.00000000 -0.00171497 0.00000000 -0.00174310 0.00000000 - -0.00144169 0.00000000 -0.00023793 0.00000000 -0.00174915 0.00000000 - 4 1 - -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.08360949 0.00000000 0.00103466 0.00000000 - 0.00000000 0.00000000 0.00422445 0.00000000 0.00373450 0.00000000 - 4 2 - -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00784790 0.00000000 -0.00218866 0.00000000 - 0.00000000 0.00000000 0.00095621 0.00000000 0.02355706 0.00000000 - 4 3 - -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.04589381 0.00000000 0.01499647 0.00000000 - 0.00000000 0.00000000 0.00752648 0.00000000 -0.02510867 0.00000000 - 4 4 - 1.04989861 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.51330794 0.00000000 -0.27558434 0.00000000 - 0.00000000 0.00000000 -0.27558434 0.00000000 0.32457497 0.00000000 - 4 5 - -0.45402227 0.00000000 0.25561456 0.00000000 -0.17805745 0.00000000 - 0.19887622 0.00000000 -0.23012753 0.00000000 0.12943346 0.00000000 - -0.14741975 0.00000000 0.13438072 0.00000000 -0.13746891 0.00000000 - 4 6 - -0.45402227 0.00000000 -0.25561456 0.00000000 0.17805745 0.00000000 - -0.19887622 0.00000000 -0.23012753 0.00000000 0.12943346 0.00000000 - 0.14741975 0.00000000 0.13438072 0.00000000 -0.13746891 0.00000000 - 4 7 - 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.01210823 0.00000000 0.00740392 0.00000000 - 0.00000000 0.00000000 0.00907041 0.00000000 -0.07314055 0.00000000 - 4 8 - -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.01939748 0.00000000 -0.00298573 0.00000000 - 0.00000000 0.00000000 -0.01170030 0.00000000 0.02668255 0.00000000 - 4 9 - 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00051211 0.00000000 -0.00231581 0.00000000 - 0.00000000 0.00000000 -0.01287658 0.00000000 0.00110165 0.00000000 - 4 10 - 0.00173537 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00140161 0.00000000 -0.00013023 0.00000000 - 0.00000000 0.00000000 0.00013023 0.00000000 -0.01764532 0.00000000 - 4 11 - -0.00060478 0.00000000 0.00030420 0.00000000 -0.00062368 0.00000000 - 0.00040537 0.00000000 -0.00052025 0.00000000 0.00046462 0.00000000 - -0.00882148 0.00000000 0.00475129 0.00000000 0.00512915 0.00000000 - 4 12 - -0.00060478 0.00000000 -0.00030420 0.00000000 0.00062368 0.00000000 - -0.00040537 0.00000000 -0.00052025 0.00000000 0.00046462 0.00000000 - 0.00882148 0.00000000 0.00475129 0.00000000 0.00512915 0.00000000 - 5 1 - 0.01925038 0.00000000 -0.03167955 0.00000000 0.00867241 0.00000000 - -0.03312385 0.00000000 -0.00978269 0.00000000 -0.01293408 0.00000000 - 0.01956453 0.00000000 -0.01830764 0.00000000 -0.02439769 0.00000000 - 5 2 - 0.00449813 0.00000000 -0.00587673 0.00000000 -0.01487396 0.00000000 - -0.00402947 0.00000000 0.00151429 0.00000000 0.00942971 0.00000000 - -0.00825895 0.00000000 0.00440330 0.00000000 -0.00713054 0.00000000 - 5 3 - 0.00753674 0.00000000 0.03191275 0.00000000 0.01870442 0.00000000 - 0.01530146 0.00000000 -0.01897960 0.00000000 0.00338128 0.00000000 - 0.01171428 0.00000000 0.01331570 0.00000000 0.01172827 0.00000000 - 5 4 - -0.45402227 0.00000000 0.19887622 0.00000000 -0.14741975 0.00000000 - 0.25561456 0.00000000 -0.23012753 0.00000000 0.13438072 0.00000000 - -0.17805745 0.00000000 0.12943346 0.00000000 -0.13746891 0.00000000 - 5 5 - 0.48758632 0.00000000 -0.21339648 0.00000000 0.14826407 0.00000000 - -0.21339648 0.00000000 0.22722893 0.00000000 -0.10952791 0.00000000 - 0.14826407 0.00000000 -0.10952791 0.00000000 0.14583933 0.00000000 - 5 6 - -0.06619473 0.00000000 0.01984028 0.00000000 -0.01338595 0.00000000 - -0.01984028 0.00000000 0.03056873 0.00000000 -0.02341442 0.00000000 - 0.01338595 0.00000000 -0.02341442 0.00000000 0.01544505 0.00000000 - 5 7 - -0.00104804 0.00000000 -0.00025909 0.00000000 0.01275350 0.00000000 - 0.00045496 0.00000000 0.00043365 0.00000000 -0.00656946 0.00000000 - 0.00261184 0.00000000 -0.00236601 0.00000000 0.00053849 0.00000000 - 5 8 - 0.00260018 0.00000000 -0.00030567 0.00000000 -0.01778799 0.00000000 - -0.00158705 0.00000000 -0.00069963 0.00000000 0.00853623 0.00000000 - -0.01000915 0.00000000 0.00642102 0.00000000 -0.00260117 0.00000000 - 5 9 - 0.00016512 0.00000000 0.00326324 0.00000000 -0.00144169 0.00000000 - 0.00030688 0.00000000 -0.00171497 0.00000000 0.00023793 0.00000000 - 0.00037550 0.00000000 0.00174310 0.00000000 -0.00174915 0.00000000 - 5 10 - -0.00060478 0.00000000 0.00040537 0.00000000 0.00882148 0.00000000 - 0.00030420 0.00000000 -0.00052025 0.00000000 -0.00475129 0.00000000 - 0.00062368 0.00000000 -0.00046462 0.00000000 0.00512915 0.00000000 - 5 11 - 0.00310282 0.00000000 -0.00138901 0.00000000 -0.00104543 0.00000000 - -0.00138901 0.00000000 0.00116852 0.00000000 0.00036956 0.00000000 - 0.00104543 0.00000000 -0.00036956 0.00000000 -0.00337297 0.00000000 - 5 12 - -0.00287051 0.00000000 -0.00138915 0.00000000 -0.00126441 0.00000000 - 0.00138915 0.00000000 0.00091142 0.00000000 0.00086395 0.00000000 - -0.00126441 0.00000000 -0.00086395 0.00000000 -0.00197263 0.00000000 - 6 1 - 0.01925038 0.00000000 0.03167955 0.00000000 -0.00867241 0.00000000 - 0.03312385 0.00000000 -0.00978269 0.00000000 -0.01293408 0.00000000 - -0.01956453 0.00000000 -0.01830764 0.00000000 -0.02439769 0.00000000 - 6 2 - 0.00449813 0.00000000 0.00587673 0.00000000 0.01487396 0.00000000 - 0.00402947 0.00000000 0.00151429 0.00000000 0.00942971 0.00000000 - 0.00825895 0.00000000 0.00440330 0.00000000 -0.00713054 0.00000000 - 6 3 - 0.00753674 0.00000000 -0.03191275 0.00000000 -0.01870442 0.00000000 - -0.01530146 0.00000000 -0.01897960 0.00000000 0.00338128 0.00000000 - -0.01171428 0.00000000 0.01331570 0.00000000 0.01172827 0.00000000 - 6 4 - -0.45402227 0.00000000 -0.19887622 0.00000000 0.14741975 0.00000000 - -0.25561456 0.00000000 -0.23012753 0.00000000 0.13438072 0.00000000 - 0.17805745 0.00000000 0.12943346 0.00000000 -0.13746891 0.00000000 - 6 5 - -0.06619473 0.00000000 -0.01984028 0.00000000 0.01338595 0.00000000 - 0.01984028 0.00000000 0.03056873 0.00000000 -0.02341442 0.00000000 - -0.01338595 0.00000000 -0.02341442 0.00000000 0.01544505 0.00000000 - 6 6 - 0.48758632 0.00000000 0.21339648 0.00000000 -0.14826407 0.00000000 - 0.21339648 0.00000000 0.22722893 0.00000000 -0.10952791 0.00000000 - -0.14826407 0.00000000 -0.10952791 0.00000000 0.14583933 0.00000000 - 6 7 - -0.00104804 0.00000000 0.00025909 0.00000000 -0.01275350 0.00000000 - -0.00045496 0.00000000 0.00043365 0.00000000 -0.00656946 0.00000000 - -0.00261184 0.00000000 -0.00236601 0.00000000 0.00053849 0.00000000 - 6 8 - 0.00260018 0.00000000 0.00030567 0.00000000 0.01778799 0.00000000 - 0.00158705 0.00000000 -0.00069963 0.00000000 0.00853623 0.00000000 - 0.01000915 0.00000000 0.00642102 0.00000000 -0.00260117 0.00000000 - 6 9 - 0.00016512 0.00000000 -0.00326324 0.00000000 0.00144169 0.00000000 - -0.00030688 0.00000000 -0.00171497 0.00000000 0.00023793 0.00000000 - -0.00037550 0.00000000 0.00174310 0.00000000 -0.00174915 0.00000000 - 6 10 - -0.00060478 0.00000000 -0.00040537 0.00000000 -0.00882148 0.00000000 - -0.00030420 0.00000000 -0.00052025 0.00000000 -0.00475129 0.00000000 - -0.00062368 0.00000000 -0.00046462 0.00000000 0.00512915 0.00000000 - 6 11 - -0.00287051 0.00000000 0.00138915 0.00000000 0.00126441 0.00000000 - -0.00138915 0.00000000 0.00091142 0.00000000 0.00086395 0.00000000 - 0.00126441 0.00000000 -0.00086395 0.00000000 -0.00197263 0.00000000 - 6 12 - 0.00310282 0.00000000 0.00138901 0.00000000 0.00104543 0.00000000 - 0.00138901 0.00000000 0.00116852 0.00000000 0.00036956 0.00000000 - -0.00104543 0.00000000 -0.00036956 0.00000000 -0.00337297 0.00000000 - 7 1 - 0.00130736 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00178111 0.00000000 0.00131520 0.00000000 - 0.00000000 0.00000000 -0.00131520 0.00000000 -0.01847965 0.00000000 - 7 2 - -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00205108 0.00000000 -0.00204797 0.00000000 - 0.00000000 0.00000000 0.00116139 0.00000000 0.02213726 0.00000000 - 7 3 - -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00154776 0.00000000 0.00040028 0.00000000 - 0.00000000 0.00000000 0.01119291 0.00000000 0.00468808 0.00000000 - 7 4 - 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.01210823 0.00000000 0.00907041 0.00000000 - 0.00000000 0.00000000 0.00740392 0.00000000 -0.07314055 0.00000000 - 7 5 - -0.00104804 0.00000000 0.00045496 0.00000000 0.00261184 0.00000000 - -0.00025909 0.00000000 0.00043365 0.00000000 -0.00236601 0.00000000 - 0.01275350 0.00000000 -0.00656946 0.00000000 0.00053849 0.00000000 - 7 6 - -0.00104804 0.00000000 -0.00045496 0.00000000 -0.00261184 0.00000000 - 0.00025909 0.00000000 0.00043365 0.00000000 -0.00236601 0.00000000 - -0.01275350 0.00000000 -0.00656946 0.00000000 0.00053849 0.00000000 - 7 7 - 0.11584946 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.82182268 0.00000000 -0.16886655 0.00000000 - 0.00000000 0.00000000 -0.16886655 0.00000000 0.95237180 0.00000000 - 7 8 - -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.10095398 0.00000000 0.01416349 0.00000000 - 0.00000000 0.00000000 -0.05891446 0.00000000 -0.69413923 0.00000000 - 7 9 - -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.62884967 0.00000000 0.11827583 0.00000000 - 0.00000000 0.00000000 0.19762180 0.00000000 -0.14963933 0.00000000 - 7 10 - -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.08360949 0.00000000 -0.00422445 0.00000000 - 0.00000000 0.00000000 -0.00103466 0.00000000 0.00373450 0.00000000 - 7 11 - 0.01925038 0.00000000 -0.03312385 0.00000000 -0.01956453 0.00000000 - -0.03167955 0.00000000 -0.00978269 0.00000000 0.01830764 0.00000000 - -0.00867241 0.00000000 0.01293408 0.00000000 -0.02439769 0.00000000 - 7 12 - 0.01925038 0.00000000 0.03312385 0.00000000 0.01956453 0.00000000 - 0.03167955 0.00000000 -0.00978269 0.00000000 0.01830764 0.00000000 - 0.00867241 0.00000000 0.01293408 0.00000000 -0.02439769 0.00000000 - 8 1 - -0.00164344 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00205108 0.00000000 -0.00116139 0.00000000 - 0.00000000 0.00000000 0.00204797 0.00000000 0.02213726 0.00000000 - 8 2 - 0.00053534 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00061854 0.00000000 0.00124916 0.00000000 - 0.00000000 0.00000000 -0.00124916 0.00000000 -0.02752885 0.00000000 - 8 3 - -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00214572 0.00000000 -0.00215791 0.00000000 - 0.00000000 0.00000000 -0.01655552 0.00000000 -0.00464964 0.00000000 - 8 4 - -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.01939748 0.00000000 -0.01170030 0.00000000 - 0.00000000 0.00000000 -0.00298573 0.00000000 0.02668255 0.00000000 - 8 5 - 0.00260018 0.00000000 -0.00158705 0.00000000 -0.01000915 0.00000000 - -0.00030567 0.00000000 -0.00069963 0.00000000 0.00642102 0.00000000 - -0.01778799 0.00000000 0.00853623 0.00000000 -0.00260117 0.00000000 - 8 6 - 0.00260018 0.00000000 0.00158705 0.00000000 0.01000915 0.00000000 - 0.00030567 0.00000000 -0.00069963 0.00000000 0.00642102 0.00000000 - 0.01778799 0.00000000 0.00853623 0.00000000 -0.00260117 0.00000000 - 8 7 - -0.04124201 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.10095398 0.00000000 -0.05891446 0.00000000 - 0.00000000 0.00000000 0.01416349 0.00000000 -0.69413923 0.00000000 - 8 8 - 0.06838210 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.11880890 0.00000000 -0.00471634 0.00000000 - 0.00000000 0.00000000 -0.00471634 0.00000000 0.69459118 0.00000000 - 8 9 - -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00704542 0.00000000 0.07432155 0.00000000 - 0.00000000 0.00000000 0.00889208 0.00000000 -0.02118800 0.00000000 - 8 10 - -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00784790 0.00000000 -0.00095621 0.00000000 - 0.00000000 0.00000000 0.00218866 0.00000000 0.02355706 0.00000000 - 8 11 - 0.00449813 0.00000000 -0.00402947 0.00000000 0.00825895 0.00000000 - -0.00587673 0.00000000 0.00151429 0.00000000 -0.00440330 0.00000000 - 0.01487396 0.00000000 -0.00942971 0.00000000 -0.00713054 0.00000000 - 8 12 - 0.00449813 0.00000000 0.00402947 0.00000000 -0.00825895 0.00000000 - 0.00587673 0.00000000 0.00151429 0.00000000 -0.00440330 0.00000000 - -0.01487396 0.00000000 -0.00942971 0.00000000 -0.00713054 0.00000000 - 9 1 - -0.00024026 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00154776 0.00000000 -0.01119291 0.00000000 - 0.00000000 0.00000000 -0.00040028 0.00000000 0.00468808 0.00000000 - 9 2 - -0.00013703 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00214572 0.00000000 0.01655552 0.00000000 - 0.00000000 0.00000000 0.00215791 0.00000000 -0.00464964 0.00000000 - 9 3 - 0.00051766 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00395634 0.00000000 0.00121369 0.00000000 - 0.00000000 0.00000000 -0.00121369 0.00000000 -0.00338285 0.00000000 - 9 4 - 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00051211 0.00000000 -0.01287658 0.00000000 - 0.00000000 0.00000000 -0.00231581 0.00000000 0.00110165 0.00000000 - 9 5 - 0.00016512 0.00000000 0.00030688 0.00000000 0.00037550 0.00000000 - 0.00326324 0.00000000 -0.00171497 0.00000000 0.00174310 0.00000000 - -0.00144169 0.00000000 0.00023793 0.00000000 -0.00174915 0.00000000 - 9 6 - 0.00016512 0.00000000 -0.00030688 0.00000000 -0.00037550 0.00000000 - -0.00326324 0.00000000 -0.00171497 0.00000000 0.00174310 0.00000000 - 0.00144169 0.00000000 0.00023793 0.00000000 -0.00174915 0.00000000 - 9 7 - -0.04325470 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.62884967 0.00000000 0.19762180 0.00000000 - 0.00000000 0.00000000 0.11827583 0.00000000 -0.14963933 0.00000000 - 9 8 - -0.00303050 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00704542 0.00000000 0.00889208 0.00000000 - 0.00000000 0.00000000 0.07432155 0.00000000 -0.02118800 0.00000000 - 9 9 - 0.06917921 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.61325234 0.00000000 -0.16954008 0.00000000 - 0.00000000 0.00000000 -0.16954008 0.00000000 0.17821400 0.00000000 - 9 10 - -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.04589381 0.00000000 -0.00752648 0.00000000 - 0.00000000 0.00000000 -0.01499647 0.00000000 -0.02510867 0.00000000 - 9 11 - 0.00753674 0.00000000 0.01530146 0.00000000 -0.01171428 0.00000000 - 0.03191275 0.00000000 -0.01897960 0.00000000 -0.01331570 0.00000000 - -0.01870442 0.00000000 -0.00338128 0.00000000 0.01172827 0.00000000 - 9 12 - 0.00753674 0.00000000 -0.01530146 0.00000000 0.01171428 0.00000000 - -0.03191275 0.00000000 -0.01897960 0.00000000 -0.01331570 0.00000000 - 0.01870442 0.00000000 -0.00338128 0.00000000 0.01172827 0.00000000 - 10 1 - 0.01311284 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.01210823 0.00000000 -0.00740392 0.00000000 - 0.00000000 0.00000000 -0.00907041 0.00000000 -0.07314055 0.00000000 - 10 2 - -0.02846483 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.01939748 0.00000000 0.00298573 0.00000000 - 0.00000000 0.00000000 0.01170030 0.00000000 0.02668255 0.00000000 - 10 3 - 0.00011131 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00051211 0.00000000 0.00231581 0.00000000 - 0.00000000 0.00000000 0.01287658 0.00000000 0.00110165 0.00000000 - 10 4 - 0.00173537 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.00140161 0.00000000 0.00013023 0.00000000 - 0.00000000 0.00000000 -0.00013023 0.00000000 -0.01764532 0.00000000 - 10 5 - -0.00060478 0.00000000 0.00030420 0.00000000 0.00062368 0.00000000 - 0.00040537 0.00000000 -0.00052025 0.00000000 -0.00046462 0.00000000 - 0.00882148 0.00000000 -0.00475129 0.00000000 0.00512915 0.00000000 - 10 6 - -0.00060478 0.00000000 -0.00030420 0.00000000 -0.00062368 0.00000000 - -0.00040537 0.00000000 -0.00052025 0.00000000 -0.00046462 0.00000000 - -0.00882148 0.00000000 -0.00475129 0.00000000 0.00512915 0.00000000 - 10 7 - -0.08008370 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.08360949 0.00000000 -0.00103466 0.00000000 - 0.00000000 0.00000000 -0.00422445 0.00000000 0.00373450 0.00000000 - 10 8 - -0.00859460 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 -0.00784790 0.00000000 0.00218866 0.00000000 - 0.00000000 0.00000000 -0.00095621 0.00000000 0.02355706 0.00000000 - 10 9 - -0.03854513 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.04589381 0.00000000 -0.01499647 0.00000000 - 0.00000000 0.00000000 -0.00752648 0.00000000 -0.02510867 0.00000000 - 10 10 - 1.04989861 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 - 0.00000000 0.00000000 0.51330794 0.00000000 0.27558434 0.00000000 - 0.00000000 0.00000000 0.27558434 0.00000000 0.32457497 0.00000000 - 10 11 - -0.45402227 0.00000000 0.25561456 0.00000000 0.17805745 0.00000000 - 0.19887622 0.00000000 -0.23012753 0.00000000 -0.12943346 0.00000000 - 0.14741975 0.00000000 -0.13438072 0.00000000 -0.13746891 0.00000000 - 10 12 - -0.45402227 0.00000000 -0.25561456 0.00000000 -0.17805745 0.00000000 - -0.19887622 0.00000000 -0.23012753 0.00000000 -0.12943346 0.00000000 - -0.14741975 0.00000000 -0.13438072 0.00000000 -0.13746891 0.00000000 - 11 1 - -0.00104804 0.00000000 -0.00025909 0.00000000 -0.01275350 0.00000000 - 0.00045496 0.00000000 0.00043365 0.00000000 0.00656946 0.00000000 - -0.00261184 0.00000000 0.00236601 0.00000000 0.00053849 0.00000000 - 11 2 - 0.00260018 0.00000000 -0.00030567 0.00000000 0.01778799 0.00000000 - -0.00158705 0.00000000 -0.00069963 0.00000000 -0.00853623 0.00000000 - 0.01000915 0.00000000 -0.00642102 0.00000000 -0.00260117 0.00000000 - 11 3 - 0.00016512 0.00000000 0.00326324 0.00000000 0.00144169 0.00000000 - 0.00030688 0.00000000 -0.00171497 0.00000000 -0.00023793 0.00000000 - -0.00037550 0.00000000 -0.00174310 0.00000000 -0.00174915 0.00000000 - 11 4 - -0.00060478 0.00000000 0.00040537 0.00000000 -0.00882148 0.00000000 - 0.00030420 0.00000000 -0.00052025 0.00000000 0.00475129 0.00000000 - -0.00062368 0.00000000 0.00046462 0.00000000 0.00512915 0.00000000 - 11 5 - 0.00310282 0.00000000 -0.00138901 0.00000000 0.00104543 0.00000000 - -0.00138901 0.00000000 0.00116852 0.00000000 -0.00036956 0.00000000 - -0.00104543 0.00000000 0.00036956 0.00000000 -0.00337297 0.00000000 - 11 6 - -0.00287051 0.00000000 -0.00138915 0.00000000 0.00126441 0.00000000 - 0.00138915 0.00000000 0.00091142 0.00000000 -0.00086395 0.00000000 - 0.00126441 0.00000000 0.00086395 0.00000000 -0.00197263 0.00000000 - 11 7 - 0.01925038 0.00000000 -0.03167955 0.00000000 -0.00867241 0.00000000 - -0.03312385 0.00000000 -0.00978269 0.00000000 0.01293408 0.00000000 - -0.01956453 0.00000000 0.01830764 0.00000000 -0.02439769 0.00000000 - 11 8 - 0.00449813 0.00000000 -0.00587673 0.00000000 0.01487396 0.00000000 - -0.00402947 0.00000000 0.00151429 0.00000000 -0.00942971 0.00000000 - 0.00825895 0.00000000 -0.00440330 0.00000000 -0.00713054 0.00000000 - 11 9 - 0.00753674 0.00000000 0.03191275 0.00000000 -0.01870442 0.00000000 - 0.01530146 0.00000000 -0.01897960 0.00000000 -0.00338128 0.00000000 - -0.01171428 0.00000000 -0.01331570 0.00000000 0.01172827 0.00000000 - 11 10 - -0.45402227 0.00000000 0.19887622 0.00000000 0.14741975 0.00000000 - 0.25561456 0.00000000 -0.23012753 0.00000000 -0.13438072 0.00000000 - 0.17805745 0.00000000 -0.12943346 0.00000000 -0.13746891 0.00000000 - 11 11 - 0.48758632 0.00000000 -0.21339648 0.00000000 -0.14826407 0.00000000 - -0.21339648 0.00000000 0.22722893 0.00000000 0.10952791 0.00000000 - -0.14826407 0.00000000 0.10952791 0.00000000 0.14583933 0.00000000 - 11 12 - -0.06619473 0.00000000 0.01984028 0.00000000 0.01338595 0.00000000 - -0.01984028 0.00000000 0.03056873 0.00000000 0.02341442 0.00000000 - -0.01338595 0.00000000 0.02341442 0.00000000 0.01544505 0.00000000 - 12 1 - -0.00104804 0.00000000 0.00025909 0.00000000 0.01275350 0.00000000 - -0.00045496 0.00000000 0.00043365 0.00000000 0.00656946 0.00000000 - 0.00261184 0.00000000 0.00236601 0.00000000 0.00053849 0.00000000 - 12 2 - 0.00260018 0.00000000 0.00030567 0.00000000 -0.01778799 0.00000000 - 0.00158705 0.00000000 -0.00069963 0.00000000 -0.00853623 0.00000000 - -0.01000915 0.00000000 -0.00642102 0.00000000 -0.00260117 0.00000000 - 12 3 - 0.00016512 0.00000000 -0.00326324 0.00000000 -0.00144169 0.00000000 - -0.00030688 0.00000000 -0.00171497 0.00000000 -0.00023793 0.00000000 - 0.00037550 0.00000000 -0.00174310 0.00000000 -0.00174915 0.00000000 - 12 4 - -0.00060478 0.00000000 -0.00040537 0.00000000 0.00882148 0.00000000 - -0.00030420 0.00000000 -0.00052025 0.00000000 0.00475129 0.00000000 - 0.00062368 0.00000000 0.00046462 0.00000000 0.00512915 0.00000000 - 12 5 - -0.00287051 0.00000000 0.00138915 0.00000000 -0.00126441 0.00000000 - -0.00138915 0.00000000 0.00091142 0.00000000 -0.00086395 0.00000000 - -0.00126441 0.00000000 0.00086395 0.00000000 -0.00197263 0.00000000 - 12 6 - 0.00310282 0.00000000 0.00138901 0.00000000 -0.00104543 0.00000000 - 0.00138901 0.00000000 0.00116852 0.00000000 -0.00036956 0.00000000 - 0.00104543 0.00000000 0.00036956 0.00000000 -0.00337297 0.00000000 - 12 7 - 0.01925038 0.00000000 0.03167955 0.00000000 0.00867241 0.00000000 - 0.03312385 0.00000000 -0.00978269 0.00000000 0.01293408 0.00000000 - 0.01956453 0.00000000 0.01830764 0.00000000 -0.02439769 0.00000000 - 12 8 - 0.00449813 0.00000000 0.00587673 0.00000000 -0.01487396 0.00000000 - 0.00402947 0.00000000 0.00151429 0.00000000 -0.00942971 0.00000000 - -0.00825895 0.00000000 -0.00440330 0.00000000 -0.00713054 0.00000000 - 12 9 - 0.00753674 0.00000000 -0.03191275 0.00000000 0.01870442 0.00000000 - -0.01530146 0.00000000 -0.01897960 0.00000000 -0.00338128 0.00000000 - 0.01171428 0.00000000 -0.01331570 0.00000000 0.01172827 0.00000000 - 12 10 - -0.45402227 0.00000000 -0.19887622 0.00000000 -0.14741975 0.00000000 - -0.25561456 0.00000000 -0.23012753 0.00000000 -0.13438072 0.00000000 - -0.17805745 0.00000000 -0.12943346 0.00000000 -0.13746891 0.00000000 - 12 11 - -0.06619473 0.00000000 -0.01984028 0.00000000 -0.01338595 0.00000000 - 0.01984028 0.00000000 0.03056873 0.00000000 0.02341442 0.00000000 - 0.01338595 0.00000000 0.02341442 0.00000000 0.01544505 0.00000000 - 12 12 - 0.48758632 0.00000000 0.21339648 0.00000000 0.14826407 0.00000000 - 0.21339648 0.00000000 0.22722893 0.00000000 0.10952791 0.00000000 - 0.14826407 0.00000000 0.10952791 0.00000000 0.14583933 0.00000000 - - Diagonalizing the dynamical matrix - - q = ( 0.000000000 0.000000000 0.000000000 ) - - ************************************************************************** - freq ( 1) = -0.614260 [THz] = -20.489523 [cm-1] - ( 0.000000 0.000000 -0.001962 0.000000 0.287945 0.000000 ) - ( 0.000000 0.000000 -0.001071 0.000000 0.287824 0.000000 ) - ( 0.000000 0.000000 -0.002314 0.000000 0.288648 0.000000 ) - ( 0.000000 0.000000 -0.001362 0.000000 0.289789 0.000000 ) - ( 0.000052 0.000000 -0.001855 0.000000 0.288903 0.000000 ) - ( -0.000052 0.000000 -0.001855 0.000000 0.288903 0.000000 ) - ( -0.000000 0.000000 0.001962 0.000000 0.287945 0.000000 ) - ( 0.000000 0.000000 0.001071 0.000000 0.287824 0.000000 ) - ( -0.000000 0.000000 0.002314 0.000000 0.288648 0.000000 ) - ( -0.000000 0.000000 0.001362 0.000000 0.289789 0.000000 ) - ( -0.000052 0.000000 0.001855 0.000000 0.288903 0.000000 ) - ( 0.000052 0.000000 0.001855 0.000000 0.288903 0.000000 ) - freq ( 2) = 0.121946 [THz] = 4.067685 [cm-1] - ( -0.000000 0.000000 0.288712 0.000000 -0.000052 0.000000 ) - ( -0.000000 0.000000 0.288716 0.000000 -0.000053 0.000000 ) - ( -0.000000 0.000000 0.288709 0.000000 -0.000032 0.000000 ) - ( -0.000000 0.000000 0.288630 0.000000 -0.000040 0.000000 ) - ( 0.000004 0.000000 0.288642 0.000000 -0.000041 0.000000 ) - ( -0.000004 0.000000 0.288642 0.000000 -0.000041 0.000000 ) - ( -0.000000 0.000000 0.288712 0.000000 0.000052 0.000000 ) - ( -0.000000 0.000000 0.288716 0.000000 0.000053 0.000000 ) - ( -0.000000 0.000000 0.288709 0.000000 0.000032 0.000000 ) - ( -0.000000 0.000000 0.288630 0.000000 0.000040 0.000000 ) - ( 0.000004 0.000000 0.288642 0.000000 0.000041 0.000000 ) - ( -0.000004 0.000000 0.288642 0.000000 0.000041 0.000000 ) - freq ( 3) = 0.207164 [THz] = 6.910251 [cm-1] - ( 0.288150 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288520 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288500 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.289016 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288932 0.000000 -0.000168 0.000000 0.000128 0.000000 ) - ( 0.288932 0.000000 0.000168 0.000000 -0.000128 0.000000 ) - ( 0.288150 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288520 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288500 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.289016 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.288932 0.000000 -0.000168 0.000000 -0.000128 0.000000 ) - ( 0.288932 0.000000 0.000168 0.000000 0.000128 0.000000 ) - freq ( 4) = 1.824548 [THz] = 60.860378 [cm-1] - ( 0.306135 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.092112 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.314874 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.292992 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.319679 0.000000 -0.000398 0.000000 -0.064873 0.000000 ) - ( 0.319679 0.000000 0.000398 0.000000 0.064873 0.000000 ) - ( -0.306135 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.092112 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.314874 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.292992 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.319679 0.000000 0.000398 0.000000 -0.064873 0.000000 ) - ( -0.319679 0.000000 -0.000398 0.000000 0.064873 0.000000 ) - freq ( 5) = 1.877116 [THz] = 62.613851 [cm-1] - ( 0.000000 0.000000 0.294936 0.000000 0.006583 0.000000 ) - ( 0.000000 0.000000 0.139856 0.000000 0.006868 0.000000 ) - ( 0.000000 0.000000 0.322994 0.000000 -0.085038 0.000000 ) - ( 0.000000 0.000000 0.288436 0.000000 -0.002559 0.000000 ) - ( -0.001014 0.000000 0.312868 0.000000 0.037616 0.000000 ) - ( 0.001014 0.000000 0.312868 0.000000 0.037616 0.000000 ) - ( -0.000000 0.000000 -0.294936 0.000000 0.006583 0.000000 ) - ( -0.000000 0.000000 -0.139856 0.000000 0.006868 0.000000 ) - ( -0.000000 0.000000 -0.322994 0.000000 -0.085038 0.000000 ) - ( -0.000000 0.000000 -0.288436 0.000000 -0.002559 0.000000 ) - ( 0.001014 0.000000 -0.312868 0.000000 0.037616 0.000000 ) - ( -0.001014 0.000000 -0.312868 0.000000 0.037616 0.000000 ) - freq ( 6) = 5.924224 [THz] = 197.610826 [cm-1] - ( 0.000000 0.000000 0.041694 0.000000 0.357517 0.000000 ) - ( 0.000000 0.000000 -0.043262 0.000000 0.358199 0.000000 ) - ( -0.000000 0.000000 0.114156 0.000000 0.223962 0.000000 ) - ( -0.000000 0.000000 -0.052961 0.000000 -0.351392 0.000000 ) - ( -0.037498 0.000000 0.054002 0.000000 -0.145170 0.000000 ) - ( 0.037498 0.000000 0.054002 0.000000 -0.145170 0.000000 ) - ( -0.000000 0.000000 0.041694 0.000000 -0.357517 0.000000 ) - ( -0.000000 0.000000 -0.043262 0.000000 -0.358199 0.000000 ) - ( 0.000000 0.000000 0.114156 0.000000 -0.223962 0.000000 ) - ( 0.000000 0.000000 -0.052961 0.000000 0.351392 0.000000 ) - ( -0.037498 0.000000 0.054002 0.000000 0.145170 0.000000 ) - ( 0.037498 0.000000 0.054002 0.000000 0.145170 0.000000 ) - freq ( 7) = 8.051612 [THz] = 268.572872 [cm-1] - ( 0.320549 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.336665 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.081599 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.312957 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.265320 0.000000 0.110710 0.000000 -0.083672 0.000000 ) - ( -0.265320 0.000000 -0.110710 0.000000 0.083672 0.000000 ) - ( -0.320549 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.336665 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.081599 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.312957 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.265320 0.000000 -0.110710 0.000000 -0.083672 0.000000 ) - ( 0.265320 0.000000 0.110710 0.000000 0.083672 0.000000 ) - freq ( 8) = 8.339785 [THz] = 278.185291 [cm-1] - ( 0.000000 0.000000 0.199694 0.000000 0.247330 0.000000 ) - ( 0.000000 0.000000 0.224913 0.000000 0.293869 0.000000 ) - ( 0.000000 0.000000 0.254527 0.000000 0.229680 0.000000 ) - ( -0.000000 0.000000 -0.221032 0.000000 -0.269203 0.000000 ) - ( 0.002130 0.000000 -0.080706 0.000000 -0.071091 0.000000 ) - ( -0.002130 0.000000 -0.080706 0.000000 -0.071091 0.000000 ) - ( -0.000000 0.000000 -0.199694 0.000000 0.247330 0.000000 ) - ( -0.000000 0.000000 -0.224913 0.000000 0.293869 0.000000 ) - ( 0.000000 0.000000 -0.254527 0.000000 0.229680 0.000000 ) - ( 0.000000 0.000000 0.221032 0.000000 -0.269203 0.000000 ) - ( -0.002130 0.000000 0.080706 0.000000 -0.071091 0.000000 ) - ( 0.002130 0.000000 0.080706 0.000000 -0.071091 0.000000 ) - freq ( 9) = 8.479599 [THz] = 282.848972 [cm-1] - ( -0.373863 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.094230 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.095277 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.347179 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.297421 0.000000 -0.122424 0.000000 0.086147 0.000000 ) - ( 0.297421 0.000000 0.122424 0.000000 -0.086147 0.000000 ) - ( -0.373863 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.094230 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.095277 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.347179 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.297421 0.000000 -0.122424 0.000000 -0.086147 0.000000 ) - ( 0.297421 0.000000 0.122424 0.000000 0.086147 0.000000 ) - freq ( 10) = 8.797510 [THz] = 293.453339 [cm-1] - ( 0.000000 0.000000 0.008630 0.000000 -0.283918 0.000000 ) - ( -0.000000 0.000000 0.007935 0.000000 -0.295395 0.000000 ) - ( -0.000000 0.000000 0.004103 0.000000 -0.272828 0.000000 ) - ( -0.000000 0.000000 -0.009736 0.000000 -0.301222 0.000000 ) - ( 0.000432 0.000000 0.001913 0.000000 -0.288709 0.000000 ) - ( -0.000432 0.000000 0.001913 0.000000 -0.288709 0.000000 ) - ( 0.000000 0.000000 0.008630 0.000000 0.283918 0.000000 ) - ( 0.000000 0.000000 0.007935 0.000000 0.295395 0.000000 ) - ( 0.000000 0.000000 0.004103 0.000000 0.272828 0.000000 ) - ( -0.000000 0.000000 -0.009736 0.000000 0.301222 0.000000 ) - ( 0.000432 0.000000 0.001913 0.000000 0.288709 0.000000 ) - ( -0.000432 0.000000 0.001913 0.000000 0.288709 0.000000 ) - freq ( 11) = 11.512386 [THz] = 384.011863 [cm-1] - ( 0.000000 0.000000 -0.331565 0.000000 0.035201 0.000000 ) - ( 0.000000 0.000000 -0.184395 0.000000 0.032016 0.000000 ) - ( 0.000000 0.000000 -0.344120 0.000000 0.041810 0.000000 ) - ( 0.000000 0.000000 0.337392 0.000000 -0.046409 0.000000 ) - ( -0.033447 0.000000 0.218817 0.000000 -0.099111 0.000000 ) - ( 0.033447 0.000000 0.218817 0.000000 -0.099111 0.000000 ) - ( -0.000000 0.000000 -0.331565 0.000000 -0.035201 0.000000 ) - ( -0.000000 0.000000 -0.184395 0.000000 -0.032016 0.000000 ) - ( 0.000000 0.000000 -0.344120 0.000000 -0.041810 0.000000 ) - ( 0.000000 0.000000 0.337392 0.000000 0.046409 0.000000 ) - ( -0.033447 0.000000 0.218817 0.000000 0.099111 0.000000 ) - ( 0.033447 0.000000 0.218817 0.000000 0.099111 0.000000 ) - freq ( 12) = 11.626042 [THz] = 387.803025 [cm-1] - ( -0.000000 0.000000 -0.239601 0.000000 0.203299 0.000000 ) - ( -0.000000 0.000000 -0.314439 0.000000 0.198897 0.000000 ) - ( -0.000000 0.000000 -0.184409 0.000000 0.059195 0.000000 ) - ( -0.000000 0.000000 0.237635 0.000000 -0.199016 0.000000 ) - ( -0.035952 0.000000 0.197060 0.000000 -0.156509 0.000000 ) - ( 0.035952 0.000000 0.197060 0.000000 -0.156509 0.000000 ) - ( 0.000000 0.000000 0.239601 0.000000 0.203299 0.000000 ) - ( 0.000000 0.000000 0.314439 0.000000 0.198897 0.000000 ) - ( -0.000000 0.000000 0.184409 0.000000 0.059195 0.000000 ) - ( -0.000000 0.000000 -0.237635 0.000000 -0.199016 0.000000 ) - ( 0.035952 0.000000 -0.197060 0.000000 -0.156509 0.000000 ) - ( -0.035952 0.000000 -0.197060 0.000000 -0.156509 0.000000 ) - freq ( 13) = 19.794106 [THz] = 660.260289 [cm-1] - ( -0.007247 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.206154 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.277056 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.008984 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.255470 0.000000 -0.308826 0.000000 0.172235 0.000000 ) - ( -0.255470 0.000000 0.308826 0.000000 -0.172235 0.000000 ) - ( -0.007247 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.206154 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.277056 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.008984 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.255470 0.000000 -0.308826 0.000000 -0.172235 0.000000 ) - ( -0.255470 0.000000 0.308826 0.000000 0.172235 0.000000 ) - freq ( 14) = 19.863612 [THz] = 662.578779 [cm-1] - ( 0.000000 0.000000 -0.005249 0.000000 -0.008036 0.000000 ) - ( -0.000000 0.000000 0.436905 0.000000 -0.003041 0.000000 ) - ( -0.000000 0.000000 -0.136053 0.000000 0.413368 0.000000 ) - ( -0.000000 0.000000 0.004052 0.000000 0.008511 0.000000 ) - ( -0.001488 0.000000 -0.140962 0.000000 -0.199744 0.000000 ) - ( 0.001488 0.000000 -0.140962 0.000000 -0.199744 0.000000 ) - ( 0.000000 0.000000 -0.005249 0.000000 0.008036 0.000000 ) - ( -0.000000 0.000000 0.436905 0.000000 0.003041 0.000000 ) - ( -0.000000 0.000000 -0.136053 0.000000 -0.413368 0.000000 ) - ( -0.000000 0.000000 0.004052 0.000000 -0.008511 0.000000 ) - ( -0.001488 0.000000 -0.140962 0.000000 0.199744 0.000000 ) - ( 0.001488 0.000000 -0.140962 0.000000 0.199744 0.000000 ) - freq ( 15) = 20.013897 [THz] = 667.591740 [cm-1] - ( 0.004130 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.005379 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.389775 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.008727 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.219327 0.000000 0.354127 0.000000 -0.021596 0.000000 ) - ( 0.219327 0.000000 -0.354127 0.000000 0.021596 0.000000 ) - ( -0.004130 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.005379 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.389775 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.008727 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.219327 0.000000 -0.354127 0.000000 -0.021596 0.000000 ) - ( -0.219327 0.000000 0.354127 0.000000 0.021596 0.000000 ) - freq ( 16) = 20.476336 [THz] = 683.017052 [cm-1] - ( 0.000566 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.375790 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.329108 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.000788 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.025139 0.000000 -0.171084 0.000000 -0.308760 0.000000 ) - ( 0.025139 0.000000 0.171084 0.000000 0.308760 0.000000 ) - ( 0.000566 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.375790 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.329108 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.000788 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.025139 0.000000 -0.171084 0.000000 0.308760 0.000000 ) - ( 0.025139 0.000000 0.171084 0.000000 -0.308760 0.000000 ) - freq ( 17) = 22.845039 [THz] = 762.028462 [cm-1] - ( -0.000000 0.000000 0.006467 0.000000 0.013313 0.000000 ) - ( -0.000000 0.000000 -0.442128 0.000000 0.004637 0.000000 ) - ( 0.000000 0.000000 0.151214 0.000000 -0.449915 0.000000 ) - ( -0.000000 0.000000 -0.030040 0.000000 -0.006016 0.000000 ) - ( 0.010010 0.000000 0.108562 0.000000 0.164747 0.000000 ) - ( -0.010010 0.000000 0.108562 0.000000 0.164747 0.000000 ) - ( 0.000000 0.000000 -0.006467 0.000000 0.013313 0.000000 ) - ( -0.000000 0.000000 0.442128 0.000000 0.004637 0.000000 ) - ( 0.000000 0.000000 -0.151214 0.000000 -0.449915 0.000000 ) - ( 0.000000 0.000000 0.030040 0.000000 -0.006016 0.000000 ) - ( -0.010010 0.000000 -0.108562 0.000000 0.164747 0.000000 ) - ( 0.010010 0.000000 -0.108562 0.000000 0.164747 0.000000 ) - freq ( 18) = 25.006256 [THz] = 834.118899 [cm-1] - ( 0.030077 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.071820 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.282270 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.023857 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.167018 0.000000 -0.023716 0.000000 0.422361 0.000000 ) - ( -0.167018 0.000000 0.023716 0.000000 -0.422361 0.000000 ) - ( -0.030077 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.071820 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.282270 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.023857 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.167018 0.000000 0.023716 0.000000 0.422361 0.000000 ) - ( 0.167018 0.000000 -0.023716 0.000000 -0.422361 0.000000 ) - freq ( 19) = 28.752969 [THz] = 959.095799 [cm-1] - ( 0.027583 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.700479 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.005033 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.017776 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.005173 0.000000 -0.005029 0.000000 -0.063731 0.000000 ) - ( 0.005173 0.000000 0.005029 0.000000 0.063731 0.000000 ) - ( -0.027583 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.700479 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.005033 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.017776 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.005173 0.000000 0.005029 0.000000 -0.063731 0.000000 ) - ( -0.005173 0.000000 -0.005029 0.000000 0.063731 0.000000 ) - freq ( 20) = 29.478312 [THz] = 983.290632 [cm-1] - ( 0.000000 0.000000 0.024280 0.000000 0.021249 0.000000 ) - ( -0.000000 0.000000 -0.239107 0.000000 0.024256 0.000000 ) - ( -0.000000 0.000000 0.080795 0.000000 -0.143353 0.000000 ) - ( 0.000000 0.000000 0.015097 0.000000 0.033496 0.000000 ) - ( 0.005035 0.000000 -0.256342 0.000000 -0.375032 0.000000 ) - ( -0.005035 0.000000 -0.256342 0.000000 -0.375032 0.000000 ) - ( 0.000000 0.000000 -0.024280 0.000000 0.021249 0.000000 ) - ( 0.000000 0.000000 0.239107 0.000000 0.024256 0.000000 ) - ( 0.000000 0.000000 -0.080795 0.000000 -0.143353 0.000000 ) - ( 0.000000 0.000000 -0.015097 0.000000 0.033496 0.000000 ) - ( -0.005035 0.000000 0.256342 0.000000 -0.375032 0.000000 ) - ( 0.005035 0.000000 0.256342 0.000000 -0.375032 0.000000 ) - freq ( 21) = 31.171406 [THz] = 1039.766193 [cm-1] - ( 0.040180 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.447581 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.422309 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.029792 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.120295 0.000000 -0.167304 0.000000 0.130193 0.000000 ) - ( -0.120295 0.000000 0.167304 0.000000 -0.130193 0.000000 ) - ( 0.040180 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.447581 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.422309 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.029792 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.120295 0.000000 -0.167304 0.000000 -0.130193 0.000000 ) - ( -0.120295 0.000000 0.167304 0.000000 0.130193 0.000000 ) - freq ( 22) = 32.440719 [THz] = 1082.105905 [cm-1] - ( -0.000000 0.000000 -0.016741 0.000000 -0.022554 0.000000 ) - ( 0.000000 0.000000 0.257880 0.000000 -0.017510 0.000000 ) - ( 0.000000 0.000000 -0.100301 0.000000 0.211066 0.000000 ) - ( -0.000000 0.000000 -0.025213 0.000000 -0.031270 0.000000 ) - ( 0.004500 0.000000 0.254123 0.000000 0.351413 0.000000 ) - ( -0.004500 0.000000 0.254123 0.000000 0.351413 0.000000 ) - ( -0.000000 0.000000 -0.016741 0.000000 0.022554 0.000000 ) - ( 0.000000 0.000000 0.257880 0.000000 0.017510 0.000000 ) - ( 0.000000 0.000000 -0.100301 0.000000 -0.211066 0.000000 ) - ( -0.000000 0.000000 -0.025213 0.000000 0.031270 0.000000 ) - ( 0.004500 0.000000 0.254123 0.000000 -0.351413 0.000000 ) - ( -0.004500 0.000000 0.254123 0.000000 -0.351413 0.000000 ) - freq ( 23) = 34.006409 [THz] = 1134.331691 [cm-1] - ( 0.018708 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.044791 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.509721 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.014744 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.049373 0.000000 -0.262416 0.000000 -0.217958 0.000000 ) - ( -0.049373 0.000000 0.262416 0.000000 0.217958 0.000000 ) - ( -0.018708 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.044791 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.509721 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.014744 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.049373 0.000000 0.262416 0.000000 -0.217958 0.000000 ) - ( 0.049373 0.000000 -0.262416 0.000000 0.217958 0.000000 ) - freq ( 24) = 35.944576 [THz] = 1198.981999 [cm-1] - ( 0.001245 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.339074 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( -0.366312 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.000993 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.004144 0.000000 -0.207683 0.000000 -0.286830 0.000000 ) - ( -0.004144 0.000000 0.207683 0.000000 0.286830 0.000000 ) - ( 0.001245 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.339074 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.366312 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.000993 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.004144 0.000000 -0.207683 0.000000 0.286830 0.000000 ) - ( -0.004144 0.000000 0.207683 0.000000 -0.286830 0.000000 ) - freq ( 25) = 47.986716 [THz] = 1600.664538 [cm-1] - ( -0.000000 0.000000 0.032186 0.000000 -0.023659 0.000000 ) - ( 0.000000 0.000000 -0.433639 0.000000 -0.017552 0.000000 ) - ( 0.000000 0.000000 -0.118475 0.000000 0.398902 0.000000 ) - ( -0.000000 0.000000 0.014019 0.000000 -0.018448 0.000000 ) - ( -0.162216 0.000000 -0.145860 0.000000 0.143490 0.000000 ) - ( 0.162216 0.000000 -0.145860 0.000000 0.143490 0.000000 ) - ( 0.000000 0.000000 -0.032186 0.000000 -0.023659 0.000000 ) - ( 0.000000 0.000000 0.433639 0.000000 -0.017552 0.000000 ) - ( -0.000000 0.000000 0.118475 0.000000 0.398902 0.000000 ) - ( 0.000000 0.000000 -0.014019 0.000000 -0.018448 0.000000 ) - ( 0.162216 0.000000 0.145860 0.000000 0.143490 0.000000 ) - ( -0.162216 0.000000 0.145860 0.000000 0.143490 0.000000 ) - freq ( 26) = 48.024405 [THz] = 1601.921727 [cm-1] - ( 0.000000 0.000000 0.028648 0.000000 -0.022735 0.000000 ) - ( -0.000000 0.000000 -0.354703 0.000000 -0.007596 0.000000 ) - ( 0.000000 0.000000 -0.104624 0.000000 0.355033 0.000000 ) - ( -0.000000 0.000000 0.026969 0.000000 -0.019256 0.000000 ) - ( -0.212732 0.000000 -0.211664 0.000000 0.165204 0.000000 ) - ( 0.212732 0.000000 -0.211664 0.000000 0.165204 0.000000 ) - ( -0.000000 0.000000 0.028648 0.000000 0.022735 0.000000 ) - ( -0.000000 0.000000 -0.354703 0.000000 0.007596 0.000000 ) - ( 0.000000 0.000000 -0.104624 0.000000 -0.355033 0.000000 ) - ( -0.000000 0.000000 0.026969 0.000000 0.019256 0.000000 ) - ( -0.212732 0.000000 -0.211664 0.000000 -0.165204 0.000000 ) - ( 0.212732 0.000000 -0.211664 0.000000 -0.165204 0.000000 ) - freq ( 27) = 49.710608 [THz] = 1658.167412 [cm-1] - ( 0.000000 0.000000 0.013399 0.000000 -0.011080 0.000000 ) - ( 0.000000 0.000000 -0.242439 0.000000 -0.001003 0.000000 ) - ( -0.000000 0.000000 -0.088878 0.000000 0.260755 0.000000 ) - ( 0.000000 0.000000 -0.032010 0.000000 0.016930 0.000000 ) - ( 0.253139 0.000000 0.294426 0.000000 -0.176330 0.000000 ) - ( -0.253139 0.000000 0.294426 0.000000 -0.176330 0.000000 ) - ( 0.000000 0.000000 -0.013399 0.000000 -0.011080 0.000000 ) - ( 0.000000 0.000000 0.242439 0.000000 -0.001003 0.000000 ) - ( -0.000000 0.000000 0.088878 0.000000 0.260755 0.000000 ) - ( 0.000000 0.000000 0.032010 0.000000 0.016930 0.000000 ) - ( -0.253139 0.000000 -0.294426 0.000000 -0.176330 0.000000 ) - ( 0.253139 0.000000 -0.294426 0.000000 -0.176330 0.000000 ) - freq ( 28) = 50.923523 [THz] = 1698.625902 [cm-1] - ( -0.000000 0.000000 -0.019459 0.000000 0.013454 0.000000 ) - ( 0.000000 0.000000 0.340859 0.000000 0.008310 0.000000 ) - ( 0.000000 0.000000 0.106566 0.000000 -0.327959 0.000000 ) - ( -0.000000 0.000000 0.021667 0.000000 -0.015472 0.000000 ) - ( -0.213842 0.000000 -0.241226 0.000000 0.166923 0.000000 ) - ( 0.213842 0.000000 -0.241226 0.000000 0.166923 0.000000 ) - ( 0.000000 0.000000 -0.019459 0.000000 -0.013454 0.000000 ) - ( -0.000000 0.000000 0.340859 0.000000 -0.008310 0.000000 ) - ( -0.000000 0.000000 0.106566 0.000000 0.327959 0.000000 ) - ( 0.000000 0.000000 0.021667 0.000000 0.015472 0.000000 ) - ( -0.213842 0.000000 -0.241226 0.000000 -0.166923 0.000000 ) - ( 0.213842 0.000000 -0.241226 0.000000 -0.166923 0.000000 ) - freq ( 29) = 87.364360 [THz] = 2914.161372 [cm-1] - ( 0.000000 0.000000 -0.020894 0.000000 0.014786 0.000000 ) - ( -0.000000 0.000000 -0.001054 0.000000 -0.351005 0.000000 ) - ( -0.000000 0.000000 0.336773 0.000000 0.115155 0.000000 ) - ( -0.000000 0.000000 -0.020865 0.000000 0.014522 0.000000 ) - ( -0.289234 0.000000 0.166149 0.000000 -0.114655 0.000000 ) - ( 0.289234 0.000000 0.166149 0.000000 -0.114655 0.000000 ) - ( -0.000000 0.000000 0.020894 0.000000 0.014786 0.000000 ) - ( -0.000000 0.000000 0.001054 0.000000 -0.351005 0.000000 ) - ( 0.000000 0.000000 -0.336773 0.000000 0.115155 0.000000 ) - ( 0.000000 0.000000 0.020865 0.000000 0.014522 0.000000 ) - ( 0.289234 0.000000 -0.166149 0.000000 -0.114655 0.000000 ) - ( -0.289234 0.000000 -0.166149 0.000000 -0.114655 0.000000 ) - freq ( 30) = 88.257758 [THz] = 2943.961933 [cm-1] - ( -0.000000 0.000000 0.033364 0.000000 0.006633 0.000000 ) - ( -0.000000 0.000000 0.007841 0.000000 0.097679 0.000000 ) - ( 0.000000 0.000000 -0.512347 0.000000 -0.170686 0.000000 ) - ( 0.000000 0.000000 0.017267 0.000000 -0.014497 0.000000 ) - ( 0.258021 0.000000 -0.149506 0.000000 0.098094 0.000000 ) - ( -0.258021 0.000000 -0.149506 0.000000 0.098094 0.000000 ) - ( 0.000000 0.000000 0.033364 0.000000 -0.006633 0.000000 ) - ( 0.000000 0.000000 0.007841 0.000000 -0.097679 0.000000 ) - ( -0.000000 0.000000 -0.512347 0.000000 0.170686 0.000000 ) - ( 0.000000 0.000000 0.017267 0.000000 0.014497 0.000000 ) - ( 0.258021 0.000000 -0.149506 0.000000 -0.098094 0.000000 ) - ( -0.258021 0.000000 -0.149506 0.000000 -0.098094 0.000000 ) - freq ( 31) = 90.484705 [THz] = 3018.244885 [cm-1] - ( -0.003948 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( -0.003248 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.006936 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.054959 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.399881 0.000000 0.244655 0.000000 -0.169387 0.000000 ) - ( -0.399881 0.000000 -0.244655 0.000000 0.169387 0.000000 ) - ( 0.003948 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.003248 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.006936 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.054959 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.399881 0.000000 -0.244655 0.000000 -0.169387 0.000000 ) - ( 0.399881 0.000000 0.244655 0.000000 0.169387 0.000000 ) - freq ( 32) = 90.508657 [THz] = 3019.043844 [cm-1] - ( 0.003767 0.000000 -0.000000 0.000000 -0.000000 0.000000 ) - ( 0.006547 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.007269 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( -0.055049 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.400025 0.000000 -0.244548 0.000000 0.169134 0.000000 ) - ( 0.400025 0.000000 0.244548 0.000000 -0.169134 0.000000 ) - ( 0.003767 0.000000 0.000000 0.000000 -0.000000 0.000000 ) - ( 0.006547 0.000000 0.000000 0.000000 0.000000 0.000000 ) - ( 0.007269 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( -0.055049 0.000000 -0.000000 0.000000 0.000000 0.000000 ) - ( 0.400025 0.000000 -0.244548 0.000000 -0.169134 0.000000 ) - ( 0.400025 0.000000 0.244548 0.000000 0.169134 0.000000 ) - freq ( 33) = 90.554693 [THz] = 3020.579432 [cm-1] - ( -0.000000 0.000000 -0.032049 0.000000 -0.044754 0.000000 ) - ( -0.000000 0.000000 -0.010764 0.000000 0.500218 0.000000 ) - ( 0.000000 0.000000 0.473441 0.000000 0.149585 0.000000 ) - ( 0.000000 0.000000 0.002255 0.000000 0.003024 0.000000 ) - ( 0.001300 0.000000 0.003032 0.000000 0.006238 0.000000 ) - ( -0.001300 0.000000 0.003032 0.000000 0.006238 0.000000 ) - ( -0.000000 0.000000 0.032049 0.000000 -0.044754 0.000000 ) - ( -0.000000 0.000000 0.010764 0.000000 0.500218 0.000000 ) - ( -0.000000 0.000000 -0.473441 0.000000 0.149585 0.000000 ) - ( 0.000000 0.000000 -0.002255 0.000000 0.003024 0.000000 ) - ( -0.001300 0.000000 -0.003032 0.000000 0.006238 0.000000 ) - ( 0.001300 0.000000 -0.003032 0.000000 0.006238 0.000000 ) - freq ( 34) = 94.958978 [THz] = 3167.490553 [cm-1] - ( -0.000000 0.000000 -0.003622 0.000000 0.040164 0.000000 ) - ( 0.000000 0.000000 -0.006978 0.000000 -0.662605 0.000000 ) - ( -0.000000 0.000000 0.070340 0.000000 0.041780 0.000000 ) - ( -0.000000 0.000000 0.009235 0.000000 -0.006539 0.000000 ) - ( 0.131157 0.000000 -0.076226 0.000000 0.056714 0.000000 ) - ( -0.131157 0.000000 -0.076226 0.000000 0.056714 0.000000 ) - ( 0.000000 0.000000 -0.003622 0.000000 -0.040164 0.000000 ) - ( 0.000000 0.000000 -0.006978 0.000000 0.662605 0.000000 ) - ( -0.000000 0.000000 0.070340 0.000000 -0.041780 0.000000 ) - ( -0.000000 0.000000 0.009235 0.000000 0.006539 0.000000 ) - ( 0.131157 0.000000 -0.076226 0.000000 -0.056714 0.000000 ) - ( -0.131157 0.000000 -0.076226 0.000000 -0.056714 0.000000 ) - freq ( 35) = 97.425645 [THz] = 3249.769715 [cm-1] - ( 0.000000 0.000000 0.019594 0.000000 -0.014154 0.000000 ) - ( -0.000000 0.000000 0.014700 0.000000 0.352428 0.000000 ) - ( -0.000000 0.000000 -0.330607 0.000000 -0.123437 0.000000 ) - ( 0.000000 0.000000 -0.019903 0.000000 0.013902 0.000000 ) - ( -0.293537 0.000000 0.161593 0.000000 -0.112505 0.000000 ) - ( 0.293537 0.000000 0.161593 0.000000 -0.112505 0.000000 ) - ( 0.000000 0.000000 -0.019594 0.000000 -0.014154 0.000000 ) - ( -0.000000 0.000000 -0.014700 0.000000 0.352428 0.000000 ) - ( -0.000000 0.000000 0.330607 0.000000 -0.123437 0.000000 ) - ( -0.000000 0.000000 0.019903 0.000000 0.013902 0.000000 ) - ( 0.293537 0.000000 -0.161593 0.000000 -0.112505 0.000000 ) - ( -0.293537 0.000000 -0.161593 0.000000 -0.112505 0.000000 ) - freq ( 36) = 97.513320 [THz] = 3252.694225 [cm-1] - ( -0.000000 0.000000 0.025803 0.000000 0.023309 0.000000 ) - ( 0.000000 0.000000 0.009229 0.000000 -0.215156 0.000000 ) - ( -0.000000 0.000000 -0.417799 0.000000 -0.143843 0.000000 ) - ( 0.000000 0.000000 -0.020440 0.000000 0.014898 0.000000 ) - ( -0.297024 0.000000 0.161735 0.000000 -0.117821 0.000000 ) - ( 0.297024 0.000000 0.161735 0.000000 -0.117821 0.000000 ) - ( 0.000000 0.000000 0.025803 0.000000 -0.023309 0.000000 ) - ( 0.000000 0.000000 0.009229 0.000000 0.215156 0.000000 ) - ( -0.000000 0.000000 -0.417799 0.000000 0.143843 0.000000 ) - ( -0.000000 0.000000 -0.020440 0.000000 -0.014898 0.000000 ) - ( -0.297024 0.000000 0.161735 0.000000 0.117821 0.000000 ) - ( 0.297024 0.000000 0.161735 0.000000 0.117821 0.000000 ) - ************************************************************************** From d14d4a15208929ea7b9483323177a1797dab3f17 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:36:05 +0100 Subject: [PATCH 16/18] Delete tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints directory --- .../symmetry_qe-checkpoint.py | 52 -------------- .../symmetry_spglib-checkpoint.py | 70 ------------------- 2 files changed, 122 deletions(-) delete mode 100644 tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py delete mode 100644 tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py diff --git a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py deleted file mode 100644 index 90ce41d2..00000000 --- a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_qe-checkpoint.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import print_function - -# Import numpy -import numpy as np - -# Import cellconstructor -import cellconstructor as CC -import cellconstructor.Structure -import cellconstructor.Phonons -import cellconstructor.symmetries - -# Define a rocksalt structure -bcc = CC.Structure.Structure(2) -bcc.coords[1,:] = 5.6402 * np.array([.5, .5, .5]) # Shift the second atom in the center -bcc.atoms = ["Na", "Cl"] -bcc.unit_cell = np.eye(3) * 5.6402 # A cubic cell of 5.64 A edge -bcc.has_unit_cell = True # Setup periodic boundary conditions - -# Setup the mass on the two atoms (Ry units) -bcc.masses = {"Na": 20953.89349715178, - "Cl": 302313.43272048925} - - - -# Lets generate the random dynamical matrix -dynamical_matrix = CC.Phonons.Phonons(bcc) -dynamical_matrix.dynmats[0] = np.random.uniform(size = (3 * bcc.N_atoms, - 3* bcc.N_atoms)) - -# Force the random matrix to be hermitian (so we can diagonalize it) -dynamical_matrix.dynmats[0] += dynamical_matrix.dynmats[0].T - -# Lets compute the phonon frequencies without symmetries -w, pols = dynamical_matrix.DiagonalizeSupercell() - -# Print on the screen the random frequencies -print("Non symmetric frequencies:") -print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM)for i,w in enumerate(w)])) - -# Symmetrize the dynamical matrix -dynamical_matrix.Symmetrize() # Use QE to symmetrize - -# Recompute the frequencies and print them in output -w, pols = dynamical_matrix.DiagonalizeSupercell() -print() -print("frequencies after the symmetrization:") -print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM) for i,w in enumerate(w)])) - - - - - diff --git a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py b/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py deleted file mode 100644 index 54eff828..00000000 --- a/tutorials/SymmetriesDynamicalMatrix/.ipynb_checkpoints/symmetry_spglib-checkpoint.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import print_function - -# Import numpy -import numpy as np - -# Import cellconstructor -import cellconstructor as CC -import cellconstructor.Structure -import cellconstructor.Phonons -import cellconstructor.symmetries - -# Define a rocksalt structure -bcc = CC.Structure.Structure(2) -bcc.coords[1,:] = 5.6402 * np.array([.5, .5, .5]) # Shift the second atom in the center -bcc.atoms = ["Na", "Cl"] -bcc.unit_cell = np.eye(3) * 5.6402 # A cubic cell of 5.64 A edge -bcc.has_unit_cell = True # Setup periodic boundary conditions - -# Setup the mass on the two atoms (Ry units) -bcc.masses = {"Na": 20953.89349715178, - "Cl": 302313.43272048925} - - - -# Lets generate the random dynamical matrix -dynamical_matrix = CC.Phonons.Phonons(bcc) -dynamical_matrix.dynmats[0] = np.random.uniform(size = (3 * bcc.N_atoms, - 3* bcc.N_atoms)) - -# Force the random matrix to be hermitian (so we can diagonalize it) -dynamical_matrix.dynmats[0] += dynamical_matrix.dynmats[0].T - -# Lets compute the phonon frequencies without symmetries -w, pols = dynamical_matrix.DiagonalizeSupercell() - -# Print on the screen the random frequencies -print("Non symmetric frequencies:") -print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM)for i,w in enumerate(w)])) - -# Initialize the symmetry class -syms = CC.symmetries.QE_Symmetry(bcc) -syms.SetupFromSPGLIB() # Setup the espresso symmetries on spglib - -# Generate the real space dynamical matrix -superdyn = dynamical_matrix.GenerateSupercellDyn(dynamical_matrix.GetSupercell()) -# Apply the symmetries to the real space matrix -CC.symmetries.CustomASR(superdyn.dynmats[0]) -syms.ApplySymmetriesToV2(superdyn.dynmats[0]) -# Get back the dyanmical matrix in q space -dynq = CC.Phonons.GetDynQFromFCSupercell(superdyn.dynmats[0], - np.array(dynamical_matrix.q_tot), - dynamical_matrix.structure, - superdyn.structure) - -# Copy each q point of the symmetrized dynamical matrix into -# the original one -for i in range(len(dynamical_matrix.q_tot)): - dynamical_matrix.dynmats[i] = dynq[i,:,:] - - -# Recompute the frequencies and print them in output -w, pols = dynamical_matrix.DiagonalizeSupercell() -print() -print("frequencies after the symmetrization:") -print("\n".join(["{:d}) {:.4f} cm-1".format(i, w * CC.Units.RY_TO_CM) for i,w in enumerate(w)])) - - - - - From 352a2206ef5feb45f49c94b2e530d7f82db910ad Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:36:28 +0100 Subject: [PATCH 17/18] Delete tests/CorrelatedHarmonicSampling/.ipynb_checkpoints directory --- .../.ipynb_checkpoints/INFO-checkpoint.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt diff --git a/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt b/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt deleted file mode 100644 index fa322882..00000000 --- a/tests/CorrelatedHarmonicSampling/.ipynb_checkpoints/INFO-checkpoint.txt +++ /dev/null @@ -1,3 +0,0 @@ -In this example one set of displacement generated by one particular dynamical -matrix is changed into another one dynamical matrix with a different unit -cell. From 5cf8f62cc6aa3727ebd2283762dafc1ccd6fa4b5 Mon Sep 17 00:00:00 2001 From: Antonio <78103925+AntonioSiciliano@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:42:02 +0100 Subject: [PATCH 18/18] Delete out.dat --- out.dat | 1402 ------------------------------------------------------- 1 file changed, 1402 deletions(-) delete mode 100644 out.dat diff --git a/out.dat b/out.dat deleted file mode 100644 index b91a9f94..00000000 --- a/out.dat +++ /dev/null @@ -1,1402 +0,0 @@ -running install -running build -running config_cc -INFO: unifing config_cc, config, build_clib, build_ext, build commands --compiler options -running config_fc -INFO: unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options -running build_src -INFO: build_src -INFO: building extension "symph" sources -INFO: f2py options: [] -INFO: f2py:> build/src.linux-x86_64-3.10/symphmodule.c -creating build -creating build/src.linux-x86_64-3.10 -Reading fortran codes... - Reading file 'FModules/symdynph_gq_new.f90' (format:free) - Reading file 'FModules/symm_base.f90' (format:free) - Reading file 'FModules/sgam_ph.f90' (format:free) - Reading file 'FModules/invmat.f90' (format:free) - Reading file 'FModules/set_asr.f90' (format:free) - Reading file 'FModules/error_handler.f90' (format:free) - Reading file 'FModules/io_global.f90' (format:free) - Reading file 'FModules/flush_unit.f90' (format:free) - Reading file 'FModules/symvector.f90' (format:free) - Reading file 'FModules/fc_supercell_from_dyn.f90' (format:free) - Reading file 'FModules/set_tau.f90' (format:free) - Reading file 'FModules/cryst_to_car.f90' (format:free) - Reading file 'FModules/recips.f90' (format:free) - Reading file 'FModules/q2qstar_out.f90' (format:free) - Reading file 'FModules/rotate_and_add_dyn.f90' (format:free) - Reading file 'FModules/trntnsc.f90' (format:free) - Reading file 'FModules/star_q.f90' (format:free) - Reading file 'FModules/eqvect.f90' (format:free) - Reading file 'FModules/symm_matrix.f90' (format:free) - Reading file 'FModules/from_matdyn.f90' (format:free) -Line #110 in FModules/from_matdyn.f90:" parameter (eps=1.0d-6,nx=2) " - get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Reading file 'FModules/interp.f90' (format:free) - Reading file 'FModules/q_gen.f90' (format:free) - Reading file 'FModules/smallgq.f90' (format:free) - Reading file 'FModules/symmetry_high_rank.f90' (format:free) - Reading file 'FModules/unwrap_tensors.f90' (format:free) - Reading file 'FModules/get_latvec.f90' (format:free) - Reading file 'FModules/contract_two_phonon_propagator.f90' (format:free) - Reading file 'FModules/get_q_grid_fast.f90' (format:free) - Reading file 'FModules/kind.f90' (format:free) - Reading file 'FModules/constants.f90' (format:free) - Reading file 'FModules/eff_charge_interp.f90' (format:free) - Reading file 'FModules/get_translations.f90' (format:free) - Reading file 'FModules/get_equivalent_atoms.f90' (format:free) -Post-processing... - Block: symph - Block: symdynph_gq_new - Block: symm_base -In: :symph:FModules/symm_base.f90:symm_base -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/symm_base.f90:symm_base -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: set_accep_threshold - Block: get_accep_threshold - Block: set_at_bg - Block: inverse_s - Block: set_sym_bl -In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/symm_base.f90:symm_base:set_sym_bl -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: find_sym - Block: sgam_at - Block: sgam_at_mag - Block: set_sym - Block: copy_sym - Block: is_group - Block: eqvect - Block: checksym - Block: checkallsym - Block: s_axis_to_cart - Block: smallg_q - Block: sgam_ph_new - Block: invmat - Block: set_asr - Block: sp_zeu - Block: sp1 - Block: sp2 - Block: sp3 - Block: errore - Block: infomsg -In: :symph:FModules/error_handler.f90:infomsg -get_useparameters: no module io_global info used by infomsg - Block: io_global - Block: io_global_start - Block: meta_io_global_start - Block: io_global_getionode - Block: io_global_getmeta - Block: flush_unit - Block: symvector - Block: fc_supercell_from_dyn - Block: fast_ft_real_space_from_dynq - Block: impose_trans_sc -In: :symph:FModules/fc_supercell_from_dyn.f90:impose_trans_sc -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: dyn_from_fc - Block: set_tau -In: :symph:FModules/set_tau.f90:set_tau -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: cryst_to_cart - Block: recips - Block: q2qstar_out - Block: rotate_and_add_dyn -In: :symph:FModules/rotate_and_add_dyn.f90:rotate_and_add_dyn -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: trntnsc - Block: star_q - Block: eqvect -In: :symph:FModules/eqvect.f90:eqvect -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: symmatrix - Block: cart_to_crys_mat - Block: crys_to_cart_mat - Block: frc_blk -In: :symph:FModules/from_matdyn.f90:frc_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: wsinit -In: :symph:FModules/from_matdyn.f90:wsinit -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: wsweight -In: :symph:FModules/from_matdyn.f90:wsweight -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: get_frc - Block: asign_supercell_index_new - Block: q_gen -In: :symph:FModules/q_gen.f90:q_gen -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: set_irotmq -In: :symph:FModules/smallgq.f90:set_irotmq -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: get_tau_sc_latvec - Block: permute_v3 - Block: permute_v4 - Block: trans_v2 - Block: trans_v3 - Block: trans_v4 - Block: sym_v2 - Block: sym_v3 - Block: sym_v4 - Block: print_symm - Block: threetosix_real - Block: sixtothree_real - Block: get_latvec - Block: contract_two_ph_propagator - Block: get_two_phonon_propagator -In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator -get_parameters[TODO]: implement evaluation of complex expression (0d0, 1d0) -In: :symph:FModules/contract_two_phonon_propagator.f90:get_two_phonon_propagator -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: get_q_grid - Block: kinds - Block: print_kind_info - Block: constants -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'kboltzmann' is not defined" on 'kboltzmann/hartree' -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'kboltzmann' is not defined" on 'kboltzmann/rydberg' -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'hartree' is not defined" on 'hartree/electronvolt' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'amu' is not defined" on 'amu/electronmass' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'hplanck' is not defined" on 'hplanck/tpi/hartree' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'electron' is not defined" on 'electron*bohrradius/debye' -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'electronvolt' is not defined" on 'electronvolt/kboltzmann' -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'rydberg' is not defined" on 'rydberg/kboltzmann' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'au' is not defined" on 'au' -In: :symph:FModules/constants.f90:constants -get_parameters: got "name 'au' is not defined" on 'au' -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/constants.f90:constants -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: rgd_blk -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:rgd_blk -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: nonanal -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "name 'csi' is not defined" on 'csi/bohrradiussi*au' -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :symph:FModules/eff_charge_interp.f90:nonanal -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: trasl - Block: get_translations - Block: get_equivalent_atoms - Block: get_closest_vector - Block: get_gr_data -In: :symph:FModules/get_equivalent_atoms.f90:get_gr_data -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 8 - Block: fix_coords_in_unit_cell - Block: matinv3 -Post-processing (stage 2)... - Block: symph - Block: unknown_interface - Block: symdynph_gq_new - Block: symm_base - Block: set_accep_threshold - Block: get_accep_threshold - Block: set_at_bg - Block: inverse_s - Block: set_sym_bl - Block: find_sym - Block: sgam_at - Block: sgam_at_mag - Block: set_sym - Block: copy_sym - Block: is_group - Block: eqvect - Block: checksym - Block: checkallsym - Block: s_axis_to_cart - Block: smallg_q - Block: sgam_ph_new - Block: invmat - Block: set_asr - Block: sp_zeu - Block: sp1 - Block: sp2 - Block: sp3 - Block: errore - Block: infomsg - Block: io_global - Block: io_global_start - Block: meta_io_global_start - Block: io_global_getionode - Block: io_global_getmeta - Block: flush_unit - Block: symvector - Block: fc_supercell_from_dyn - Block: fast_ft_real_space_from_dynq - Block: impose_trans_sc - Block: dyn_from_fc - Block: set_tau - Block: cryst_to_cart - Block: recips - Block: q2qstar_out - Block: rotate_and_add_dyn - Block: trntnsc - Block: star_q - Block: eqvect - Block: symmatrix - Block: cart_to_crys_mat - Block: crys_to_cart_mat - Block: frc_blk - Block: wsinit - Block: wsweight - Block: get_frc - Block: asign_supercell_index_new - Block: q_gen - Block: set_irotmq - Block: get_tau_sc_latvec - Block: permute_v3 - Block: permute_v4 - Block: trans_v2 - Block: trans_v3 - Block: trans_v4 - Block: sym_v2 - Block: sym_v3 - Block: sym_v4 - Block: print_symm - Block: threetosix_real - Block: sixtothree_real - Block: get_latvec - Block: contract_two_ph_propagator - Block: get_two_phonon_propagator - Block: get_q_grid - Block: kinds - Block: print_kind_info - Block: constants - Block: rgd_blk - Block: nonanal - Block: trasl - Block: get_translations - Block: get_equivalent_atoms - Block: get_closest_vector - Block: get_gr_data - Block: fix_coords_in_unit_cell - Block: matinv3 -Building modules... - Building module "symph"... - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "symdynph_gq_new"... - symdynph_gq_new(xq,phi,s,invs,rtau,irt,irotmq,minus_q,nsymq,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sgam_ph_new"... - rtau = sgam_ph_new(at,bg,nsym,s,irt,tau,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "invmat"... - a_inv = invmat(a,[n]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "set_asr"... - set_asr(asr,axis,tau,dyn,zeu,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sp_zeu"... - sp_zeu(zeu_u,zeu_v,scal,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sp1"... - sp1(u,v,scal,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sp2"... - sp2(u,v,ind_v,scal,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sp3"... - sp3(u,v,i,na,scal,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "errore"... - errore(calling_routine,message,ierr) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "infomsg"... - infomsg(routine,message) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "flush_unit"... - flush_unit(unit_tobeflushed) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "symvector"... - symvector(nsym,irt,s,at,bg,vect,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "fc_supercell_from_dyn"... - phitot_sc = fc_supercell_from_dyn(phitot,q,tau,tau_sc,itau,[nat,nq]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "fast_ft_real_space_from_dynq"... - fc_supercell = fast_ft_real_space_from_dynq(unit_cell_coords,super_cell_coords,itau,q_tot,dynq,[nat,nat_sc,nq]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "impose_trans_sc"... - impose_trans_sc(fc_sc,tau_sc_cryst,itau,[nat_sc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "dyn_from_fc"... - dyn = dyn_from_fc(phitot_sc,q,tau,tau_sc,itau,nq,nat) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "set_tau"... - set_tau(at,at_blk,tau,tau_blk,ityp,ityp_blk,itau_blk,[nat,nat_blk]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "cryst_to_cart"... - cryst_to_cart(vec,trmat,iflag,[nvec]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "recips"... - b1,b2,b3 = recips(a1,a2,a3) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "q2qstar_out"... - dynqstar = q2qstar_out(dyn,at,bg,nsym,s,invs,irt,rtau,nq,sxq,isq,imq,nqtot,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "rotate_and_add_dyn"... - phi2 = rotate_and_add_dyn(phi,isym,s,invs,irt,rtau,sxq,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "trntnsc"... - trntnsc(phi,at,bg,iflg) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "star_q"... - nq,sxq,isq,imq = star_q(xq,at,bg,nsym,s,invs,verbosity) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Creating wrapper for Fortran function "eqvect"("eqvect")... - Constructing wrapper function "eqvect"... - eqvect = eqvect(x,y,f) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "symmatrix"... - symmatrix(matr,s,nsym,at,bg) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "cart_to_crys_mat"... - cart_to_crys_mat(matr,at) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "crys_to_cart_mat"... - crys_to_cart_mat(matr,bg) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "frc_blk"... - dyn = frc_blk(q,tau,frc,at,rws,nrws,[nat,nr1,nr2,nr3,nrwsx]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "wsinit"... - nrws = wsinit(rws,atw,[nrwsx]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Creating wrapper for Fortran function "wsweight"("wsweight")... - Constructing wrapper function "wsweight"... - wsweight = wsweight(r,rws,nrws,[nrwsx]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_frc"... - frc = get_frc(phi_sc,tau,tau_sc,at,itau,size1,size2,size3,[nat,natsc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "asign_supercell_index_new"... - l,m,n = asign_supercell_index_new(vect,at) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "q_gen"... - qbid = q_gen(nsc,at_blk,bg_blk,at,bg) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "set_irotmq"... - irotmq = set_irotmq(xq,s,nsymq,nsym,minus_q,bg,at,lgamma) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_tau_sc_latvec"... - tau_sc_latvec = get_tau_sc_latvec(tau_sc,latvec,at_sc,[nat_sc,nr]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "permute_v3"... - permute_v3(v3,[n]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "permute_v4"... - permute_v4(v4,[n]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "trans_v2"... - trans_v2(v2,tau_sc_latvec,[nat_sc,nr]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "trans_v3"... - trans_v3(v3,tau_sc_latvec,[nat_sc,nr]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "trans_v4"... - trans_v4(v4,tau_sc_latvec,[nat_sc,nr]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sym_v2"... - sym_v2(v2,at_sc,bg_sc,s,irt,nsym,[nat_sc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sym_v3"... - sym_v3(v3,at_sc,s,irt,nsym,[nat_sc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sym_v4"... - sym_v4(v4,at_sc,s,irt,nsym,[nat_sc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "print_symm"... - print_symm(s,nsym,irt,supercell,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "threetosix_real"... - mat6 = threetosix_real(mat3,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "sixtothree_real"... - mat3 = sixtothree_real(mat6,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers2.f90" - Maybe empty "symph-f2pywrappers.f" - Creating wrapper for Fortran subroutine "get_latvec"("get_latvec")... - Constructing wrapper function "get_latvec"... - latvec = get_latvec(tau_sc,tau,itau,nr,[nat,nat_sc]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "contract_two_ph_propagator"... - f_output = contract_two_ph_propagator(w_array,w_mu,t,smearing,m,[n_w,n_modes]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_two_phonon_propagator"... - chi = get_two_phonon_propagator(w_value,ws,t,smearing,[n_w]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_q_grid"... - q_list = get_q_grid(bg,supercell_size,n_size) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "rgd_blk"... - rgd_blk(nr1,nr2,nr3,dyn,q,tau,epsil,zeu,bg,omega,alat,loto_2d,sign,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "nonanal"... - nonanal(itau_blk,epsil,q,zeu,omega,dyn,[nat,nat_blk]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "trasl"... - phid = trasl(phiq,nq,nr1,nr2,nr3,m1,m2,m3,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_translations"... - trans = get_translations(pols,masses,[nmod,nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_equivalent_atoms"... - eq_atoms = get_equivalent_atoms(coords1,coords2,unit_cell,ityp1,ityp2,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_closest_vector"... - new_v_dist = get_closest_vector(unit_cell,v_dist) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "get_gr_data"... - r_value,gr = get_gr_data(cells,coords,ityp,type1,type2,r_min,r_max,n_r,[n_structs,nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "fix_coords_in_unit_cell"... - new_coords = fix_coords_in_unit_cell(coords,unit_cell,[nat]) - Generating possibly empty wrappers" - Maybe empty "symph-f2pywrappers.f" - Constructing wrapper function "matinv3"... - b = matinv3(a) - Constructing F90 module support for "symm_base"... - Variables: eps1 eps2 s sr sname ft ftau nrot nsym nsym_ns nsym_na t_rev no_t_rev time_reversal irt invs invsym d1 d2 d3 allfrac nofrac nosym nosym_evc at bg -getarrdims:warning: assumed shape array, using 0 instead of ':' -getarrdims:warning: assumed shape array, using 0 instead of ':' - Constructing wrapper function "symm_base.set_accep_threshold"... - set_accep_threshold(thr) - Constructing wrapper function "symm_base.get_accep_threshold"... - thr = get_accep_threshold() - Constructing wrapper function "symm_base.set_at_bg"... - set_at_bg(new_at,new_bg) - Constructing wrapper function "symm_base.inverse_s"... - inverse_s() - Constructing wrapper function "symm_base.set_sym_bl"... - set_sym_bl() - Constructing wrapper function "symm_base.find_sym"... - find_sym(tau,ityp,nr1,nr2,nr3,magnetic_sym,m_loc,[nat]) - Constructing wrapper function "symm_base.sgam_at"... - sym = sgam_at(tau,ityp,nr1,nr2,nr3,[nat]) - Constructing wrapper function "symm_base.sgam_at_mag"... - sgam_at_mag(m_loc,sym,[nat]) - Constructing wrapper function "symm_base.set_sym"... - set_sym(tau,ityp,nspin_mag,m_loc,nr1,nr2,nr3,[nat]) - Creating wrapper for Fortran function "copy_sym"("copy_sym")... - Constructing wrapper function "symm_base.copy_sym"... - copy_sym = copy_sym(nrot_,sym) - Creating wrapper for Fortran function "is_group"("is_group")... - Constructing wrapper function "symm_base.is_group"... - is_group = is_group() - Creating wrapper for Fortran function "eqvect"("eqvect")... - Constructing wrapper function "symm_base.eqvect"... - eqvect = eqvect(x,y,f) - Creating wrapper for Fortran function "checksym"("checksym")... - Constructing wrapper function "symm_base.checksym"... - checksym = checksym(irot,ityp,xau,rau,ft_,[nat]) - Constructing wrapper function "symm_base.checkallsym"... - checkallsym(tau,ityp,nr1,nr2,nr3,[nat]) - Constructing wrapper function "symm_base.s_axis_to_cart"... - s_axis_to_cart() - Constructing wrapper function "symm_base.smallg_q"... - minus_q = smallg_q(aq,modenum,sym) - Constructing F90 module support for "io_global"... - Variables: stdout_bn ionode ionode_id meta_ionode meta_ionode_id xmlinputunit xmloutputunit xmltmpunit - Constructing wrapper function "io_global.io_global_start"... - io_global_start(mpime,ionode_set) - Constructing wrapper function "io_global.meta_io_global_start"... - meta_io_global_start(mpime,ionode_set) - Constructing wrapper function "io_global.io_global_getionode"... - ionode_out,ionode_id_out = io_global_getionode() - Constructing wrapper function "io_global.io_global_getmeta"... - io_global_getmeta(myrank,root) - Constructing F90 module support for "kinds"... - Variables: dp sgl i4b - Constructing wrapper function "kinds.print_kind_info"... - print_kind_info(stdout_bn) - Constructing F90 module support for "constants"... - Variables: pi tpi fpi sqrtpi sqrtpm1 sqrt2 h_planck_si k_boltzmann_si electron_si electronvolt_si electronmass_si hartree_si rydberg_si bohr_radius_si amu_si c_si munought_si epsnought_si k_boltzmann_au k_boltzmann_ry autoev rytoev amu_au amu_ry au_sec au_ps au_gpa ry_kbar debye_si au_debye ev_to_kelvin ry_to_kelvin evtonm rytonm c_au eps4 eps6 eps8 eps12 eps14 eps16 eps24 eps32 gsmall e2 degspin bohr_radius_cm bohr_radius_angs angstrom_au dip_debye au_terahertz au_to_ohmcmm1 ry_to_thz ry_to_ghz ry_to_cmm1 avogadro - Wrote C/API module "symph" to file "build/src.linux-x86_64-3.10/symphmodule.c" - Fortran 77 wrappers are saved to "build/src.linux-x86_64-3.10/symph-f2pywrappers.f" - Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/symph-f2pywrappers2.f90" -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. -creating build/src.linux-x86_64-3.10/build -creating build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 -copying /usr/local/lib/python3.10/dist-packages/numpy/f2py/src/fortranobject.c -> build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 -copying /usr/local/lib/python3.10/dist-packages/numpy/f2py/src/fortranobject.h -> build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10 -INFO: adding 'build/src.linux-x86_64-3.10/symph-f2pywrappers.f' to sources. -INFO: adding 'build/src.linux-x86_64-3.10/symph-f2pywrappers2.f90' to sources. -INFO: building extension "cc_linalg" sources -INFO: building extension "thirdorder" sources -INFO: f2py options: [] -INFO: f2py:> build/src.linux-x86_64-3.10/thirdordermodule.c -Reading fortran codes... - Reading file 'FModules/third_order_centering.f90' (format:free) - Reading file 'FModules/third_order_ASR.f90' (format:free) - Reading file 'FModules/third_order_interpol.f90' (format:free) - Reading file 'FModules/third_order_dynbubble.f90' (format:free) -Post-processing... - Block: thirdorder - Block: third_order_centering - Block: analysis - Block: center - Block: center_sparse - Block: pre_center - Block: assign - Block: within_dmax - Block: compute_perimeter - Block: three_to_one_len - Block: three_to_one - Block: one_to_three_len - Block: one_to_three - Block: min_el_wise_2 - Block: max_el_wise_2 - Block: min_el_wise_3 - Block: max_el_wise_3 - Block: cryst_to_cart - Block: third_order_asr - Block: initialize_r2index - Block: initialize_perm - Block: geq - Block: f - Block: impose_perm_sym - Block: impose_asr_3rd - Block: impose_asr - Block: third_order_interpol - Block: interpol -In: :thirdorder:FModules/third_order_interpol.f90:third_order_interpol:interpol -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: third_order_bubble - Block: compute_static_bubble - Block: compute_dynamic_bubble - Block: compute_diag_dynamic_bubble - Block: compute_perturb_selfnrg - Block: compute_spectralf -In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:compute_spectralf -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: compute_spectralf_diag -In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:compute_spectralf_diag -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: lambda - Block: lambda_dynamic - Block: lambda_dynamic_value - Block: bose_freq - Block: f_bose -In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:f_bose -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 -In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:f_bose -analyzevars: prefix ('elemental') were not used - Block: df_bose -In: :thirdorder:FModules/third_order_dynbubble.f90:third_order_bubble:df_bose -get_parameters: got "eval() arg 1 must be a string, bytes or code object" on 4 - Block: invzmat - Block: eliminate_transl -Post-processing (stage 2)... - Block: thirdorder - Block: unknown_interface - Block: third_order_centering - Block: analysis - Block: center - Block: center_sparse - Block: pre_center - Block: assign - Block: within_dmax - Block: compute_perimeter - Block: three_to_one_len - Block: three_to_one - Block: one_to_three_len - Block: one_to_three - Block: min_el_wise_2 - Block: max_el_wise_2 - Block: min_el_wise_3 - Block: max_el_wise_3 - Block: cryst_to_cart - Block: third_order_asr - Block: initialize_r2index - Block: initialize_perm - Block: geq - Block: f - Block: impose_perm_sym - Block: impose_asr_3rd - Block: impose_asr - Block: third_order_interpol - Block: interpol - Block: third_order_bubble - Block: compute_static_bubble - Block: compute_dynamic_bubble - Block: compute_diag_dynamic_bubble - Block: compute_perturb_selfnrg - Block: compute_spectralf - Block: compute_spectralf_diag - Block: lambda - Block: lambda_dynamic - Block: lambda_dynamic_value - Block: bose_freq - Block: f_bose - Block: df_bose - Block: invzmat - Block: eliminate_transl -Building modules... - Building module "thirdorder"... - Constructing F90 module support for "third_order_centering"... - Constructing wrapper function "third_order_centering.analysis"... - weight,xr2,xr3 = analysis(far,tol,dmax,sc_size,xr2_list,xr3_list,alat,tau,tensor,[nat,n_blocks]) - Constructing wrapper function "third_order_centering.center"... - centered = center(original,weight,xr2_list,xr2,xr3_list,xr3,far,[nat,n_blocks,n_blocks_old]) - Constructing wrapper function "third_order_centering.center_sparse"... - centered,n_sparse_blocks,xr2_sparse_list,xr3_sparse_list,atom_sparse_list,r_blocks_sparse_list = center_sparse(original,weight,xr2_list,xr2,xr3_list,xr3,far,[nat,n_sup,n_blocks]) - Constructing wrapper function "third_order_centering.pre_center"... - centered,lat_min,lat_max = pre_center(far,nq1,nq2,nq3,tol,alat,tau,original,[nat]) - Constructing wrapper function "third_order_centering.assign"... - centered,x2,x3,r2,r3 = assign(alat,lat_min_prev,lat_max_prev,centered_prev,lat_min,lat_max,n_sup_ws,[nat,n_sup_ws_prev]) - Creating wrapper for Fortran function "within_dmax"("within_dmax")... - Constructing wrapper function "third_order_centering.within_dmax"... - within_dmax = within_dmax(v1,v2,v3,d1,d2,d3,tol) - Creating wrapper for Fortran function "compute_perimeter"("compute_perimeter")... - Constructing wrapper function "third_order_centering.compute_perimeter"... - compute_perimeter = compute_perimeter(v1,v2,v3) - Creating wrapper for Fortran function "three_to_one_len"("three_to_one_len")... - Constructing wrapper function "third_order_centering.three_to_one_len"... - three_to_one_len = three_to_one_len(v,v_min,v_len) - Creating wrapper for Fortran function "three_to_one"("three_to_one")... - Constructing wrapper function "third_order_centering.three_to_one"... - three_to_one = three_to_one(v,v_min,v_max) - Creating wrapper for Fortran function "one_to_three_len"("one_to_three_len")... - Constructing wrapper function "third_order_centering.one_to_three_len"... - one_to_three_len = one_to_three_len(j,v_min,v_len) - Creating wrapper for Fortran function "one_to_three"("one_to_three")... - Constructing wrapper function "third_order_centering.one_to_three"... - one_to_three = one_to_three(j,v_min,v_max) - Creating wrapper for Fortran function "min_el_wise_2"("min_el_wise_2")... - Constructing wrapper function "third_order_centering.min_el_wise_2"... - min_el_wise_2 = min_el_wise_2(a,b) - Creating wrapper for Fortran function "max_el_wise_2"("max_el_wise_2")... - Constructing wrapper function "third_order_centering.max_el_wise_2"... - max_el_wise_2 = max_el_wise_2(a,b) - Creating wrapper for Fortran function "min_el_wise_3"("min_el_wise_3")... - Constructing wrapper function "third_order_centering.min_el_wise_3"... - min_el_wise_3 = min_el_wise_3(a,b,c) - Creating wrapper for Fortran function "max_el_wise_3"("max_el_wise_3")... - Constructing wrapper function "third_order_centering.max_el_wise_3"... - max_el_wise_3 = max_el_wise_3(a,b,c) - Creating wrapper for Fortran function "cryst_to_cart"("cryst_to_cart")... - Constructing wrapper function "third_order_centering.cryst_to_cart"... - cryst_to_cart = cryst_to_cart(v,alat) - Constructing F90 module support for "third_order_asr"... - Variables: perm_initialized p r2index_initialized index_blocks_r2 num_blocks_r2 -getarrdims:warning: assumed shape array, using 0 instead of ':' -getarrdims:warning: assumed shape array, using 0 instead of ':' -getarrdims:warning: assumed shape array, using 0 instead of ':' -getarrdims:warning: assumed shape array, using 0 instead of ':' -getarrdims:warning: assumed shape array, using 0 instead of ':' - Constructing wrapper function "third_order_asr.initialize_r2index"... - initialize_r2index(xr2,xr2list,pbc,[totnum_r2,n_blocks,sclat]) - Constructing wrapper function "third_order_asr.initialize_perm"... - initialize_perm(r23,sclat,pbc,[n_blocks]) - Creating wrapper for Fortran function "geq"("geq")... - Constructing wrapper function "third_order_asr.geq"... - geq = geq(v1,v2,lat,pbc) - Creating wrapper for Fortran function "f"("f")... - Constructing wrapper function "third_order_asr.f"... - f = f(x) - Constructing wrapper function "third_order_asr.impose_perm_sym"... - fcvar,fc_sym = impose_perm_sym(fc,r23,sclat,pbc,verbose,[nat,n_blocks]) - Constructing wrapper function "third_order_asr.impose_asr_3rd"... - fcvar,sum3rd,fc_asr = impose_asr_3rd(fc,xr2,xr2list,pow,sclat,pbc,verbose,[totnum_r2,nat,n_blocks]) - Constructing wrapper function "third_order_asr.impose_asr"... - fc_out = impose_asr(fc,r23,xr2,xr2list,pow,sclat,pbc,threshold,maxite,verbose,[totnum_r2,nat,n_blocks]) - Constructing F90 module support for "third_order_interpol"... - Constructing wrapper function "third_order_interpol.interpol"... - fc_interp = interpol(fc,r2,r3,q2,q3,[n_blocks,nat]) - Constructing F90 module support for "third_order_bubble"... - Constructing wrapper function "third_order_bubble.compute_static_bubble"... - bubble = compute_static_bubble(t,freq,is_gamma,d3,[n_mod]) - Constructing wrapper function "third_order_bubble.compute_dynamic_bubble"... - bubble = compute_dynamic_bubble(energies,sigma,static_limit,t,freq,is_gamma,d3,diag_approx,[ne,nsig,n_mod]) - Constructing wrapper function "third_order_bubble.compute_diag_dynamic_bubble"... - bubble = compute_diag_dynamic_bubble(energies,sigma,t,freq,is_gamma,d3,[ne,nsig,n_mod]) - Constructing wrapper function "third_order_bubble.compute_perturb_selfnrg"... - selfnrg = compute_perturb_selfnrg(sigma,t,freq,is_gamma,d3,[nsig,n_mod]) - Constructing wrapper function "third_order_bubble.compute_spectralf"... - spectralf = compute_spectralf(smear_id,ener,d2,pi,notransl,mass,[nat,ne,nsmear]) - Constructing wrapper function "third_order_bubble.compute_spectralf_diag"... - spectralf = compute_spectralf_diag(smear_id,ener,d2_freq,selfnrg,[nat,ne,nsmear]) - Constructing wrapper function "third_order_bubble.lambda"... - lambda_out = lambda(t,w_q2,w_q3) - Constructing wrapper function "third_order_bubble.lambda_dynamic"... - lambda_out = lambda_dynamic(energies,sigma,t,static_limit,w_q2,w_q3,[ne,nsigma]) - Constructing wrapper function "third_order_bubble.lambda_dynamic_value"... - lambda_out = lambda_dynamic_value(value,sigma,t,w_q2,w_q3,[n_mod,nsigma]) - Constructing wrapper function "third_order_bubble.bose_freq"... - bose = bose_freq(t,freq,[n_mod]) - Creating wrapper for Fortran function "f_bose"("f_bose")... - Constructing wrapper function "third_order_bubble.f_bose"... - f_bose = f_bose(freq,t) - Creating wrapper for Fortran function "df_bose"("df_bose")... - Constructing wrapper function "third_order_bubble.df_bose"... - df_bose = df_bose(freq,t) - Constructing wrapper function "third_order_bubble.invzmat"... - invzmat(a,[n]) - Constructing wrapper function "third_order_bubble.eliminate_transl"... - eliminate_transl(a,mass,[nat]) - Wrote C/API module "thirdorder" to file "build/src.linux-x86_64-3.10/thirdordermodule.c" - Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/thirdorder-f2pywrappers2.f90" -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. -INFO: adding 'build/src.linux-x86_64-3.10/thirdorder-f2pywrappers2.f90' to sources. -INFO: building extension "secondorder" sources -INFO: f2py options: [] -INFO: f2py:> build/src.linux-x86_64-3.10/secondordermodule.c -Reading fortran codes... - Reading file 'FModules/second_order_centering.f90' (format:free) - Reading file 'FModules/second_order_ASR.f90' (format:free) -Post-processing... - Block: secondorder - Block: second_order_centering - Block: analysis - Block: center - Block: within_dmax - Block: compute_perimeter - Block: cryst_to_cart - Block: second_order_asr - Block: initialize_perm - Block: clear_all - Block: geq - Block: impose_perm_sym - Block: impose_asr_2nd - Block: impose_asr -Post-processing (stage 2)... - Block: secondorder - Block: unknown_interface - Block: second_order_centering - Block: analysis - Block: center - Block: within_dmax - Block: compute_perimeter - Block: cryst_to_cart - Block: second_order_asr - Block: initialize_perm - Block: clear_all - Block: geq - Block: impose_perm_sym - Block: impose_asr_2nd - Block: impose_asr -Building modules... - Building module "secondorder"... - Constructing F90 module support for "second_order_centering"... - Constructing wrapper function "second_order_centering.analysis"... - weight,xr2 = analysis(far,tol,dmax,sc_size,xr2_list,alat,tau,tensor,[nat,n_blocks]) - Constructing wrapper function "second_order_centering.center"... - centered = center(original,weight,xr2_list,xr2,far,[nat,n_blocks,n_blocks_old]) - Creating wrapper for Fortran function "within_dmax"("within_dmax")... - Constructing wrapper function "second_order_centering.within_dmax"... - within_dmax = within_dmax(v1,v2,d1,d2,tol) - Creating wrapper for Fortran function "compute_perimeter"("compute_perimeter")... - Constructing wrapper function "second_order_centering.compute_perimeter"... - compute_perimeter = compute_perimeter(v1,v2) - Creating wrapper for Fortran function "cryst_to_cart"("cryst_to_cart")... - Constructing wrapper function "second_order_centering.cryst_to_cart"... - cryst_to_cart = cryst_to_cart(v,alat) - Constructing F90 module support for "second_order_asr"... - Variables: perm_initialized p -getarrdims:warning: assumed shape array, using 0 instead of ':' - Constructing wrapper function "second_order_asr.initialize_perm"... - initialize_perm(r2,sclat,pbc,[n_blocks]) - Constructing wrapper function "second_order_asr.clear_all"... - clear_all() - Creating wrapper for Fortran function "geq"("geq")... - Constructing wrapper function "second_order_asr.geq"... - geq = geq(v1,v2,lat,pbc) - Constructing wrapper function "second_order_asr.impose_perm_sym"... - fcvar,fc_sym = impose_perm_sym(fc,r2,sclat,pbc,verbose,[nat,n_blocks]) - Constructing wrapper function "second_order_asr.impose_asr_2nd"... - fcvar,sum2nd,fc_asr = impose_asr_2nd(fc,pow,sclat,pbc,verbose,[nat,n_blocks]) - Constructing wrapper function "second_order_asr.impose_asr"... - fc_out = impose_asr(fc,r2,pow,sclat,pbc,threshold,maxite,verbose,[nat,n_blocks]) - Wrote C/API module "secondorder" to file "build/src.linux-x86_64-3.10/secondordermodule.c" - Fortran 90 wrappers are saved to "build/src.linux-x86_64-3.10/secondorder-f2pywrappers2.f90" -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10/fortranobject.c' to sources. -INFO: adding 'build/src.linux-x86_64-3.10/build/src.linux-x86_64-3.10' to include_dirs. -INFO: adding 'build/src.linux-x86_64-3.10/secondorder-f2pywrappers2.f90' to sources. -INFO: build_src: building npy-pkg config files -running build_py -creating build/lib.linux-x86_64-3.10 -creating build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Methods.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/calculators.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Phonons.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/AnharmonicForceFields.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/ForceTensor.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/__init__.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Spectral.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Bands.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Manipulate.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Units.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Settings.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Structure.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/symmetries.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Timer.py -> build/lib.linux-x86_64-3.10/cellconstructor -copying cellconstructor/Moro_object.py -> build/lib.linux-x86_64-3.10/cellconstructor -creating build/lib.linux-x86_64-3.10/cellconstructor/SymData -copying cellconstructor/SymData/64.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData -copying cellconstructor/SymData/36_red.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData -copying cellconstructor/SymData/15.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData -copying cellconstructor/SymData/60.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData -copying cellconstructor/SymData/36.dat -> build/lib.linux-x86_64-3.10/cellconstructor/SymData -running build_ext -INFO: customize UnixCCompiler -INFO: customize UnixCCompiler using build_ext -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-march=native) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -creating /tmp/tmpcmtles2_/usr -creating /tmp/tmpcmtles2_/usr/local -creating /tmp/tmpcmtles2_/usr/local/lib -creating /tmp/tmpcmtles2_/usr/local/lib/python3.10 -creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages -creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy -creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy/distutils -creating /tmp/tmpcmtles2_/usr/local/lib/python3.10/dist-packages/numpy/distutils/checks -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-march=native' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-O3) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-O3' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-Werror) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-Werror' -INFO: CCompilerOpt.__init__[1782] : check requested baseline -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse2' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE' with flags (-msse -msse2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE2' with flags (-msse -msse2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -Werror' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse3) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse3' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE3' with flags (-msse -msse2 -msse3) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -Werror' -INFO: CCompilerOpt.__init__[1791] : check requested dispatch-able features -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mssse3) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mssse3' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse4.1) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse4.1' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mpopcnt) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mpopcnt' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'POPCNT' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -Werror' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-msse4.2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse4.2' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mf16c) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mf16c' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx2' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX2' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mavx2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mavx2 -Werror' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mfma) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mfma' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512f -mno-mmx) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx512f -mno-mmx' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512cd) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx512cd' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512vl -mavx512bw -mavx512dq) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx512vl -mavx512bw -mavx512dq' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512vnni) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx512vnni' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_CLX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512vnni) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512vnni -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE41' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'F16C' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -Werror' -INFO: CCompilerOpt.cc_test_flags[1073] : testing flags (-mavx512ifma -mavx512vbmi) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-mavx512ifma -mavx512vbmi' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_CNL' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512CD' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSSE3' with flags (-msse -msse2 -msse3 -mssse3) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'FMA3' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'SSE42' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -Werror' -INFO: CCompilerOpt.feature_test[1547] : testing feature 'AVX512_SKX' with flags (-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq) -INFO: C compiler: x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC - -INFO: compile options: '-I/usr/include/python3.10 -c' -extra options: '-msse -msse2 -msse3 -mssse3 -msse4.1 -mpopcnt -msse4.2 -mavx -mf16c -mfma -mavx2 -mavx512f -mno-mmx -mavx512cd -mavx512vl -mavx512bw -mavx512dq -Werror' -INFO: CCompilerOpt.cache_flush[857] : write cache to path -> /home/antonio/CellConstructor/build/temp.linux-x86_64-3.10/ccompiler_opt_cache_ext.py