!> @file
!!    Define the module for Poisson Solver
!!
!! @author
!!    Luigi Genovese (February 2007)<br/>
!!    PSolverNC added by Anders Bergman, March 2008<br/>
!!    Copyright (C) 2002-2017 BigDFT group<br/>
!!    This file is distributed under the terms of the
!!    GNU General Public License, see ~/COPYING file
!!    or http://www.gnu.org/copyleft/gpl.txt .
!!    For the list of contributors, see ~/AUTHORS


!> Module used by the Poisson Solver library.
!! It must be used in the parent routine.
!! @details
!!    In the main routine in which the Poisson Solver is called:
!!
!!    1. The Poisson kernel must be declared as a pointer, then the
!!       routine createKernel can be called. On exit, the kernel will be allocated and
!!       ready to use. See the documentation of the createKernel routine for more details
!!    2. The correct sizes for allocating the density/potential and the pot_ion arrays
!!       are given from the routine PS_dim4allocation (see routine documentation for details).
!!       Its argument MUST be in agreement with the arguments of the PSolver routine.
!!       WARNING: No cross-check of the arguments is performed!
!!    3. The PSolver routine can then be called. On exit, the Hartree potential is computed
!!       and summed (following ixc value) to XC and external potential.
!!       The input array is overwritten. Again, see routine documentation for details.
!!    4. QUICK INSTRUCTION FOR THE IMPATIENT:If you want to use the Poisson Solver in the
!!       "ordinary" way, for a grid of dimensions nx,ny,nz and grid spacings hx,hy,hz,
!!       just create the Kernel with
!!           call createKernel(geocode,nx,ny,nz,hx,hy,hz,14,0,1,kernel)
!!       where kernel is a pointer as described above;
!!       geocode is 'F','S' or 'P' for Free, Surfaces of Periodic BC respectively.
!!       (Beware that for Surfaces BC the isolated direction is y!)
!!       After that you can calculate the potential with
!!           call PSolver(geocode,'G',0,1,nx,ny,nz,0,hx,hy,hz,&
!!                rhopot,kernel,fake_arr,energy,fake_exc,fake_vxc,0.d0,.false.,1)
!!       where:
!!          rhopot    is the density on input, and the electrostatic potential on output
!!                    (an array of dimension(nx,ny,nz))
!!          energy    is the result of @f$ 1/2 \int dx rho(x) potA @f$(x)
!!          fake_arr  is an array of dimension(1), untouched
!!          fake_*xc  values of the XC energies, automatically zero in that case
!!
!!       Any other changement of the arguments require reading of the documentation.
!!       See documentations of the Public routines
!!
!! @warning
!!    This module REQUIRES the module of XC functional from ABINIT, defs_xc, which
!!    require defs_basis and defs_datatypes.
!!    Such routines are provided inside the abinit directory of this bundle.
!!    They are based on XC functionals present in ABINIT 5.x
!!    If you want to use this Poisson Solver without the XC functionals, you can comment out
!!    the XC part in the PSolver routine
!!    Search for
!!
module Poisson_Solver
   use f_enums, only: f_enumerator
   use dictionaries_base, only: dictionary
   use wrapper_MPI, only: mpi_environment, MPI_DOUBLE_PRECISION
   use box, only: cell
   use PSbase
   use psolver_environment, only: cavity_data
   use PStypes, only: FFT_metadata, PSolver_options, PS_workarrays, PSolver_energies
   use workarrays, only: GPU_workarrays, dielectric_arrays, grid_observables, PCG_GPU_workarrays

   implicit none

   private

   ! Associated MPI precisions.
   integer, parameter :: mpidtypg=MPI_DOUBLE_PRECISION
   integer, parameter :: mpidtypd=MPI_DOUBLE_PRECISION
   integer, parameter :: mpidtypw=MPI_DOUBLE_PRECISION

   include 'configure.inc'

   !> Defines the fundamental structure for the kernel
   type, public :: coulomb_operator
      !variables with physical meaning
      integer :: itype_scf             !< Order of the ISF family to be used
      real(gp) :: mu                   !< Inverse screening length for the Helmholtz Eq. (Poisson Eq. -> mu=0)
!!$     !> geocode is used in all the code to specify the boundary conditions (BC) the problem:
!!$     !!          - 'F' free BC, isolated systems.
!!$     !!                The program calculates the solution as if the given density is
!!$     !!                "alone" in R^3 space.
!!$     !!          - 'S' surface BC, isolated in y direction, periodic in xz plane
!!$     !!                The given density is supposed to be periodic in the xz plane,
!!$     !!                so the dimensions in these direction mus be compatible with the FFT
!!$     !!                Beware of the fact that the isolated direction is y!
!!$     !!          - 'P' periodic BC.
!!$     !!                The density is supposed to be periodic in all the three directions,
!!$     !!                then all the dimensions must be compatible with the FFT.
!!$     !!                No need for setting up the kernel (in principle for Plane Waves)
!!$     !!          - 'W' Wires BC.
!!$     !!                The density is supposed to be periodic in z direction,
!!$     !!                which has to be compatible with the FFT.
!!$     !!          - 'H' Helmholtz Equation Solver
!!$!     character(len=1) :: geocode
!!$     !> method of embedding in the environment
!!$     !!          - 'VAC' Poisson Equation in vacuum. Default case.
!!$     !!          - 'PCG' Generalized Poisson Equation, Preconditioned Conjugate Gradient
!!$     !!          - 'PI'  Generalized Poisson Equation, Polarization Iteration method
!!$     !character(len=3) :: method
!!$     !! this represents the information for the equation and the algorithm to be solved
!!$     !! this enumerator contains the algorithm and has the attribute associated to the
!!$     !! type of cavity to be used
      type(f_enumerator) :: method
      type(cell) :: mesh !< structure which includes all cell informations
!!$     integer, dimension(3) :: ndims   !< dimension of the box of the density
!!$     real(gp), dimension(3) :: hgrids !<grid spacings in each direction
!!$     real(gp), dimension(3) :: angrad !< angles in radiants between each of the axis
      type(cavity_data) :: cavity !< description of the cavity for the dielectric medium
      type(PSolver_options) :: opt !<Datatype controlling the operations of the solver
      real(dp), dimension(:), pointer :: kernel !< kernel of the Poisson Solver
      !>workarrays for the application of the Solver. Might have different
      !!memory footprints dependently of the treatment.
      type(PS_workarrays) :: w
      type(GPU_workarrays) :: GPU
      type(PCG_GPU_workarrays) :: PCG_GPU
      type(dielectric_arrays) :: diel
      type(grid_observables) :: arr
      !variables with computational meaning
      type(mpi_environment) :: mpi_env !< complete environment for the Poisson Solver
      type(mpi_environment) :: inplane_mpi,part_mpi !<mpi_environment for internal ini-plane parallelization
      type(FFT_metadata) :: grid !<dimensions of the FFT grid associated to this kernel
      logical :: use_gpu_direct
      integer :: igpu !< control the usage of the GPU
      integer :: keepzf
      !parameters for the iterative methods
      !> default set of radii for the rigid cavity
      integer :: radii_set
      !> dictionary of the atom species radii defined by the user
      type(dictionary), pointer :: radii_dict
      integer :: max_iter    !< Maximum number of convergence iterations for PCG or SC.
      real(dp) :: minres     !< Convergence criterion for the PCG or SC loop.
      real(dp) :: PI_eta     !< Mixing parameter for the update of SC iteration
      integer :: max_iter_PB !< Max conv iterations for PB treatment
      real(dp) :: minres_PB  !< Convergence criterion for PB residue
      real(dp) :: PB_eta     !< Mixing scheme for PB
   end type coulomb_operator

   ! Intialization of the timings
   public :: PS_initialize_timing_categories, TCAT_PSOLV_COMPUT, TCAT_PSOLV_COMMUN, TCAT_PSOLV_KERNEL
   public :: PSolver_energies
   ! Calculate the allocation dimensions
   public :: PS_dim4allocation,PSolver_logo,ps_soft_PCM_forces
   ! Routine that creates the kernel
   public :: pkernel_init, pkernel_set, pkernel_free, pkernel_set_epsilon
   public :: pkernel_allocate_cavity,pkernel_get_radius, pkernel_null
   public :: rebuild_cavity_from_rho
   ! Calculate the poisson solver
   public :: H_potential,Electrostatic_Solver,PS_set_options,PS_get_GPU_setup
   public :: dp,gp,PS_dump_coulomb_operator, xc_dimensions
   ! To be moved to a legacy module ?
   public :: P_FFT_dimensions, S_FFT_dimensions, W_FFT_dimensions, F_FFT_dimensions

   !> This structure is used to indicate the arguments of the routine which are used commonly
   !! Doxygen will duplicate the documentation for the arguments
   type doc
      character(len=1) :: geocode !< @copydoc poisson_solver::coulomb_operator::geocode
      !> Indicates the distribution of the data of the input/output array:
      !!    - 'G' global data. Each process has the whole array of the density
      !!          which will be overwritten with the whole array of the potential.
      !!    - 'D' distributed data. Each process has only the needed part of the density
      !!          and of the potential. The data distribution is such that each processor
      !!          has the xy planes needed for the calculation AND for the evaluation of the
      !!          gradient, needed for XC part, and for the White-Bird correction, which
      !!          may lead up to 8 planes more on each side. Due to this fact, the information
      !!          between the processors may overlap.
      character(len=1) :: datacode
   end type doc

contains

  function PS_getVersion() result(str)
    character(len = 128) :: str

    write(str, "(A)") package_version
  end function PS_getVersion

  subroutine PSolver_logo()
    use wrapper_MPI
    use yaml_output
    implicit none
    call yaml_map('Reference Paper','The Journal of Chemical Physics 137, 134108 (2012)')
    call yaml_map('Version Number', "PSolver " // trim(PS_getVersion()))
    call yaml_map('Timestamp of this run',yaml_date_and_time_toa())
      call yaml_map('Root process Hostname',mpihostname())
  end subroutine PSolver_logo


  include 'PSolver_Main.f90'

  include 'createKernel.f90'

end module Poisson_Solver
