compute_moments_of_inertia Subroutine

public subroutine compute_moments_of_inertia(coords, atomic_numbers, n_atoms, center_of_mass, moments, principal_axes, is_linear, total_mass)

Compute the principal moments of inertia and detect linear molecules.

Calculates the center of mass, inertia tensor, and diagonalizes to get principal moments. A molecule is considered linear if one moment is essentially zero (< LINEAR_THRESHOLD).

Arguments

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

Atomic coordinates (3, n_atoms) in Bohr

integer, intent(in) :: atomic_numbers(:)

Atomic numbers

integer, intent(in) :: n_atoms

Number of atoms

real(kind=dp), intent(out) :: center_of_mass(3)

Center of mass in Angstrom

real(kind=dp), intent(out) :: moments(3)

Principal moments in amu*Angstrom^2

real(kind=dp), intent(out) :: principal_axes(3,3)

Principal axis vectors (columns)

logical, intent(out) :: is_linear

True if molecule is linear

real(kind=dp), intent(out) :: total_mass

Total mass in amu


Calls

proc~~compute_moments_of_inertia~~CallsGraph proc~compute_moments_of_inertia compute_moments_of_inertia 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 warning warning proc~compute_moments_of_inertia->warning

Called by

proc~~compute_moments_of_inertia~~CalledByGraph proc~compute_moments_of_inertia compute_moments_of_inertia proc~compute_thermochemistry compute_thermochemistry proc~compute_thermochemistry->proc~compute_moments_of_inertia proc~compute_mbe compute_mbe proc~compute_mbe->proc~compute_thermochemistry proc~print_vibrational_analysis print_vibrational_analysis proc~compute_mbe->proc~print_vibrational_analysis proc~gmbe_pie_coordinator gmbe_pie_coordinator proc~gmbe_pie_coordinator->proc~compute_thermochemistry proc~gmbe_pie_coordinator->proc~print_vibrational_analysis proc~hessian_coordinator hessian_coordinator proc~hessian_coordinator->proc~compute_thermochemistry proc~hessian_coordinator->proc~print_vibrational_analysis proc~print_vibrational_analysis->proc~compute_thermochemistry proc~serial_gmbe_pie_processor serial_gmbe_pie_processor proc~serial_gmbe_pie_processor->proc~compute_thermochemistry proc~serial_gmbe_pie_processor->proc~print_vibrational_analysis proc~unfragmented_calculation unfragmented_calculation proc~unfragmented_calculation->proc~compute_thermochemistry 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~compute_gmbe compute_gmbe proc~compute_gmbe->proc~print_vibrational_analysis 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 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

Variables

Type Visibility Attributes Name Initial
real(kind=dp), private :: coords_ang(3,n_atoms)
integer, private :: i
real(kind=dp), private :: inertia_tensor(3,3)
integer, private :: info
real(kind=dp), private :: mass_i
real(kind=dp), private :: masses(n_atoms)
real(kind=dp), private :: rel_coords(3,n_atoms)
real(kind=dp), private :: x
real(kind=dp), private :: y
real(kind=dp), private :: z

Source Code

   subroutine compute_moments_of_inertia(coords, atomic_numbers, n_atoms, &
                                         center_of_mass, moments, principal_axes, is_linear, total_mass)
      !! Compute the principal moments of inertia and detect linear molecules.
      !!
      !! Calculates the center of mass, inertia tensor, and diagonalizes to get
      !! principal moments. A molecule is considered linear if one moment is
      !! essentially zero (< LINEAR_THRESHOLD).
      real(dp), intent(in) :: coords(:, :)       !! Atomic coordinates (3, n_atoms) in Bohr
      integer, intent(in) :: atomic_numbers(:)   !! Atomic numbers
      integer, intent(in) :: n_atoms             !! Number of atoms
      real(dp), intent(out) :: center_of_mass(3)  !! Center of mass in Angstrom
      real(dp), intent(out) :: moments(3)        !! Principal moments in amu*Angstrom^2
      real(dp), intent(out) :: principal_axes(3, 3)  !! Principal axis vectors (columns)
      logical, intent(out) :: is_linear          !! True if molecule is linear
      real(dp), intent(out) :: total_mass        !! Total mass in amu

      real(dp) :: coords_ang(3, n_atoms)
      real(dp) :: masses(n_atoms)
      real(dp) :: rel_coords(3, n_atoms)
      real(dp) :: inertia_tensor(3, 3)
      integer :: i, info
      real(dp) :: mass_i, x, y, z

      ! Convert coordinates to Angstrom
      coords_ang = coords*BOHR_TO_ANGSTROM

      ! Get atomic masses
      total_mass = 0.0_dp
      do i = 1, n_atoms
         masses(i) = element_mass(atomic_numbers(i))
         total_mass = total_mass + masses(i)
      end do

      ! Compute center of mass
      center_of_mass = 0.0_dp
      do i = 1, n_atoms
         center_of_mass(:) = center_of_mass(:) + masses(i)*coords_ang(:, i)
      end do
      center_of_mass = center_of_mass/total_mass

      ! Compute coordinates relative to center of mass
      do i = 1, n_atoms
         rel_coords(:, i) = coords_ang(:, i) - center_of_mass(:)
      end do

      ! Build inertia tensor
      inertia_tensor = 0.0_dp
      do i = 1, n_atoms
         mass_i = masses(i)
         x = rel_coords(1, i)
         y = rel_coords(2, i)
         z = rel_coords(3, i)

         ! Diagonal elements: I_xx = sum(m * (y^2 + z^2)), etc.
         inertia_tensor(1, 1) = inertia_tensor(1, 1) + mass_i*(y*y + z*z)
         inertia_tensor(2, 2) = inertia_tensor(2, 2) + mass_i*(x*x + z*z)
         inertia_tensor(3, 3) = inertia_tensor(3, 3) + mass_i*(x*x + y*y)

         ! Off-diagonal elements: I_xy = -sum(m * x * y), etc.
         inertia_tensor(1, 2) = inertia_tensor(1, 2) - mass_i*x*y
         inertia_tensor(1, 3) = inertia_tensor(1, 3) - mass_i*x*z
         inertia_tensor(2, 3) = inertia_tensor(2, 3) - mass_i*y*z
      end do

      ! Symmetrize
      inertia_tensor(2, 1) = inertia_tensor(1, 2)
      inertia_tensor(3, 1) = inertia_tensor(1, 3)
      inertia_tensor(3, 2) = inertia_tensor(2, 3)

      ! Diagonalize to get principal moments
      principal_axes = inertia_tensor
      call pic_syev(principal_axes, moments, 'V', 'U', info)

      if (info /= 0) then
         call logger%warning("Failed to diagonalize inertia tensor, info = "// &
                             trim(adjustl(to_char(info))))
         moments = 0.0_dp
         is_linear = .false.
         return
      end if

      ! Check for linear molecule: one moment should be ~0
      ! Moments are returned in ascending order
      is_linear = (moments(1) < LINEAR_THRESHOLD)

   end subroutine compute_moments_of_inertia