LCOV - code coverage report
Current view: top level - src/45_geomoptim - m_ipi.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 103 0
Test Date: 2026-09-19 17:42:43 Functions: 0.0 % 5 0

            Line data    Source code
       1              : !!****m* ABINIT/m_ipi
       2              : !! NAME
       3              : !!  m_ipi
       4              : !!
       5              : !! FUNCTION
       6              : !!  This module implements the client-side parf of the i-pi protocol.
       7              : !!  The communication between the driver and the client (Abinit) is implemented as follows:
       8              : !!     1)
       9              : !!     2)
      10              : !!
      11              : !!  Only structural relaxations with ionmov 28 are implemented.
      12              : !!  Support MD runs requires the proper handling of velocities.
      13              : !!  No parallelism over images.
      14              : !!
      15              : !! COPYRIGHT
      16              : !!  Copyright (C) 2020-2026 ABINIT group (MG)
      17              : !!  This file is distributed under the terms of the
      18              : !!  GNU General Public License, see ~abinit/COPYING
      19              : !!  or http://www.gnu.org/copyleft/gpl.txt .
      20              : !!
      21              : !! NOTES
      22              : !!
      23              : !!  From the i-pi user guide available at: https://github.com/i-pi/i-pi/blob/master/doc/manual.tex
      24              : !!
      25              : !!  The server assumes that 4-byte integers, 8-byte floats and 1-byte characters are used.
      26              : !!  The typical communication flow is as follows:
      27              : !!
      28              : !!  1. a header string "STATUS" is sent by the server to the client that has connected to it;
      29              : !!
      30              : !!  2. a header string is then returned, giving the status of the client code.
      31              : !!     Recognized messages are:
      32              : !!
      33              : !!  "NEEDINIT": if the client code needs any initialising data, it can be sent here.
      34              : !!  The server code will then send a header string "INIT", followed by an integer
      35              : !!  corresponding to the bead index, another integer giving the number of bits
      36              : !!  in the initialization string, and finally the initialization string itself.
      37              : !!
      38              : !!  "READY": sent if the client code is ready to calculate the forces. The server
      39              : !!  socket will then send a string "POSDATA", then nine floats for the cell vector
      40              : !!  matrix, then another nine floats for the inverse matrix. The server socket
      41              : !!  will then send one integer giving the number of atoms, then the position data
      42              : !!  as 3 floats for each atom giving the 3 cartesian components of its position.
      43              : !!
      44              : !!  "HAVEDATA": is sent if the client has finished computing the potential and
      45              : !!  forces. The server socket then sends a string "GETFORCE", and the client
      46              : !!  socket returns "FORCEREADY". The potential is then returned as a float,
      47              : !!  the number of atoms as an integer, then the force data as 3 floats per atom
      48              : !!  in the same way as the positions, and the virial as 9 floats in the same
      49              : !!  way as the cell vector matrix. Finally, the client may return an arbitrary
      50              : !!  string containing additional data that have been obtained by the electronic
      51              : !!  structure calculation (atomic charges, dipole moment). The client first
      52              : !!  returns an integer specifying the number of characters, and then the string,
      53              : !!  which will be output verbatim if this "extra" information is requested in the
      54              : !!  output section (see 3.2.2).
      55              : !!
      56              : !!  3. The server socket waits until the force data for each replica of the system has
      57              : !!  been calculated and returned, then the MD can be propagated for one more time
      58              : !!  step, and new force requests will be dispatched.
      59              : !!
      60              : !! SOURCE
      61              : 
      62              : #if defined HAVE_CONFIG_H
      63              : #include "config.h"
      64              : #endif
      65              : 
      66              : #include "abi_common.h"
      67              : 
      68              : module m_ipi
      69              : 
      70              :  use defs_basis
      71              :  use m_abicore
      72              :  use m_abimover
      73              :  use m_abihist
      74              :  use m_xmpi
      75              :  use m_errors
      76              :  use m_fsockets
      77              : 
      78              :  use m_fstrings,  only : sjoin, itoa
      79              :  use m_geometry,  only : xcart2xred, det3r, stress_voigt_to_mat
      80              : 
      81              :  implicit none
      82              : 
      83              :  private
      84              : 
      85              :  public :: ipi_setup
      86              :  public :: ipi_shutdown
      87              :  public :: ipi_pred
      88              :  public :: ipi_check_initial_consistency
      89              : !!***
      90              : 
      91              : ! =============
      92              : ! private stuff
      93              : ! =============
      94              : 
      95              :  INTEGER, PARAMETER :: HDRLEN = 12
      96              :  integer, save :: socket
      97              :  integer, save :: origin_natom = 0
      98              :  real(dp), save :: origin_rprimd(3, 3) = zero
      99              :  real(dp), save, allocatable :: origin_xred(:,:)
     100              : 
     101              : contains
     102              : !!***
     103              : 
     104              : !!****f* m_ipi/ipi_setup
     105              : !! NAME
     106              : !! ipi_setup
     107              : !!
     108              : !! FUNCTION
     109              : !!  Initialize the socket from a string that is usually passed via the command line interface.
     110              : !!  Get the initial configuration from the server and save it in module global variables
     111              : !!  for subsequent consistency check.
     112              : !!  These values, indeed, must agree with those read from the input file.
     113              : !!  See also ipi_check_initial_consistency.
     114              : !!
     115              : !! INPUTS
     116              : !!
     117              : !! OUTPUT
     118              : !!
     119              : !! OUTPUT
     120              : !!
     121              : !! SOURCE
     122              : 
     123            0 : subroutine ipi_setup(string, comm)
     124              : 
     125              : !Arguments ------------------------------------
     126              : !scalars
     127              :  character(len=*),intent(in) :: string
     128              :  integer,intent(in) :: comm
     129              : 
     130              : !local variables
     131              :  integer,parameter :: master = 0
     132              :  integer :: my_rank, ierr
     133              :  character(len=HDRLEN) :: header
     134              : 
     135              : ! *************************************************************************
     136              : 
     137            0 :  my_rank = xmpi_comm_rank(comm)
     138              : 
     139            0 :  call wrtout(std_out, "Initializing i-pi protocol...")
     140            0 :  if (my_rank == master) then
     141            0 :    call socket_from_string(string, socket)
     142            0 :    call readbuffer(socket, header, HDRLEN)
     143            0 :    ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
     144            0 :    call writebuffer(socket, "READY       ", HDRLEN)
     145            0 :    call readbuffer(socket, header, HDRLEN)
     146            0 :    ABI_CHECK(trim(header) == "POSDATA", sjoin("Expecting POSDATA header, got:", trim(header)))
     147            0 :    call handle_posdata(origin_natom, origin_rprimd, origin_xred)
     148              :  end if
     149              : 
     150              :  ! broadcast data to other procs.
     151            0 :  call xmpi_bcast(origin_natom, master, comm, ierr)
     152            0 :  call xmpi_bcast(origin_rprimd, master, comm, ierr)
     153            0 :  if (my_rank /= master) then
     154            0 :    ABI_MALLOC(origin_xred, (3, origin_natom))
     155              :  end if
     156            0 :  call xmpi_bcast(origin_xred, master, comm, ierr)
     157            0 :  call wrtout(std_out, "ipi_setup completed")
     158              : 
     159            0 : end subroutine ipi_setup
     160              : !!***
     161              : 
     162              : !!****f* m_ipi/ipi_check_initial_consistency
     163              : !! NAME
     164              : !! ipi_check_initial_consistency
     165              : !!
     166              : !! FUNCTION
     167              : !! Compare initial configuration read from input file with the one trasmitted by the server.
     168              : !!
     169              : !! INPUTS
     170              : !!
     171              : !! OUTPUT
     172              : !!
     173              : !! OUTPUT
     174              : !!
     175              : !! SOURCE
     176              : 
     177            0 : subroutine ipi_check_initial_consistency(in_natom, in_rprimd, in_xred, ierr)
     178              : 
     179              :  integer,intent(in) :: in_natom
     180              :  real(dp),intent(in) :: in_rprimd(3,3)
     181              :  real(dp),intent(in) :: in_xred(3,in_natom)
     182              :  integer,intent(out) :: ierr
     183              : 
     184              :  integer :: ii
     185              : 
     186            0 :  call wrtout(std_out, "ipi mode: Checking whether initial geometry from server agrees with input file")
     187              : 
     188            0 :  ierr = 0
     189            0 :  if (in_natom /= origin_natom) then
     190            0 :    ABI_WARNING(sjoin("in_natom:", sjoin(itoa(in_natom), " != origin_natom", itoa(origin_natom))))
     191            0 :    ierr = ierr + 1
     192              :  end if
     193              : 
     194            0 :  if (any(abs(in_rprimd - origin_rprimd) > tol6)) then
     195            0 :    ABI_WARNING("Mismatch between input file and data from socket: in_rprimd and origin_rprimd do not agree within 1e-6")
     196            0 :    write(std_out, "(a)")" in_rprind(:,ii), origin_rprimd(:,ii)"
     197            0 :    do ii=1,3
     198            0 :      write(std_out, *)in_rprimd(:,ii), origin_rprimd(:,ii)
     199              :    end do
     200            0 :    ierr = ierr + 1
     201              :  end if
     202              : 
     203            0 :  if (.not. allocated(origin_xred)) then
     204            0 :    ierr = ierr + 1
     205            0 :    ABI_WARNING("origin_xred is not allocated!")
     206              :  end if
     207              : 
     208            0 :  if (in_natom == origin_natom .and. allocated(origin_xred)) then
     209            0 :    if (any(abs(in_xred - origin_xred) > tol6)) then
     210            0 :      ABI_WARNING("Mismatch between input file and data from socket: in_xred and origin_xred do not agree withing 1e-6")
     211            0 :      ierr = ierr + 1
     212            0 :      write(std_out, "(a)")" in_xred(:,ii), origin_xred(:,ii)"
     213            0 :      do ii=1,3
     214            0 :        write(std_out, *)in_xred(:,ii), origin_xred(:,ii)
     215              :      end do
     216              :    end if
     217              :  end if
     218              : 
     219              :  !ABI_CHECK(ierr == 0, "Initial structure sent by i-pi server does not match the one read from file! See messages above")
     220              : 
     221            0 : end subroutine ipi_check_initial_consistency
     222              : !!***
     223              : 
     224              : !!****f* m_ipi/handle_posdata
     225              : !! NAME
     226              : !! handle_posdata
     227              : !!
     228              : !! FUNCTION
     229              : !! Receive new geometry from the server after POSDATA header.
     230              : !!
     231              : !! INPUTS
     232              : !!
     233              : !! OUTPUT
     234              : !!
     235              : !! OUTPUT
     236              : !!
     237              : !! SOURCE
     238              : 
     239            0 : subroutine handle_posdata(out_natom, out_rprimd, out_xred)
     240              : 
     241              :  integer,intent(out) :: out_natom
     242              :  real(dp),intent(out) :: out_rprimd(3,3)
     243              :  real(dp),allocatable,intent(out) :: out_xred(:,:)
     244              : 
     245              :  real(dp) :: gprimd(3,3), mtxbuffer(9)
     246            0 :  real(dp), allocatable :: combuf(:), xcart(:,:)
     247              : 
     248            0 :  call readbuffer(socket, mtxbuffer, 9)
     249            0 :  out_rprimd = transpose(reshape(mtxbuffer, [3, 3]))
     250            0 :  call readbuffer(socket, mtxbuffer, 9)
     251            0 :  gprimd = transpose(reshape(mtxbuffer, [3, 3]))
     252            0 :  call readbuffer(socket, out_natom)
     253            0 :  ABI_MALLOC(combuf, (3 * out_natom))
     254            0 :  call readbuffer(socket, combuf, 3 * out_natom)
     255            0 :  ABI_MALLOC(xcart, (3, out_natom))
     256            0 :  xcart = reshape(combuf, [3, out_natom])
     257            0 :  ABI_MALLOC(out_xred, (3, out_natom))
     258            0 :  call xcart2xred(out_natom, out_rprimd, xcart, out_xred)
     259            0 :  ABI_FREE(combuf)
     260            0 :  ABI_FREE(xcart)
     261              : 
     262            0 : end subroutine handle_posdata
     263              : !!***
     264              : 
     265              : !!****f* m_ipi/ipi_shutdown
     266              : !! NAME
     267              : !! ipi_shutdown
     268              : !!
     269              : !! FUNCTION
     270              : !!
     271              : !! INPUTS
     272              : !!
     273              : !! OUTPUT
     274              : !!
     275              : !! OUTPUT
     276              : !!
     277              : !! SOURCE
     278              : 
     279            0 : subroutine ipi_shutdown()
     280              : 
     281              : ! *************************************************************************
     282              : 
     283            0 :  origin_natom = 0
     284            0 :  origin_rprimd = zero
     285            0 :  ABI_SFREE(origin_xred)
     286              :  !call close_socket()
     287              : 
     288            0 : end subroutine ipi_shutdown
     289              : !!***
     290              : 
     291              : !!****f* m_ipi/ipi_pred
     292              : !! NAME
     293              : !! ipi_pred
     294              : !!
     295              : !! FUNCTION
     296              : !!  "predict" new structure using the data sent by the server.
     297              : !!
     298              : !! INPUTS
     299              : !! ab_mover <type(abimover)> : Datatype with all the information needed by the predictor
     300              : !! itime  : Index of the present iteration
     301              : !! ntime  : Maximal number of iterations
     302              : !! zDEBUG : if true print some debugging information
     303              : !!
     304              : !! OUTPUT
     305              : !!
     306              : !! SIDE EFFECTS
     307              : !! hist <type(abihist)>: History of positions,forces,acell, rprimd, stresses
     308              : !!
     309              : !! OUTPUT
     310              : !!
     311              : !! SOURCE
     312              : 
     313            0 : subroutine ipi_pred(ab_mover, hist, itime, ntime, zDEBUG, iexit, comm_cell)
     314              : 
     315              : !Arguments ------------------------------------
     316              : !scalars
     317              :  integer,intent(in) :: itime, ntime, iexit, comm_cell
     318              :  logical,intent(in) :: zDEBUG
     319              :  type(abimover),intent(in) :: ab_mover
     320              :  type(abihist),intent(inout) :: hist
     321              : 
     322              : !local variables
     323              :  integer,parameter :: master = 0
     324              :  integer :: my_rank, natom, new_natom, ierr !, ihist_prev
     325              :  real(dp) :: etotal, ucvol
     326              :  character(len=HDRLEN) :: header
     327              :  character(len=500) :: msg
     328              : !arrays
     329            0 :  real(dp) :: acell(3), rprimd(3,3), xred(3,ab_mover%natom), strten(6), sigma(3,3)
     330              :  real(dp) :: new_rprimd(3, 3)
     331            0 :  real(dp),allocatable :: new_xred(:,:)
     332              : 
     333              : ! *************************************************************************
     334              : 
     335            0 :  write(msg, "(3(a,i0))")" ipi_pred: itime: ", itime, ", ntime: ", ntime, ", iexit:", iexit
     336            0 :  call wrtout(std_out, msg)
     337              : 
     338            0 :  natom = ab_mover%natom
     339              :  ! Obtain the present values from the history
     340            0 :  call hist2var(acell, hist, natom, rprimd, xred, zDEBUG)
     341            0 :  etotal = hist%etot(hist%ihist)
     342            0 :  strten = hist%strten(:,hist%ihist)
     343            0 :  ucvol = det3r(rprimd)
     344              : 
     345            0 :  if (my_rank == master) then
     346            0 :    call readbuffer(socket, header, HDRLEN)
     347            0 :    ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
     348            0 :    call writebuffer( socket, "HAVEDATA    ", HDRLEN )
     349            0 :    call readbuffer(socket, header, HDRLEN)
     350            0 :    ABI_CHECK(trim(header) == "GETFORCE", sjoin("Expecting GETFORCE header, got:", trim(header)))
     351              : 
     352              :    ! Communicate energy info back to i-pi
     353            0 :    call wrtout(std_out, " i-pi mode: Returning etotal, forces, and stress tensor to server...")
     354            0 :    call writebuffer(socket, "FORCEREADY  ", HDRLEN)
     355            0 :    call writebuffer(socket, etotal)
     356            0 :    call writebuffer(socket, natom)
     357            0 :    call writebuffer_dv(socket, hist%fcart(:,:,hist%ihist), 3 * natom)
     358            0 :    call stress_voigt_to_mat(strten, sigma)
     359              :    ! Well it's a symmetric tensor.
     360              :    ! TODO: Check volume
     361            0 :    sigma = -ucvol * transpose(sigma)
     362            0 :    call writebuffer_dv(socket, sigma, 9)
     363              : 
     364              :    ! Note: i-pi can also receive an arbitrary string, that will be printed
     365              :    ! out to the "extra" trajectory file. This is useful if you want to
     366              :    ! return additional information, e.g. atomic charges, wannier centres,
     367              :    ! etc. one must return the number of characters, then the string. Here
     368              :    ! we just send back zero characters.
     369              :    !
     370            0 :    call writebuffer(socket, 0)
     371              : 
     372              :    ! ===============================================================
     373              :    ! Here master waits for new lattice and positions from the server
     374              :    ! ===============================================================
     375            0 :    call readbuffer(socket, header, HDRLEN)
     376            0 :    ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
     377            0 :    call writebuffer(socket, "READY       ", HDRLEN)
     378            0 :    call readbuffer(socket, header, HDRLEN)
     379            0 :    ABI_CHECK(trim(header) == "POSDATA", sjoin("Expecting POSDATA header, got:", trim(header)))
     380            0 :    call handle_posdata(new_natom, new_rprimd, new_xred)
     381              : 
     382              :    ! Some basic consistency checks. Obviously there are lot of things that can go wrong here
     383              :    ! if the driver changes the space group or the input file is not consistent with
     384              :    ! the logic implemented by the server.
     385            0 :    ABI_CHECK(new_natom == ab_mover%natom, "ipi server shall not change the number of atoms!")
     386            0 :    if (ab_mover%optcell == 0 .and. any(abs(new_rprimd - origin_rprimd) > tol6)) then
     387            0 :      ABI_ERROR("Mismatch between origin_rprimd and data from socket: origin_rprimd and new_rprimd do not agree within 1e-6")
     388              :    end if
     389              :  end if
     390              : 
     391              :  ! ====================
     392              :  ! All procs block here
     393              :  ! ====================
     394            0 :  call xmpi_barrier(comm_cell)
     395            0 :  call xmpi_bcast(new_rprimd, master, comm_cell, ierr)
     396            0 :  call xmpi_bcast(new_xred, master, comm_cell, ierr)
     397              : 
     398              :  ! Update the history with the prediction
     399              :  ! Increase indexes
     400            0 :  hist%ihist = abihist_findIndex(hist, +1)
     401              :  !call mkradim(acell,rprim,rprimd_symm)
     402              : 
     403              :  ! Fill the history with the variables xred, acell, rprimd, vel
     404            0 :  acell = one
     405            0 :  call var2hist(acell, hist, ab_mover%natom, new_rprimd, new_xred, zDEBUG)
     406              :  !ihist_prev = abihist_findIndex(hist, -1)
     407              :  ! FIXME: I don't know how to handle vel if MD run!
     408              :  !hist%vel(:,:,hist%ihist) = hist%vel(:,:,ihist_prev)
     409              : 
     410            0 : end subroutine ipi_pred
     411              : !!***
     412              : 
     413              : end module m_ipi
     414              : !!***
        

Generated by: LCOV version 2.3-1