serial_gmbe_pie_processor Subroutine

public subroutine serial_gmbe_pie_processor(pie_atom_sets, pie_coefficients, n_pie_terms, sys_geom, method_config, calc_type, json_data)

Uses

  • proc~~serial_gmbe_pie_processor~~UsesGraph proc~serial_gmbe_pie_processor serial_gmbe_pie_processor module~mqc_calc_types mqc_calc_types proc~serial_gmbe_pie_processor->module~mqc_calc_types module~mqc_error mqc_error proc~serial_gmbe_pie_processor->module~mqc_error module~mqc_physical_fragment mqc_physical_fragment proc~serial_gmbe_pie_processor->module~mqc_physical_fragment pic_logger pic_logger proc~serial_gmbe_pie_processor->pic_logger pic_types pic_types module~mqc_calc_types->pic_types module~mqc_physical_fragment->module~mqc_error 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_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_physical_fragment->pic_types 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->module~mqc_error module~mqc_xyz_reader->module~mqc_geometry module~mqc_xyz_reader->pic_types

Serial GMBE processor using PIE coefficients Evaluates each unique atom set once and sums with PIE coefficients Supports energy-only, energy+gradient, and energy+gradient+Hessian calculations If json_data is present, populates it for centralized JSON output Bond connectivity is accessed via sys_geom%bonds

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: pie_atom_sets(:,:)

Unique atom sets (max_atoms, n_pie_terms)

integer, intent(in) :: pie_coefficients(:)

PIE coefficient for each term

integer(kind=int64), intent(in) :: n_pie_terms
type(system_geometry_t), intent(in) :: sys_geom
type(method_config_t), intent(in) :: method_config

Method configuration

integer(kind=int32), intent(in) :: calc_type
type(json_output_data_t), intent(out), optional :: json_data

JSON output data


Calls

proc~~serial_gmbe_pie_processor~~CallsGraph proc~serial_gmbe_pie_processor serial_gmbe_pie_processor cart_disp cart_disp proc~serial_gmbe_pie_processor->cart_disp configuration configuration proc~serial_gmbe_pie_processor->configuration error error proc~serial_gmbe_pie_processor->error fc_mdyne fc_mdyne proc~serial_gmbe_pie_processor->fc_mdyne force_constants force_constants proc~serial_gmbe_pie_processor->force_constants frequencies frequencies proc~serial_gmbe_pie_processor->frequencies info info proc~serial_gmbe_pie_processor->info interface~do_fragment_work do_fragment_work proc~serial_gmbe_pie_processor->interface~do_fragment_work proc~build_fragment_from_atom_list build_fragment_from_atom_list proc~serial_gmbe_pie_processor->proc~build_fragment_from_atom_list proc~calc_type_to_string calc_type_to_string proc~serial_gmbe_pie_processor->proc~calc_type_to_string proc~compute_thermochemistry compute_thermochemistry proc~serial_gmbe_pie_processor->proc~compute_thermochemistry proc~compute_vibrational_analysis compute_vibrational_analysis proc~serial_gmbe_pie_processor->proc~compute_vibrational_analysis proc~energy_total energy_t%energy_total proc~serial_gmbe_pie_processor->proc~energy_total proc~error_get_full_trace error_t%error_get_full_trace proc~serial_gmbe_pie_processor->proc~error_get_full_trace proc~error_get_message error_t%error_get_message proc~serial_gmbe_pie_processor->proc~error_get_message proc~error_has_error error_t%error_has_error proc~serial_gmbe_pie_processor->proc~error_has_error proc~fragment_destroy physical_fragment_t%fragment_destroy proc~serial_gmbe_pie_processor->proc~fragment_destroy proc~print_vibrational_analysis print_vibrational_analysis proc~serial_gmbe_pie_processor->proc~print_vibrational_analysis proc~redistribute_cap_dipole_derivatives redistribute_cap_dipole_derivatives proc~serial_gmbe_pie_processor->proc~redistribute_cap_dipole_derivatives proc~redistribute_cap_gradients redistribute_cap_gradients proc~serial_gmbe_pie_processor->proc~redistribute_cap_gradients proc~redistribute_cap_hessian redistribute_cap_hessian proc~serial_gmbe_pie_processor->proc~redistribute_cap_hessian reduced_masses reduced_masses proc~serial_gmbe_pie_processor->reduced_masses to_char to_char proc~serial_gmbe_pie_processor->to_char verbose verbose proc~serial_gmbe_pie_processor->verbose proc~do_fragment_work do_fragment_work interface~do_fragment_work->proc~do_fragment_work proc~build_fragment_from_atom_list->proc~error_has_error proc~add_hydrogen_caps add_hydrogen_caps proc~build_fragment_from_atom_list->proc~add_hydrogen_caps proc~check_duplicate_atoms check_duplicate_atoms proc~build_fragment_from_atom_list->proc~check_duplicate_atoms proc~count_hydrogen_caps count_hydrogen_caps proc~build_fragment_from_atom_list->proc~count_hydrogen_caps proc~error_add_context error_t%error_add_context proc~build_fragment_from_atom_list->proc~error_add_context proc~fragment_compute_nelec physical_fragment_t%fragment_compute_nelec proc~build_fragment_from_atom_list->proc~fragment_compute_nelec 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_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 compute_vibrational_frequencies proc~compute_vibrational_analysis->proc~compute_vibrational_frequencies proc~mp2_total mp2_energy_t%mp2_total proc~energy_total->proc~mp2_total proc~error_get_full_trace->proc~error_has_error proc~basis_set_destroy molecular_basis_type%basis_set_destroy proc~fragment_destroy->proc~basis_set_destroy 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 warning warning proc~print_vibrational_analysis->warning proc~atomic_basis_destroy atomic_basis_type%atomic_basis_destroy proc~basis_set_destroy->proc~atomic_basis_destroy proc~check_duplicate_atoms->error proc~check_duplicate_atoms->to_char proc~check_duplicate_atoms->proc~element_number_to_symbol proc~error_set error_t%error_set proc~check_duplicate_atoms->proc~error_set 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->warning pic_syev pic_syev proc~compute_moments_of_inertia->pic_syev proc~compute_moments_of_inertia->proc~element_mass proc~compute_reduced_masses->proc~element_mass proc~compute_vibrational_frequencies->error proc~compute_vibrational_frequencies->warning 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 proc~compute_zpe->to_char proc~compute_zpe->warning proc~do_fragment_work->configuration proc~do_fragment_work->proc~calc_type_to_string proc~do_fragment_work->to_char proc~do_fragment_work->proc~error_add_context calc_energy calc_energy proc~do_fragment_work->calc_energy calc_gradient calc_gradient proc~do_fragment_work->calc_gradient calc_hessian calc_hessian proc~do_fragment_work->calc_hessian proc~create_method create_method proc~do_fragment_work->proc~create_method proc~energy_reset energy_t%energy_reset proc~do_fragment_work->proc~energy_reset proc~do_fragment_work->proc~error_set proc~print_fragment_xyz print_fragment_xyz proc~do_fragment_work->proc~print_fragment_xyz proc~print_thermochemistry->info proc~cgto_destroy cgto_type%cgto_destroy proc~atomic_basis_destroy->proc~cgto_destroy proc~factory_create method_factory_t%factory_create proc~create_method->proc~factory_create proc~mp2_reset mp2_energy_t%mp2_reset proc~energy_reset->proc~mp2_reset proc~mass_weight_hessian->proc~element_mass proc~print_fragment_xyz->info proc~print_fragment_xyz->to_char proc~print_fragment_xyz->proc~element_number_to_symbol proc~to_angstrom to_angstrom proc~print_fragment_xyz->proc~to_angstrom proc~project_translation_rotation->proc~element_mass pic_gesvd pic_gesvd proc~project_translation_rotation->pic_gesvd 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

Called by

proc~~serial_gmbe_pie_processor~~CalledByGraph proc~serial_gmbe_pie_processor serial_gmbe_pie_processor proc~gmbe_run_serial gmbe_context_t%gmbe_run_serial proc~gmbe_run_serial->proc~serial_gmbe_pie_processor

Variables

Type Visibility Attributes Name Initial
integer, private, allocatable :: atom_list(:)
integer, private :: coeff
integer, private :: current_log_level
type(error_t), private :: error
integer, private :: hess_dim
integer, private :: iatom
real(kind=dp), private, allocatable :: ir_intensities(:)

IR intensities in km/mol

integer, private :: max_atoms
integer, private :: n_atoms
type(physical_fragment_t), private :: phys_frag
real(kind=dp), private, allocatable :: pie_energies(:)

Store individual energies for JSON output

type(calculation_result_t), private, allocatable :: results(:)
real(kind=dp), private, allocatable :: term_dipole_derivs(:,:)

Temporary dipole derivatives for each term

real(kind=dp), private :: term_energy
real(kind=dp), private, allocatable :: term_gradient(:,:)

Temporary gradient for each term

real(kind=dp), private, allocatable :: term_hessian(:,:)

Temporary Hessian for each term

integer(kind=int64), private :: term_idx
real(kind=dp), private, allocatable :: total_dipole_derivs(:,:)

Total dipole derivatives (3, 3*total_atoms)

real(kind=dp), private :: total_energy
real(kind=dp), private, allocatable :: total_gradient(:,:)

Total gradient (3, total_atoms)

real(kind=dp), private, allocatable :: total_hessian(:,:)

Total Hessian (3total_atoms, 3total_atoms)


Source Code

   subroutine serial_gmbe_pie_processor(pie_atom_sets, pie_coefficients, n_pie_terms, &
                                        sys_geom, method_config, calc_type, json_data)
      !! Serial GMBE processor using PIE coefficients
      !! Evaluates each unique atom set once and sums with PIE coefficients
      !! Supports energy-only, energy+gradient, and energy+gradient+Hessian calculations
      !! If json_data is present, populates it for centralized JSON output
      !! Bond connectivity is accessed via sys_geom%bonds
      use mqc_calc_types, only: CALC_TYPE_GRADIENT, CALC_TYPE_HESSIAN, CALC_TYPE_ENERGY, calc_type_to_string
      use mqc_physical_fragment, only: redistribute_cap_gradients, redistribute_cap_hessian, &
                                       redistribute_cap_dipole_derivatives
      use mqc_error, only: error_t
      use pic_logger, only: info_level
      integer, intent(in) :: pie_atom_sets(:, :)  !! Unique atom sets (max_atoms, n_pie_terms)
      integer, intent(in) :: pie_coefficients(:)  !! PIE coefficient for each term
      integer(int64), intent(in) :: n_pie_terms
      type(system_geometry_t), intent(in) :: sys_geom
      type(method_config_t), intent(in) :: method_config  !! Method configuration
      integer(int32), intent(in) :: calc_type
      type(json_output_data_t), intent(out), optional :: json_data  !! JSON output data

      type(physical_fragment_t) :: phys_frag
      type(calculation_result_t), allocatable :: results(:)
      type(error_t) :: error
      integer :: n_atoms, max_atoms, iatom, current_log_level, hess_dim
      integer(int64) :: term_idx
      integer, allocatable :: atom_list(:)
      real(dp) :: total_energy, term_energy
      real(dp), allocatable :: pie_energies(:)  !! Store individual energies for JSON output
      real(dp), allocatable :: total_gradient(:, :)  !! Total gradient (3, total_atoms)
      real(dp), allocatable :: term_gradient(:, :)  !! Temporary gradient for each term
      real(dp), allocatable :: total_hessian(:, :)  !! Total Hessian (3*total_atoms, 3*total_atoms)
      real(dp), allocatable :: term_hessian(:, :)  !! Temporary Hessian for each term
      real(dp), allocatable :: total_dipole_derivs(:, :)  !! Total dipole derivatives (3, 3*total_atoms)
      real(dp), allocatable :: term_dipole_derivs(:, :)   !! Temporary dipole derivatives for each term
      real(dp), allocatable :: ir_intensities(:)          !! IR intensities in km/mol
      integer :: coeff

      if (int(size(pie_atom_sets, 2), int64) < n_pie_terms .or. &
          int(size(pie_coefficients), int64) < n_pie_terms) then
         call logger%error("PIE term arrays are smaller than n_pie_terms")
         error stop "Invalid PIE term array sizes"
      end if

      call logger%info("Processing "//to_char(n_pie_terms)//" unique PIE terms...")
      call logger%info("  Calculation type: "//calc_type_to_string(calc_type))

      total_energy = 0.0_dp
      max_atoms = size(pie_atom_sets, 1)
      allocate (pie_energies(n_pie_terms))
      allocate (results(n_pie_terms))

      ! Allocate gradient and Hessian arrays if needed
      if (calc_type == CALC_TYPE_GRADIENT .or. calc_type == CALC_TYPE_HESSIAN) then
         allocate (total_gradient(3, sys_geom%total_atoms))
         allocate (term_gradient(3, sys_geom%total_atoms))
         total_gradient = 0.0_dp
      end if

      if (calc_type == CALC_TYPE_HESSIAN) then
         hess_dim = 3*sys_geom%total_atoms
         allocate (total_hessian(hess_dim, hess_dim))
         allocate (term_hessian(hess_dim, hess_dim))
         total_hessian = 0.0_dp
         ! Allocate dipole derivative arrays for IR intensities
         allocate (total_dipole_derivs(3, hess_dim))
         allocate (term_dipole_derivs(3, hess_dim))
         total_dipole_derivs = 0.0_dp
      end if

      do term_idx = 1_int64, n_pie_terms
         coeff = pie_coefficients(term_idx)

         ! Skip terms with zero coefficient (shouldn't happen, but safety check)
         if (coeff == 0) then
            pie_energies(term_idx) = 0.0_dp  ! Mark as skipped
            cycle
         end if

         ! Extract atom list for this term
         n_atoms = 0
         do while (n_atoms < max_atoms .and. pie_atom_sets(n_atoms + 1, term_idx) >= 0)
            n_atoms = n_atoms + 1
         end do

         if (n_atoms == 0) then
            pie_energies(term_idx) = 0.0_dp  ! Mark as skipped
            cycle
         end if

         allocate (atom_list(n_atoms))
         atom_list = pie_atom_sets(1:n_atoms, term_idx)

         ! Build fragment from atom list
         call build_fragment_from_atom_list(sys_geom, atom_list, n_atoms, phys_frag, error, sys_geom%bonds)
         if (error%has_error()) then
            call logger%error(error%get_full_trace())
            error stop "Failed to build intersection fragment"
         end if

         ! Compute energy (and gradient if requested)
         call do_fragment_work(term_idx, results(term_idx), method_config, phys_frag, calc_type)

         ! Check for calculation errors
         if (results(term_idx)%has_error) then
            call logger%error("PIE term "//to_char(term_idx)//" calculation failed: "// &
                              results(term_idx)%error%get_message())
            error stop "PIE term calculation failed in serial processing"
         end if

         term_energy = results(term_idx)%energy%total()

         ! Store energy for JSON output
         pie_energies(term_idx) = term_energy

         ! Accumulate with PIE coefficient
         total_energy = total_energy + real(coeff, dp)*term_energy

         ! Accumulate gradient if present
         if ((calc_type == CALC_TYPE_GRADIENT .or. calc_type == CALC_TYPE_HESSIAN) .and. &
             results(term_idx)%has_gradient) then
            ! Map fragment gradient to system coordinates with proper cap handling
            term_gradient = 0.0_dp
            call redistribute_cap_gradients(phys_frag, results(term_idx)%gradient, term_gradient)

            ! Accumulate with PIE coefficient
            total_gradient = total_gradient + real(coeff, dp)*term_gradient
         end if

         ! Accumulate Hessian if present
         if (calc_type == CALC_TYPE_HESSIAN .and. results(term_idx)%has_hessian) then
            ! Map fragment Hessian to system coordinates with proper cap handling
            term_hessian = 0.0_dp
            call redistribute_cap_hessian(phys_frag, results(term_idx)%hessian, term_hessian)

            ! Accumulate with PIE coefficient
            total_hessian = total_hessian + real(coeff, dp)*term_hessian

            ! Accumulate dipole derivatives if present (for IR intensities)
            if (results(term_idx)%has_dipole_derivatives) then
               term_dipole_derivs = 0.0_dp
               call redistribute_cap_dipole_derivatives(phys_frag, results(term_idx)%dipole_derivatives, &
                                                        term_dipole_derivs)
               total_dipole_derivs = total_dipole_derivs + real(coeff, dp)*term_dipole_derivs
            end if
         end if

         call logger%verbose("PIE term "//to_char(term_idx)//"/"//to_char(n_pie_terms)// &
                             ": "//to_char(n_atoms)//" atoms, coeff="//to_char(coeff)// &
                             ", E="//to_char(term_energy))

         deallocate (atom_list)
         call phys_frag%destroy()
      end do

      call logger%info(" ")
      call logger%info("GMBE PIE calculation completed successfully")
      call logger%info("Final GMBE energy: "//to_char(total_energy)//" Hartree")

      ! Print gradient info if computed
      if (calc_type == CALC_TYPE_GRADIENT .or. calc_type == CALC_TYPE_HESSIAN) then
         call logger%info("GMBE PIE gradient computation completed")
         call logger%info("  Total gradient norm: "//to_char(sqrt(sum(total_gradient**2))))

         ! Print detailed gradient if info level and small system
         call logger%configuration(level=current_log_level)
         if (current_log_level >= info_level .and. sys_geom%total_atoms < 100) then
            call logger%info(" ")
            call logger%info("Total GMBE PIE Gradient (Hartree/Bohr):")
            do iatom = 1, sys_geom%total_atoms
               block
                  character(len=256) :: grad_line
                  write (grad_line, '(a,i5,a,3f20.12)') "  Atom ", iatom, ": ", &
                     total_gradient(1, iatom), total_gradient(2, iatom), total_gradient(3, iatom)
                  call logger%info(trim(grad_line))
               end block
            end do
            call logger%info(" ")
         end if
      end if

      ! Print Hessian info if computed
      if (calc_type == CALC_TYPE_HESSIAN) then
         call logger%info("GMBE PIE Hessian computation completed")
         call logger%info("  Total Hessian Frobenius norm: "//to_char(sqrt(sum(total_hessian**2))))

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

            call logger%info("  Computing vibrational analysis (projecting trans/rot modes)...")
            call compute_vibrational_analysis(total_hessian, sys_geom%element_numbers, frequencies, &
                                              reduced_masses, force_constants, cart_disp, &
                                              coordinates=sys_geom%coordinates, &
                                              project_trans_rot=.true., &
                                              force_constants_mdyne=fc_mdyne, &
                                              dipole_derivatives=total_dipole_derivs, &
                                              ir_intensities=ir_intensities)

            if (allocated(frequencies)) then
               ! Compute thermochemistry
               n_at = size(sys_geom%element_numbers)
               n_modes = size(frequencies)
               call compute_thermochemistry(sys_geom%coordinates, sys_geom%element_numbers, &
                                            frequencies, n_at, n_modes, thermo_result)

               ! Print vibrational analysis to log
               call print_vibrational_analysis(frequencies, 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=total_energy)

               ! Build temporary mbe_result for JSON output
               gmbe_result%total_energy = total_energy
               gmbe_result%has_energy = .true.
               gmbe_result%has_hessian = .true.
               if (allocated(total_gradient)) then
                  gmbe_result%has_gradient = .true.
                  allocate (gmbe_result%gradient, source=total_gradient)
               end if
               allocate (gmbe_result%hessian, source=total_hessian)

               ! Populate json_data for vibrational output if present
               if (present(json_data)) then
                  json_data%output_mode = OUTPUT_MODE_GMBE_PIE
                  json_data%total_energy = total_energy
                  json_data%has_energy = .true.
                  json_data%has_vibrational = .true.

                  allocate (json_data%frequencies(n_modes))
                  allocate (json_data%reduced_masses(n_modes))
                  allocate (json_data%force_constants(n_modes))
                  json_data%frequencies = frequencies
                  json_data%reduced_masses = reduced_masses
                  json_data%force_constants = fc_mdyne
                  json_data%thermo = thermo_result

                  if (allocated(ir_intensities)) then
                     allocate (json_data%ir_intensities(n_modes))
                     json_data%ir_intensities = ir_intensities
                     json_data%has_ir_intensities = .true.
                  end if

                  if (allocated(total_gradient)) then
                     allocate (json_data%gradient, source=total_gradient)
                     json_data%has_gradient = .true.
                  end if

                  allocate (json_data%hessian, source=total_hessian)
                  json_data%has_hessian = .true.
               end if

               if (allocated(ir_intensities)) deallocate (ir_intensities)
               call gmbe_result%destroy()
               deallocate (frequencies, reduced_masses, force_constants, cart_disp, fc_mdyne)
            end if
         end block
      end if

      call logger%info(" ")

      ! Populate json_data for non-Hessian case if present
      if (present(json_data) .and. calc_type /= CALC_TYPE_HESSIAN) then
         json_data%output_mode = OUTPUT_MODE_GMBE_PIE
         json_data%total_energy = total_energy
         json_data%has_energy = .true.
         json_data%n_pie_terms = n_pie_terms

         ! Copy PIE data
         allocate (json_data%pie_atom_sets, source=pie_atom_sets(:, 1:n_pie_terms))
         allocate (json_data%pie_coefficients(n_pie_terms))
         json_data%pie_coefficients = pie_coefficients(1:n_pie_terms)
         allocate (json_data%pie_energies(n_pie_terms))
         json_data%pie_energies = pie_energies

         if (allocated(total_gradient)) then
            allocate (json_data%gradient, source=total_gradient)
            json_data%has_gradient = .true.
         end if
         if (allocated(total_hessian)) then
            allocate (json_data%hessian, source=total_hessian)
            json_data%has_hessian = .true.
         end if
      end if

      deallocate (pie_energies, results)
      if (allocated(total_gradient)) deallocate (total_gradient)
      if (allocated(term_gradient)) deallocate (term_gradient)
      if (allocated(total_hessian)) deallocate (total_hessian)
      if (allocated(term_hessian)) deallocate (term_hessian)
      if (allocated(total_dipole_derivs)) deallocate (total_dipole_derivs)
      if (allocated(term_dipole_derivs)) deallocate (term_dipole_derivs)

   end subroutine serial_gmbe_pie_processor