!--------------------------------------------------------------------------------------------------!
!   CP2K: A general program to perform molecular dynamics simulations                              !
!   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
!                                                                                                  !
!   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
!--------------------------------------------------------------------------------------------------!

! **************************************************************************************************
!> \brief Defines CDFT control structures
!> \par   History
!>                 separated from cp_control_types [03.2017]
!> \author Nico Holmberg [03.2017]
! **************************************************************************************************
MODULE qs_cdft_types
   USE cp_array_utils,                  ONLY: cp_1d_r_p_type
   USE cp_dbcsr_api,                    ONLY: dbcsr_p_type
   USE cp_fm_types,                     ONLY: cp_fm_type
   USE hirshfeld_types,                 ONLY: hirshfeld_type,&
                                              release_hirshfeld_type
   USE input_constants,                 ONLY: becke_cutoff_global,&
                                              outer_scf_becke_constraint,&
                                              outer_scf_hirshfeld_constraint,&
                                              outer_scf_none,&
                                              radius_single,&
                                              shape_function_gaussian
   USE kinds,                           ONLY: default_path_length,&
                                              dp
   USE outer_scf_control_types,         ONLY: outer_scf_control_type,&
                                              qs_outer_scf_type
   USE pw_types,                        ONLY: pw_r3d_rs_type
   USE qs_cdft_opt_types,               ONLY: cdft_opt_type_release
#include "./base/base_uses.f90"

   IMPLICIT NONE

   PRIVATE

! **************************************************************************************************
!> \brief some parameters useful for becke_constraints
!> \param aij             pairwise parameters used to adjust the Becke cell boundaries built from atomic radii
!> \param adjust          logical which determines if the Becke potential is adjusted with atomic radii
!> \param cavity          the Gaussian confinement cavity: the constraint is nonzero outside this cavity
!> \param cavity_confine  logical which determines if cavity confinement is active
!> \param cavity_mat      a compacted version of cavity
!> \param cavity_shape    the confinement cavity shape id
!> \param cavity_env      the structure used to build the Gaussian cavity
!> \param confine_bounds  grid point indices outside which the constraint vanishes along Z-axis
!> \param cutoff_type     the cutoff type to use for building the constraint
!> \param cutoffs         element specific cutoffs
!> \param cutoffs_tmp     same as cutoffs but a temporary read during parsing of this type
!> \param eps_cavity      threshold used screen small values of the Gaussian cavity density
!> \param in_memory       logical which determines if the gradients of the Becke potential should be
!> \param print_cavity    logical to print the Gaussian confinement cavity
!> \param radii           permanent copy of radii_tmp
!> \param radii_tmp       temporary list of element specific atomic radii used to adjust the Becke cells
!> \param rcavity         an optional global radius parameter used to define the Gaussian confinement cavity
!> \param rglobal         global cutoff to use for building the constraint
!>                        computed simultaneously with the potential instead of separately
!> \param should_skip     logical which determines is grid points should be skipped if all constraint
!>                        atoms are found to reside beyond the cutoff distance from it
!> \param use_bohr        decides whether to use angstrom or bohr units for the confinement cavity radius
! **************************************************************************************************
   ! Utility vector container for building becke constraint
   TYPE becke_vector_buffer
      LOGICAL                              :: store_vectors = .FALSE.
      REAL(kind=dp), ALLOCATABLE, &
         DIMENSION(:)                      :: distances
      REAL(kind=dp), ALLOCATABLE, &
         DIMENSION(:, :)                   :: distance_vecs, &
                                              position_vecs, &
                                              R12
      REAL(kind=dp), ALLOCATABLE, &
         DIMENSION(:, :, :)                :: pair_dist_vecs
   END TYPE becke_vector_buffer

   TYPE becke_constraint_type
      INTEGER                              :: cavity_shape = -1, cutoff_type = -1, &
                                              confine_bounds(2) = -1
      LOGICAL                              :: in_memory = .FALSE., &
                                              adjust = .FALSE., cavity_confine = .FALSE., &
                                              should_skip = .FALSE., print_cavity = .FALSE., &
                                              use_bohr = .FALSE.
      REAL(KIND=dp)                        :: rglobal = -1.0_dp, &
                                              rcavity = -1.0_dp, eps_cavity = -1.0_dp
      REAL(KIND=dp), DIMENSION(:), POINTER :: cutoffs => NULL(), cutoffs_tmp => NULL(), &
                                              radii_tmp => NULL(), radii => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :)                   :: aij => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :, :)                :: cavity_mat => NULL()
      TYPE(becke_vector_buffer)            :: vector_buffer = becke_vector_buffer()
      TYPE(hirshfeld_type), POINTER        :: cavity_env => NULL()
      TYPE(pw_r3d_rs_type)                      :: cavity = pw_r3d_rs_type()
   END TYPE becke_constraint_type

! **************************************************************************************************
! \brief control parameters for Hirshfeld constraints
!> \param gaussian_shape  the type of Gaussian to use (shape_function Gaussian)
!> \param radii           list of Gaussian radii for different atomic kinds
!> \param radius          Gaussian radius parameter
!> \param shape_function  the constraint type: atomic density or single Gaussian
!> \param use_bohr        determines whether to use angstrom or bohr units for the radii of Gaussians
!> \param use_atomic_cutoff        Logical to control use of ATOMIC_CUTOFF
!> \param atomic_cutoff        Numerical cutoff for calculation of Hirshfeld densities
!> \param atoms_memory        Number of atomic gradients to store in memory
!> \param eps_cutoff       Numerical cutoff for calculation of weight function
!> \param print_density    Logical to control printing of Hirshfeld densities to .cube file
!> \param hirshfeld_env   auxiliary type storing information about the Gaussians
! **************************************************************************************************
   TYPE hirshfeld_constraint_type
      INTEGER                              :: gaussian_shape = -1, shape_function = -1, atoms_memory = -1
      LOGICAL                              :: use_bohr = .FALSE., print_density = .FALSE., use_atomic_cutoff = .FALSE.
      REAL(KIND=dp)                        :: radius = -1.0_dp, eps_cutoff = -1.0_dp, atomic_cutoff = -1.0_dp
      REAL(KIND=dp), DIMENSION(:), POINTER :: radii => NULL()
      TYPE(hirshfeld_type), POINTER        :: hirshfeld_env => NULL()
   END TYPE hirshfeld_constraint_type

! **************************************************************************************************
!> \brief control parameters for CDFT simulations
!> \param fragment_a_fname      filename of cube file holding the total electron density
!>                              of isolated fragment a
!> \param fragment_b_fname      filename of cube file holding the total electron density
!>                              of isolated fragment b
!> \param fragment_a_spin_fname filename of cube file holding the spin difference density
!>                              of isolated fragment a
!> \param fragment_b_spin_fname filename of cube file holding the spin difference density
!>                              of isolated fragment b
!> \param ref_count             the ref count
!> \param need_pot              logical which determines if the Becke potential needs to be built
!> \param save_pot              logical which determines if the Becke potential should be saved until forces
!>                              have been evaluated
!> \param atomic_charges        flag that determines if atomic CDFT charges should be computed
!> \param total_steps           counter to keep track of the total number of SCF steps
!> \param type                  the type of CDFT constraint to use
!> \param precond_freq          preconditioner can be used if SCF converged in less than precond_freq steps
!> \param nreused               determines how many times the current OT preconditioner has been reused
!> \param max_reuse             the same preconditioner can be used a maximum of max_reuse times
!> \param purge_freq            determines how large nbad_conv can grow before purging the wfn/constraint history
!> \param nbad_conv             a running counter keeping track of the number of CDFT SCF loops when the first
!>                              CDFT SCF iteration required more than 1 outer SCF loop. Reset when convergence is
!>                              smooth
!> \param purge_offset          purging is only allowed when more than purge_offset steps have passed since
!>                              last purge
!> \param istep                 a counter to keep track of how many steps have passed since the last purge
!> \param ienergy               a counter tracking the total number of CDFT energy evaluations
!> \param natoms                the total number of atoms included in constraint/dummy atom groups
!> \param atoms                 list of constraint atoms
!> \param need_pot              logical which determines if the constraint potential needs to be built
!> \param save_pot              logical which determines if the constraint potential should be saved until forces
!>                              have been evaluated
!> \param do_et                 logical which determines if a ET coupling calculation was requested
!> \param reuse_precond         logical which determines if a preconditioner can be reused
!> \param purge_history         logical which determines if the wfn/constraint history can be purged
!> \param should_purge          logical which determines if purging should take place after this CDFT SCF loop
!> \param calculate_metric      logical which determines if the ET coupling reliability metric is computed
!> \param fragment_density      use isolated fragment densities as a reference for the constraint
!> \param fragments_integrated  logical to determine if the fragment densities have been integrated
!> \param flip_fragment         should the spin difference density of the either fragment be flipped
!> \param transfer_pot          logical which determines if constraint should be saved for reuse later
!> \param external_control      logical which determines if the constraint has already been built
!>                              in a mixed_env that holds multiple CDFT states
!> \param first_iteration       a flag to mark the first iteration for printing of additional data
!> \param print_weight          logical which determines if CDFT weight functions should be saved to a file
!> \param in_memory       logical which determines if the gradients of the Becke potential should be
!> \param is_constraint         list of logicals which determines if an atom is included in a constraint group
!> \param strength              Lagrangian multipliers of the constraints
!> \param target                target values of the constraints
!> \param value                 integrated values of the constraints
!> \param charges_fragment      atomic partial charges computed from the isolated fragment densities
!> \param becke_control         control parameters for Becke constraints
!> \param group                 container for atom groups each defining their own constraint
!> \param occupations           occupation numbers in case non-uniform MO occupation (for do_et)
!> \param mo_coeff              save the MO coeffs (for do_et)
!> \param matrix_s              save the overlap matrix (for do_et)
!> \param wmat                  matrix representation of the weight function (for do_et)
!> \param matrix_p              save the density matrix (for calculate_metric)
!> \param hirshfeld_control     control parameters for Hirshfeld constraints
!> \param constraint_control    the outer_scf_control_type for the CDFT constraints
!> \param ot_control            the outer_scf_control_type for OT where data is stashed when outside the OT
!>                              outer loop
!> \param charge                atomic CDFT real space potentials needed to calculate CDFT charges
!> \param fragments             container for isolated fragment densities read from cube files
!> \param constraint            holds information about the CDFT SCF loop
! **************************************************************************************************
   ! To build multiple constraints
   TYPE cdft_group_type
      ! Atoms of this constraint group
      INTEGER, POINTER, DIMENSION(:)       :: atoms => NULL()
      ! Constraint type: charge constraint, magnetization density constraint, or spin channel specific constraint
      INTEGER                              :: constraint_type = -1
      ! Is the constraint fragment based
      LOGICAL                              :: is_fragment_constraint = .FALSE.
      ! Temporary array holding a component of the weight function gradient that only includes
      ! terms defined on constraint atoms
      REAL(kind=dp), ALLOCATABLE, &
         DIMENSION(:, :)                   :: d_sum_const_dR
      ! Coefficients that determine how to sum up the atoms to form the constraint
      REAL(KIND=dp), POINTER, DIMENSION(:) :: coeff => NULL()
      ! Result of integration dw/dR * rho_r dr where dw/dR is the weight function gradient
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :)                   :: integrated => NULL()
      ! Atomic gradients of the weight function at every grid point
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :, :, :)             :: gradients => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :, :, :)             :: gradients_x => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :, :, :)             :: gradients_y => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :, :, :)             :: gradients_z => NULL()
      ! The weight function of this constraint group
      TYPE(pw_r3d_rs_type), POINTER                      :: weight => NULL()
      TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER             :: hw_rho_atomic => NULL()
      TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER             :: hw_rho_atomic_dr => NULL()
      TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER             :: hw_rho_atomic_charge => NULL()
      TYPE(pw_r3d_rs_type)             :: hw_rho_total_constraint = pw_r3d_rs_type()
   END TYPE cdft_group_type

   TYPE cdft_control_type
      CHARACTER(LEN=default_path_length)   :: fragment_a_fname = "", &
                                              fragment_b_fname = "", &
                                              fragment_a_spin_fname = "", &
                                              fragment_b_spin_fname = ""
      INTEGER                              :: ref_count = -1, total_steps = -1, TYPE = -1, &
                                              precond_freq = -1, nreused = -1, max_reuse = -1, &
                                              purge_freq = -1, nbad_conv = -1, purge_offset = -1, &
                                              istep = -1, ienergy = -1, natoms = -1
      INTEGER, POINTER, DIMENSION(:)       :: atoms => NULL()
      LOGICAL                              :: need_pot = .FALSE., save_pot = .FALSE., do_et = .FALSE., &
                                              reuse_precond = .FALSE., purge_history = .FALSE., &
                                              should_purge = .FALSE., calculate_metric = .FALSE., &
                                              atomic_charges = .FALSE., fragment_density = .FALSE., &
                                              fragments_integrated = .FALSE., flip_fragment(2) = .FALSE., &
                                              transfer_pot = .FALSE., external_control = .FALSE., &
                                              first_iteration = .FALSE., print_weight = .FALSE., in_memory = .FALSE.
      LOGICAL, POINTER, DIMENSION(:)       :: is_constraint => NULL()
      REAL(KIND=dp), DIMENSION(:), POINTER :: strength => NULL(), TARGET => NULL(), value => NULL()
      REAL(KIND=dp), POINTER, &
         DIMENSION(:, :)                   :: charges_fragment => NULL()
      TYPE(becke_constraint_type), POINTER :: becke_control => NULL()
      TYPE(cdft_group_type), POINTER, &
         DIMENSION(:)                      :: group => NULL()
      TYPE(cp_1d_r_p_type), ALLOCATABLE, &
         DIMENSION(:)                      :: occupations
      TYPE(cp_fm_type), DIMENSION(:), &
         POINTER                           :: mo_coeff => NULL()
      TYPE(dbcsr_p_type)                   :: matrix_s
      TYPE(dbcsr_p_type), DIMENSION(:), &
         POINTER                           :: wmat => NULL(), matrix_p => NULL()
      TYPE(hirshfeld_constraint_type), &
         POINTER                           :: hirshfeld_control => NULL()
      TYPE(outer_scf_control_type)         :: constraint_control = outer_scf_control_type(), ot_control = outer_scf_control_type()
      TYPE(pw_r3d_rs_type), POINTER, &
         DIMENSION(:)                      :: charge => NULL()
      TYPE(pw_r3d_rs_type), POINTER, &
         DIMENSION(:, :)                   :: fragments => NULL()
      TYPE(qs_outer_scf_type)              :: constraint = qs_outer_scf_type()
      TYPE(pw_r3d_rs_type)                 :: hw_rho_total = pw_r3d_rs_type()
   END TYPE cdft_control_type

   CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_cdft_types'

   ! Public data types

   PUBLIC :: becke_constraint_type, &
             cdft_control_type, &
             cdft_group_type, &
             hirshfeld_constraint_type

   ! Public subroutines

   PUBLIC :: cdft_control_create, &
             cdft_control_release

CONTAINS

! **************************************************************************************************
!> \brief create the becke_constraint_type
!> \param becke_control the structure to create
!> \par History
!>      02.2007 created [Florian Schiffmann]
! **************************************************************************************************
   SUBROUTINE becke_control_create(becke_control)
      TYPE(becke_constraint_type), INTENT(OUT)           :: becke_control

      becke_control%adjust = .FALSE.
      becke_control%cutoff_type = becke_cutoff_global
      becke_control%cavity_confine = .FALSE.
      becke_control%should_skip = .FALSE.
      becke_control%print_cavity = .FALSE.
      becke_control%in_memory = .FALSE.
      becke_control%use_bohr = .FALSE.
      becke_control%confine_bounds = 0
      becke_control%rcavity = 3.0_dp
      becke_control%rglobal = 6.0_dp
      becke_control%eps_cavity = 1.0e-6_dp
      becke_control%cavity_shape = radius_single
      becke_control%vector_buffer%store_vectors = .TRUE.
      NULLIFY (becke_control%aij)
      NULLIFY (becke_control%cavity_mat)
      NULLIFY (becke_control%cavity_env)
      NULLIFY (becke_control%cutoffs)
      NULLIFY (becke_control%cutoffs_tmp)
      NULLIFY (becke_control%radii)
      NULLIFY (becke_control%radii_tmp)
   END SUBROUTINE becke_control_create

! **************************************************************************************************
!> \brief release the becke_constraint_type
!> \param becke_control the structure to release
!> \par History
!>      02.2007 created [Florian Schiffmann]
! **************************************************************************************************
   SUBROUTINE becke_control_release(becke_control)
      TYPE(becke_constraint_type), INTENT(INOUT)         :: becke_control

      IF (becke_control%vector_buffer%store_vectors) THEN
         IF (ALLOCATED(becke_control%vector_buffer%distances)) &
            DEALLOCATE (becke_control%vector_buffer%distances)
         IF (ALLOCATED(becke_control%vector_buffer%distance_vecs)) &
            DEALLOCATE (becke_control%vector_buffer%distance_vecs)
         IF (ALLOCATED(becke_control%vector_buffer%position_vecs)) &
            DEALLOCATE (becke_control%vector_buffer%position_vecs)
         IF (ALLOCATED(becke_control%vector_buffer%R12)) &
            DEALLOCATE (becke_control%vector_buffer%R12)
         IF (ALLOCATED(becke_control%vector_buffer%pair_dist_vecs)) &
            DEALLOCATE (becke_control%vector_buffer%pair_dist_vecs)
      END IF
      IF (ASSOCIATED(becke_control%cutoffs)) &
         DEALLOCATE (becke_control%cutoffs)
      IF (ASSOCIATED(becke_control%cutoffs_tmp)) &
         DEALLOCATE (becke_control%cutoffs_tmp)
      IF (ASSOCIATED(becke_control%radii_tmp)) &
         DEALLOCATE (becke_control%radii_tmp)
      IF (ASSOCIATED(becke_control%radii)) &
         DEALLOCATE (becke_control%radii)
      IF (ASSOCIATED(becke_control%aij)) &
         DEALLOCATE (becke_control%aij)
      IF (ASSOCIATED(becke_control%cavity_mat)) &
         DEALLOCATE (becke_control%cavity_mat)
      IF (becke_control%cavity_confine) &
         CALL release_hirshfeld_type(becke_control%cavity_env)

   END SUBROUTINE becke_control_release

! **************************************************************************************************
!> \brief create the cdft_control_type
!> \param cdft_control the structure to create
!> \par History
!>      12.2015 created [Nico Holmberg]
! **************************************************************************************************
   SUBROUTINE cdft_control_create(cdft_control)
      TYPE(cdft_control_type), INTENT(OUT)               :: cdft_control

      cdft_control%total_steps = 0
      NULLIFY (cdft_control%strength)
      NULLIFY (cdft_control%target)
      NULLIFY (cdft_control%value)
      NULLIFY (cdft_control%atoms)
      NULLIFY (cdft_control%is_constraint)
      NULLIFY (cdft_control%charges_fragment)
      NULLIFY (cdft_control%fragments)
      NULLIFY (cdft_control%group)
      NULLIFY (cdft_control%charge)
      cdft_control%natoms = 0
      cdft_control%type = outer_scf_none
      cdft_control%need_pot = .TRUE.
      cdft_control%save_pot = .FALSE.
      cdft_control%transfer_pot = .FALSE.
      cdft_control%atomic_charges = .FALSE.
      cdft_control%first_iteration = .TRUE.
      cdft_control%fragment_density = .FALSE.
      cdft_control%fragments_integrated = .FALSE.
      cdft_control%flip_fragment = .FALSE.
      cdft_control%external_control = .FALSE.
      cdft_control%do_et = .FALSE.
      cdft_control%reuse_precond = .FALSE.
      cdft_control%nreused = 0
      cdft_control%precond_freq = 0
      cdft_control%max_reuse = 0
      cdft_control%should_purge = .FALSE.
      cdft_control%purge_history = .FALSE.
      cdft_control%calculate_metric = .FALSE.
      cdft_control%in_memory = .FALSE.
      cdft_control%purge_freq = 0
      cdft_control%nbad_conv = 0
      cdft_control%purge_offset = 0
      cdft_control%istep = 0
      cdft_control%ienergy = 0
      NULLIFY (cdft_control%becke_control)
      ALLOCATE (cdft_control%becke_control)
      CALL becke_control_create(cdft_control%becke_control)
      NULLIFY (cdft_control%hirshfeld_control)
      ALLOCATE (cdft_control%hirshfeld_control)
      CALL hirshfeld_control_create(cdft_control%hirshfeld_control)
      NULLIFY (cdft_control%wmat)
      NULLIFY (cdft_control%matrix_s%matrix)
      NULLIFY (cdft_control%mo_coeff)
      NULLIFY (cdft_control%matrix_p)
      ! Outer SCF default settings
      cdft_control%ot_control%have_scf = .FALSE.
      cdft_control%ot_control%max_scf = 0
      cdft_control%ot_control%eps_scf = 0.0_dp
      cdft_control%ot_control%step_size = 0.0_dp
      cdft_control%ot_control%type = -1
      cdft_control%ot_control%optimizer = -1
      cdft_control%ot_control%diis_buffer_length = -1
      NULLIFY (cdft_control%ot_control%cdft_opt_control)
      cdft_control%constraint_control%have_scf = .FALSE.
      cdft_control%constraint_control%max_scf = 0
      cdft_control%constraint_control%eps_scf = 0.0_dp
      cdft_control%constraint_control%step_size = 0.0_dp
      cdft_control%constraint_control%type = -1
      cdft_control%constraint_control%optimizer = -1
      cdft_control%constraint_control%diis_buffer_length = -1
      NULLIFY (cdft_control%constraint_control%cdft_opt_control)
      cdft_control%constraint%iter_count = 0
      NULLIFY (cdft_control%constraint%variables)
      NULLIFY (cdft_control%constraint%gradient)
      NULLIFY (cdft_control%constraint%energy)
      NULLIFY (cdft_control%constraint%count)
      NULLIFY (cdft_control%constraint%inv_jacobian)
      cdft_control%constraint%deallocate_jacobian = .TRUE.
   END SUBROUTINE cdft_control_create

! **************************************************************************************************
!> \brief release the cdft_control_type
!> \param cdft_control the structure to release
!> \par History
!>      12.2015 created [Nico Holmberg]
! **************************************************************************************************
   SUBROUTINE cdft_control_release(cdft_control)
      TYPE(cdft_control_type), INTENT(INOUT)             :: cdft_control

      INTEGER                                            :: i

      ! Constraint settings
      IF (ASSOCIATED(cdft_control%atoms)) &
         DEALLOCATE (cdft_control%atoms)
      IF (ASSOCIATED(cdft_control%strength)) &
         DEALLOCATE (cdft_control%strength)
      IF (ASSOCIATED(cdft_control%target)) &
         DEALLOCATE (cdft_control%target)
      IF (ASSOCIATED(cdft_control%value)) &
         DEALLOCATE (cdft_control%value)
      IF (ASSOCIATED(cdft_control%charges_fragment)) &
         DEALLOCATE (cdft_control%charges_fragment)
      IF (ASSOCIATED(cdft_control%fragments)) &
         DEALLOCATE (cdft_control%fragments)
      IF (ASSOCIATED(cdft_control%is_constraint)) &
         DEALLOCATE (cdft_control%is_constraint)
      IF (ASSOCIATED(cdft_control%charge)) &
         DEALLOCATE (cdft_control%charge)
      ! Constraint atom groups
      IF (ASSOCIATED(cdft_control%group)) THEN
         DO i = 1, SIZE(cdft_control%group)
            IF (ASSOCIATED(cdft_control%group(i)%atoms)) &
               DEALLOCATE (cdft_control%group(i)%atoms)
            IF (ASSOCIATED(cdft_control%group(i)%coeff)) &
               DEALLOCATE (cdft_control%group(i)%coeff)
            IF (ALLOCATED(cdft_control%group(i)%d_sum_const_dR)) &
               DEALLOCATE (cdft_control%group(i)%d_sum_const_dR)
            IF (cdft_control%type == outer_scf_becke_constraint) THEN
               IF (ASSOCIATED(cdft_control%group(i)%gradients)) &
                  DEALLOCATE (cdft_control%group(i)%gradients)
            ELSE IF (cdft_control%type == outer_scf_hirshfeld_constraint) THEN
               IF (ASSOCIATED(cdft_control%group(i)%gradients_x)) &
                  DEALLOCATE (cdft_control%group(i)%gradients_x)
               IF (ASSOCIATED(cdft_control%group(i)%gradients_y)) &
                  DEALLOCATE (cdft_control%group(i)%gradients_y)
               IF (ASSOCIATED(cdft_control%group(i)%gradients_z)) &
                  DEALLOCATE (cdft_control%group(i)%gradients_z)
            END IF
            IF (ASSOCIATED(cdft_control%group(i)%integrated)) &
               DEALLOCATE (cdft_control%group(i)%integrated)
         END DO
         DEALLOCATE (cdft_control%group)
      END IF
      ! Constraint type specific deallocations
      IF (ASSOCIATED(cdft_control%becke_control)) THEN
         CALL becke_control_release(cdft_control%becke_control)
         DEALLOCATE (cdft_control%becke_control)
      END IF
      IF (ASSOCIATED(cdft_control%hirshfeld_control)) THEN
         CALL hirshfeld_control_release(cdft_control%hirshfeld_control)
         DEALLOCATE (cdft_control%hirshfeld_control)
      END IF
      ! Release OUTER_SCF types
      CALL cdft_opt_type_release(cdft_control%ot_control%cdft_opt_control)
      CALL cdft_opt_type_release(cdft_control%constraint_control%cdft_opt_control)
      IF (ASSOCIATED(cdft_control%constraint%variables)) &
         DEALLOCATE (cdft_control%constraint%variables)
      IF (ASSOCIATED(cdft_control%constraint%count)) &
         DEALLOCATE (cdft_control%constraint%count)
      IF (ASSOCIATED(cdft_control%constraint%gradient)) &
         DEALLOCATE (cdft_control%constraint%gradient)
      IF (ASSOCIATED(cdft_control%constraint%energy)) &
         DEALLOCATE (cdft_control%constraint%energy)
      IF (ASSOCIATED(cdft_control%constraint%inv_jacobian)) &
         DEALLOCATE (cdft_control%constraint%inv_jacobian)
      ! Storage for mixed CDFT calculations
      IF (ALLOCATED(cdft_control%occupations)) THEN
         DO i = 1, SIZE(cdft_control%occupations)
            IF (ASSOCIATED(cdft_control%occupations(i)%array)) &
               DEALLOCATE (cdft_control%occupations(i)%array)
         END DO
         DEALLOCATE (cdft_control%occupations)
      END IF
      ! Release control
      cdft_control%type = outer_scf_none

   END SUBROUTINE cdft_control_release

! **************************************************************************************************
!> \brief create the hirshfeld_constraint_type
!> \param hirshfeld_control the structure to create
!> \par History
!>      09.2018 created [Nico Holmberg]
! **************************************************************************************************
   SUBROUTINE hirshfeld_control_create(hirshfeld_control)
      TYPE(hirshfeld_constraint_type), INTENT(OUT)       :: hirshfeld_control

      hirshfeld_control%use_bohr = .FALSE.
      hirshfeld_control%print_density = .FALSE.
      hirshfeld_control%use_atomic_cutoff = .TRUE.
      hirshfeld_control%radius = 3.0_dp
      hirshfeld_control%eps_cutoff = 1.0e-12_dp
      hirshfeld_control%atomic_cutoff = 1.0e-12_dp
      hirshfeld_control%shape_function = shape_function_gaussian
      hirshfeld_control%atoms_memory = 80
      hirshfeld_control%gaussian_shape = radius_single
      NULLIFY (hirshfeld_control%hirshfeld_env)
      NULLIFY (hirshfeld_control%radii)

   END SUBROUTINE hirshfeld_control_create

! **************************************************************************************************
!> \brief release the hirshfeld_constraint_type
!> \param hirshfeld_control the structure to release
!> \par History
!>      09.2018 created [Nico Holmberg]
! **************************************************************************************************
   SUBROUTINE hirshfeld_control_release(hirshfeld_control)
      TYPE(hirshfeld_constraint_type), INTENT(INOUT)     :: hirshfeld_control

      IF (ASSOCIATED(hirshfeld_control%radii)) &
         DEALLOCATE (hirshfeld_control%radii)
      CALL release_hirshfeld_type(hirshfeld_control%hirshfeld_env)

   END SUBROUTINE hirshfeld_control_release

END MODULE qs_cdft_types
