LCOV - code coverage report
Current view: top level - src - ipi_server.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 0.0 % 107 0
Test Date: 2026-07-25 06:35:44 Functions: 0.0 % 7 0

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief i–PI server mode: Communication with i–PI clients
      10              : !> \par History
      11              : !>      03.2024 created
      12              : !> \author Sebastian Seidenath (sebastian.seidenath@uni-jena.de)
      13              : ! **************************************************************************************************
      14              : MODULE ipi_server
      15              :    USE ISO_C_BINDING, ONLY: C_CHAR, &
      16              :                             C_DOUBLE, &
      17              :                             C_INT, &
      18              :                             C_LOC, &
      19              :                             C_NULL_CHAR, &
      20              :                             C_PTR
      21              :    USE cell_methods, ONLY: cell_create, &
      22              :                            init_cell
      23              :    USE cell_types, ONLY: cell_release, &
      24              :                          cell_type
      25              :    USE cp_external_control, ONLY: external_control
      26              :    USE cp_log_handling, ONLY: cp_logger_get_default_io_unit
      27              :    USE cp_subsys_types, ONLY: cp_subsys_get, &
      28              :                               cp_subsys_set, &
      29              :                               cp_subsys_type
      30              :    USE global_types, ONLY: global_environment_type
      31              :    USE input_section_types, ONLY: section_vals_get_subs_vals, &
      32              :                                   section_vals_type, &
      33              :                                   section_vals_val_get
      34              :    USE ipi_environment_types, ONLY: ipi_environment_type, &
      35              :                                     ipi_env_set
      36              :    USE kinds, ONLY: default_path_length, &
      37              :                     default_string_length, &
      38              :                     dp, &
      39              :                     int_4
      40              :    USE message_passing, ONLY: mp_para_env_type, &
      41              :                               mp_request_type, &
      42              :                               mp_testany
      43              :    USE particle_list_types, ONLY: particle_list_type
      44              :    USE particle_types, ONLY: particle_type
      45              : #ifndef __NO_SOCKETS
      46              :    USE sockets_interface, ONLY: writebuffer, &
      47              :                                 readbuffer, &
      48              :                                 uwait, &
      49              :                                 open_bind_socket, &
      50              :                                 listen_socket, &
      51              :                                 accept_socket, &
      52              :                                 close_socket, &
      53              :                                 remove_socket_file
      54              : #endif
      55              :    USE virial_types, ONLY: virial_type
      56              : #include "./base/base_uses.f90"
      57              : 
      58              :    IMPLICIT NONE
      59              : 
      60              :    PRIVATE
      61              : 
      62              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ipi_server'
      63              :    INTEGER, PARAMETER                   :: msglength = 12
      64              : 
      65              :    PUBLIC                               :: start_server, &
      66              :                                            shutdown_server, &
      67              :                                            request_forces
      68              : 
      69              : CONTAINS
      70              : 
      71              : ! **************************************************************************************************
      72              : !> \brief Starts the i–PI server. Will block until it recieves a connection.
      73              : !> \param driver_section The driver section from the input file
      74              : !> \param para_env ...
      75              : !> \param ipi_env The ipi environment
      76              : !> \author Sebastian Seidenath (sebastian.seidenath@uni-jena.de)
      77              : ! **************************************************************************************************
      78            0 :    SUBROUTINE start_server(driver_section, para_env, ipi_env)
      79              :       TYPE(section_vals_type), POINTER         :: driver_section
      80              :       TYPE(mp_para_env_type), POINTER          :: para_env
      81              :       TYPE(ipi_environment_type), POINTER      :: ipi_env
      82              : 
      83              :       CHARACTER(len=*), PARAMETER :: routineN = 'start_server'
      84              : 
      85              : #ifdef __NO_SOCKETS
      86              :       INTEGER                                  :: handle
      87              :       CALL timeset(routineN, handle)
      88              :       CPABORT("CP2K was compiled with the __NO_SOCKETS option!")
      89              : #else
      90              :       CHARACTER(len=default_path_length)       :: c_hostname, drv_hostname, drv_prefix
      91              :       INTEGER                                  :: drv_port, handle, i_drv_unix, &
      92              :                                                   output_unit, socket, comm_socket
      93              :       CHARACTER(len=msglength)                 :: msgbuffer
      94              :       CHARACTER(len=msglength), PARAMETER      :: initmsg = "INIT"
      95              :       LOGICAL                                  :: drv_unix, ionode
      96              : 
      97            0 :       CALL timeset(routineN, handle)
      98            0 :       ionode = para_env%is_source()
      99            0 :       output_unit = cp_logger_get_default_io_unit()
     100              : 
     101              :       ! Read connection parameters
     102            0 :       CALL section_vals_val_get(driver_section, "HOST", c_val=drv_hostname)
     103            0 :       CALL section_vals_val_get(driver_section, "PORT", i_val=drv_port)
     104            0 :       CALL section_vals_val_get(driver_section, "UNIX", l_val=drv_unix)
     105            0 :       CALL section_vals_val_get(driver_section, "PREFIX", c_val=drv_prefix)
     106            0 :       IF (output_unit > 0) THEN
     107            0 :          WRITE (output_unit, *) "@ i-PI SERVER BEING STARTED"
     108            0 :          WRITE (output_unit, *) "@ HOSTNAME: ", TRIM(drv_hostname)
     109            0 :          WRITE (output_unit, *) "@ PORT: ", drv_port
     110            0 :          WRITE (output_unit, *) "@ UNIX SOCKET: ", drv_unix
     111              :       END IF
     112              : 
     113              :       ! opens the socket
     114            0 :       socket = 0
     115              :       !inet = 1
     116            0 :       i_drv_unix = 1 ! a bit convoluted. socket.c uses a different convention...
     117            0 :       IF (drv_unix) i_drv_unix = 0
     118              : 
     119            0 :       IF (drv_unix) THEN
     120              :          ! for UNIX sockets, HOST names the socket file which lives at
     121              :          ! /tmp/<PREFIX>_<HOST> (PREFIX must match the peer's convention,
     122              :          ! e.g. "ipi" for i-PI)
     123            0 :          c_hostname = "/tmp/"//TRIM(drv_prefix)//"_"//TRIM(drv_hostname)//C_NULL_CHAR
     124              :       ELSE
     125            0 :          c_hostname = TRIM(drv_hostname)//C_NULL_CHAR
     126              :       END IF
     127            0 :       IF (ionode) THEN
     128            0 :          CALL open_bind_socket(socket, i_drv_unix, drv_port, c_hostname)
     129            0 :          CALL listen_socket(socket, 1_c_int)
     130            0 :          CALL accept_socket(socket, comm_socket)
     131            0 :          CALL close_socket(socket)
     132            0 :          CALL remove_socket_file(c_hostname)
     133            0 :          CALL ipi_env_set(ipi_env=ipi_env, sockfd=comm_socket)
     134              :       END IF
     135              : 
     136              :       ! Check if the client needs initialization
     137              :       ! We only send a meaningless message since we have no general way of
     138              :       ! knowing what the client is expecting
     139            0 :       CALL ask_status(comm_socket, msgbuffer)
     140            0 :       IF (TRIM(msgbuffer) == "NEEDINIT") THEN
     141            0 :          CALL writebuffer(comm_socket, initmsg, msglength)
     142            0 :          CALL writebuffer(comm_socket, 1) ! Bead index - just send 1
     143            0 :          CALL writebuffer(comm_socket, 12) ! Bits in the following message
     144            0 :          CALL writebuffer(comm_socket, "Initializing", 12)
     145              :       END IF
     146              : 
     147              : #endif
     148              : 
     149            0 :       CALL timestop(handle)
     150              : 
     151            0 :    END SUBROUTINE start_server
     152              : 
     153              : ! **************************************************************************************************
     154              : !> \brief Shut down the i–PI server.
     155              : !> \param ipi_env The ipi environment in charge of the server
     156              : !> \author Sebastian Seidenath (sebastian.seidenath@uni-jena.de)
     157              : ! **************************************************************************************************
     158            0 :    SUBROUTINE shutdown_server(ipi_env)
     159              :       TYPE(ipi_environment_type), POINTER                :: ipi_env
     160              : 
     161              :       CHARACTER(len=msglength), PARAMETER                :: msg = "EXIT"
     162              : 
     163              :       INTEGER                                            :: output_unit
     164              : 
     165            0 :       output_unit = cp_logger_get_default_io_unit()
     166            0 :       WRITE (output_unit, *) "@ i–PI: Shutting down server."
     167            0 :       CALL writebuffer(ipi_env%sockfd, msg, msglength)
     168            0 :       CALL close_socket(ipi_env%sockfd)
     169            0 :    END SUBROUTINE shutdown_server
     170              : 
     171              : ! **************************************************************************************************
     172              : !> \brief Send atomic positions to a client and retrieve forces
     173              : !> \param ipi_env The ipi environment in charge of the connection
     174              : !> \author Sebastian Seidenath
     175              : ! **************************************************************************************************
     176            0 :    SUBROUTINE request_forces(ipi_env)
     177              :       TYPE(ipi_environment_type), POINTER                :: ipi_env
     178              : 
     179              :       CHARACTER(len=msglength)                           :: msgbuffer
     180              :       INTEGER                                            :: comm_socket, i, nAtom, p, xyz
     181              :       REAL(kind=dp)                                      :: energy
     182            0 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: forces
     183              : 
     184            0 :       i = 0
     185            0 :       nAtom = ipi_env%subsys%particles%n_els
     186            0 :       comm_socket = ipi_env%sockfd
     187              : 
     188              :       ! Step 1: See if the client is ready
     189            0 :       CALL ask_status(comm_socket, msgbuffer)
     190            0 :       IF (TRIM(msgbuffer) /= "READY") THEN
     191            0 :          CPABORT("i–PI: Expected READY header but recieved "//TRIM(msgbuffer))
     192              :       END IF
     193              : 
     194              :       ! Step 2: Send cell and position data to client
     195            0 :       CALL send_posdata(comm_socket, subsys=ipi_env%subsys)
     196              : 
     197              :       ! Step 3: Ask for status, should be done now
     198            0 :       CALL ask_status(comm_socket, msgbuffer)
     199            0 :       IF (TRIM(msgbuffer) /= "HAVEDATA") THEN
     200            0 :          CPABORT("i–PI: Expected HAVEDATA header but recieved "//TRIM(msgbuffer))
     201              :       END IF
     202              : 
     203              :       ! Step 4: Ask for data
     204            0 :       ALLOCATE (forces(3, nAtom))
     205            0 :       CALL ask_getforce(comm_socket, energy=energy, forces=forces)
     206              : 
     207              :       ! Step 4.5: Check for sanity
     208            0 :       IF (SIZE(forces) /= (nAtom*3)) THEN
     209            0 :          CPABORT("i–PI: Mismatch in particle number between CP2K and i–PI client")
     210              :       END IF
     211              : 
     212              :       ! Step 5: Return data
     213            0 :       DO p = 1, nAtom
     214            0 :          DO xyz = 1, 3
     215            0 :             ipi_env%subsys%particles%els(p)%f(xyz) = forces(xyz, p)
     216              :          END DO
     217              :       END DO
     218            0 :       CALL ipi_env_set(ipi_env=ipi_env, ipi_energy=energy, ipi_forces=forces)
     219            0 :    END SUBROUTINE request_forces
     220              : 
     221              : ! **************************************************************************************************
     222              : !> \brief ...
     223              : !> \param sockfd ...
     224              : !> \param buffer ...
     225              : ! **************************************************************************************************
     226            0 :    SUBROUTINE get_header(sockfd, buffer)
     227              :       INTEGER, INTENT(IN)                                :: sockfd
     228              :       CHARACTER(len=msglength), INTENT(OUT)              :: buffer
     229              : 
     230              :       INTEGER                                            :: output_unit
     231              : 
     232            0 :       CALL readbuffer(sockfd, buffer, msglength)
     233            0 :       output_unit = cp_logger_get_default_io_unit()
     234            0 :       IF (output_unit > 0) WRITE (output_unit, *) " @ i–PI Server: recieved ", TRIM(buffer)
     235            0 :    END SUBROUTINE get_header
     236              : 
     237              : ! **************************************************************************************************
     238              : !> \brief ...
     239              : !> \param sockfd ...
     240              : !> \param buffer ...
     241              : ! **************************************************************************************************
     242            0 :    SUBROUTINE ask_status(sockfd, buffer)
     243              :       INTEGER, INTENT(IN)                                :: sockfd
     244              :       CHARACTER(len=msglength), INTENT(OUT)              :: buffer
     245              : 
     246              :       CHARACTER(len=msglength), PARAMETER                :: msg = "STATUS"
     247              : 
     248            0 :       CALL writebuffer(sockfd, msg, msglength)
     249            0 :       CALL get_header(sockfd, buffer)
     250            0 :    END SUBROUTINE ask_status
     251              : 
     252              : ! **************************************************************************************************
     253              : !> \brief ...
     254              : !> \param sockfd ...
     255              : !> \param energy ...
     256              : !> \param forces ...
     257              : !> \param virial ...
     258              : !> \param extra ...
     259              : ! **************************************************************************************************
     260            0 :    SUBROUTINE ask_getforce(sockfd, energy, forces, virial, extra)
     261              :       INTEGER, INTENT(IN)                                :: sockfd
     262              :       REAL(kind=dp), INTENT(OUT)                         :: energy
     263              :       REAL(kind=dp), DIMENSION(:, :), INTENT(OUT), &
     264              :          OPTIONAL, POINTER                               :: forces
     265              :       REAL(kind=dp), DIMENSION(3, 3), INTENT(OUT), &
     266              :          OPTIONAL                                        :: virial
     267              :       CHARACTER(len=:), INTENT(OUT), OPTIONAL, POINTER   :: extra
     268              : 
     269              :       CHARACTER(len=msglength), PARAMETER                :: msg = "GETFORCE"
     270              : 
     271            0 :       CHARACTER(len=:), ALLOCATABLE                      :: extra_buffer
     272              :       CHARACTER(len=msglength)                           :: msgbuffer
     273              :       INTEGER                                            :: extraLength, nAtom
     274            0 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: forces_buffer
     275              :       REAL(kind=dp), DIMENSION(9)                        :: virial_buffer
     276              : 
     277              :       ! Exchange headers
     278            0 :       CALL writebuffer(sockfd, msg, msglength)
     279            0 :       CALL get_header(sockfd, msgbuffer)
     280            0 :       IF (TRIM(msgbuffer) /= "FORCEREADY") THEN
     281            0 :          CPABORT("i–PI: Expected FORCEREADY header but recieved "//TRIM(msgbuffer))
     282              :       END IF
     283              : 
     284              :       ! Recieve data
     285            0 :       CALL readbuffer(sockfd, energy)
     286            0 :       CALL readbuffer(sockfd, nAtom)
     287            0 :       ALLOCATE (forces_buffer(3*nAtom))
     288            0 :       CALL readbuffer(sockfd, forces_buffer, nAtom*3)
     289            0 :       CALL readbuffer(sockfd, virial_buffer, 9)
     290            0 :       CALL readbuffer(sockfd, extraLength)
     291            0 :       ALLOCATE (CHARACTER(len=extraLength) :: extra_buffer)
     292            0 :       IF (extraLength /= 0) THEN ! readbuffer(x,y,0) is always an error
     293            0 :          CALL readbuffer(sockfd, extra_buffer, extraLength)
     294              :       END IF
     295              : 
     296            0 :       IF (PRESENT(forces)) forces = RESHAPE(forces_buffer, shape=[3, nAtom])
     297            0 :       IF (PRESENT(virial)) virial = RESHAPE(virial_buffer, shape=[3, 3])
     298            0 :       IF (PRESENT(extra)) extra = extra_buffer
     299            0 :    END SUBROUTINE ask_getforce
     300              : 
     301              : ! **************************************************************************************************
     302              : !> \brief ...
     303              : !> \param sockfd ...
     304              : !> \param subsys ...
     305              : ! **************************************************************************************************
     306            0 :    SUBROUTINE send_posdata(sockfd, subsys)
     307              :       INTEGER, INTENT(IN)                                :: sockfd
     308              :       TYPE(cp_subsys_type), POINTER                      :: subsys
     309              : 
     310              :       CHARACTER(len=msglength), PARAMETER                :: msg = "POSDATA"
     311              : 
     312              :       INTEGER                                            :: i, nAtom, p, xyz
     313            0 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: particle_buffer
     314              :       REAL(kind=dp), DIMENSION(9)                        :: cell_data, icell_data
     315              : 
     316            0 :       i = 0
     317              : 
     318            0 :       CALL writebuffer(sockfd, msg, msglength)
     319              : 
     320            0 :       cell_data = RESHAPE(TRANSPOSE(subsys%cell%hmat), [9])
     321            0 :       CALL writebuffer(sockfd, cell_data, 9)
     322              : 
     323            0 :       icell_data = RESHAPE(TRANSPOSE(subsys%cell%h_inv), [9])
     324            0 :       CALL writebuffer(sockfd, icell_data, 9)
     325              : 
     326            0 :       nAtom = subsys%particles%n_els
     327            0 :       CALL writebuffer(sockfd, nAtom)
     328              : 
     329            0 :       ALLOCATE (particle_buffer(3*nAtom))
     330            0 :       DO p = 1, nAtom
     331            0 :          DO xyz = 1, 3
     332            0 :             i = i + 1
     333            0 :             particle_buffer(i) = subsys%particles%els(p)%r(xyz)
     334              :          END DO
     335              :       END DO
     336            0 :       CALL writebuffer(sockfd, particle_buffer, nAtom*3)
     337              : 
     338            0 :    END SUBROUTINE send_posdata
     339              : 
     340              : END MODULE ipi_server
        

Generated by: LCOV version 2.0-1