project_translation_rotation Subroutine

public subroutine project_translation_rotation(mw_hessian, coordinates, element_numbers)

Project out translation and rotation modes from mass-weighted Hessian.

Builds 6 vectors (3 translation + 3 rotation) in mass-weighted coordinates, orthonormalizes them using SVD, then projects them out: H_proj = (I - D @ D^T) @ H @ (I - D @ D^T)

This sets the 6 translation/rotation eigenvalues to exactly zero.

Arguments

Type IntentOptional Attributes Name
real(kind=dp), intent(inout) :: mw_hessian(:,:)

Mass-weighted Hessian (modified in place)

real(kind=dp), intent(in) :: coordinates(:,:)

Atomic coordinates in Bohr (3, N)

integer, intent(in) :: element_numbers(:)

Atomic numbers for each atom (N atoms)


Calls

proc~~project_translation_rotation~~CallsGraph proc~project_translation_rotation project_translation_rotation pic_gesvd pic_gesvd proc~project_translation_rotation->pic_gesvd proc~element_mass element_mass proc~project_translation_rotation->proc~element_mass

Called by

proc~~project_translation_rotation~~CalledByGraph proc~project_translation_rotation project_translation_rotation proc~compute_vibrational_frequencies compute_vibrational_frequencies proc~compute_vibrational_frequencies->proc~project_translation_rotation proc~compute_vibrational_analysis compute_vibrational_analysis proc~compute_vibrational_analysis->proc~compute_vibrational_frequencies proc~hessian_coordinator hessian_coordinator proc~hessian_coordinator->proc~compute_vibrational_frequencies proc~hessian_coordinator->proc~compute_vibrational_analysis proc~unfragmented_calculation unfragmented_calculation proc~unfragmented_calculation->proc~compute_vibrational_frequencies proc~unfragmented_calculation->proc~compute_vibrational_analysis interface~hessian_coordinator hessian_coordinator interface~hessian_coordinator->proc~hessian_coordinator interface~unfragmented_calculation unfragmented_calculation interface~unfragmented_calculation->proc~unfragmented_calculation proc~compute_gmbe compute_gmbe proc~compute_gmbe->proc~compute_vibrational_analysis proc~compute_mbe compute_mbe proc~compute_mbe->proc~compute_vibrational_analysis proc~gmbe_pie_coordinator gmbe_pie_coordinator proc~gmbe_pie_coordinator->proc~compute_vibrational_analysis proc~serial_gmbe_pie_processor serial_gmbe_pie_processor proc~serial_gmbe_pie_processor->proc~compute_vibrational_analysis proc~distributed_unfragmented_hessian distributed_unfragmented_hessian proc~distributed_unfragmented_hessian->interface~hessian_coordinator proc~global_coordinator global_coordinator proc~global_coordinator->proc~compute_mbe proc~gmbe_run_distributed gmbe_context_t%gmbe_run_distributed proc~gmbe_run_distributed->proc~gmbe_pie_coordinator proc~gmbe_run_serial gmbe_context_t%gmbe_run_serial proc~gmbe_run_serial->proc~serial_gmbe_pie_processor proc~run_unfragmented_calculation run_unfragmented_calculation proc~run_unfragmented_calculation->interface~unfragmented_calculation proc~serial_fragment_processor serial_fragment_processor proc~serial_fragment_processor->proc~compute_mbe interface~distributed_unfragmented_hessian distributed_unfragmented_hessian interface~distributed_unfragmented_hessian->proc~distributed_unfragmented_hessian interface~global_coordinator global_coordinator interface~global_coordinator->proc~global_coordinator interface~serial_fragment_processor serial_fragment_processor interface~serial_fragment_processor->proc~serial_fragment_processor proc~run_calculation run_calculation proc~run_calculation->proc~run_unfragmented_calculation

Variables

Type Visibility Attributes Name Initial
real(kind=dp), private, allocatable :: D(:,:)
real(kind=dp), private, allocatable :: D_orth(:,:)
real(kind=dp), private, allocatable :: S(:)
real(kind=dp), private, allocatable :: U(:,:)
real(kind=dp), private, allocatable :: VT(:,:)
real(kind=dp), private, allocatable :: com(:)
integer, private :: i
integer, private :: iatom
integer, private :: idx
integer, private :: info
integer, private :: j
integer, private :: k
real(kind=dp), private :: mass
integer, private :: n_atoms
integer, private :: n_coords
integer, private :: n_modes
real(kind=dp), private :: norm
real(kind=dp), private, allocatable :: proj(:,:)
real(kind=dp), private, allocatable :: r(:,:)
real(kind=dp), private, allocatable :: sqrt_mass(:)
real(kind=dp), private, allocatable :: temp(:,:)
real(kind=dp), private :: total_mass

Source Code

   subroutine project_translation_rotation(mw_hessian, coordinates, element_numbers)
      !! Project out translation and rotation modes from mass-weighted Hessian.
      !!
      !! Builds 6 vectors (3 translation + 3 rotation) in mass-weighted coordinates,
      !! orthonormalizes them using SVD, then projects them out:
      !!   H_proj = (I - D @ D^T) @ H @ (I - D @ D^T)
      !!
      !! This sets the 6 translation/rotation eigenvalues to exactly zero.
      real(dp), intent(inout) :: mw_hessian(:, :)
         !! Mass-weighted Hessian (modified in place)
      real(dp), intent(in) :: coordinates(:, :)
         !! Atomic coordinates in Bohr (3, N)
      integer, intent(in) :: element_numbers(:)
         !! Atomic numbers for each atom (N atoms)

      real(dp), allocatable :: D(:, :)        ! Translation/rotation vectors (3N, 6)
      real(dp), allocatable :: com(:)         ! Center of mass
      real(dp), allocatable :: r(:, :)        ! Coordinates relative to COM
      real(dp), allocatable :: sqrt_mass(:)   ! sqrt(mass) for each atom
      real(dp), allocatable :: S(:)           ! Singular values
      real(dp), allocatable :: U(:, :)        ! Left singular vectors
      real(dp), allocatable :: VT(:, :)       ! Right singular vectors (transposed)
      real(dp), allocatable :: D_orth(:, :)   ! Orthonormalized D vectors
      real(dp), allocatable :: proj(:, :)     ! Projector matrix
      real(dp), allocatable :: temp(:, :)     ! Temporary matrix
      real(dp) :: total_mass, mass, norm
      integer :: n_atoms, n_coords, iatom, i, j, k, n_modes, info
      integer :: idx

      n_atoms = size(element_numbers)
      n_coords = 3*n_atoms

      ! Allocate arrays
      allocate (D(n_coords, 6))
      allocate (com(3))
      allocate (r(3, n_atoms))
      allocate (sqrt_mass(n_atoms))

      ! Compute sqrt(mass) for each atom and total mass
      total_mass = 0.0_dp
      do iatom = 1, n_atoms
         mass = element_mass(element_numbers(iatom))
         sqrt_mass(iatom) = sqrt(mass)
         total_mass = total_mass + mass
      end do

      ! Compute center of mass
      com = 0.0_dp
      do iatom = 1, n_atoms
         mass = element_mass(element_numbers(iatom))
         com(:) = com(:) + mass*coordinates(:, iatom)
      end do
      com = com/total_mass

      ! Compute coordinates relative to center of mass
      do iatom = 1, n_atoms
         r(:, iatom) = coordinates(:, iatom) - com(:)
      end do

      ! Initialize D to zero
      D = 0.0_dp

      ! Build translation vectors (mass-weighted)
      ! D_trans_k: displacement along axis k, weighted by sqrt(mass)
      do iatom = 1, n_atoms
         idx = 3*(iatom - 1)
         ! Translation along x
         D(idx + 1, 1) = sqrt_mass(iatom)
         ! Translation along y
         D(idx + 2, 2) = sqrt_mass(iatom)
         ! Translation along z
         D(idx + 3, 3) = sqrt_mass(iatom)
      end do

      ! Build rotation vectors (mass-weighted)
      ! D_rot_k: rotation around axis k, proportional to r × e_k, weighted by sqrt(mass)
      do iatom = 1, n_atoms
         idx = 3*(iatom - 1)
         ! Rotation around x-axis: r × e_x = (0, r_z, -r_y)
         D(idx + 2, 4) = sqrt_mass(iatom)*r(3, iatom)
         D(idx + 3, 4) = -sqrt_mass(iatom)*r(2, iatom)
         ! Rotation around y-axis: r × e_y = (-r_z, 0, r_x)
         D(idx + 1, 5) = -sqrt_mass(iatom)*r(3, iatom)
         D(idx + 3, 5) = sqrt_mass(iatom)*r(1, iatom)
         ! Rotation around z-axis: r × e_z = (r_y, -r_x, 0)
         D(idx + 1, 6) = sqrt_mass(iatom)*r(2, iatom)
         D(idx + 2, 6) = -sqrt_mass(iatom)*r(1, iatom)
      end do

      ! Normalize each column of D
      do k = 1, 6
         norm = sqrt(sum(D(:, k)**2))
         if (norm > 1.0e-10_dp) then
            D(:, k) = D(:, k)/norm
         end if
      end do

      ! Orthonormalize D using SVD: D = U @ S @ VT
      ! The orthonormal basis is given by the columns of U corresponding to non-zero singular values
      allocate (S(6))
      allocate (U(n_coords, 6))
      allocate (VT(6, 6))

      ! pic_gesvd(A, S, U, VT, info) - A is input, U and VT are separate outputs
      call pic_gesvd(D, S, U, VT, info=info)

      ! Count non-zero singular values (determines number of modes to project)
      n_modes = 0
      do k = 1, 6
         if (S(k) > 1.0e-10_dp) n_modes = n_modes + 1
      end do

      ! Build orthonormalized D matrix from U (columns with non-zero singular values)
      allocate (D_orth(n_coords, n_modes))
      j = 0
      do k = 1, 6
         if (S(k) > 1.0e-10_dp) then
            j = j + 1
            D_orth(:, j) = U(:, k)
         end if
      end do

      ! Build projector: P = I - D_orth @ D_orth^T
      allocate (proj(n_coords, n_coords))
      proj = 0.0_dp
      do i = 1, n_coords
         proj(i, i) = 1.0_dp
      end do

      ! Subtract D_orth @ D_orth^T
      do i = 1, n_coords
         do j = 1, n_coords
            do k = 1, n_modes
               proj(i, j) = proj(i, j) - D_orth(i, k)*D_orth(j, k)
            end do
         end do
      end do

      ! Apply projection: H_proj = P @ H @ P
      allocate (temp(n_coords, n_coords))

      ! temp = H @ P
      do i = 1, n_coords
         do j = 1, n_coords
            temp(i, j) = 0.0_dp
            do k = 1, n_coords
               temp(i, j) = temp(i, j) + mw_hessian(i, k)*proj(k, j)
            end do
         end do
      end do

      ! H_proj = P @ temp
      do i = 1, n_coords
         do j = 1, n_coords
            mw_hessian(i, j) = 0.0_dp
            do k = 1, n_coords
               mw_hessian(i, j) = mw_hessian(i, j) + proj(i, k)*temp(k, j)
            end do
         end do
      end do

      ! Cleanup
      deallocate (D, com, r, sqrt_mass, S, U, VT, D_orth, proj, temp)

   end subroutine project_translation_rotation