hessian_coordinator Module Subroutine

module subroutine hessian_coordinator(world_comm, sys_geom, config, json_data)

Uses

  • proc~~hessian_coordinator~~UsesGraph proc~hessian_coordinator hessian_coordinator module~mqc_finite_differences mqc_finite_differences proc~hessian_coordinator->module~mqc_finite_differences module~mqc_json_output_types mqc_json_output_types proc~hessian_coordinator->module~mqc_json_output_types module~mqc_method_base mqc_method_base proc~hessian_coordinator->module~mqc_method_base module~mqc_method_factory mqc_method_factory proc~hessian_coordinator->module~mqc_method_factory module~mqc_thermochemistry mqc_thermochemistry proc~hessian_coordinator->module~mqc_thermochemistry module~mqc_vibrational_analysis mqc_vibrational_analysis proc~hessian_coordinator->module~mqc_vibrational_analysis 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_json_output_types->module~mqc_thermochemistry module~mqc_json_output_types->pic_types module~mqc_method_base->module~mqc_physical_fragment module~mqc_result_types mqc_result_types module~mqc_method_base->module~mqc_result_types module~mqc_method_base->pic_types module~mqc_method_factory->module~mqc_method_base mctc_env mctc_env module~mqc_method_factory->mctc_env module~mqc_method_config mqc_method_config module~mqc_method_factory->module~mqc_method_config module~mqc_method_dft mqc_method_dft module~mqc_method_factory->module~mqc_method_dft module~mqc_method_hf mqc_method_hf module~mqc_method_factory->module~mqc_method_hf module~mqc_method_mcscf mqc_method_mcscf module~mqc_method_factory->module~mqc_method_mcscf module~mqc_method_types mqc_method_types module~mqc_method_factory->module~mqc_method_types module~mqc_method_xtb mqc_method_xtb module~mqc_method_factory->module~mqc_method_xtb module~mqc_method_factory->pic_types module~mqc_elements mqc_elements module~mqc_thermochemistry->module~mqc_elements module~mqc_physical_constants mqc_physical_constants module~mqc_thermochemistry->module~mqc_physical_constants pic_io pic_io module~mqc_thermochemistry->pic_io pic_lapack_interfaces pic_lapack_interfaces module~mqc_thermochemistry->pic_lapack_interfaces pic_logger pic_logger module~mqc_thermochemistry->pic_logger module~mqc_thermochemistry->pic_types module~mqc_vibrational_analysis->module~mqc_thermochemistry module~mqc_vibrational_analysis->module~mqc_elements module~mqc_vibrational_analysis->module~mqc_physical_constants module~mqc_vibrational_analysis->pic_lapack_interfaces module~mqc_vibrational_analysis->pic_logger module~mqc_vibrational_analysis->pic_types module~mqc_calculation_defaults->pic_types module~mqc_elements->pic_types pic_ascii pic_ascii module~mqc_elements->pic_ascii module~mqc_method_config->module~mqc_method_types module~mqc_method_config->pic_types module~mqc_method_dft->module~mqc_method_base module~mqc_method_dft->module~mqc_physical_fragment module~mqc_method_dft->module~mqc_result_types module~mqc_method_dft->pic_types module~mqc_method_hf->module~mqc_method_base module~mqc_method_hf->module~mqc_physical_fragment module~mqc_method_hf->module~mqc_result_types module~mqc_method_hf->pic_types module~mqc_method_mcscf->module~mqc_method_base module~mqc_method_mcscf->module~mqc_physical_fragment module~mqc_method_mcscf->module~mqc_result_types module~mqc_method_mcscf->pic_types module~mqc_method_types->pic_types module~mqc_method_xtb->module~mqc_method_base module~mqc_method_xtb->mctc_env module~mqc_method_xtb->module~mqc_physical_fragment module~mqc_method_xtb->module~mqc_result_types module~mqc_method_xtb->pic_logger module~mqc_method_xtb->pic_types mctc_io mctc_io module~mqc_method_xtb->mctc_io module~mqc_error mqc_error module~mqc_method_xtb->module~mqc_error pic_timer pic_timer module~mqc_method_xtb->pic_timer tblite_container tblite_container module~mqc_method_xtb->tblite_container tblite_context_type tblite_context_type module~mqc_method_xtb->tblite_context_type tblite_solvation tblite_solvation module~mqc_method_xtb->tblite_solvation tblite_wavefunction tblite_wavefunction module~mqc_method_xtb->tblite_wavefunction tblite_xtb_calculator tblite_xtb_calculator module~mqc_method_xtb->tblite_xtb_calculator tblite_xtb_gfn1 tblite_xtb_gfn1 module~mqc_method_xtb->tblite_xtb_gfn1 tblite_xtb_gfn2 tblite_xtb_gfn2 module~mqc_method_xtb->tblite_xtb_gfn2 tblite_xtb_singlepoint tblite_xtb_singlepoint module~mqc_method_xtb->tblite_xtb_singlepoint module~mqc_physical_constants->pic_types module~mqc_physical_fragment->module~mqc_elements module~mqc_physical_fragment->module~mqc_physical_constants module~mqc_physical_fragment->pic_types module~mqc_cgto mqc_cgto module~mqc_physical_fragment->module~mqc_cgto module~mqc_physical_fragment->module~mqc_error module~mqc_geometry mqc_geometry module~mqc_physical_fragment->module~mqc_geometry module~mqc_xyz_reader mqc_xyz_reader module~mqc_physical_fragment->module~mqc_xyz_reader module~mqc_result_types->pic_types module~mqc_result_types->module~mqc_error pic_mpi_lib pic_mpi_lib module~mqc_result_types->pic_mpi_lib module~mqc_cgto->pic_types module~mqc_geometry->pic_types module~mqc_xyz_reader->pic_types module~mqc_xyz_reader->module~mqc_error module~mqc_xyz_reader->module~mqc_geometry

Coordinator for distributed Hessian calculation Distributes displacement work and collects gradient results

Arguments

Type IntentOptional Attributes Name
type(comm_t), intent(in) :: world_comm
type(system_geometry_t), intent(in) :: sys_geom
type(driver_config_t), intent(in) :: config

Driver configuration

type(json_output_data_t), intent(out), optional :: json_data

JSON output data


Calls

proc~~hessian_coordinator~~CallsGraph proc~hessian_coordinator hessian_coordinator calc_gradient calc_gradient proc~hessian_coordinator->calc_gradient cart_disp cart_disp proc~hessian_coordinator->cart_disp configuration configuration proc~hessian_coordinator->configuration error error proc~hessian_coordinator->error fc_mdyne fc_mdyne proc~hessian_coordinator->fc_mdyne force_constants force_constants proc~hessian_coordinator->force_constants get_elapsed_time get_elapsed_time proc~hessian_coordinator->get_elapsed_time info info proc~hessian_coordinator->info iprobe iprobe proc~hessian_coordinator->iprobe ir_intensities ir_intensities proc~hessian_coordinator->ir_intensities irecv irecv proc~hessian_coordinator->irecv isend isend proc~hessian_coordinator->isend proc~compute_thermochemistry compute_thermochemistry proc~hessian_coordinator->proc~compute_thermochemistry proc~compute_vibrational_analysis compute_vibrational_analysis proc~hessian_coordinator->proc~compute_vibrational_analysis proc~compute_vibrational_frequencies compute_vibrational_frequencies proc~hessian_coordinator->proc~compute_vibrational_frequencies proc~create_method create_method proc~hessian_coordinator->proc~create_method proc~energy_total energy_t%energy_total proc~hessian_coordinator->proc~energy_total proc~finite_diff_dipole_derivatives finite_diff_dipole_derivatives proc~hessian_coordinator->proc~finite_diff_dipole_derivatives proc~finite_diff_hessian_from_gradients finite_diff_hessian_from_gradients proc~hessian_coordinator->proc~finite_diff_hessian_from_gradients proc~fragment_compute_nelec physical_fragment_t%fragment_compute_nelec proc~hessian_coordinator->proc~fragment_compute_nelec proc~populate_unfragmented_json_data populate_unfragmented_json_data proc~hessian_coordinator->proc~populate_unfragmented_json_data proc~populate_vibrational_json_data populate_vibrational_json_data proc~hessian_coordinator->proc~populate_vibrational_json_data proc~print_vibrational_analysis print_vibrational_analysis proc~hessian_coordinator->proc~print_vibrational_analysis proc~result_destroy calculation_result_t%result_destroy proc~hessian_coordinator->proc~result_destroy recv recv proc~hessian_coordinator->recv reduced_masses reduced_masses proc~hessian_coordinator->reduced_masses start start proc~hessian_coordinator->start to_char to_char proc~hessian_coordinator->to_char vib_freqs vib_freqs proc~hessian_coordinator->vib_freqs 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~compute_vibrational_analysis->proc~compute_vibrational_frequencies proc~compute_cartesian_displacements compute_cartesian_displacements proc~compute_vibrational_analysis->proc~compute_cartesian_displacements proc~compute_force_constants compute_force_constants proc~compute_vibrational_analysis->proc~compute_force_constants proc~compute_ir_intensities compute_ir_intensities proc~compute_vibrational_analysis->proc~compute_ir_intensities proc~compute_reduced_masses compute_reduced_masses proc~compute_vibrational_analysis->proc~compute_reduced_masses 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~factory_create method_factory_t%factory_create proc~create_method->proc~factory_create proc~mp2_total mp2_energy_t%mp2_total proc~energy_total->proc~mp2_total proc~populate_unfragmented_json_data->proc~energy_total proc~populate_vibrational_json_data->proc~energy_total proc~print_vibrational_analysis->info 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 proc~print_vibrational_analysis->warning proc~result_reset calculation_result_t%result_reset proc~result_destroy->proc~result_reset proc~element_mass element_mass proc~compute_cartesian_displacements->proc~element_mass proc~compute_ir_intensities->proc~element_mass proc~compute_moments_of_inertia->to_char proc~compute_moments_of_inertia->pic_syev proc~compute_moments_of_inertia->warning proc~compute_moments_of_inertia->proc~element_mass proc~compute_reduced_masses->proc~element_mass proc~compute_zpe->to_char proc~compute_zpe->warning proc~configure_dft configure_dft proc~factory_create->proc~configure_dft proc~configure_hf configure_hf proc~factory_create->proc~configure_hf proc~configure_mcscf configure_mcscf proc~factory_create->proc~configure_mcscf proc~configure_xtb configure_xtb proc~factory_create->proc~configure_xtb proc~mass_weight_hessian->proc~element_mass proc~print_thermochemistry->info pic_gesvd pic_gesvd proc~project_translation_rotation->pic_gesvd proc~project_translation_rotation->proc~element_mass 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 state_weights state_weights proc~configure_mcscf->state_weights proc~method_type_to_string method_type_to_string proc~configure_xtb->proc~method_type_to_string proc~xtb_has_solvation xtb_config_t%xtb_has_solvation proc~configure_xtb->proc~xtb_has_solvation proc~mp2_reset mp2_energy_t%mp2_reset proc~energy_reset->proc~mp2_reset

Called by

proc~~hessian_coordinator~~CalledByGraph proc~hessian_coordinator hessian_coordinator interface~hessian_coordinator hessian_coordinator interface~hessian_coordinator->proc~hessian_coordinator proc~distributed_unfragmented_hessian distributed_unfragmented_hessian proc~distributed_unfragmented_hessian->interface~hessian_coordinator interface~distributed_unfragmented_hessian distributed_unfragmented_hessian interface~distributed_unfragmented_hessian->proc~distributed_unfragmented_hessian proc~run_unfragmented_calculation run_unfragmented_calculation proc~run_unfragmented_calculation->interface~distributed_unfragmented_hessian proc~run_calculation run_calculation proc~run_calculation->proc~run_unfragmented_calculation

Variables

Type Visibility Attributes Name Initial
real(kind=dp), private, allocatable :: backward_dipoles(:,:)
real(kind=dp), private, allocatable :: backward_gradients(:,:,:)
integer, private :: backward_received
class(qc_method_t), private, allocatable :: calculator

Polymorphic calculator

logical, private :: compute_dipole_derivs
type(timer_type), private :: coord_timer
integer, private :: current_disp
integer, private :: current_log_level
real(kind=dp), private, allocatable :: dipole_buffer(:)
integer, private :: disp_idx
integer, private :: dummy_msg
real(kind=dp), private, allocatable :: eigenvalues(:)
integer, private :: finished_workers
real(kind=dp), private, allocatable :: forward_dipoles(:,:)
real(kind=dp), private, allocatable :: forward_gradients(:,:,:)
integer, private :: forward_received
real(kind=dp), private, allocatable :: frequencies(:)
type(physical_fragment_t), private :: full_system
real(kind=dp), private, allocatable :: grad_buffer(:,:)
integer, private :: gradient_type
logical, private :: has_dipole_flag
logical, private :: has_pending
real(kind=dp), private :: hess_norm
real(kind=dp), private, allocatable :: hessian(:,:)
integer, private :: i
logical, private :: is_verbose
integer, private :: j
type(method_config_t), private :: local_config

Local copy for verbose override

integer, private :: n_atoms
integer, private :: n_displacements
integer, private :: n_ranks
real(kind=dp), private, allocatable :: projected_hessian(:,:)
type(request_t), private :: req
type(calculation_result_t), private :: result
character(len=2048), private :: result_line
type(MPI_Status), private :: status
integer, private :: worker_rank

Source Code

   module subroutine hessian_coordinator(world_comm, sys_geom, config, json_data)
      !! Coordinator for distributed Hessian calculation
      !! Distributes displacement work and collects gradient results
      use mqc_finite_differences, only: finite_diff_hessian_from_gradients, finite_diff_dipole_derivatives
      use mqc_vibrational_analysis, only: compute_vibrational_frequencies, &
                                          compute_vibrational_analysis, print_vibrational_analysis
      use mqc_thermochemistry, only: thermochemistry_result_t, compute_thermochemistry
      use mqc_json_output_types, only: json_output_data_t, OUTPUT_MODE_UNFRAGMENTED
      use mqc_method_base, only: qc_method_t
      use mqc_method_factory, only: create_method

      type(comm_t), intent(in) :: world_comm
      type(system_geometry_t), intent(in) :: sys_geom
      type(driver_config_t), intent(in) :: config  !! Driver configuration
      type(json_output_data_t), intent(out), optional :: json_data  !! JSON output data

      type(physical_fragment_t) :: full_system
      type(timer_type) :: coord_timer
      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
      real(dp), allocatable :: dipole_buffer(:)  ! (3)
      real(dp), allocatable :: hessian(:, :)
      real(dp), allocatable :: grad_buffer(:, :)
      logical :: has_dipole_flag, compute_dipole_derivs
      type(calculation_result_t) :: result
      integer :: n_atoms, n_displacements, n_ranks
      integer :: current_disp, finished_workers, dummy_msg, worker_rank
      integer :: disp_idx, gradient_type  ! gradient_type: 1=forward, 2=backward
      integer :: forward_received, backward_received  ! Diagnostic counters
      type(MPI_Status) :: status
      logical :: has_pending
      type(request_t) :: req
      integer :: current_log_level
      logical :: is_verbose
      character(len=2048) :: result_line  ! Large buffer for Hessian matrix rows
      real(dp) :: hess_norm
      integer :: i, j
      real(dp), allocatable :: frequencies(:)
      real(dp), allocatable :: eigenvalues(:)
      real(dp), allocatable :: projected_hessian(:, :)
      class(qc_method_t), allocatable :: calculator  !! Polymorphic calculator
      type(method_config_t) :: local_config  !! Local copy for verbose override

      n_ranks = world_comm%size()
      n_atoms = sys_geom%total_atoms
      n_displacements = 3*n_atoms

      call logger%configuration(level=current_log_level)
      is_verbose = (current_log_level >= verbose_level)

      call logger%info("============================================")
      call logger%info("Distributed unfragmented Hessian calculation")
      call logger%info("  Total 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(config%hessian%displacement)//" Bohr")
      call logger%info("  MPI ranks: "//to_char(n_ranks))
      call logger%info("  Work distribution: Dynamic queue")
      call logger%info("============================================")

      ! Build full system geometry
      full_system%n_atoms = n_atoms
      full_system%n_caps = 0
      allocate (full_system%element_numbers(n_atoms))
      allocate (full_system%coordinates(3, n_atoms))
      full_system%element_numbers = sys_geom%element_numbers
      full_system%coordinates = sys_geom%coordinates
      full_system%charge = sys_geom%charge
      full_system%multiplicity = sys_geom%multiplicity
      call full_system%compute_nelec()

      ! Allocate storage for all gradients (initialize to zero for diagnostics)
      allocate (forward_gradients(n_displacements, 3, n_atoms))
      allocate (backward_gradients(n_displacements, 3, n_atoms))
      forward_gradients = 0.0_dp
      backward_gradients = 0.0_dp
      allocate (grad_buffer(3, n_atoms))
      forward_received = 0
      backward_received = 0

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

      current_disp = 1
      finished_workers = 0

      ! Process work requests and collect results
      call coord_timer%start()
      do while (finished_workers < n_ranks - 1)

         ! Check for incoming gradient results
         call iprobe(world_comm, MPI_ANY_SOURCE, TAG_WORKER_SCALAR_RESULT, has_pending, status)
         if (has_pending) then
            worker_rank = status%MPI_SOURCE

            ! Receive: displacement index, gradient type (1=forward, 2=backward), gradient data, dipole
            call irecv(world_comm, disp_idx, worker_rank, TAG_WORKER_SCALAR_RESULT, req)
            call wait(req)
            call irecv(world_comm, gradient_type, worker_rank, TAG_WORKER_SCALAR_RESULT, req)
            call wait(req)
            call recv(world_comm, grad_buffer, worker_rank, TAG_WORKER_SCALAR_RESULT, status)

            ! Receive dipole flag and data
            call recv(world_comm, has_dipole_flag, worker_rank, TAG_WORKER_SCALAR_RESULT, status)
            if (has_dipole_flag) then
               call recv(world_comm, dipole_buffer, worker_rank, TAG_WORKER_SCALAR_RESULT, status)
            else
               compute_dipole_derivs = .false.
            end if

            ! Store gradient and dipole in appropriate arrays
            if (gradient_type == 1) then
               forward_gradients(disp_idx, :, :) = grad_buffer
               forward_received = forward_received + 1
               if (has_dipole_flag) forward_dipoles(disp_idx, :) = dipole_buffer
            else
               backward_gradients(disp_idx, :, :) = grad_buffer
               backward_received = backward_received + 1
               if (has_dipole_flag) backward_dipoles(disp_idx, :) = dipole_buffer
            end if

            ! Log progress every 10% or at completion (count both forward and backward)
            if (gradient_type == 2) then  ! Only log after backward gradient to count complete displacements
               if (mod(disp_idx, max(1, n_displacements/10)) == 0 .or. disp_idx == n_displacements) then
                  call logger%info("  Completed "//to_char(disp_idx)//"/"//to_char(n_displacements)// &
                                   " displacement pairs in "//to_char(coord_timer%get_elapsed_time())//" s")
               end if
            end if
         end if

         ! Check for work requests from workers
         call iprobe(world_comm, MPI_ANY_SOURCE, TAG_WORKER_REQUEST, has_pending, status)
         if (has_pending) then
            worker_rank = status%MPI_SOURCE
            call irecv(world_comm, dummy_msg, worker_rank, TAG_WORKER_REQUEST, req)
            call wait(req)

            if (current_disp <= n_displacements) then
               ! Send next displacement index to worker
               call isend(world_comm, current_disp, worker_rank, TAG_WORKER_FRAGMENT, req)
               call wait(req)
               current_disp = current_disp + 1
            else
               ! No more work - tell worker to finish
               call isend(world_comm, -1, worker_rank, TAG_WORKER_FINISH, req)
               call wait(req)
               finished_workers = finished_workers + 1
            end if
         end if
      end do

      deallocate (grad_buffer)
      call coord_timer%stop()
      call logger%info("All gradient calculations completed in "//to_char(coord_timer%get_elapsed_time())//" s")

      ! Verify all gradients were received
      call logger%info("  Gradients received - Forward: "//to_char(forward_received)//"/"//to_char(n_displacements)// &
                       "  Backward: "//to_char(backward_received)//"/"//to_char(n_displacements))
      if (forward_received /= n_displacements .or. backward_received /= n_displacements) then
         call logger%error("ERROR: Missing gradients! Expected "//to_char(n_displacements)//" each.")
      end if

      ! Assemble Hessian from finite differences
      call logger%info("  Assembling Hessian matrix...")
      call coord_timer%start()
      call finite_diff_hessian_from_gradients(full_system, forward_gradients, backward_gradients, &
                                              config%hessian%displacement, hessian)
      call coord_timer%stop()
      call logger%info("Hessian assembly completed in "//to_char(coord_timer%get_elapsed_time())//" s")

      ! Compute energy and gradient at reference geometry
      call logger%info("  Computing reference energy and gradient...")
      local_config = config%method_config
      local_config%verbose = is_verbose
      calculator = create_method(local_config)
      call calculator%calc_gradient(full_system, result)
      deallocate (calculator)

      ! Store Hessian in result
      if (allocated(result%hessian)) deallocate (result%hessian)
      allocate (result%hessian(size(hessian, 1), size(hessian, 2)))
      result%hessian = hessian
      result%has_hessian = .true.

      ! Compute dipole derivatives for IR intensities if all dipoles were received
      if (compute_dipole_derivs) then
         call logger%info("  Computing dipole derivatives for IR intensities...")
         call finite_diff_dipole_derivatives(n_atoms, forward_dipoles, backward_dipoles, &
                                             config%hessian%displacement, result%dipole_derivatives)
         result%has_dipole_derivatives = .true.
      end if

      ! Compute vibrational frequencies from the Hessian (with trans/rot projection)
      call logger%info("  Computing vibrational frequencies (projecting trans/rot modes)...")
      call compute_vibrational_frequencies(result%hessian, sys_geom%element_numbers, frequencies, eigenvalues, &
                                           coordinates=sys_geom%coordinates, project_trans_rot=.true., &
                                           projected_hessian_out=projected_hessian)

      ! Print results
      call logger%info("============================================")
      call logger%info("Distributed Hessian calculation completed")
      write (result_line, '(a,f25.15)') "  Final energy: ", result%energy%total()
      call logger%info(trim(result_line))

      if (result%has_gradient) then
         write (result_line, '(a,f25.15)') "  Gradient norm: ", sqrt(sum(result%gradient**2))
         call logger%info(trim(result_line))
      end if

      if (result%has_hessian) then
         hess_norm = sqrt(sum(result%hessian**2))
         write (result_line, '(a,f25.15)') "  Hessian Frobenius norm: ", hess_norm
         call logger%info(trim(result_line))

         if (is_verbose .and. n_atoms < 20) then
            call logger%info(" ")
            call logger%info("Hessian matrix (Hartree/Bohr^2):")
            do i = 1, 3*n_atoms
               write (result_line, '(a,i5,a,999f15.8)') "  Row ", i, ": ", (result%hessian(i, j), j=1, 3*n_atoms)
               call logger%info(trim(result_line))
            end do
            call logger%info(" ")

            ! Print projected mass-weighted Hessian
            if (allocated(projected_hessian)) then
               call logger%info("Mass-weighted Hessian after trans/rot projection (a.u.):")
               do i = 1, 3*n_atoms
                  write (result_line, '(a,i5,a,999f15.8)') "  Row ", i, ": ", (projected_hessian(i, j), j=1, 3*n_atoms)
                  call logger%info(trim(result_line))
               end do
               call logger%info(" ")
            end if
         end if
      end if

      ! Compute and print full vibrational analysis with thermochemistry
      if (allocated(frequencies)) then
         block
            real(dp), allocatable :: vib_freqs(:), reduced_masses(:), force_constants(:)
            real(dp), allocatable :: cart_disp(:, :), fc_mdyne(:), ir_intensities(:)
            type(thermochemistry_result_t) :: thermo_result
            integer :: n_at, n_modes

            if (result%has_dipole_derivatives) then
               call compute_vibrational_analysis(result%hessian, sys_geom%element_numbers, vib_freqs, &
                                                 reduced_masses, force_constants, cart_disp, &
                                                 coordinates=sys_geom%coordinates, &
                                                 project_trans_rot=.true., &
                                                 force_constants_mdyne=fc_mdyne, &
                                                 dipole_derivatives=result%dipole_derivatives, &
                                                 ir_intensities=ir_intensities)
            else
               call compute_vibrational_analysis(result%hessian, sys_geom%element_numbers, vib_freqs, &
                                                 reduced_masses, force_constants, cart_disp, &
                                                 coordinates=sys_geom%coordinates, &
                                                 project_trans_rot=.true., &
                                                 force_constants_mdyne=fc_mdyne)
            end if

            if (allocated(vib_freqs)) then
               ! Compute thermochemistry
               n_at = size(sys_geom%element_numbers)
               n_modes = size(vib_freqs)
               call compute_thermochemistry(sys_geom%coordinates, sys_geom%element_numbers, &
                                            vib_freqs, n_at, n_modes, thermo_result, &
                                            temperature=config%hessian%temperature, pressure=config%hessian%pressure)

               ! Print vibrational analysis to log
               if (allocated(ir_intensities)) then
                  call print_vibrational_analysis(vib_freqs, reduced_masses, force_constants, &
                                                  cart_disp, sys_geom%element_numbers, &
                                                  force_constants_mdyne=fc_mdyne, &
                                                  ir_intensities=ir_intensities, &
                                                  coordinates=sys_geom%coordinates, &
                                                  electronic_energy=result%energy%total(), &
                                                 temperature=config%hessian%temperature, pressure=config%hessian%pressure)

                  ! Populate json_data
                  if (present(json_data)) then
                     call populate_vibrational_json_data(json_data, result, vib_freqs, reduced_masses, &
                                                         fc_mdyne, thermo_result, ir_intensities)
                  end if
                  deallocate (ir_intensities)
               else
                  call print_vibrational_analysis(vib_freqs, reduced_masses, force_constants, &
                                                  cart_disp, sys_geom%element_numbers, &
                                                  force_constants_mdyne=fc_mdyne, &
                                                  coordinates=sys_geom%coordinates, &
                                                  electronic_energy=result%energy%total(), &
                                                 temperature=config%hessian%temperature, pressure=config%hessian%pressure)

                  ! Populate json_data
                  if (present(json_data)) then
                     call populate_vibrational_json_data(json_data, result, vib_freqs, reduced_masses, &
                                                         fc_mdyne, thermo_result)
                  end if
               end if
               deallocate (vib_freqs, reduced_masses, force_constants, cart_disp, fc_mdyne)
            end if
         end block
      else
         ! No Hessian/frequencies - populate basic unfragmented data
         if (present(json_data)) then
            call populate_unfragmented_json_data(json_data, result)
         end if
      end if

      ! Cleanup
      call result%destroy()
      deallocate (forward_gradients, backward_gradients)
      deallocate (forward_dipoles, backward_dipoles, dipole_buffer)
      if (allocated(hessian)) deallocate (hessian)
      if (allocated(frequencies)) deallocate (frequencies)
      if (allocated(eigenvalues)) deallocate (eigenvalues)
      if (allocated(projected_hessian)) deallocate (projected_hessian)

   end subroutine hessian_coordinator