xtb_calc_hessian Subroutine

private subroutine xtb_calc_hessian(this, fragment, result)

Uses

  • proc~~xtb_calc_hessian~~UsesGraph proc~xtb_calc_hessian xtb_method_t%xtb_calc_hessian module~mqc_finite_differences mqc_finite_differences proc~xtb_calc_hessian->module~mqc_finite_differences pic_io pic_io proc~xtb_calc_hessian->pic_io pic_logger pic_logger proc~xtb_calc_hessian->pic_logger module~mqc_calculation_defaults mqc_calculation_defaults module~mqc_finite_differences->module~mqc_calculation_defaults module~mqc_physical_fragment mqc_physical_fragment module~mqc_finite_differences->module~mqc_physical_fragment pic_types pic_types module~mqc_finite_differences->pic_types module~mqc_calculation_defaults->pic_types module~mqc_physical_fragment->pic_types module~mqc_cgto mqc_cgto module~mqc_physical_fragment->module~mqc_cgto module~mqc_elements mqc_elements module~mqc_physical_fragment->module~mqc_elements module~mqc_error mqc_error module~mqc_physical_fragment->module~mqc_error module~mqc_geometry mqc_geometry module~mqc_physical_fragment->module~mqc_geometry module~mqc_physical_constants mqc_physical_constants module~mqc_physical_fragment->module~mqc_physical_constants module~mqc_xyz_reader mqc_xyz_reader module~mqc_physical_fragment->module~mqc_xyz_reader module~mqc_cgto->pic_types module~mqc_elements->pic_types pic_ascii pic_ascii module~mqc_elements->pic_ascii module~mqc_geometry->pic_types module~mqc_physical_constants->pic_types module~mqc_xyz_reader->pic_types module~mqc_xyz_reader->module~mqc_error module~mqc_xyz_reader->module~mqc_geometry

Calculate Hessian using finite differences of gradients

Since tblite does not natively support analytic Hessians, this routine computes the Hessian numerically via central finite differences: H[i,j] = (grad_j(x_i + h) - grad_j(x_i - h)) / (2h)

This requires 6N gradient calculations (forward and backward for each coordinate)

Type Bound

xtb_method_t

Arguments

Type IntentOptional Attributes Name
class(xtb_method_t), intent(in) :: this
type(physical_fragment_t), intent(in) :: fragment
type(calculation_result_t), intent(out) :: result

Calls

proc~~xtb_calc_hessian~~CallsGraph proc~xtb_calc_hessian xtb_method_t%xtb_calc_hessian info info proc~xtb_calc_hessian->info proc~error_set error_t%error_set proc~xtb_calc_hessian->proc~error_set proc~finite_diff_dipole_derivatives finite_diff_dipole_derivatives proc~xtb_calc_hessian->proc~finite_diff_dipole_derivatives proc~finite_diff_hessian_from_gradients finite_diff_hessian_from_gradients proc~xtb_calc_hessian->proc~finite_diff_hessian_from_gradients proc~generate_perturbed_geometries generate_perturbed_geometries proc~xtb_calc_hessian->proc~generate_perturbed_geometries proc~result_destroy calculation_result_t%result_destroy proc~xtb_calc_hessian->proc~result_destroy proc~xtb_calc_gradient xtb_method_t%xtb_calc_gradient proc~xtb_calc_hessian->proc~xtb_calc_gradient to_char to_char proc~xtb_calc_hessian->to_char proc~copy_and_displace_geometry copy_and_displace_geometry proc~generate_perturbed_geometries->proc~copy_and_displace_geometry proc~result_reset calculation_result_t%result_reset proc~result_destroy->proc~result_reset proc~xtb_calc_gradient->proc~error_set dpat dpat proc~xtb_calc_gradient->dpat new new proc~xtb_calc_gradient->new new_gfn1_calculator new_gfn1_calculator proc~xtb_calc_gradient->new_gfn1_calculator new_gfn2_calculator new_gfn2_calculator proc~xtb_calc_gradient->new_gfn2_calculator new_wavefunction new_wavefunction proc~xtb_calc_gradient->new_wavefunction proc~add_solvation_to_calc add_solvation_to_calc proc~xtb_calc_gradient->proc~add_solvation_to_calc proc~energy_total energy_t%energy_total proc~xtb_calc_gradient->proc~energy_total qat qat proc~xtb_calc_gradient->qat xtb_singlepoint xtb_singlepoint proc~xtb_calc_gradient->xtb_singlepoint new_solvation new_solvation proc~add_solvation_to_calc->new_solvation new_solvation_cds new_solvation_cds proc~add_solvation_to_calc->new_solvation_cds new_solvation_shift new_solvation_shift proc~add_solvation_to_calc->new_solvation_shift proc~get_solvent_dielectric get_solvent_dielectric proc~add_solvation_to_calc->proc~get_solvent_dielectric push_back push_back proc~add_solvation_to_calc->push_back proc~mp2_total mp2_energy_t%mp2_total proc~energy_total->proc~mp2_total proc~energy_reset energy_t%energy_reset proc~result_reset->proc~energy_reset proc~error_clear error_t%error_clear proc~result_reset->proc~error_clear proc~mp2_reset mp2_energy_t%mp2_reset proc~energy_reset->proc~mp2_reset

Variables

Type Visibility Attributes Name Initial
real(kind=dp), private, allocatable :: backward_dipoles(:,:)
type(displaced_geometry_t), private, allocatable :: backward_geoms(:)
real(kind=dp), private, allocatable :: backward_gradients(:,:,:)
logical, private :: compute_dipole_derivs
type(calculation_result_t), private :: disp_result
real(kind=dp), private :: displacement
real(kind=dp), private, allocatable :: forward_dipoles(:,:)
type(displaced_geometry_t), private, allocatable :: forward_geoms(:)
real(kind=dp), private, allocatable :: forward_gradients(:,:,:)
integer, private :: i
integer, private :: n_atoms
integer, private :: n_displacements

Source Code

   subroutine xtb_calc_hessian(this, fragment, result)
      !! Calculate Hessian using finite differences of gradients
      !!
      !! Since tblite does not natively support analytic Hessians, this routine
      !! computes the Hessian numerically via central finite differences:
      !!   H[i,j] = (grad_j(x_i + h) - grad_j(x_i - h)) / (2h)
      !!
      !! This requires 6N gradient calculations (forward and backward for each coordinate)
      use mqc_finite_differences, only: generate_perturbed_geometries, displaced_geometry_t, &
                                        finite_diff_hessian_from_gradients, finite_diff_dipole_derivatives, &
                                        DEFAULT_DISPLACEMENT
      use pic_logger, only: logger => global_logger
      use pic_io, only: to_char

      class(xtb_method_t), intent(in) :: this
      type(physical_fragment_t), intent(in) :: fragment
      type(calculation_result_t), intent(out) :: result

      type(displaced_geometry_t), allocatable :: forward_geoms(:), backward_geoms(:)
      real(dp), allocatable :: forward_gradients(:, :, :)  ! (n_displacements, 3, n_atoms)
      real(dp), allocatable :: backward_gradients(:, :, :)  ! (n_displacements, 3, n_atoms)
      real(dp), allocatable :: forward_dipoles(:, :)   ! (n_displacements, 3) for IR intensities
      real(dp), allocatable :: backward_dipoles(:, :)  ! (n_displacements, 3) for IR intensities
      type(calculation_result_t) :: disp_result
      real(dp) :: displacement
      integer :: n_atoms, n_displacements, i
      logical :: compute_dipole_derivs

      n_atoms = fragment%n_atoms
      n_displacements = 3*n_atoms
      displacement = DEFAULT_DISPLACEMENT

      if (this%verbose) then
         call logger%info("XTB: Computing Hessian via finite differences")
         call logger%info("  Method: Central differences of gradients")
         call logger%info("  Atoms: "//to_char(n_atoms))
         call logger%info("  Gradient calculations needed: "//to_char(2*n_displacements))
         call logger%info("  Finite difference step size: "//to_char(displacement)//" Bohr")
      end if

      ! Generate all perturbed geometries
      call generate_perturbed_geometries(fragment, displacement, forward_geoms, backward_geoms)

      ! Allocate storage for gradients at displaced geometries
      allocate (forward_gradients(n_displacements, 3, n_atoms))
      allocate (backward_gradients(n_displacements, 3, n_atoms))

      ! Allocate storage for dipoles at displaced geometries (for IR intensities)
      allocate (forward_dipoles(n_displacements, 3))
      allocate (backward_dipoles(n_displacements, 3))
      forward_dipoles = 0.0_dp
      backward_dipoles = 0.0_dp
      compute_dipole_derivs = .true.  ! Will be set to false if any dipole is missing

      ! Compute gradients at all forward-displaced geometries
      if (this%verbose) then
         call logger%info("  Computing forward-displaced gradients...")
      end if
      do i = 1, n_displacements

         ! Forward
         call this%calc_gradient(forward_geoms(i)%geometry, disp_result)
         if (disp_result%has_error .or. .not. disp_result%has_gradient) then
            call result%error%set(ERROR_GENERIC, "Failed to compute gradient for forward displacement "//to_char(i))
            result%has_error = .true.
            call disp_result%destroy()
            return
         end if
         forward_gradients(i, :, :) = disp_result%gradient
         ! Capture dipole for IR intensity calculation
         if (disp_result%has_dipole) then
            forward_dipoles(i, :) = disp_result%dipole
         else
            compute_dipole_derivs = .false.
         end if
         call disp_result%destroy()

         ! Backward
         call this%calc_gradient(backward_geoms(i)%geometry, disp_result)
         if (disp_result%has_error .or. .not. disp_result%has_gradient) then
            call result%error%set(ERROR_GENERIC, "Failed to compute gradient for backward displacement "//to_char(i))
            result%has_error = .true.
            call disp_result%destroy()
            return
         end if
         backward_gradients(i, :, :) = disp_result%gradient
         ! Capture dipole for IR intensity calculation
         if (disp_result%has_dipole) then
            backward_dipoles(i, :) = disp_result%dipole
         else
            compute_dipole_derivs = .false.
         end if
         call disp_result%destroy()

      end do
      if (this%verbose) then
         call logger%info("  Forward and backward gradient calculations complete ")
      end if
      ! Compute Hessian from finite differences
      if (this%verbose) then
         call logger%info("  Assembling Hessian matrix...")
      end if
      call finite_diff_hessian_from_gradients(fragment, forward_gradients, backward_gradients, &
                                              displacement, result%hessian)

      ! Compute dipole derivatives for IR intensity calculation
      if (compute_dipole_derivs) then
         call finite_diff_dipole_derivatives(n_atoms, forward_dipoles, backward_dipoles, &
                                             displacement, result%dipole_derivatives)
         result%has_dipole_derivatives = .true.
         if (this%verbose) then
            call logger%info("  Dipole derivatives computed for IR intensities")
         end if
      end if

      ! Also compute energy and gradient at reference geometry for completeness
      call this%calc_gradient(fragment, disp_result)
      if (disp_result%has_error) then
         result%error = disp_result%error
         result%has_error = .true.
         call disp_result%destroy()
         return
      end if
      result%energy = disp_result%energy
      result%has_energy = disp_result%has_energy
      if (disp_result%has_gradient) then
         allocate (result%gradient(3, n_atoms))
         result%gradient = disp_result%gradient
         result%has_gradient = .true.
      end if
      call disp_result%destroy()

      result%has_hessian = .true.

      if (this%verbose) then
         call logger%info("  Hessian calculation complete")
      end if

      ! Cleanup
      deallocate (forward_gradients, backward_gradients)
      deallocate (forward_dipoles, backward_dipoles)
      do i = 1, n_displacements
         call forward_geoms(i)%destroy()
         call backward_geoms(i)%destroy()
      end do
      deallocate (forward_geoms, backward_geoms)

   end subroutine xtb_calc_hessian