compute_vibrational_frequencies Subroutine

public subroutine compute_vibrational_frequencies(hessian, element_numbers, frequencies, eigenvalues_out, eigenvectors, coordinates, project_trans_rot, projected_hessian_out)

Compute vibrational frequencies from the Hessian matrix.

Algorithm: 1. Mass-weight the Hessian: H_mw = M^{-1/2} * H * M^{-1/2} 2. Optionally project out translation/rotation modes 3. Diagonalize H_mw to get eigenvalues 4. Convert eigenvalues to frequencies in cm⁻¹

Negative eigenvalues produce negative frequencies (imaginary modes, indicating transition states or saddle points).

Arguments

Type IntentOptional Attributes Name
real(kind=dp), intent(in) :: hessian(:,:)

Hessian matrix in Hartree/Bohr² (3N x 3N)

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

Atomic numbers for each atom (N atoms)

real(kind=dp), intent(out), allocatable :: frequencies(:)

Vibrational frequencies in cm⁻¹ (3N modes, or 3N-6 if projected)

real(kind=dp), intent(out), optional, allocatable :: eigenvalues_out(:)

Raw eigenvalues from diagonalization (Hartree/Bohr²/amu)

real(kind=dp), intent(out), optional, allocatable :: eigenvectors(:,:)

Normal mode eigenvectors (3N x 3N), columns are modes

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

Atomic coordinates in Bohr (3, N) - required for projection

logical, intent(in), optional :: project_trans_rot

If true, project out translation/rotation modes (requires coordinates)

real(kind=dp), intent(out), optional, allocatable :: projected_hessian_out(:,:)

Mass-weighted Hessian after trans/rot projection (before diagonalization)


Calls

proc~~compute_vibrational_frequencies~~CallsGraph proc~compute_vibrational_frequencies compute_vibrational_frequencies error error proc~compute_vibrational_frequencies->error pic_syev pic_syev proc~compute_vibrational_frequencies->pic_syev proc~mass_weight_hessian mass_weight_hessian proc~compute_vibrational_frequencies->proc~mass_weight_hessian proc~project_translation_rotation project_translation_rotation proc~compute_vibrational_frequencies->proc~project_translation_rotation warning warning proc~compute_vibrational_frequencies->warning proc~element_mass element_mass proc~mass_weight_hessian->proc~element_mass pic_gesvd pic_gesvd proc~project_translation_rotation->pic_gesvd proc~project_translation_rotation->proc~element_mass

Called by

proc~~compute_vibrational_frequencies~~CalledByGraph proc~compute_vibrational_frequencies compute_vibrational_frequencies 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 interface~distributed_unfragmented_hessian distributed_unfragmented_hessian proc~run_unfragmented_calculation->interface~distributed_unfragmented_hessian proc~serial_fragment_processor serial_fragment_processor proc~serial_fragment_processor->proc~compute_mbe 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 proc~compute_energy_and_forces compute_energy_and_forces proc~compute_energy_and_forces->proc~run_calculation proc~mbe_run_distributed mbe_context_t%mbe_run_distributed proc~mbe_run_distributed->interface~global_coordinator proc~mbe_run_serial mbe_context_t%mbe_run_serial proc~mbe_run_serial->interface~serial_fragment_processor proc~run_multi_molecule_calculations run_multi_molecule_calculations proc~run_multi_molecule_calculations->proc~run_calculation program~main main program~main->proc~run_calculation

Variables

Type Visibility Attributes Name Initial
logical, private :: do_projection
real(kind=dp), private, allocatable :: eigenvalues(:)
integer, private :: i
integer, private :: info
real(kind=dp), private, allocatable :: mw_hessian(:,:)
integer, private :: n_coords

Source Code

   subroutine compute_vibrational_frequencies(hessian, element_numbers, frequencies, &
                                              eigenvalues_out, eigenvectors, &
                                              coordinates, project_trans_rot, &
                                              projected_hessian_out)
      !! Compute vibrational frequencies from the Hessian matrix.
      !!
      !! Algorithm:
      !! 1. Mass-weight the Hessian: H_mw = M^{-1/2} * H * M^{-1/2}
      !! 2. Optionally project out translation/rotation modes
      !! 3. Diagonalize H_mw to get eigenvalues
      !! 4. Convert eigenvalues to frequencies in cm⁻¹
      !!
      !! Negative eigenvalues produce negative frequencies (imaginary modes,
      !! indicating transition states or saddle points).
      real(dp), intent(in) :: hessian(:, :)
         !! Hessian matrix in Hartree/Bohr² (3*N x 3*N)
      integer, intent(in) :: element_numbers(:)
         !! Atomic numbers for each atom (N atoms)
      real(dp), allocatable, intent(out) :: frequencies(:)
         !! Vibrational frequencies in cm⁻¹ (3*N modes, or 3*N-6 if projected)
      real(dp), allocatable, intent(out), optional :: eigenvalues_out(:)
         !! Raw eigenvalues from diagonalization (Hartree/Bohr²/amu)
      real(dp), allocatable, intent(out), optional :: eigenvectors(:, :)
         !! Normal mode eigenvectors (3*N x 3*N), columns are modes
      real(dp), intent(in), optional :: coordinates(:, :)
         !! Atomic coordinates in Bohr (3, N) - required for projection
      logical, intent(in), optional :: project_trans_rot
         !! If true, project out translation/rotation modes (requires coordinates)
      real(dp), allocatable, intent(out), optional :: projected_hessian_out(:, :)
         !! Mass-weighted Hessian after trans/rot projection (before diagonalization)

      real(dp), allocatable :: mw_hessian(:, :)
      real(dp), allocatable :: eigenvalues(:)
      integer :: n_coords, info, i
      logical :: do_projection

      n_coords = size(hessian, 1)

      ! Check if projection is requested
      do_projection = .false.
      if (present(project_trans_rot)) then
         if (project_trans_rot) then
            if (.not. present(coordinates)) then
               ! Cannot project without coordinates - fall back to no projection
               call logger%warning("Missing coordinates, not projecting out tran/rot motions")
               do_projection = .false.
            else
               do_projection = .true.
            end if
         end if
      end if

      ! Mass-weight the Hessian
      call mass_weight_hessian(hessian, element_numbers, mw_hessian)

      ! Optionally project out translation/rotation modes
      if (do_projection) then
         call project_translation_rotation(mw_hessian, coordinates, element_numbers)
      end if

      ! Return projected Hessian if requested (before diagonalization destroys it)
      if (present(projected_hessian_out)) then
         allocate (projected_hessian_out(n_coords, n_coords))
         projected_hessian_out = mw_hessian
      end if

      ! Allocate eigenvalue storage
      allocate (eigenvalues(n_coords))

      ! Diagonalize the mass-weighted Hessian
      ! pic_syev overwrites mw_hessian with eigenvectors (if jobz='V', default)
      call pic_syev(mw_hessian, eigenvalues, info=info)

      if (info /= 0) then
         ! Eigenvalue decomposition failed
         call logger%error("Eigenvalue decomposition in vibrational frequencies failed")
         allocate (frequencies(n_coords))
         frequencies = 0.0_dp
         return
      end if

      ! Convert eigenvalues to frequencies in cm⁻¹
      allocate (frequencies(n_coords))
      do i = 1, n_coords
         if (eigenvalues(i) >= 0.0_dp) then
            ! Real frequency
            frequencies(i) = sqrt(eigenvalues(i)*AU_TO_CM1)
         else
            ! Imaginary frequency (negative eigenvalue) - report as negative
            frequencies(i) = -sqrt(abs(eigenvalues(i))*AU_TO_CM1)
         end if
      end do

      ! Return eigenvalues if requested
      if (present(eigenvalues_out)) then
         allocate (eigenvalues_out(n_coords))
         eigenvalues_out = eigenvalues
      end if

      ! Return eigenvectors if requested
      if (present(eigenvectors)) then
         allocate (eigenvectors(n_coords, n_coords))
         eigenvectors = mw_hessian
      end if

      deallocate (eigenvalues, mw_hessian)

   end subroutine compute_vibrational_frequencies