Convert mass-weighted eigenvectors to Cartesian displacements.
The Cartesian displacement for coordinate i in mode k is: x_{i,k} = L_mw_{i,k} / √(m_i)
The output can be normalized in two ways: - normalize_max=.true. (default): normalize so max|x| = 1 for each mode (Gaussian convention) - normalize_max=.false.: normalize so Σ_i x²_{i,k} = 1
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=dp), | intent(in) | :: | eigenvectors(:,:) |
Mass-weighted eigenvectors (3N x 3N) |
||
| integer, | intent(in) | :: | element_numbers(:) |
Atomic numbers for each atom (N atoms) |
||
| real(kind=dp), | intent(out), | allocatable | :: | cartesian_displacements(:,:) |
Cartesian displacement vectors (3N x 3N), columns are modes |
|
| logical, | intent(in), | optional | :: | normalize_max |
If true, normalize so max displacement = 1 (default: true) |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | private | :: | iatom | ||||
| integer, | private | :: | icoord | ||||
| integer, | private | :: | idx | ||||
| real(kind=dp), | private | :: | inv_sqrt_mass | ||||
| integer, | private | :: | k | ||||
| real(kind=dp), | private | :: | mass | ||||
| real(kind=dp), | private | :: | max_disp | ||||
| integer, | private | :: | n_atoms | ||||
| integer, | private | :: | n_coords | ||||
| real(kind=dp), | private | :: | norm | ||||
| logical, | private | :: | use_max_norm |
subroutine compute_cartesian_displacements(eigenvectors, element_numbers, & cartesian_displacements, normalize_max) !! Convert mass-weighted eigenvectors to Cartesian displacements. !! !! The Cartesian displacement for coordinate i in mode k is: !! x_{i,k} = L_mw_{i,k} / √(m_i) !! !! The output can be normalized in two ways: !! - normalize_max=.true. (default): normalize so max|x| = 1 for each mode (Gaussian convention) !! - normalize_max=.false.: normalize so Σ_i x²_{i,k} = 1 real(dp), intent(in) :: eigenvectors(:, :) !! Mass-weighted eigenvectors (3*N x 3*N) integer, intent(in) :: element_numbers(:) !! Atomic numbers for each atom (N atoms) real(dp), allocatable, intent(out) :: cartesian_displacements(:, :) !! Cartesian displacement vectors (3*N x 3*N), columns are modes logical, intent(in), optional :: normalize_max !! If true, normalize so max displacement = 1 (default: true) integer :: n_atoms, n_coords, iatom, icoord, k, idx real(dp) :: mass, inv_sqrt_mass, norm, max_disp logical :: use_max_norm n_atoms = size(element_numbers) n_coords = 3*n_atoms use_max_norm = .true. if (present(normalize_max)) use_max_norm = normalize_max allocate (cartesian_displacements(n_coords, n_coords)) ! Convert from mass-weighted to Cartesian: x = L_mw / √m do k = 1, n_coords do iatom = 1, n_atoms mass = element_mass(element_numbers(iatom)) inv_sqrt_mass = 1.0_dp/sqrt(mass) do icoord = 1, 3 idx = 3*(iatom - 1) + icoord cartesian_displacements(idx, k) = eigenvectors(idx, k)*inv_sqrt_mass end do end do end do ! Normalize each mode do k = 1, n_coords if (use_max_norm) then ! Gaussian convention: normalize so max |displacement| = 1 max_disp = maxval(abs(cartesian_displacements(:, k))) if (max_disp > 1.0e-14_dp) then cartesian_displacements(:, k) = cartesian_displacements(:, k)/max_disp end if else ! Standard normalization: Σ_i x²_{i,k} = 1 norm = sqrt(sum(cartesian_displacements(:, k)**2)) if (norm > 1.0e-14_dp) then cartesian_displacements(:, k) = cartesian_displacements(:, k)/norm end if end if end do end subroutine compute_cartesian_displacements