print_vibrational_analysis Subroutine

public subroutine print_vibrational_analysis(frequencies, reduced_masses, force_constants, cartesian_displacements, element_numbers, force_constants_mdyne, print_displacements, n_atoms, ir_intensities, coordinates, electronic_energy, temperature, pressure)

Print vibrational analysis results in a formatted table.

Output format is similar to Gaussian, with frequencies grouped in columns. Optionally prints Cartesian displacement vectors for each mode. If coordinates and electronic_energy are provided, also computes and prints thermochemistry using the RRHO approximation.

Arguments

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

Vibrational frequencies in cm⁻¹

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

Reduced masses in amu

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

Force constants in Hartree/Bohr² (or mdyne/Å if force_constants_mdyne provided)

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

Cartesian displacement vectors (3N x 3N)

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

Atomic numbers for each atom

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

Force constants in mdyne/Å (if provided, these are printed instead)

logical, intent(in), optional :: print_displacements

If true, print Cartesian displacement vectors (default: true)

integer, intent(in), optional :: n_atoms

Number of atoms (if not provided, derived from size of element_numbers)

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

IR intensities in km/mol

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

Atomic coordinates (3, n_atoms) in Bohr - needed for thermochemistry

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

Electronic energy in Hartree - needed for thermochemistry

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

Temperature for thermochemistry in K (default: 298.15)

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

Pressure for thermochemistry in atm (default: 1.0)


Calls

proc~~print_vibrational_analysis~~CallsGraph proc~print_vibrational_analysis print_vibrational_analysis info info proc~print_vibrational_analysis->info proc~compute_thermochemistry compute_thermochemistry proc~print_vibrational_analysis->proc~compute_thermochemistry proc~element_number_to_symbol element_number_to_symbol proc~print_vibrational_analysis->proc~element_number_to_symbol proc~print_thermochemistry print_thermochemistry proc~print_vibrational_analysis->proc~print_thermochemistry warning warning proc~print_vibrational_analysis->warning proc~compute_electronic_entropy compute_electronic_entropy proc~compute_thermochemistry->proc~compute_electronic_entropy proc~compute_moments_of_inertia compute_moments_of_inertia proc~compute_thermochemistry->proc~compute_moments_of_inertia proc~compute_partition_functions compute_partition_functions proc~compute_thermochemistry->proc~compute_partition_functions proc~compute_rotational_constants compute_rotational_constants proc~compute_thermochemistry->proc~compute_rotational_constants proc~compute_rotational_thermo compute_rotational_thermo proc~compute_thermochemistry->proc~compute_rotational_thermo proc~compute_translational_thermo compute_translational_thermo proc~compute_thermochemistry->proc~compute_translational_thermo proc~compute_vibrational_thermo compute_vibrational_thermo proc~compute_thermochemistry->proc~compute_vibrational_thermo proc~compute_zpe compute_zpe proc~compute_thermochemistry->proc~compute_zpe proc~print_thermochemistry->info proc~compute_moments_of_inertia->warning pic_syev pic_syev proc~compute_moments_of_inertia->pic_syev proc~element_mass element_mass proc~compute_moments_of_inertia->proc~element_mass to_char to_char proc~compute_moments_of_inertia->to_char proc~compute_zpe->warning proc~compute_zpe->to_char

Called by

proc~~print_vibrational_analysis~~CalledByGraph proc~print_vibrational_analysis print_vibrational_analysis proc~compute_gmbe compute_gmbe proc~compute_gmbe->proc~print_vibrational_analysis proc~compute_mbe compute_mbe proc~compute_mbe->proc~print_vibrational_analysis proc~gmbe_pie_coordinator gmbe_pie_coordinator proc~gmbe_pie_coordinator->proc~print_vibrational_analysis proc~hessian_coordinator hessian_coordinator proc~hessian_coordinator->proc~print_vibrational_analysis proc~serial_gmbe_pie_processor serial_gmbe_pie_processor proc~serial_gmbe_pie_processor->proc~print_vibrational_analysis proc~unfragmented_calculation unfragmented_calculation proc~unfragmented_calculation->proc~print_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~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~serial_fragment_processor serial_fragment_processor proc~serial_fragment_processor->proc~compute_mbe 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~distributed_unfragmented_hessian distributed_unfragmented_hessian proc~distributed_unfragmented_hessian->interface~hessian_coordinator 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 interface~distributed_unfragmented_hessian->proc~distributed_unfragmented_hessian 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_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~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
character(len=3), private :: coord_label
logical, private :: do_print_disp
character(len=2), private :: elem_sym
character(len=16), private :: fc_str
real(kind=dp), private :: fc_value
character(len=16), private :: freq_str
integer, private :: iatom
integer, private :: icoord
integer, private :: igroup
integer, private :: imode
character(len=16), private :: ir_str
integer, private :: k
character(len=512), private :: line
character(len=16), private :: mass_str
integer, private :: mode_end
integer, private :: mode_start
integer, private :: modes_in_group
integer, private :: n_at
integer, private :: n_groups
integer, private :: n_modes

Source Code

   subroutine print_vibrational_analysis(frequencies, reduced_masses, force_constants, &
                                         cartesian_displacements, element_numbers, &
                                         force_constants_mdyne, print_displacements, &
                                         n_atoms, ir_intensities, &
                                         coordinates, electronic_energy, &
                                         temperature, pressure)
      !! Print vibrational analysis results in a formatted table.
      !!
      !! Output format is similar to Gaussian, with frequencies grouped in columns.
      !! Optionally prints Cartesian displacement vectors for each mode.
      !! If coordinates and electronic_energy are provided, also computes and prints
      !! thermochemistry using the RRHO approximation.
      real(dp), intent(in) :: frequencies(:)
         !! Vibrational frequencies in cm⁻¹
      real(dp), intent(in) :: reduced_masses(:)
         !! Reduced masses in amu
      real(dp), intent(in) :: force_constants(:)
         !! Force constants in Hartree/Bohr² (or mdyne/Å if force_constants_mdyne provided)
      real(dp), intent(in) :: cartesian_displacements(:, :)
         !! Cartesian displacement vectors (3*N x 3*N)
      integer, intent(in) :: element_numbers(:)
         !! Atomic numbers for each atom
      real(dp), intent(in), optional :: force_constants_mdyne(:)
         !! Force constants in mdyne/Å (if provided, these are printed instead)
      logical, intent(in), optional :: print_displacements
         !! If true, print Cartesian displacement vectors (default: true)
      integer, intent(in), optional :: n_atoms
         !! Number of atoms (if not provided, derived from size of element_numbers)
      real(dp), intent(in), optional :: ir_intensities(:)
         !! IR intensities in km/mol
      real(dp), intent(in), optional :: coordinates(:, :)
         !! Atomic coordinates (3, n_atoms) in Bohr - needed for thermochemistry
      real(dp), intent(in), optional :: electronic_energy
         !! Electronic energy in Hartree - needed for thermochemistry
      real(dp), intent(in), optional :: temperature
         !! Temperature for thermochemistry in K (default: 298.15)
      real(dp), intent(in), optional :: pressure
         !! Pressure for thermochemistry in atm (default: 1.0)

      integer :: n_modes, n_at, n_groups, igroup, imode, iatom, icoord, k
      integer :: mode_start, mode_end, modes_in_group
      logical :: do_print_disp
      character(len=512) :: line
      character(len=16) :: freq_str, mass_str, fc_str, ir_str
      character(len=2) :: elem_sym
      character(len=3) :: coord_label
      real(dp) :: fc_value

      n_modes = size(frequencies)
      if (present(n_atoms)) then
         n_at = n_atoms
      else
         n_at = size(element_numbers)
      end if

      do_print_disp = .true.
      if (present(print_displacements)) do_print_disp = print_displacements

      call logger%info(" ")
      call logger%info("============================================================")
      call logger%info("                  VIBRATIONAL ANALYSIS")
      call logger%info("============================================================")
      call logger%info(" ")

      ! Print in groups of 3 modes (like Gaussian)
      n_groups = (n_modes + 2)/3

      do igroup = 1, n_groups
         mode_start = (igroup - 1)*3 + 1
         mode_end = min(igroup*3, n_modes)
         modes_in_group = mode_end - mode_start + 1

         ! Mode numbers header
         line = "                    "
         do k = mode_start, mode_end
            write (freq_str, '(i12)') k
            line = trim(line)//freq_str
         end do
         call logger%info(trim(line))

         ! Frequencies (show "i" only for significant imaginary frequencies)
         line = " Frequencies --  "
         do k = mode_start, mode_end
            if (frequencies(k) < 0.0_dp .and. abs(frequencies(k)) > 10.0_dp) then
               ! Significant imaginary frequency - show with "i"
               write (freq_str, '(f12.4,a)') abs(frequencies(k)), "i"
            else
               ! Real or near-zero frequency
               write (freq_str, '(f12.4)') abs(frequencies(k))
            end if
            line = trim(line)//freq_str
         end do
         call logger%info(trim(line))

         ! Reduced masses
         line = " Red. masses --  "
         do k = mode_start, mode_end
            write (mass_str, '(f12.4)') reduced_masses(k)
            line = trim(line)//mass_str
         end do
         call logger%info(trim(line))

         ! Force constants
         if (present(force_constants_mdyne)) then
            line = " Frc consts  --  "
            do k = mode_start, mode_end
               write (fc_str, '(f12.4)') force_constants_mdyne(k)
               line = trim(line)//fc_str
            end do
         else
            line = " Frc consts  --  "
            do k = mode_start, mode_end
               write (fc_str, '(f12.6)') force_constants(k)
               line = trim(line)//fc_str
            end do
         end if
         call logger%info(trim(line))

         ! IR intensities (if provided)
         if (present(ir_intensities)) then
            line = " IR Intens  --  "
            do k = mode_start, mode_end
               write (ir_str, '(f12.4)') ir_intensities(k)
               line = trim(line)//ir_str
            end do
            call logger%info(trim(line))
         end if

         ! Cartesian displacements
         if (do_print_disp) then
          call logger%info(" Atom          X         Y         Z       X         Y         Z       X         Y         Z")

            do iatom = 1, n_at
               elem_sym = element_number_to_symbol(element_numbers(iatom))

               ! Build line with atom info and displacements for each mode
               write (line, '(i4,1x,a2)') iatom, elem_sym

               do k = mode_start, mode_end
                  do icoord = 1, 3
                     write (freq_str, '(f10.5)') cartesian_displacements(3*(iatom - 1) + icoord, k)
                     line = trim(line)//freq_str
                  end do
               end do
               call logger%info(trim(line))
            end do
         end if

         call logger%info(" ")
      end do

      ! Summary statistics
      call logger%info("------------------------------------------------------------")
      call logger%info(" Summary:")

      ! Count real vs imaginary frequencies
      block
         integer :: n_real, n_imag, n_zero
         real(dp) :: zero_thresh
         zero_thresh = 10.0_dp  ! frequencies below 10 cm⁻¹ considered "zero"

         n_real = 0
         n_imag = 0
         n_zero = 0
         do k = 1, n_modes
            if (abs(frequencies(k)) < zero_thresh) then
               n_zero = n_zero + 1
            else if (frequencies(k) < 0.0_dp) then
               n_imag = n_imag + 1
            else
               n_real = n_real + 1
            end if
         end do

         write (line, '(a,i5)') "   Total modes:              ", n_modes
         call logger%info(trim(line))
         write (line, '(a,i5)') "   Real frequencies:         ", n_real
         call logger%info(trim(line))
         write (line, '(a,i5)') "   Imaginary frequencies:    ", n_imag
         call logger%info(trim(line))
         write (line, '(a,i5)') "   Near-zero (trans/rot):    ", n_zero
         call logger%info(trim(line))

         if (n_imag > 0) then
            call logger%warning("System has imaginary frequencies - may be a transition state")
         end if
      end block

      call logger%info("============================================================")
      call logger%info(" ")

      ! Compute and print thermochemistry if coordinates and energy are provided
      if (present(coordinates) .and. present(electronic_energy)) then
         block
            type(thermochemistry_result_t) :: thermo_result
            call compute_thermochemistry(coordinates, element_numbers, frequencies, &
                                         n_at, n_modes, thermo_result, &
                                         temperature=temperature, pressure=pressure)
            call print_thermochemistry(thermo_result, electronic_energy)
         end block
      end if

   end subroutine print_vibrational_analysis