Work in progress for extract_global; committing but will probably undo later

This commit is contained in:
Karl Hammond
2022-08-10 17:56:28 -05:00
parent d178a00ff6
commit b191e29561
2 changed files with 220 additions and 35 deletions

View File

@ -80,7 +80,7 @@ the optional logical argument set to ``.true.``. Here is a simple example:
END PROGRAM testlib
It is also possible to pass command line flags from Fortran to C/C++ and
thus make the resulting executable behave similar to the standalone
thus make the resulting executable behave similarly to the standalone
executable (it will ignore the `-in/-i` flag, though). This allows to
use the command line to configure accelerator and suffix settings,
configure screen and logfile output, or to set index style variables
@ -190,6 +190,7 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
:f reset_box: :f:func:`reset_box`
:f memory_usage: :f:func:`memory_usage`
:f extract_setting: :f:func:`extract_setting`
:f extract_global: :f:func:`extract_global`
--------
@ -210,7 +211,57 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
:o integer comm [optional]: MPI communicator
:r lammps: an instance of the :f:type:`lammps` derived type
--------
.. note::
The ``MPI_F08`` module, which defines Fortran 2008 bindings for MPI,
is not directly supported by this interface due to the complexities of
supporting both the ``MPI_F08`` and ``MPI`` modules at the same time.
However, you should be able to use the ``MPI_VAL`` member of the
``MPI_comm`` derived type to access the integer value of the
communicator, such as in
.. code-block:: Fortran
PROGRAM testmpi
USE LIBLAMMPS
USE MPI_F08
TYPE(lammps) :: lmp
lmp = lammps(MPI_COMM_SELF%MPI_VAL)
END PROGRAM testmpi
Constants Defined by the API
============================
The following constants are declared by the Fortran API to resolve the
type/kind/rank signature for return values. These serve the same role as
``LAMMPS_INT``, ``LAMMPS_DOUBLE``, and similar constants in ``src/library.h``
and those in ``python/lammps/constants.py`` for the C and Python APIs,
respectively. Unlike their C and Python bretheren, however, it is the type
(e.g., ``INTEGER``), kind (e.g., ``C_int``), and rank (e.g., ``DIMENSION(:)``)
of these constants that is used by the calling routine, rather than their
numerical values.
:f:LMP_INT: 32-bit integer scalars
:f:LMP_INT_1D: 32-bit integer vectors
:f:LMP_INT_2D: 32-bit integer matrices
:f:LMP_DOUBLE: 64-bit real scalars
:f:LMP_DOUBLE_1D: 64-bit real vectors
:f:LMP_DOUBLE_2D: 64-bit real matrices
:f:LMP_INT64: 64-bit integer scalars
:f:LMP_INT64_1D: 64-bit integer vectors
:f:LMP_INT64_2D: 64-bit integer matrices
.. admonition:: Interaction with LAMMPS_BIGBIG and such
LAMMPS uses different-sized integers to store various entities, such as
the number of timesteps or the total number of atoms, depending on certain
compiler flags (see the :doc:`size limits <Build_settings_size>`
documentation). This API is currently agnostic to these settings, and it
is up to the user to know the size of LAMMPS_BIGINT and such and pass
LMP_INT or LMP_INT64, as appropriate, for such entities.
Procedures Bound to the lammps Derived Type
===========================================
.. f:subroutine:: close([finalize])
@ -278,12 +329,20 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
.. f:function:: get_thermo(name)
This function will call :cpp:func:`lammps_get_thermo` and return the value
of the corresponding thermodynamic keyword.
:p character(len=*) name: string with the name of the thermo keyword
:r real(C_double): value of the requested thermo property or 0.0_C_double
--------
.. f:subroutine:: extract_box(boxlo, boxhi, xy, yz, xz, pflags, boxflag)
This subroutine will call :cpp:func:`lammps_extract_box`. All parameters
are optional, though obviously at least one should be present. The
parameters *pflags* and *boxflag* are stored in LAMMPS as integers, but
should be declared as ``LOGICAL`` variables when calling from Fortran.
:p real(c_double) boxlo [dimension(3),optional]: vector in which to store
lower-bounds of simulation box
@ -302,6 +361,9 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
.. f:subroutine:: reset_box(boxlo, boxhi, xy, yz, xz)
This subroutine will call :cpp:func:`lammps_reset_box`. All parameters
are required.
:p real(c_double) boxlo [dimension(3)]: vector of three doubles containing
the lower box boundary
:p real(c_double) boxhi [dimension(3)]: vector of three doubles containing
@ -314,6 +376,9 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
.. f:subroutine:: memory_usage(meminfo)
This subroutine will call :cpp:func:`lammps_memory_usage` and store the
result in the three-element array *meminfo*.
:p real(c_double) meminfo [dimension(3)]: vector of three doubles in which
to store memory usage data
@ -321,30 +386,92 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
.. f:function:: get_mpi_comm()
This function returns a Fortran representation of the LAMMPS "world"
communicator.
:r integer: Fortran integer equivalent to the MPI communicator LAMMPS is
using
.. note::
.. note::
The MPI_F08 module, which is in compliance with the Fortran 2008 standard,
is not directly supported by this function. However, you should be able to
convert between the two using the MPI_VAL member of the communicator. For
example,
The C library interface currently returns type "int" instead of type
"MPI_Fint", which is the C type correspending to Fortran "INTEGER"
types of the default kind. On most compilers, these are the same anyway,
but this interface exchanges values this way to avoid warning messages.
.. code-block:: fortran
.. note::
USE MPI_F08
USE LIBLAMMPS
TYPE (LAMMPS) :: lmp
TYPE (MPI_Comm) :: comm
! ... [commands to set up LAMMPS/etc.]
comm%MPI_VAL = lmp%get_mpi_comm()
The MPI_F08 module, which defines Fortran 2008 bindings for MPI, is not
directly supported by this function. However, you should be able to
convert between the two using the MPI_VAL member of the communicator.
For example,
should assign an MPI_F08 communicator properly.
.. code-block:: fortran
USE MPI_F08
USE LIBLAMMPS
TYPE (LAMMPS) :: lmp
TYPE (MPI_Comm) :: comm
! ... [commands to set up LAMMPS/etc.]
comm%MPI_VAL = lmp%get_mpi_comm()
should assign an MPI_F08 communicator properly.
--------
.. f:function:: extract_setting(keyword)
Query LAMMPS about global settings. See the documentation for the
:c:func:`lammps_extract_setting` function from the C library.
:p character(len=*) keyword: string containing the name of the thermo keyword
:r integer(c_int): value of the queried setting or :math:`-1` if unknown
--------
.. f:function:: extract_global(name, dtype)
Overloaded function to get internal global LAMMPS data. Note that all
currently implemented global types only return scalars or strings; all
array-returning entities currently supported use :f:func:`extract_box`.
Note that type/kind/rank of the *dtype* argument is used to determine
whether to return a type correspending to a C int, a C int64_t, or a
C double. The type/kind/rank signature of dtype is checked at runtime to
match that of the return value; this type of check cannot be performed at
compile time. For example,
.. code-block:: fortran
PROGRAM demo
USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t
USE LIBLAMMPS
TYPE(lammps) :: lmp
INTEGER(C_int) :: nlocal
INTEGER(C_int64_t) :: ntimestep
CHARACTER(LEN=10) :: units
REAL(C_double) :: dt
lmp = lammps()
! other commands
nlocal = lmp%extract_global('nlocal', LMP_INT)
ntimestep = lmp%extract_global('ntimestep', LMP_INT64)
dt = lmp%extract_global('dt', LMP_DOUBLE)
units = lmp%extract_global('units', LMP_STRING)
! more commands
lmp.close(.TRUE.)
END PROGRAM demo
would extract the number of atoms on this processor, the current time step,
the size of the current time step, and the units being used into the
variables *nlocal*, *ntimestep*, *dt*, and *units*, respectively.
*Note*: if this function returns a string, the string must have
length greater than or equal to the length of the string (not including the
terminal NULL character) that LAMMPS returns. If the variable's length is
too short, the string will be truncated. As usual in Fortran, strings
are padded with spaces at the end.
:p character(len=*) name: string with the name of the extracted property
:p polymorphic dtype: one of *LMP_INT*, *LMP_INT64*, *LMP_DOUBLE*, or
*LMP_STRING* designating the type/kind/rank of the return value
:r polymorphic: value of the extracted property

View File

@ -38,10 +38,10 @@ MODULE LIBLAMMPS
! These are public-interface constants that have the same purpose as the
! constants in library.h, except that their types match the type of the
! constant in question. Their purpose is to determine the type of the
! constant in question. Their purpose is to specify the type of the
! return value without something akin to a C/C++ type cast
INTEGER (c_int), PUBLIC, PARAMETER :: LMP_INT = 0_c_int
INTEGER (c_int), PUBLIC, DIMENSION(3), PARAMETER :: LMP_INT_1D = 1_c_int
INTEGER (c_int), PUBLIC, DIMENSION(3), PARAMETER :: LMP_INT_1D = 0_c_int
INTEGER (c_int), PUBLIC, DIMENSION(3,3), PARAMETER :: LMP_INT_2D = 1_c_int
REAL (c_double), PUBLIC, PARAMETER :: LMP_DOUBLE = 2.0_c_double
REAL (c_double), PUBLIC, DIMENSION(3), PARAMETER :: &
@ -59,6 +59,8 @@ MODULE LIBLAMMPS
!
! Must be kept in sync with the equivalent declarations in
! src/library.h and python/lammps/constants.py
!
! NOT part of the API (the part the user sees)
INTEGER (c_int), PARAMETER :: &
LAMMPS_INT = 0_c_int, & ! 32-bit integer (array)
LAMMPS_INT_2D = 1, & ! two-dimensional 32-bit integer array
@ -84,8 +86,12 @@ MODULE LIBLAMMPS
PROCEDURE :: get_mpi_comm => lmp_get_mpi_comm
PROCEDURE :: extract_setting => lmp_extract_setting
PROCEDURE, PRIVATE :: lmp_extract_global_int
GENERIC :: extract_global => lmp_extract_global_int ! TODO
PROCEDURE, PRIVATE :: lmp_extract_global_int64_t
PROCEDURE, PRIVATE :: lmp_extract_global_double
PROCEDURE, PRIVATE :: lmp_extract_global_str
GENERIC :: extract_global => lmp_extract_global_int, &
lmp_extract_global_int64_t, lmp_extract_global_double, &
lmp_extract_global_str
PROCEDURE :: version => lmp_version
END TYPE lammps
@ -207,6 +213,13 @@ MODULE LIBLAMMPS
INTEGER (c_int) :: lammps_extract_global_datatype
END FUNCTION lammps_extract_global_datatype
FUNCTION c_strlen (str) bind(C,name='strlen')
IMPORT :: c_ptr, c_size_t
IMPLICIT NONE
TYPE(c_ptr) :: str
INTEGER(c_size_t) :: c_strlen
END FUNCTION c_strlen
FUNCTION lammps_extract_global(handle, name) BIND(C)
IMPORT :: c_ptr
TYPE(c_ptr), VALUE :: handle, name
@ -530,49 +543,94 @@ CONTAINS
! END FUNCTION lmp_extract_global_datatype
! equivalent functions to lammps_extract_global (overloaded)
! This implementation assumes there are no non-scalar data that can be
! extracted through lammps_extract_global
FUNCTION lmp_extract_global_int (self, name, dtype)
CLASS(lammps), INTENT(IN) :: self
CHARACTER(LEN=*), INTENT(IN) :: name
INTEGER(c_int) :: dtype
INTEGER(c_int), INTENT(IN) :: dtype
INTEGER(c_int) :: lmp_extract_global_int
TYPE(c_ptr) :: Cname, Cptr
INTEGER(c_int) :: datatype
INTEGER(c_int), POINTER :: ptr
Cname = f2c_string(name)
datatype = lammps_extract_global_datatype(Cname)
datatype = lammps_extract_global_datatype(c_null_ptr, Cname)
IF ( datatype /= LAMMPS_INT ) THEN
! throw an exception or something; data type doesn't match!
WRITE(0,*) 'WARNING: global data type is inconsistent'
WRITE(0,*) 'WARNING: global data type is inconsistent (not an int)'
END IF
Cptr = lammps_extract_global(self%handle, Cname)
CALL c_f_pointer(Cptr, ptr)
lmp_extract_global_int = ptr
CALL lammps_free(Cname)
END FUNCTION lmp_extract_global_int
FUNCTION lmp_extract_global_int_1d (self, name, dtype)
! This implementation assumes there are three elements to all arrays
FUNCTION lmp_extract_global_int64_t (self, name, dtype)
CLASS(lammps), INTENT(IN) :: self
CHARACTER(LEN=*), INTENT(IN) :: name
INTEGER(c_int), DIMENSION(3) :: dtype
INTEGER(c_int) :: lmp_extract_global_int
INTEGER(c_int64_t), INTENT(IN) :: dtype
INTEGER(c_int64_t) :: lmp_extract_global_int64_t
TYPE(c_ptr) :: Cname, Cptr
INTEGER(c_int) :: datatype
INTEGER(c_int), DIMENSION(3), POINTER :: ptr
INTEGER(c_int64_t), POINTER :: ptr
Cname = f2c_string(name)
datatype = lammps_extract_global_datatype(Cname)
IF ( datatype /= LAMMPS_INT ) THEN
datatype = lammps_extract_global_datatype(c_null_ptr, Cname)
IF ( datatype /= LAMMPS_INT64 ) THEN
! throw an exception or something; data type doesn't match!
WRITE(0,*) 'WARNING: global data type is inconsistent (not an int64_t)'
END IF
Cptr = lammps_extract_global(self%handle, Cname)
CALL c_f_pointer(Cptr, ptr, shape(dtype))
lmp_extract_global_int = ptr
CALL c_f_pointer(Cptr, ptr)
lmp_extract_global_int64_t = ptr
CALL lammps_free(Cname)
END FUNCTION lmp_extract_global_int_1d
! TODO need more generics here to match TKR of LMP_INT_1D, LMP_BIGINT,
! LMP_DOUBLE, LMP_DOUBLE_1D, LMS_STRING [this assumes no one adds anything
! requiring LMP_DOUBLE_2D and the like!]
END FUNCTION lmp_extract_global_int64_t
FUNCTION lmp_extract_global_double (self, name, dtype)
CLASS(lammps), INTENT(IN) :: self
CHARACTER(LEN=*), INTENT(IN) :: name
REAL(c_double), INTENT(IN) :: dtype
REAL(c_double) :: lmp_extract_global_double
TYPE(c_ptr) :: Cname, Cptr
INTEGER(c_int) :: datatype
REAL(c_double), POINTER :: ptr
Cname = f2c_string(name)
datatype = lammps_extract_global_datatype(c_null_ptr, Cname)
IF ( datatype /= LAMMPS_DOUBLE ) THEN
! throw an exception or something; data type doesn't match!
WRITE(0,*) 'WARNING: global data type is inconsistent (not a double)'
END IF
Cptr = lammps_extract_global(self%handle, Cname)
CALL c_f_pointer(Cptr, ptr)
lmp_extract_global_double = ptr
CALL lammps_free(Cname)
END FUNCTION lmp_extract_global_double
FUNCTION lmp_extract_global_str (self, name, dtype)
CLASS(lammps), INTENT(IN) :: self
CHARACTER(LEN=*), INTENT(IN) :: name, dtype
CHARACTER(LEN=:), ALLOCATABLE :: lmp_extract_global_str
TYPE(c_ptr) :: Cname, Cptr
INTEGER(c_int) :: datatype
CHARACTER(KIND=c_char,LEN=1), dimension(:), POINTER :: ptr
INTEGER(c_size_t) :: length
INTEGER :: i
Cname = f2c_string(name)
datatype = lammps_extract_global_datatype(c_null_ptr, Cname)
IF ( datatype /= LAMMPS_STRING ) THEN
! throw an exception or something; data type doesn't match!
WRITE(0,*) 'WARNING: global data type is inconsistent (not a string)'
END IF
Cptr = lammps_extract_global(self%handle, Cname)
length = c_strlen(Cptr)
CALL c_f_pointer(Cptr, ptr, [length])
ALLOCATE ( CHARACTER(LEN=length) :: lmp_extract_global_str )
FORALL ( I=1:length )
lmp_extract_global_str(i:i) = ptr(i)
END FORALL
CALL lammps_free(Cname)
! the allocatable scalar (return value) gets auto-deallocated on return
END FUNCTION lmp_extract_global_str
! equivalent function to lammps_version()
INTEGER FUNCTION lmp_version(self)