diff --git a/doc/src/Fortran.rst b/doc/src/Fortran.rst index dd848a812e..77ab447c7c 100644 --- a/doc/src/Fortran.rst +++ b/doc/src/Fortran.rst @@ -3,7 +3,9 @@ The ``LIBLAMMPS`` Fortran Module The ``LIBLAMMPS`` module provides an interface to call LAMMPS from a Fortran code. It is based on the LAMMPS C-library interface and -requires a Fortran 2003 compatible compiler to be compiled. +requires a Fortran 2003 compatible compiler to be compiled. It is +designed to be self-contained and not require any support functions +written in C, C++, or Fortran. While C libraries have a defined binary interface (ABI) and can thus be used from multiple compiler versions from different vendors for as long @@ -19,12 +21,20 @@ for a simple program using the Fortran interface would be: mpifort -o testlib.x lammps.f90 testlib.f90 -L. -llammps Please note, that the MPI compiler wrapper is only required when the -calling the library from an MPI parallel code. Please also note the -order of the source files: the ``lammps.f90`` file needs to be compiled -first, since it provides the ``LIBLAMMPS`` module that is imported by -the Fortran code using the interface. A working example code can be -found together with equivalent examples in C and C++ in the -``examples/COUPLE/simple`` folder of the LAMMPS distribution. +calling the library from an MPI parallel code. Otherwise, using the +fortran compiler (gfortran, ifort, flang, etc.) will suffice. It may be +necessary to link to additional libraries depending on how LAMMPS was +configured and whether the LAMMPS library :doc:`was compiled as a static +or shared library `. + +If the LAMMPS library itself has been compiled with MPI support, the +resulting executable will still be able to run LAMMPS in parallel with +``mpirun`` or equivalent. Please also note that the order of the source +files matters: the ``lammps.f90`` file needs to be compiled first, since +it provides the ``LIBLAMMPS`` module that is imported by the Fortran +code using the interface. A working example code can be found together +with equivalent examples in C and C++ in the ``examples/COUPLE/simple`` +folder of the LAMMPS distribution. .. versionadded:: 9Oct2020 @@ -49,17 +59,18 @@ found together with equivalent examples in C and C++ in the Creating or deleting a LAMMPS object ************************************ -With the Fortran interface the creation of a :cpp:class:`LAMMPS +With the Fortran interface, the creation of a :cpp:class:`LAMMPS ` instance is included in the constructor for creating the :f:func:`lammps` derived type. To import the definition of -that type and its type bound procedures you need to add a ``USE +that type and its type bound procedures, you need to add a ``USE LIBLAMMPS`` statement. Internally it will call either :cpp:func:`lammps_open_fortran` or :cpp:func:`lammps_open_no_mpi` from the C library API to create the class instance. All arguments are optional and :cpp:func:`lammps_mpi_init` will be called automatically, -if it is needed. Similarly, a possible call to :cpp:func:`lammps_finalize` -is integrated into the :f:func:`close` function and triggered with -the optional logical argument set to ``.true.``. Here is a simple example: +if it is needed. Similarly, a possible call to +:cpp:func:`lammps_mpi_finalize` is integrated into the :f:func:`close` +function and triggered with the optional logical argument set to +``.true.``. Here is a simple example: .. code-block:: fortran @@ -80,11 +91,11 @@ 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 -executable (it will ignore the `-in/-i` flag, though). This allows to -use the command line to configure accelerator and suffix settings, +thus make the resulting executable behave similarly to the standalone +executable (it will ignore the `-in/-i` flag, though). This allows +using the command line to configure accelerator and suffix settings, configure screen and logfile output, or to set index style variables -from the command line and more. Here is a correspondingly adapted +from the command line and more. Here is a correspondingly adapted version of the previous example: .. code-block:: fortran @@ -117,13 +128,13 @@ version of the previous example: -------------------- Executing LAMMPS commands -========================= +************************* Once a LAMMPS instance is created, it is possible to "drive" the LAMMPS -simulation by telling LAMMPS to read commands from a file, or pass +simulation by telling LAMMPS to read commands from a file or to pass individual or multiple commands from strings or lists of strings. This -is done similar to how it is implemented in the `C-library -` interface. Before handing off the calls to the +is done similarly to how it is implemented in the :doc:`C-library +interface `. Before handing off the calls to the C-library interface, the corresponding Fortran versions of the calls (:f:func:`file`, :f:func:`command`, :f:func:`commands_list`, and :f:func:`commands_string`) have to make a copy of the strings passed as @@ -165,6 +176,57 @@ Below is a small demonstration of the uses of the different functions: --------------- +Accessing system properties +*************************** + +The C-library interface allows the :doc:`extraction of different kinds +of information ` about the active simulation +instance and also - in some cases - to apply modifications to it. In +some cases, the C-library interface makes pointers to internal data +structures accessible, thus when accessing them from Fortran, special +care is needed to avoid data corruption and crashes. Thus please see +the documentation of the individual type bound procedures for details. + +Below is an example demonstrating some of the possible uses. + +.. code-block:: fortran + + PROGRAM testprop + USE LIBLAMMPS + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int64_t + TYPE(lammps) :: lmp + INTEGER(kind=8) :: natoms + REAL(c_double), POINTER :: dt + INTEGER(c_int64_t), POINTER :: ntimestep + REAL(kind=8) :: pe, ke + + lmp = lammps() + CALL lmp%file('in.sysinit') + natoms = INT(lmp%get_natoms(),8) + WRITE(6,'(A,I8,A)') 'Running a simulation with', natoms, ' atoms' + WRITE(6,'(I8,A,I8,A,I3,A)') lmp%extract_setting('nlocal'), ' local and', & + lmp%extract_setting('nghost'), ' ghost atom. ', & + lmp%extract_setting('ntypes'), ' atom types' + + CALL lmp%command('run 2 post no') + dt = lmp%extract_global('dt') + ntimestep = lmp%extract_global('ntimestep') + WRITE(6,'(A,I4,A,F4.1,A)') 'At step:', ntimestep, ' Changing timestep from', dt, ' to 0.5' + dt = 0.5 + CALL lmp%command('run 2 post no') + + WRITE(6,'(A,I4)') 'At step:', ntimestep + pe = lmp%get_thermo('pe') + ke = lmp%get_thermo('ke') + PRINT*, 'PE = ', pe + PRINT*, 'KE = ', ke + + CALL lmp%close(.TRUE.) + + END PROGRAM testprop + +--------------- + The ``LIBLAMMPS`` module API **************************** @@ -178,14 +240,24 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS. class instance that any of the included calls are forwarded to. :f c_ptr handle: reference to the LAMMPS class - :f close: :f:func:`close` - :f version: :f:func:`version` - :f file: :f:func:`file` - :f command: :f:func:`command` - :f commands_list: :f:func:`commands_list` - :f commands_string: :f:func:`commands_string` + :f subroutine close: :f:func:`close` + :f subroutine error: :f:func:`error` + :f function version: :f:func:`version` + :f subroutine file: :f:func:`file` + :f subroutine command: :f:func:`command` + :f subroutine commands_list: :f:func:`commands_list` + :f subroutine commands_string: :f:func:`commands_string` + :f function get_natoms: :f:func:`get_natoms` + :f function get_thermo: :f:func:`get_thermo` + :f subroutine extract_box: :f:func:`extract_box` + :f subroutine reset_box: :f:func:`reset_box` + :f subroutine memory_usage: :f:func:`memory_usage` + :f function extract_setting: :f:func:`extract_setting` + :f function extract_global: :f:func:`extract_global` -.. f:function:: lammps(args[,comm]) +-------- + +.. f:function:: lammps([args][,comm]) This is the constructor for the Fortran class and will forward the arguments to a call to either :cpp:func:`lammps_open_fortran` @@ -198,10 +270,31 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS. If *comm* is not provided, ``MPI_COMM_WORLD`` is assumed. For more details please see the documentation of :cpp:func:`lammps_open`. - :p character(len=*) args(*) [optional]: arguments as list of strings + :o character(len=\*) args(\*) [optional]: arguments as list of strings :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 + +Procedures Bound to the lammps Derived Type +=========================================== + .. f:subroutine:: close([finalize]) This method will close down the LAMMPS instance through calling @@ -211,6 +304,20 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS. :o logical finalize [optional]: shut down the MPI environment of the LAMMPS library if true. +-------- + +.. f:subroutine:: error(error_type, error_text) + + This method is a wrapper around the :cpp:func:`lammps_error` function and will dispatch + an error through the LAMMPS Error class. + + .. versionadded:: TBD + + :p integer error_type: constant to select which Error class function to call + :p character(len=\*) error_text: error message + +-------- + .. f:function:: version() This method returns the numeric LAMMPS version like :cpp:func:`lammps_version` @@ -224,25 +331,243 @@ of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS. This method will call :cpp:func:`lammps_file` to have LAMMPS read and process commands from a file. - :p character(len=*) filename: name of file with LAMMPS commands + :p character(len=\*) filename: name of file with LAMMPS commands + +-------- .. f:subroutine:: command(cmd) This method will call :cpp:func:`lammps_command` to have LAMMPS execute a single command. - :p character(len=*) cmd: single LAMMPS command + :p character(len=\*) cmd: single LAMMPS command + +-------- .. f:subroutine:: commands_list(cmds) This method will call :cpp:func:`lammps_commands_list` to have LAMMPS execute a list of input lines. - :p character(len=*) cmd(:): list of LAMMPS input lines + :p character(len=\*) cmd(:): list of LAMMPS input lines + +-------- .. f:subroutine:: commands_string(str) This method will call :cpp:func:`lammps_commands_string` to have LAMMPS execute a block of commands from a string. - :p character(len=*) str: LAMMPS input in string + :p character(len=\*) str: LAMMPS input in string + +-------- + +.. f:function:: get_natoms() + + This function will call :cpp:func:`lammps_get_natoms` and return the number + of atoms in the system. + + :r real(c_double): number of atoms + +-------- + +.. f:function:: get_thermo(name) + + This function will call :cpp:func:`lammps_get_thermo` and return the value + of the corresponding thermodynamic keyword. + + .. versionadded:: TBD + + :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. + + .. versionadded:: TBD + + :o real(c_double) boxlo [dimension(3),optional]: vector in which to store + lower-bounds of simulation box + :o real(c_double) boxhi [dimension(3),optional]: vector in which to store + upper-bounds of simulation box + :o real(c_double) xy [optional]: variable in which to store *xy* tilt factor + :o real(c_double) yz [optional]: variable in which to store *yz* tilt factor + :o real(c_double) xz [optional]: variable in which to store *xz* tilt factor + :o logical pflags [dimension(3),optional]: vector in which to store + periodicity flags (``.TRUE.`` means periodic in that dimension) + :o logical boxflag [optional]: variable in which to store boolean denoting + whether the box will change during a simulation + (``.TRUE.`` means box will change) + +.. note:: + + Note that a frequent use case of this function is to extract only one or + more of the options rather than all seven. For example, assuming "lmp" + represents a properly-initialized LAMMPS instance, the following code will + extract the periodic box settings into the variable "periodic": + + .. code-block:: Fortran + + ! code to start up + logical :: periodic(3) + ! code to initialize LAMMPS / run things / etc. + call lmp%extract_box(pflags = periodic) + +-------- + +.. f:subroutine:: reset_box(boxlo, boxhi, xy, yz, xz) + + This subroutine will call :cpp:func:`lammps_reset_box`. All parameters + are required. + + .. versionadded:: TBD + + :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 + the upper box boundary + :p real(c_double) xy: *x--y* tilt factor + :p real(c_double) yz: *y--z* tilt factor + :p real(c_double) xz: *x--z* tilt factor + +-------- + +.. f:subroutine:: memory_usage(meminfo) + + This subroutine will call :cpp:func:`lammps_memory_usage` and store the + result in the three-element array *meminfo*. + + .. versionadded:: TBD + + :p real(c_double) meminfo [dimension(3)]: vector of three doubles in which + to store memory usage data + +-------- + +.. f:function:: get_mpi_comm() + + This function returns a Fortran representation of the LAMMPS "world" + communicator. + + .. versionadded:: TBD + + :r integer: Fortran integer equivalent to the MPI communicator LAMMPS is + using + + .. note:: + + The C library interface currently returns type ``int`` instead of + type ``MPI_Fint``, which is the C type corresponding 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. + + .. note:: + + 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, + + .. 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 + :cpp:func:`lammps_extract_setting` function from the C library. + + .. versionadded:: TBD + + :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) + + This function calls :cpp:func:`lammps_extract_global` and returns + either a string or a pointer to internal global LAMMPS data, + depending on the data requested through *name*. + + .. versionadded:: TBD + + Note that this function actually does not return a value, but rather + associates the pointer on the left side of the assignment to point to + internal LAMMPS data (with the exception of string data, which are + copied and returned as ordinary Fortran strings). Pointers must be of + the correct data type to point to said data (typically + ``INTEGER(c_int)``, ``INTEGER(c_int64_t)``, or ``REAL(c_double)``) + and have compatible kind and rank. The pointer being associated with + LAMMPS data is type-, kind-, and rank-checked at run-time via an + overloaded assignment operator. The pointers returned by this + function are generally persistent; therefore it is not necessary to + call the function again, unless a :doc:`clear` command has been + issued, which wipes out and recreates the contents of the + :cpp:class:`LAMMPS ` class. + + For example, + + .. code-block:: fortran + + PROGRAM demo + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_int64_t + USE LIBLAMMPS + TYPE(lammps) :: lmp + INTEGER(c_int), POINTER :: nlocal + INTEGER(c_int64_t), POINTER :: ntimestep + CHARACTER(LEN=10) :: units + REAL(c_double), POINTER :: dt + lmp = lammps() + ! other commands + nlocal = lmp%extract_global('nlocal') + ntimestep = lmp%extract_global('ntimestep') + dt = lmp%extract_global('dt') + units = lmp%extract_global('units') + ! 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 + :r polymorphic: pointer to LAMMPS data. The left-hand side of the assignment + should be either a string (if expecting string data) or a C-compatible + pointer (e.g., ``INTEGER (c_int), POINTER :: nlocal``) to the extracted + property. If expecting vector data, the pointer should have dimension ":". + +.. warning:: + + Modifying the data in the location pointed to by the returned pointer + may lead to inconsistent internal data and thus may cause failures or + crashes or bogus simulations. In general it is thus usually better + to use a LAMMPS input command that sets or changes these parameters. + Those will take care of all side effects and necessary updates of + settings derived from such settings. diff --git a/doc/src/Library_create.rst b/doc/src/Library_create.rst index 8043819891..8ccc2e80ce 100644 --- a/doc/src/Library_create.rst +++ b/doc/src/Library_create.rst @@ -11,6 +11,7 @@ This section documents the following functions: - :cpp:func:`lammps_mpi_finalize` - :cpp:func:`lammps_kokkos_finalize` - :cpp:func:`lammps_python_finalize` +- :cpp:func:`lammps_error` -------------------- @@ -115,3 +116,8 @@ calling program. .. doxygenfunction:: lammps_python_finalize :project: progguide + +----------------------- + +.. doxygenfunction:: lammps_error + :project: progguide diff --git a/doc/src/Library_properties.rst b/doc/src/Library_properties.rst index e023c78185..a5c9c79c64 100644 --- a/doc/src/Library_properties.rst +++ b/doc/src/Library_properties.rst @@ -15,21 +15,21 @@ This section documents the following functions: -------------------- -The library interface allows extraction of different kinds of -information about the active simulation instance and also -modifications to it. This enables combining of a LAMMPS simulation -with other processing and simulation methods computed by the calling -code, or by another code that is coupled to LAMMPS via the library -interface. In some cases the data returned is direct reference to the -original data inside LAMMPS, cast to a void pointer. In that case the -data needs to be cast to a suitable pointer for the calling program to -access it, and you may need to know the correct dimensions and -lengths. This also means you can directly change those value(s) from -the calling program, e.g. to modify atom positions. Of course, this -should be done with care. When accessing per-atom data, please note -that this data is the per-processor **local** data and is indexed -accordingly. Per-atom data can change sizes and ordering at every -neighbor list rebuild or atom sort event as atoms migrate between +The library interface allows the extraction of different kinds of +information about the active simulation instance and also - in some +cases - to apply modifications to it. This enables combining of a +LAMMPS simulation with other processing and simulation methods computed +by the calling code, or by another code that is coupled to LAMMPS via +the library interface. In some cases the data returned is direct +reference to the original data inside LAMMPS, cast to a void pointer. +In that case the data needs to be cast to a suitable pointer for the +calling program to access it, and you may need to know the correct +dimensions and lengths. This also means you can directly change those +value(s) from the calling program, e.g. to modify atom positions. Of +course, this should be done with care. When accessing per-atom data, +please note that this data is the per-processor **local** data and is +indexed accordingly. Per-atom data can change sizes and ordering at +every neighbor list rebuild or atom sort event as atoms migrate between sub-domains and processors. .. code-block:: C diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 15316b4f09..aba474d257 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1081,6 +1081,7 @@ filesystems Fily Fincham Finchham +Fint fingerprintconstants fingerprintsperelement Finnis @@ -1093,6 +1094,7 @@ flagHI flaglog flagN flagVF +flang fld floralwhite Florez diff --git a/fortran/README b/fortran/README index 653b6a966e..57d163e197 100644 --- a/fortran/README +++ b/fortran/README @@ -3,9 +3,9 @@ and allows the LAMMPS library interface to be invoked from Fortran codes. It requires a Fortran compiler that supports the Fortran 2003 standard. This interface is based on and supersedes the previous Fortran interfaces -in the examples/COUPLE/fortran* folders. But is fully supported by the +in the examples/COUPLE/fortran* folders, but is fully supported by the LAMMPS developers and included in the documentation and unit testing. Details on this Fortran interface and how to build programs using it -are in the manual in the doc/html/pg_fortran.html file. +are in the manual in the doc/html/Fortran.html file. diff --git a/fortran/lammps.f90 b/fortran/lammps.f90 index 144fd15652..98378c833a 100644 --- a/fortran/lammps.f90 +++ b/fortran/lammps.f90 @@ -19,117 +19,358 @@ ! Karl D. Hammond ! University of Missouri, 2012-2020 ! -! The Fortran module tries to follow the API of the C-library interface -! closely, but like the Python wrapper it employs an object oriented -! approach. To accommodate the object oriented approach, all exported -! subroutine and functions have to be implemented in Fortran to then -! call the interfaced C style functions with adapted calling conventions -! as needed. The C-library interfaced functions retain their names -! starting with "lammps_" while the Fortran versions start with "lmp_". +! The Fortran module tries to follow the API of the C library interface +! closely, but like the Python wrapper it employs an object-oriented +! approach. To accommodate the object-oriented approach, all exported +! subroutines and functions have to be implemented in Fortran and +! call the interfaced C-style functions with adapted calling conventions +! as needed. The C library interface functions retain their names +! starting with "lammps_", while the Fortran versions start with "lmp_". ! MODULE LIBLAMMPS USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_ptr, c_null_ptr, c_loc, & - c_int, c_char, c_null_char, c_double, c_size_t, c_f_pointer + c_int, c_int64_t, c_char, c_null_char, c_double, c_size_t, c_f_pointer + USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY : ERROR_UNIT IMPLICIT NONE PRIVATE - PUBLIC :: lammps + PUBLIC :: lammps, ASSIGNMENT(=) + + ! Data type constants for extracting data from global, atom, compute, and fix + ! + ! 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, & ! 32-bit integer (array) + LAMMPS_INT_2D = 1, & ! two-dimensional 32-bit integer array + LAMMPS_DOUBLE = 2, & ! 64-bit double (array) + LAMMPS_DOUBLE_2D = 3, & ! two-dimensional 64-bit double array + LAMMPS_INT64 = 4, & ! 64-bit integer (array) + LAMMPS_INT64_2D = 5, & ! two-dimensional 64-bit integer array + LAMMPS_STRING = 6 ! C-String TYPE lammps TYPE(c_ptr) :: handle CONTAINS PROCEDURE :: close => lmp_close + PROCEDURE :: error => lmp_error PROCEDURE :: file => lmp_file PROCEDURE :: command => lmp_command PROCEDURE :: commands_list => lmp_commands_list PROCEDURE :: commands_string => lmp_commands_string - PROCEDURE :: version => lmp_version PROCEDURE :: get_natoms => lmp_get_natoms + PROCEDURE :: get_thermo => lmp_get_thermo + PROCEDURE :: extract_box => lmp_extract_box + PROCEDURE :: reset_box => lmp_reset_box + PROCEDURE :: memory_usage => lmp_memory_usage + PROCEDURE :: get_mpi_comm => lmp_get_mpi_comm + PROCEDURE :: extract_setting => lmp_extract_setting + PROCEDURE :: extract_global => lmp_extract_global + PROCEDURE :: version => lmp_version + PROCEDURE :: is_running => lmp_is_running END TYPE lammps INTERFACE lammps - MODULE PROCEDURE lmp_open + MODULE PROCEDURE lmp_open END INTERFACE lammps + ! Constants to use in working with lammps_data + ENUM, BIND(C) + ENUMERATOR :: DATA_INT, DATA_INT_1D, DATA_INT_2D + ENUMERATOR :: DATA_INT64, DATA_INT64_1D, DATA_INT64_2D + ENUMERATOR :: DATA_DOUBLE, DATA_DOUBLE_1D, DATA_DOUBLE_2D + ENUMERATOR :: DATA_STRING + END ENUM + + ! Derived type for receiving LAMMPS data (in lieu of the ability to type cast + ! pointers) + TYPE lammps_data + INTEGER(c_int) :: datatype + INTEGER(c_int), POINTER :: i32 + INTEGER(c_int), DIMENSION(:), POINTER :: i32_vec + INTEGER(c_int64_t), POINTER :: i64 + INTEGER(c_int64_t), DIMENSION(:), POINTER :: i64_vec + REAL(c_double), POINTER :: r64 + REAL(c_double), DIMENSION(:), POINTER :: r64_vec + CHARACTER(LEN=:), ALLOCATABLE :: str + END TYPE lammps_data + + ! This overloads the assignment operator (=) so that assignments of the + ! form + ! nlocal = extract_global('nlocal') + ! which are of the form "pointer to double = type(lammps_data)" result in + ! re-associating the pointer on the left with the appropriate piece of + ! LAMMPS data (after checking type-compatibility) + INTERFACE ASSIGNMENT(=) + MODULE PROCEDURE assign_int_to_lammps_data, assign_int64_to_lammps_data, & + assign_intvec_to_lammps_data, & + assign_double_to_lammps_data, assign_doublevec_to_lammps_data, & + assign_string_to_lammps_data + END INTERFACE + ! interface definitions for calling functions in library.cpp INTERFACE - FUNCTION lammps_open(argc, argv, comm) BIND(C, name='lammps_open_fortran') - IMPORT :: c_ptr, c_int - INTEGER(c_int), VALUE, INTENT(in) :: argc, comm - TYPE(c_ptr), DIMENSION(*), INTENT(in) :: argv - TYPE(c_ptr) :: lammps_open - END FUNCTION lammps_open + FUNCTION lammps_open(argc, argv, comm) BIND(C,name='lammps_open_fortran') + IMPORT :: c_ptr, c_int + IMPLICIT NONE + INTEGER(c_int), VALUE, INTENT(IN) :: argc, comm + TYPE(c_ptr), DIMENSION(*), INTENT(IN) :: argv + TYPE(c_ptr) :: lammps_open + END FUNCTION lammps_open - FUNCTION lammps_open_no_mpi(argc, argv, handle) BIND(C, name='lammps_open_no_mpi') - IMPORT :: c_ptr, c_int - INTEGER(c_int), VALUE, INTENT(in) :: argc - TYPE(c_ptr), DIMENSION(*), INTENT(in) :: argv - TYPE(c_ptr), VALUE, INTENT(in) :: handle - TYPE(c_ptr) :: lammps_open_no_mpi - END FUNCTION lammps_open_no_mpi + FUNCTION lammps_open_no_mpi(argc, argv, handle) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + INTEGER(c_int), VALUE, INTENT(IN) :: argc + TYPE(c_ptr), DIMENSION(*), INTENT(IN) :: argv + TYPE(c_ptr), VALUE, INTENT(IN) :: handle + TYPE(c_ptr) :: lammps_open_no_mpi + END FUNCTION lammps_open_no_mpi - SUBROUTINE lammps_close(handle) BIND(C, name='lammps_close') - IMPORT :: c_ptr - TYPE(c_ptr), VALUE :: handle - END SUBROUTINE lammps_close + SUBROUTINE lammps_close(handle) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + END SUBROUTINE lammps_close - SUBROUTINE lammps_mpi_init() BIND(C, name='lammps_mpi_init') - END SUBROUTINE lammps_mpi_init + SUBROUTINE lammps_mpi_init() BIND(C) + END SUBROUTINE lammps_mpi_init - SUBROUTINE lammps_mpi_finalize() BIND(C, name='lammps_mpi_finalize') - END SUBROUTINE lammps_mpi_finalize + SUBROUTINE lammps_mpi_finalize() BIND(C) + END SUBROUTINE lammps_mpi_finalize - SUBROUTINE lammps_kokkos_finalize() BIND(C, name='lammps_kokkos_finalize') - END SUBROUTINE lammps_kokkos_finalize + SUBROUTINE lammps_kokkos_finalize() BIND(C) + END SUBROUTINE lammps_kokkos_finalize - SUBROUTINE lammps_file(handle, filename) BIND(C, name='lammps_file') - IMPORT :: c_ptr - TYPE(c_ptr), VALUE :: handle - TYPE(c_ptr), VALUE :: filename - END SUBROUTINE lammps_file + SUBROUTINE lammps_error(handle, error_type, error_text) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + INTEGER(c_int), VALUE :: error_type + TYPE(c_ptr), VALUE :: error_text + END SUBROUTINE lammps_error - SUBROUTINE lammps_command(handle, cmd) BIND(C, name='lammps_command') - IMPORT :: c_ptr - TYPE(c_ptr), VALUE :: handle - TYPE(c_ptr), VALUE :: cmd - END SUBROUTINE lammps_command + SUBROUTINE lammps_file(handle, filename) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + TYPE(c_ptr), VALUE :: filename + END SUBROUTINE lammps_file - SUBROUTINE lammps_commands_list(handle, ncmd, cmds) BIND(C, name='lammps_commands_list') - IMPORT :: c_ptr, c_int - TYPE(c_ptr), VALUE :: handle - INTEGER(c_int), VALUE, INTENT(in) :: ncmd - TYPE(c_ptr), DIMENSION(*), INTENT(in) :: cmds - END SUBROUTINE lammps_commands_list + SUBROUTINE lammps_command(handle, cmd) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + TYPE(c_ptr), VALUE :: cmd + END SUBROUTINE lammps_command - SUBROUTINE lammps_commands_string(handle, str) BIND(C, name='lammps_commands_string') - IMPORT :: c_ptr - TYPE(c_ptr), VALUE :: handle - TYPE(c_ptr), VALUE :: str - END SUBROUTINE lammps_commands_string + SUBROUTINE lammps_commands_list(handle, ncmd, cmds) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + INTEGER(c_int), VALUE, INTENT(IN) :: ncmd + TYPE(c_ptr), DIMENSION(*), INTENT(IN) :: cmds + END SUBROUTINE lammps_commands_list - FUNCTION lammps_malloc(size) BIND(C, name='malloc') - IMPORT :: c_ptr, c_size_t - INTEGER(c_size_t), value :: size - TYPE(c_ptr) :: lammps_malloc - END FUNCTION lammps_malloc + SUBROUTINE lammps_commands_string(handle, str) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + TYPE(c_ptr), VALUE :: str + END SUBROUTINE lammps_commands_string - SUBROUTINE lammps_free(ptr) BIND(C, name='lammps_free') - IMPORT :: c_ptr - TYPE(c_ptr), VALUE :: ptr - END SUBROUTINE lammps_free + FUNCTION lammps_get_natoms(handle) BIND(C) + IMPORT :: c_ptr, c_double + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + REAL(c_double) :: lammps_get_natoms + END FUNCTION lammps_get_natoms - FUNCTION lammps_version(handle) BIND(C, name='lammps_version') - IMPORT :: c_ptr, c_int - TYPE(c_ptr), VALUE :: handle - INTEGER(c_int) :: lammps_version - END FUNCTION lammps_version + FUNCTION lammps_get_thermo(handle,name) BIND(C) + IMPORT :: c_ptr, c_double + IMPLICIT NONE + REAL(c_double) :: lammps_get_thermo + TYPE(c_ptr), VALUE :: handle + TYPE(c_ptr), VALUE :: name + END FUNCTION lammps_get_thermo + + SUBROUTINE lammps_extract_box(handle,boxlo,boxhi,xy,yz,xz,pflags, & + boxflag) BIND(C) + IMPORT :: c_ptr, c_double, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle, boxlo, boxhi, xy, yz, xz, pflags, & + boxflag + END SUBROUTINE lammps_extract_box + + SUBROUTINE lammps_reset_box(handle,boxlo,boxhi,xy,yz,xz) BIND(C) + IMPORT :: c_ptr, c_double + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + REAL(c_double), DIMENSION(3) :: boxlo, boxhi + REAL(c_double), VALUE :: xy, yz, xz + END SUBROUTINE lammps_reset_box + + SUBROUTINE lammps_memory_usage(handle,meminfo) BIND(C) + IMPORT :: c_ptr, c_double + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + REAL(c_double), DIMENSION(*) :: meminfo + END SUBROUTINE lammps_memory_usage + + FUNCTION lammps_get_mpi_comm(handle) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + INTEGER(c_int) :: lammps_get_mpi_comm + END FUNCTION lammps_get_mpi_comm + + FUNCTION lammps_extract_setting(handle,keyword) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle, keyword + INTEGER(c_int) :: lammps_extract_setting + END FUNCTION lammps_extract_setting + + FUNCTION lammps_extract_global_datatype(handle,name) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle, name + 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), VALUE :: str + INTEGER(c_size_t) :: c_strlen + END FUNCTION c_strlen + + FUNCTION lammps_extract_global(handle, name) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle, name + TYPE(c_ptr) :: lammps_extract_global + END FUNCTION lammps_extract_global + + !INTEGER (c_int) FUNCTION lammps_extract_atom_datatype + + !(generic) lammps_extract_atom + + !(generic) lammps_extract_compute + + !(generic) lammps_extract_fix + + !(generic) lammps_extract_variable + + !INTEGER (c_int) lammps_set_variable + + !SUBROUTINE lammps_gather_atoms + + !SUBROUTINE lammps_gather_atoms_concat + + !SUBROUTINE lammps_gather_atoms_subset + + !SUBROUTINE lammps_scatter_atoms + + !SUBROUTINE lammps_scatter_atoms_subset + + !SUBROUTINE lammps_gather_bonds + + !SUBROUTINE lammps_gather + + !SUBROUTINE lammps_gather_concat + + !SUBROUTINE lammps_gather_subset + + !SUBROUTINE lammps_scatter_subset + + !(generic / id, type, and image are special) / requires LAMMPS_BIGBIG + !INTEGER (C_int) FUNCTION lammps_create_atoms + + !INTEGER (C_int) FUNCTION lammps_find_pair_neighlist + + !INTEGER (C_int) FUNCTION lammps_find_fix_neighlist + + !INTEGER (C_int) FUNCTION lammps_find_compute_neighlist + + !INTEGER (C_int) FUNCTION lammps_neighlist_num_elements + + !SUBROUTINE lammps_neighlist_element_neighbors + + FUNCTION lammps_version(handle) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + INTEGER(c_int) :: lammps_version + END FUNCTION lammps_version + + !SUBROUTINE lammps_get_os_info + + !LOGICAL FUNCTION lammps_config_has_mpi_support + !LOGICAL FUNCTION lammps_config_has_gzip_support + !LOGICAL FUNCTION lammps_config_has_png_support + !LOGICAL FUNCTION lammps_config_has_jpeg_support + !LOGICAL FUNCTION lammps_config_has_ffmpeg_support + !LOGICAL FUNCTION lammps_config_has_exceptions + !LOGICAL FUNCTION lammps_config_has_package + !INTEGER (C_int) FUNCTION lammps_config_package_count + !SUBROUTINE lammps_config_package_name + + !LOGICAL FUNCTION lammps_config_accelerator + !LOGICAL FUNCTION lammps_has_gpu_device + !SUBROUTINE lammps_get_gpu_device + + !LOGICAL FUNCTION lammps_has_id + !INTEGER (C_int) FUNCTION lammps_id_count + !SUBROUTINE lammps_id_name + + !INTEGER (C_int) FUNCTION lammps_plugin_count + !SUBROUTINE lammps_plugin_name + + !Both of these use LAMMPS_BIGBIG + !INTEGER (LAMMPS_imageint) FUNCTION lammps_encode_image_flags + !SUBROUTINE lammps_decode_image_flags + + !SUBROUTINE lammps_set_fix_external_callback ! may have trouble.... + !FUNCTION lammps_fix_external_get_force() ! returns real(c_double) (:) + + !SUBROUTINE lammps_fix_external_set_energy_global + !SUBROUTINE lammps_fix_external_set_energy_peratom + !SUBROUTINE lammps_fix_external_set_virial_global + !SUBROUTINE lammps_fix_external_set_virial_peratom + !SUBROUTINE lammps_fix_external_set_vector_length + !SUBROUTINE lammps_fix_external_set_vector + + !SUBROUTINE lammps_flush_buffers + + FUNCTION lammps_malloc(size) BIND(C, name='malloc') + IMPORT :: c_ptr, c_size_t + IMPLICIT NONE + INTEGER(c_size_t), VALUE :: size + TYPE(c_ptr) :: lammps_malloc + END FUNCTION lammps_malloc + + SUBROUTINE lammps_free(ptr) BIND(C) + IMPORT :: c_ptr + IMPLICIT NONE + TYPE(c_ptr), VALUE :: ptr + END SUBROUTINE lammps_free + + INTEGER(c_int) FUNCTION lammps_is_running(handle) BIND(C) + IMPORT :: c_ptr, c_int + IMPLICIT NONE + TYPE(c_ptr), VALUE :: handle + END FUNCTION lammps_is_running + + !SUBROUTINE lammps_force_timeout + + !LOGICAL FUNCTION lammps_has_error + + !INTEGER (c_int) FUNCTION lammps_get_last_error_message - FUNCTION lammps_get_natoms(handle) BIND(C, name='lammps_get_natoms') - IMPORT :: c_ptr, c_double - TYPE(c_ptr), VALUE :: handle - REAL(c_double) :: lammps_get_natoms - END FUNCTION lammps_get_natoms END INTERFACE CONTAINS @@ -138,9 +379,8 @@ CONTAINS ! Constructor for the LAMMPS class. ! Combined wrapper around lammps_open_fortran() and lammps_open_no_mpi() TYPE(lammps) FUNCTION lmp_open(args, comm) - IMPLICIT NONE - INTEGER, INTENT(in), OPTIONAL :: comm - CHARACTER(len=*), INTENT(in), OPTIONAL :: args(:) + INTEGER, INTENT(IN), OPTIONAL :: comm + CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: args(:) TYPE(c_ptr), ALLOCATABLE :: argv(:) INTEGER(c_int) :: i, c_comm, argc @@ -173,9 +413,8 @@ CONTAINS ! Combined Fortran wrapper around lammps_close() and lammps_mpi_finalize() SUBROUTINE lmp_close(self, finalize) - IMPLICIT NONE CLASS(lammps) :: self - LOGICAL, INTENT(in), OPTIONAL :: finalize + LOGICAL, INTENT(IN), OPTIONAL :: finalize CALL lammps_close(self%handle) @@ -187,22 +426,20 @@ CONTAINS END IF END SUBROUTINE lmp_close - INTEGER FUNCTION lmp_version(self) - IMPLICIT NONE + ! equivalent function to lammps_error() + SUBROUTINE lmp_error(self, error_type, error_text) CLASS(lammps) :: self + INTEGER :: error_type + CHARACTER(len=*) :: error_text + TYPE(c_ptr) :: str - lmp_version = lammps_version(self%handle) - END FUNCTION lmp_version - - DOUBLE PRECISION FUNCTION lmp_get_natoms(self) - IMPLICIT NONE - CLASS(lammps) :: self - - lmp_get_natoms = lammps_get_natoms(self%handle) - END FUNCTION lmp_get_natoms + str = f2c_string(error_text) + CALL lammps_error(self%handle, error_type, str) + CALL lammps_free(str) + END SUBROUTINE lmp_error + ! equivalent function to lammps_file() SUBROUTINE lmp_file(self, filename) - IMPLICIT NONE CLASS(lammps) :: self CHARACTER(len=*) :: filename TYPE(c_ptr) :: str @@ -214,7 +451,6 @@ CONTAINS ! equivalent function to lammps_command() SUBROUTINE lmp_command(self, cmd) - IMPLICIT NONE CLASS(lammps) :: self CHARACTER(len=*) :: cmd TYPE(c_ptr) :: str @@ -226,9 +462,8 @@ CONTAINS ! equivalent function to lammps_commands_list() SUBROUTINE lmp_commands_list(self, cmds) - IMPLICIT NONE CLASS(lammps) :: self - CHARACTER(len=*), INTENT(in), OPTIONAL :: cmds(:) + CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: cmds(:) TYPE(c_ptr), ALLOCATABLE :: cmdv(:) INTEGER :: i, ncmd @@ -250,7 +485,6 @@ CONTAINS ! equivalent function to lammps_commands_string() SUBROUTINE lmp_commands_string(self, str) - IMPLICIT NONE CLASS(lammps) :: self CHARACTER(len=*) :: str TYPE(c_ptr) :: tmp @@ -260,13 +494,274 @@ CONTAINS CALL lammps_free(tmp) END SUBROUTINE lmp_commands_string + ! equivalent function to lammps_get_natoms + DOUBLE PRECISION FUNCTION lmp_get_natoms(self) + CLASS(lammps) :: self + + lmp_get_natoms = lammps_get_natoms(self%handle) + END FUNCTION lmp_get_natoms + + ! equivalent function to lammps_get_thermo + REAL (C_double) FUNCTION lmp_get_thermo(self,name) + CLASS(lammps), INTENT(IN) :: self + CHARACTER(LEN=*) :: name + TYPE(C_ptr) :: Cname + + Cname = f2c_string(name) + lmp_get_thermo = lammps_get_thermo(self%handle, Cname) + CALL lammps_free(Cname) + END FUNCTION lmp_get_thermo + + ! equivalent subroutine to lammps_extract_box + SUBROUTINE lmp_extract_box(self, boxlo, boxhi, xy, yz, xz, pflags, boxflag) + CLASS(lammps), INTENT(IN) :: self + REAL(c_double), INTENT(OUT), TARGET, OPTIONAL :: boxlo(3), boxhi(3) + REAL(c_double), INTENT(OUT), TARGET, OPTIONAL :: xy, yz, xz + LOGICAL, INTENT(OUT), OPTIONAL :: pflags(3), boxflag + INTEGER(c_int), TARGET :: C_pflags(3), C_boxflag + TYPE (c_ptr) :: ptr(7) + + ptr = c_null_ptr + IF ( PRESENT(boxlo) ) ptr(1) = C_LOC(boxlo(1)) + IF ( PRESENT(boxhi) ) ptr(2) = C_LOC(boxhi(1)) + IF ( PRESENT(xy) ) ptr(3) = C_LOC(xy) + IF ( PRESENT(yz) ) ptr(4) = C_LOC(yz) + IF ( PRESENT(xz) ) ptr(5) = C_LOC(xz) + IF ( PRESENT(pflags) ) ptr(6) = C_LOC(C_pflags(1)) + IF ( PRESENT(boxflag) ) ptr(7) = C_LOC(C_boxflag) + CALL lammps_extract_box(self%handle, ptr(1), ptr(2), ptr(3), ptr(4), & + ptr(5), ptr(6), ptr(7)) + IF ( PRESENT(pflags) ) pflags = ( C_pflags /= 0_C_int ) + IF ( PRESENT(boxflag) ) boxflag = ( C_boxflag /= 0_C_int ) + END SUBROUTINE lmp_extract_box + + ! equivalent function to lammps_reset_box + SUBROUTINE lmp_reset_box(self, boxlo, boxhi, xy, yz, xz) + CLASS(lammps), INTENT(IN) :: self + REAL(C_double), INTENT(IN) :: boxlo(3), boxhi(3), xy, yz, xz + + CALL lammps_reset_box(self%handle, boxlo, boxhi, xy, yz, xz) + END SUBROUTINE lmp_reset_box + + ! equivalent function to lammps_memory_usage + SUBROUTINE lmp_memory_usage(self,meminfo) + CLASS(lammps), INTENT(IN) :: self + INTEGER, PARAMETER :: MEMINFO_ELEM = 3 + REAL (c_double), DIMENSION(MEMINFO_ELEM), INTENT(OUT) :: meminfo + + CALL lammps_memory_usage(self%handle,meminfo) + END SUBROUTINE lmp_memory_usage + + ! equivalent function to lammps_get_mpi_comm + INTEGER FUNCTION lmp_get_mpi_comm(self) + CLASS(lammps), INTENT(IN) :: self + + lmp_get_mpi_comm = lammps_get_mpi_comm(self%handle) + END FUNCTION lmp_get_mpi_comm + + ! equivalent function to lammps_extract_setting + INTEGER (c_int) FUNCTION lmp_extract_setting(self, keyword) + CLASS(lammps), INTENT(IN) :: self + CHARACTER(LEN=*), INTENT(IN) :: keyword + TYPE(c_ptr) :: Ckeyword + + Ckeyword = f2c_string(keyword) + lmp_extract_setting = lammps_extract_setting(self%handle, Ckeyword) + CALL lammps_free(Ckeyword) + END FUNCTION lmp_extract_setting + + ! equivalent function to lammps_extract_global + ! the assignment is actually overloaded so as to bind the pointers to + ! lammps data based on the information available from LAMMPS + FUNCTION lmp_extract_global(self, name) RESULT (global_data) + CLASS(lammps), INTENT(IN) :: self + CHARACTER(LEN=*), INTENT(IN) :: name + TYPE(lammps_data) :: global_data + + INTEGER(c_int) :: datatype + TYPE(c_ptr) :: Cname, Cptr + INTEGER(c_size_t) :: length, i + CHARACTER(KIND=c_char, LEN=1), DIMENSION(:), POINTER :: Fptr + + ! Determine vector length + ! FIXME Is there a way to get the length of the vector from C rather + ! than defining it here AND in the Python API? + SELECT CASE (name) + CASE ('boxlo','boxhi','sublo','subhi','sublo_lambda','subhi_lambda', & + 'periodicity') + length = 3 + CASE DEFAULT + length = 1 + ! string cases are overridden later + END SELECT + + Cname = f2c_string(name) + datatype = lammps_extract_global_datatype(self%handle, Cname) + ! above could be c_null_ptr in place of self%handle...doesn't matter + Cptr = lammps_extract_global(self%handle, Cname) + CALL lammps_free(Cname) + + SELECT CASE (datatype) + CASE (LAMMPS_INT) + IF ( length == 1 ) THEN + global_data%datatype = DATA_INT + CALL C_F_POINTER(Cptr, global_data%i32) + ELSE + global_data%datatype = DATA_INT_1D + CALL C_F_POINTER(Cptr, global_data%i32_vec, [length]) + END IF + CASE (LAMMPS_INT64) + IF ( length == 1 ) THEN + global_data%datatype = DATA_INT64 + CALL C_F_POINTER(Cptr, global_data%i64) + ELSE + global_data%datatype = DATA_INT64_1D + CALL C_F_POINTER(Cptr, global_data%i64_vec, [length]) + END IF + CASE (LAMMPS_DOUBLE) + IF ( length == 1 ) THEN + global_data%datatype = DATA_DOUBLE + CALL C_F_POINTER(Cptr, global_data%r64) + ELSE + global_data%datatype = DATA_DOUBLE_1D + CALL C_F_POINTER(Cptr, global_data%r64_vec, [length]) + END IF + CASE (LAMMPS_STRING) + global_data%datatype = DATA_STRING + length = c_strlen(Cptr) + CALL C_F_POINTER(Cptr, Fptr, [length]) + ALLOCATE ( CHARACTER(LEN=length) :: global_data%str ) + FORALL ( I=1:length ) + global_data%str(i:i) = Fptr(i) + END FORALL + CASE DEFAULT + ! FIXME convert to use symbolic constants later + CALL lmp_error(self, 6, 'Unknown pointer type in extract_global') + END SELECT + END FUNCTION + + ! equivalent function to lammps_version() + INTEGER FUNCTION lmp_version(self) + CLASS(lammps) :: self + + lmp_version = lammps_version(self%handle) + END FUNCTION lmp_version + + ! equivalent function to lammps_is_running + LOGICAL FUNCTION lmp_is_running(self) + CLASS(lammps) :: self + + lmp_is_running = ( lammps_is_running(self%handle) /= 0_C_int ) + END FUNCTION lmp_is_running + + ! ---------------------------------------------------------------------- + ! functions to assign user-space pointers to LAMMPS data + ! ---------------------------------------------------------------------- + SUBROUTINE assign_int_to_lammps_data (lhs, rhs) + INTEGER(c_int), INTENT(OUT), POINTER :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_INT ) THEN + lhs => rhs%i32 + ELSE + CALL assignment_error(rhs%datatype, 'scalar int') + END IF + END SUBROUTINE assign_int_to_lammps_data + + SUBROUTINE assign_int64_to_lammps_data (lhs, rhs) + INTEGER(c_int64_t), INTENT(OUT), POINTER :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_INT64 ) THEN + lhs => rhs%i64 + ELSE + CALL assignment_error(rhs%datatype, 'scalar long int') + END IF + END SUBROUTINE assign_int64_to_lammps_data + + SUBROUTINE assign_intvec_to_lammps_data (lhs, rhs) + INTEGER(c_int), DIMENSION(:), INTENT(OUT), POINTER :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_INT_1D ) THEN + lhs => rhs%i32_vec + ELSE + CALL assignment_error(rhs%datatype, 'vector of ints') + END IF + END SUBROUTINE assign_intvec_to_lammps_data + + SUBROUTINE assign_double_to_lammps_data (lhs, rhs) + REAL(c_double), INTENT(OUT), POINTER :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_DOUBLE ) THEN + lhs => rhs%r64 + ELSE + CALL assignment_error(rhs%datatype, 'scalar double') + END IF + END SUBROUTINE assign_double_to_lammps_data + + SUBROUTINE assign_doublevec_to_lammps_data (lhs, rhs) + REAL(c_double), DIMENSION(:), INTENT(OUT), POINTER :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_DOUBLE_1D ) THEN + lhs => rhs%r64_vec + ELSE + CALL assignment_error(rhs%datatype, 'vector of doubles') + END IF + END SUBROUTINE assign_doublevec_to_lammps_data + + SUBROUTINE assign_string_to_lammps_data (lhs, rhs) + CHARACTER(LEN=*), INTENT(OUT) :: lhs + CLASS(lammps_data), INTENT(IN) :: rhs + + IF ( rhs%datatype == DATA_STRING ) THEN + lhs = rhs%str + ELSE + CALL assignment_error(rhs%datatype, 'string') + END IF + END SUBROUTINE assign_string_to_lammps_data + + SUBROUTINE assignment_error (type1, type2) + INTEGER (c_int) :: type1 + CHARACTER (LEN=*) :: type2 + INTEGER, PARAMETER :: ERROR_CODE = 1 + CHARACTER (LEN=:), ALLOCATABLE :: str1 + + SELECT CASE (type1) + CASE (DATA_INT) + str1 = 'scalar int' + CASE (DATA_INT_1D) + str1 = 'vector of ints' + CASE (DATA_INT_2D) + str1 = 'matrix of ints' + CASE (DATA_INT64) + str1 = 'scalar long int' + CASE (DATA_INT64_1D) + str1 = 'vector of long ints' + CASE (DATA_INT64_2D) + str1 = 'matrix of long ints' + CASE (DATA_DOUBLE) + str1 = 'scalar double' + CASE (DATA_DOUBLE_1D) + str1 = 'vector of doubles' + CASE (DATA_DOUBLE_2D) + str1 = 'matrix of doubles' + CASE DEFAULT + str1 = 'that type' + END SELECT + WRITE (ERROR_UNIT,'(A)') 'Cannot associate ' // str1 // ' with ' // type2 + STOP ERROR_CODE + END SUBROUTINE assignment_error + ! ---------------------------------------------------------------------- ! local helper functions ! copy fortran string to zero terminated c string ! ---------------------------------------------------------------------- FUNCTION f2c_string(f_string) RESULT(ptr) - CHARACTER (len=*), INTENT(in) :: f_string - CHARACTER (len=1, kind=c_char), POINTER :: c_string(:) + CHARACTER(LEN=*), INTENT(IN) :: f_string + CHARACTER(LEN=1, KIND=c_char), POINTER :: c_string(:) TYPE(c_ptr) :: ptr INTEGER(c_size_t) :: i, n @@ -279,3 +774,5 @@ CONTAINS c_string(n+1) = c_null_char END FUNCTION f2c_string END MODULE LIBLAMMPS + +! vim: ts=2 sts=2 sw=2 et diff --git a/python/lammps/constants.py b/python/lammps/constants.py index a50d58b28f..26bb92626a 100644 --- a/python/lammps/constants.py +++ b/python/lammps/constants.py @@ -13,29 +13,35 @@ # various symbolic constants to be used # in certain calls to select data formats -LAMMPS_AUTODETECT = None -LAMMPS_INT = 0 -LAMMPS_INT_2D = 1 -LAMMPS_DOUBLE = 2 -LAMMPS_DOUBLE_2D = 3 -LAMMPS_INT64 = 4 -LAMMPS_INT64_2D = 5 -LAMMPS_STRING = 6 +LAMMPS_AUTODETECT = None +LAMMPS_INT = 0 +LAMMPS_INT_2D = 1 +LAMMPS_DOUBLE = 2 +LAMMPS_DOUBLE_2D = 3 +LAMMPS_INT64 = 4 +LAMMPS_INT64_2D = 5 +LAMMPS_STRING = 6 # these must be kept in sync with the enums in library.h -LMP_STYLE_GLOBAL = 0 -LMP_STYLE_ATOM = 1 -LMP_STYLE_LOCAL = 2 +LMP_STYLE_GLOBAL = 0 +LMP_STYLE_ATOM = 1 +LMP_STYLE_LOCAL = 2 -LMP_TYPE_SCALAR = 0 -LMP_TYPE_VECTOR = 1 -LMP_TYPE_ARRAY = 2 -LMP_SIZE_VECTOR = 3 -LMP_SIZE_ROWS = 4 -LMP_SIZE_COLS = 5 +LMP_TYPE_SCALAR = 0 +LMP_TYPE_VECTOR = 1 +LMP_TYPE_ARRAY = 2 +LMP_SIZE_VECTOR = 3 +LMP_SIZE_ROWS = 4 +LMP_SIZE_COLS = 5 -LMP_VAR_EQUAL = 0 -LMP_VAR_ATOM = 1 +LMP_ERROR_WARNING = 0 +LMP_ERROR_ONE = 1 +LMP_ERROR_ALL = 2 +LMP_ERROR_WORLD = 4 +LMP_ERROR_UNIVERSE = 8 + +LMP_VAR_EQUAL = 0 +LMP_VAR_ATOM = 1 # ------------------------------------------------------------------------- diff --git a/python/lammps/core.py b/python/lammps/core.py index 930a40a4b0..aa4aae13db 100644 --- a/python/lammps/core.py +++ b/python/lammps/core.py @@ -167,6 +167,8 @@ class lammps(object): self.lib.lammps_flush_buffers.argtypes = [c_void_p] self.lib.lammps_free.argtypes = [c_void_p] + self.lib.lammps_error.argtypes = [c_void_p, c_int, c_char_p] + self.lib.lammps_file.argtypes = [c_void_p, c_char_p] self.lib.lammps_file.restype = None @@ -486,6 +488,26 @@ class lammps(object): # ------------------------------------------------------------------------- + def error(self, error_type, error_text): + """Forward error to the LAMMPS Error class. + + This is a wrapper around the :cpp:func:`lammps_error` function of the C-library interface. + + .. versionadded:: TBD + + :param error_type: + :type error_type: int + :param error_text: + :type error_text: string + """ + if error_text: error_text = error_text.encode() + else: error_text = "(unknown error)".encode() + + with ExceptionCheck(self): + self.lib.lammps_error(self.lmp, error_type, error_text) + + # ------------------------------------------------------------------------- + def version(self): """Return a numerical representation of the LAMMPS version in use. @@ -1622,8 +1644,8 @@ class lammps(object): """Return a string with detailed information about any devices that are usable by the GPU package. - This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` - function of the C-library interface. + This is a wrapper around the :cpp:func:`lammps_get_gpu_device_info` + function of the C-library interface. :return: GPU device info string :rtype: string diff --git a/src/library.cpp b/src/library.cpp index fc3700c786..cc547106f2 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -416,6 +416,89 @@ void lammps_python_finalize() Python::finalize(); } + +/* ---------------------------------------------------------------------- */ + +/** Call a LAMMPS Error class function + * +\verbatim embed:rst + +This function is a wrapper around functions in the ``Error`` to print an +error message and then stop LAMMPS. + +The *error_type* parameter selects which function to call. It is a sum +of constants from :cpp:enum:`_LMP_ERROR_CONST`. If the value does not +match any valid combination of constants a warning is printed and the +function returns. + +.. versionadded:: TBD + +\endverbatim + * + * \param handle pointer to a previously created LAMMPS instance + * \param error_type parameter to select function in the Error class + * \param error_text error message */ + +void lammps_error(void *handle, int error_type, const char *error_text) +{ + auto lmp = (LAMMPS *) handle; + + BEGIN_CAPTURE + { + switch (error_type) { + case LMP_ERROR_WARNING: + lmp->error->warning("(library)", 0, error_text); + break; + case LMP_ERROR_ONE: + lmp->error->one("(library)", 0, error_text); + break; + case LMP_ERROR_ALL: + lmp->error->all("(library)", 0, error_text); + break; + case LMP_ERROR_WARNING|LMP_ERROR_WORLD: + lmp->error->warning("(library)", 0, error_text); + break; + case LMP_ERROR_ONE|LMP_ERROR_WORLD: + lmp->error->one("(library)", 0, error_text); + break; + case LMP_ERROR_ALL|LMP_ERROR_WORLD: + lmp->error->all("(library)", 0, error_text); + break; + case LMP_ERROR_WARNING|LMP_ERROR_UNIVERSE: + lmp->error->universe_warn("(library)", 0, error_text); + break; + case LMP_ERROR_ONE|LMP_ERROR_UNIVERSE: + lmp->error->universe_one("(library)", 0, error_text); + break; + case LMP_ERROR_ALL|LMP_ERROR_UNIVERSE: + lmp->error->universe_all("(library)", 0, error_text); + break; + default: + auto mesg = fmt::format("Unknown error type {} for message: {}", error_type, error_text); + lmp->error->warning("(library)", 0, mesg); + } + } + END_CAPTURE + +#if defined(LAMMPS_EXCEPTIONS) + // with enabled exceptions the above code will simply throw an + // exception and record the error message. So we have to explicitly + // stop here like we do in main.cpp + if (lammps_has_error(handle)) { + if (error_type & 1) { + lammps_kokkos_finalize(); + lammps_python_finalize(); + MPI_Abort(lmp->universe->uworld, 1); + } else if (error_type & 2) { + lammps_kokkos_finalize(); + lammps_python_finalize(); + lammps_mpi_finalize(); + exit(1); + } + } +#endif +} + // ---------------------------------------------------------------------- // Library functions to process commands // ---------------------------------------------------------------------- @@ -1156,8 +1239,8 @@ Please also see :cpp:func:`lammps_extract_setting`, may lead to inconsistent internal data and thus may cause failures or crashes or bogus simulations. In general it is thus usually better to use a LAMMPS input command that sets or changes these parameters. - Those will takes care of all side effects and necessary updates of - settings derived from such settings. Where possible a reference to + Those will take care of all side effects and necessary updates of + settings derived from such settings. Where possible, a reference to such a command or a relevant section of the manual is given below. The following tables list the supported names, their data types, length diff --git a/src/library.h b/src/library.h index 311219e5ba..4e50cbee84 100644 --- a/src/library.h +++ b/src/library.h @@ -75,6 +75,18 @@ enum _LMP_TYPE_CONST { LMP_SIZE_COLS = 5 /*!< return number of columns */ }; +/** Error codes to select the suitable function in the Error class + * + * Must be kept in sync with the equivalent constants in lammps/constants.py */ + +enum _LMP_ERROR_CONST { + LMP_ERROR_WARNING = 0, /*!< call Error::warning() */ + LMP_ERROR_ONE = 1, /*!< called from one MPI rank */ + LMP_ERROR_ALL = 2, /*!< called from all MPI ranks */ + LMP_ERROR_WORLD = 4, /*!< error on Comm::world */ + LMP_ERROR_UNIVERSE = 8 /*!< error on Comm::universe */ +}; + /* Ifdefs to allow this file to be included in C and C++ programs */ #ifdef __cplusplus @@ -97,6 +109,8 @@ void lammps_mpi_finalize(); void lammps_kokkos_finalize(); void lammps_python_finalize(); +void lammps_error(void *handle, int error_type, const char *error_text); + /* ---------------------------------------------------------------------- * Library functions to process commands * ---------------------------------------------------------------------- */ diff --git a/unittest/c-library/test_library_open.cpp b/unittest/c-library/test_library_open.cpp index c75fd3738b..1cd65d843d 100644 --- a/unittest/c-library/test_library_open.cpp +++ b/unittest/c-library/test_library_open.cpp @@ -198,3 +198,32 @@ TEST(lammps_open_fortran, no_args) if (verbose) std::cout << output; MPI_Comm_free(&mycomm); } + +TEST(lammps_open_no_mpi, lammps_error) +{ + const char *args[] = {"liblammps", "-log", "none", "-nocite"}; + char **argv = (char **)args; + int argc = sizeof(args) / sizeof(char *); + + ::testing::internal::CaptureStdout(); + void *alt_ptr; + void *handle = lammps_open_no_mpi(argc, argv, &alt_ptr); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_EQ(handle, alt_ptr); + LAMMPS_NS::LAMMPS *lmp = (LAMMPS_NS::LAMMPS *)handle; + + EXPECT_EQ(lmp->world, MPI_COMM_WORLD); + EXPECT_EQ(lmp->infile, stdin); + EXPECT_NE(lmp->screen, nullptr); + EXPECT_EQ(lmp->logfile, nullptr); + EXPECT_EQ(lmp->citeme, nullptr); + EXPECT_EQ(lmp->suffix_enable, 0); + + EXPECT_STREQ(lmp->exename, "liblammps"); + EXPECT_EQ(lmp->num_package, 0); + ::testing::internal::CaptureStdout(); + lammps_error(handle, 0, "test_warning"); + output = ::testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, HasSubstr("WARNING: test_warning")); +} + diff --git a/unittest/fortran/CMakeLists.txt b/unittest/fortran/CMakeLists.txt index 9e46cef23e..d1f632f959 100644 --- a/unittest/fortran/CMakeLists.txt +++ b/unittest/fortran/CMakeLists.txt @@ -40,6 +40,23 @@ if(CMAKE_Fortran_COMPILER) add_executable(test_fortran_commands wrap_commands.cpp test_fortran_commands.f90) target_link_libraries(test_fortran_commands PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTestMain) add_test(NAME FortranCommands COMMAND test_fortran_commands) + + add_executable(test_fortran_get_thermo wrap_get_thermo.cpp test_fortran_get_thermo.f90) + target_link_libraries(test_fortran_get_thermo PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTestMain) + add_test(NAME FortranGetThermo COMMAND test_fortran_get_thermo) + + add_executable(test_fortran_box wrap_box.cpp test_fortran_box.f90) + target_link_libraries(test_fortran_box PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTestMain) + add_test(NAME FortranBox COMMAND test_fortran_box) + + add_executable(test_fortran_properties wrap_properties.cpp test_fortran_properties.f90 test_fortran_commands.f90) + target_link_libraries(test_fortran_properties PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTestMain) + add_test(NAME FortranProperties COMMAND test_fortran_properties) + + add_executable(test_fortran_extract_global wrap_extract_global.cpp test_fortran_extract_global.f90) + target_link_libraries(test_fortran_extract_global PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTestMain) + add_test(NAME FortranExtractGlobal COMMAND test_fortran_extract_global) + else() message(STATUS "Skipping Tests for the LAMMPS Fortran Module: no Fortran compiler") endif() diff --git a/unittest/fortran/test_fortran_box.f90 b/unittest/fortran/test_fortran_box.f90 new file mode 100644 index 0000000000..c21918afd0 --- /dev/null +++ b/unittest/fortran/test_fortran_box.f90 @@ -0,0 +1,142 @@ +MODULE keepbox + USE liblammps + IMPLICIT NONE + TYPE(LAMMPS) :: lmp + CHARACTER(len=40), DIMENSION(3), PARAMETER :: demo_input = & + [ CHARACTER(len=40) :: & + 'region box block 0 $x 0 2 0 2', & + 'create_box 1 box', & + 'create_atoms 1 single 1.0 1.0 ${zpos}' ] + CHARACTER(len=40), DIMENSION(2), PARAMETER :: cont_input = & + [ CHARACTER(len=40) :: & + 'create_atoms 1 single &', & + ' 0.2 0.1 0.1' ] +END MODULE keepbox + +FUNCTION f_lammps_with_args() BIND(C, name="f_lammps_with_args") + USE ISO_C_BINDING, ONLY: c_ptr + USE liblammps + USE keepbox, ONLY: lmp + IMPLICIT NONE + TYPE(c_ptr) :: f_lammps_with_args + + CHARACTER(len=12), DIMENSION(12), PARAMETER :: args = & + [ CHARACTER(len=12) :: 'liblammps', '-log', 'none', & + '-echo','screen','-nocite','-var','zpos','1.5','-var','x','2'] + + lmp = lammps(args) + f_lammps_with_args = lmp%handle +END FUNCTION f_lammps_with_args + +SUBROUTINE f_lammps_close() BIND(C, name="f_lammps_close") + USE ISO_C_BINDING, ONLY: c_null_ptr + USE liblammps + USE keepbox, ONLY: lmp + IMPLICIT NONE + + CALL lmp%close() + lmp%handle = c_null_ptr +END SUBROUTINE f_lammps_close + +SUBROUTINE f_lammps_box_setup () BIND(C) + USE liblammps + USE keepbox, ONLY : lmp, demo_input + IMPLICIT NONE + + CALL lmp%commands_list(demo_input) +END SUBROUTINE f_lammps_box_setup + +SUBROUTINE f_lammps_delete_everything() BIND(C) + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + + CALL lmp%command("delete_atoms group all"); +END SUBROUTINE f_lammps_delete_everything + +FUNCTION f_lammps_extract_box_xlo () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_xlo + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxlo=boxdim) + f_lammps_extract_box_xlo = boxdim(1) +END FUNCTION f_lammps_extract_box_xlo + +FUNCTION f_lammps_extract_box_xhi () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_xhi + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxhi=boxdim) + f_lammps_extract_box_xhi = boxdim(1) +END FUNCTION f_lammps_extract_box_xhi + +FUNCTION f_lammps_extract_box_ylo () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_ylo + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxlo=boxdim) + f_lammps_extract_box_ylo = boxdim(2) +END FUNCTION f_lammps_extract_box_ylo + +FUNCTION f_lammps_extract_box_yhi () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_yhi + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxhi=boxdim) + f_lammps_extract_box_yhi = boxdim(2) +END FUNCTION f_lammps_extract_box_yhi + +FUNCTION f_lammps_extract_box_zlo () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_zlo + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxlo=boxdim) + f_lammps_extract_box_zlo = boxdim(2) +END FUNCTION f_lammps_extract_box_zlo + +FUNCTION f_lammps_extract_box_zhi () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_extract_box_zhi + REAL (c_double) :: boxdim(3) + + CALL lmp%extract_box(boxhi=boxdim) + f_lammps_extract_box_zhi = boxdim(2) +END FUNCTION f_lammps_extract_box_zhi + +SUBROUTINE f_lammps_reset_box_2x () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double + USE liblammps + USE keepbox, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: newlo(3), newhi(3), xy, yz, xz + + xy = 0.0_c_double + yz = 0.0_c_double + xz = 0.0_c_double + newlo = [-1.0_c_double, -1.0_c_double, -1.0_c_double] + newhi = [3.0_c_double, 3.0_c_double, 3.0_c_double] + CALL lmp%reset_box(newlo, newhi, xy, yz, xz) +END SUBROUTINE f_lammps_reset_box_2x diff --git a/unittest/fortran/test_fortran_commands.f90 b/unittest/fortran/test_fortran_commands.f90 index b5cffe698f..d85b0f183e 100644 --- a/unittest/fortran/test_fortran_commands.f90 +++ b/unittest/fortran/test_fortran_commands.f90 @@ -1,5 +1,6 @@ MODULE keepcmds USE liblammps + IMPLICIT NONE TYPE(LAMMPS) :: lmp CHARACTER(len=40), DIMENSION(3), PARAMETER :: demo_input = & [ CHARACTER(len=40) :: & diff --git a/unittest/fortran/test_fortran_extract_global.f90 b/unittest/fortran/test_fortran_extract_global.f90 new file mode 100644 index 0000000000..9a8552d677 --- /dev/null +++ b/unittest/fortran/test_fortran_extract_global.f90 @@ -0,0 +1,491 @@ +MODULE keepglobal + USE liblammps + TYPE(LAMMPS) :: lmp + CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: demo_input = & + [ CHARACTER(len=40) :: & + 'region box block 0 $x 0 3 0 4', & + 'create_box 1 box', & + 'create_atoms 1 single 1.0 1.0 ${zpos}' ] + CHARACTER(LEN=40), DIMENSION(2), PARAMETER :: cont_input = & + [ CHARACTER(len=40) :: & + 'create_atoms 1 single &', & + ' 0.2 0.1 0.1' ] + CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: pair_input = & + [ CHARACTER(LEN=40) :: & + 'pair_style lj/cut 2.5', & + 'pair_coeff 1 1 1.0 1.0', & + 'mass 1 1.0' ] +END MODULE keepglobal + +FUNCTION f_lammps_with_args() BIND(C, name="f_lammps_with_args") + USE ISO_C_BINDING, ONLY: c_ptr + USE liblammps + USE keepglobal, ONLY: lmp + IMPLICIT NONE + TYPE(c_ptr) :: f_lammps_with_args + CHARACTER(len=12), DIMENSION(12), PARAMETER :: args = & + [ CHARACTER(len=12) :: 'liblammps', '-log', 'none', & + '-echo','screen','-nocite','-var','zpos','1.5','-var','x','2'] + + lmp = lammps(args) + f_lammps_with_args = lmp%handle +END FUNCTION f_lammps_with_args + +SUBROUTINE f_lammps_close() BIND(C, name="f_lammps_close") + USE ISO_C_BINDING, ONLY: c_null_ptr + USE liblammps + USE keepglobal, ONLY: lmp + IMPLICIT NONE + + CALL lmp%close() + lmp%handle = c_null_ptr +END SUBROUTINE f_lammps_close + +SUBROUTINE f_lammps_setup_extract_global () BIND(C) + USE LIBLAMMPS + USE keepglobal, ONLY : lmp, demo_input, cont_input, pair_input + IMPLICIT NONE + + CALL lmp%commands_list(demo_input) + CALL lmp%commands_list(cont_input) + CALL lmp%commands_list(pair_input) + CALL lmp%command('run 0') +END SUBROUTINE f_lammps_setup_extract_global + +SUBROUTINE f_lammps_setup_full_extract_global () BIND(C) + USE LIBLAMMPS + USE keepglobal, ONLY : lmp + IMPLICIT NONE + INTERFACE + SUBROUTINE f_lammps_setup_extract_global () BIND(C) + END SUBROUTINE f_lammps_setup_extract_global + END INTERFACE + + CALL lmp%command('atom_style full') + CALL f_lammps_setup_extract_global + CALL lmp%command('bond_style zero') + CALL lmp%command('angle_style zero') + CALL lmp%command('dihedral_style zero') + CALL lmp%command('run 0') +END SUBROUTINE f_lammps_setup_full_extract_global + +FUNCTION f_lammps_extract_global_units () BIND(C) RESULT(success) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE LIBLAMMPS + USE keepglobal, ONLY : lmp + IMPLICIT NONE + INTEGER (C_int) :: success + CHARACTER (LEN=16) :: units + + ! passing strings from Fortran to C is icky, so we do the test here and + ! report the result instead + units = lmp%extract_global('units') + IF ( TRIM(units) == 'lj' ) THEN + success = 1_C_int + ELSE + success = 0_C_int + END IF +END FUNCTION f_lammps_extract_global_units + +FUNCTION f_lammps_extract_global_ntimestep () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: ntimestep + INTEGER (C_int) :: f_lammps_extract_global_ntimestep + + ntimestep = lmp%extract_global("ntimestep") + f_lammps_extract_global_ntimestep = ntimestep +END FUNCTION f_lammps_extract_global_ntimestep +FUNCTION f_lammps_extract_global_ntimestep_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: ntimestep + INTEGER (C_int64_t) :: f_lammps_extract_global_ntimestep_big + + ntimestep = lmp%extract_global("ntimestep") + f_lammps_extract_global_ntimestep_big = ntimestep +END FUNCTION f_lammps_extract_global_ntimestep_big + +FUNCTION f_lammps_extract_global_dt () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double), POINTER :: dt + REAL (C_double) :: f_lammps_extract_global_dt + + dt = lmp%extract_global("dt") + f_lammps_extract_global_dt = dt +END FUNCTION f_lammps_extract_global_dt + +SUBROUTINE f_lammps_extract_global_boxlo (C_boxlo) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double), DIMENSION(3) :: C_boxlo + REAL (C_double), DIMENSION(:), POINTER :: boxlo + + boxlo = lmp%extract_global("boxlo") + C_boxlo = boxlo +END SUBROUTINE f_lammps_extract_global_boxlo + +SUBROUTINE f_lammps_extract_global_boxhi (C_boxhi) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double), DIMENSION(3) :: C_boxhi + REAL (C_double), DIMENSION(:), POINTER :: boxhi + + boxhi = lmp%extract_global("boxhi") + C_boxhi = boxhi +END SUBROUTINE f_lammps_extract_global_boxhi + +FUNCTION f_lammps_extract_global_boxxlo () BIND(C) RESULT(C_boxxlo) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxxlo + REAL (C_double), POINTER :: boxxlo + + boxxlo = lmp%extract_global("boxxlo") + C_boxxlo = boxxlo +END FUNCTION f_lammps_extract_global_boxxlo + +FUNCTION f_lammps_extract_global_boxxhi () BIND(C) RESULT(C_boxxhi) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxxhi + REAL (C_double), POINTER :: boxxhi + + boxxhi = lmp%extract_global("boxxhi") + C_boxxhi = boxxhi +END FUNCTION f_lammps_extract_global_boxxhi + +FUNCTION f_lammps_extract_global_boxylo () BIND(C) RESULT(C_boxylo) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxylo + REAL (C_double), POINTER :: boxylo + + boxylo = lmp%extract_global("boxylo") + C_boxylo = boxylo +END FUNCTION f_lammps_extract_global_boxylo + +FUNCTION f_lammps_extract_global_boxyhi () BIND(C) RESULT(C_boxyhi) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxyhi + REAL (C_double), POINTER :: boxyhi + + boxyhi = lmp%extract_global("boxyhi") + C_boxyhi = boxyhi +END FUNCTION f_lammps_extract_global_boxyhi + +FUNCTION f_lammps_extract_global_boxzlo () BIND(C) RESULT(C_boxzlo) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxzlo + REAL (C_double), POINTER :: boxzlo + + boxzlo = lmp%extract_global("boxzlo") + C_boxzlo = boxzlo +END FUNCTION f_lammps_extract_global_boxzlo + +FUNCTION f_lammps_extract_global_boxzhi () BIND(C) RESULT(C_boxzhi) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_boxzhi + REAL (C_double), POINTER :: boxzhi + + boxzhi = lmp%extract_global("boxzhi") + C_boxzhi = boxzhi +END FUNCTION f_lammps_extract_global_boxzhi + +SUBROUTINE f_lammps_extract_global_periodicity (C_periodicity) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), DIMENSION(3) :: C_periodicity + INTEGER (C_int), DIMENSION(:), POINTER :: periodicity + + periodicity = lmp%extract_global("periodicity") + C_periodicity = periodicity +END SUBROUTINE f_lammps_extract_global_periodicity + +FUNCTION f_lammps_extract_global_triclinic () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: triclinic + INTEGER (C_int) :: f_lammps_extract_global_triclinic + + triclinic = lmp%extract_global("triclinic") + f_lammps_extract_global_triclinic = triclinic +END FUNCTION f_lammps_extract_global_triclinic + +FUNCTION f_lammps_extract_global_xy () BIND(C) RESULT(C_xy) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_xy + REAL (C_double), POINTER :: xy + + xy = lmp%extract_global("xy") + C_xy = xy +END FUNCTION f_lammps_extract_global_xy + +FUNCTION f_lammps_extract_global_xz () BIND(C) RESULT(C_xz) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_xz + REAL (C_double), POINTER :: xz + + xz = lmp%extract_global("xz") + C_xz = xz +END FUNCTION f_lammps_extract_global_xz + +FUNCTION f_lammps_extract_global_yz () BIND(C) RESULT(C_yz) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_yz + REAL (C_double), POINTER :: yz + + yz = lmp%extract_global("yz") + C_yz = yz +END FUNCTION f_lammps_extract_global_yz + +FUNCTION f_lammps_extract_global_natoms () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: natoms + INTEGER (C_int) :: f_lammps_extract_global_natoms + + natoms = lmp%extract_global("natoms") + f_lammps_extract_global_natoms = natoms +END FUNCTION f_lammps_extract_global_natoms +FUNCTION f_lammps_extract_global_natoms_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: natoms + INTEGER (C_int64_t) :: f_lammps_extract_global_natoms_big + + natoms = lmp%extract_global("natoms") + f_lammps_extract_global_natoms_big = natoms +END FUNCTION f_lammps_extract_global_natoms_big + +FUNCTION f_lammps_extract_global_nbonds () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nbonds + INTEGER (C_int) :: f_lammps_extract_global_nbonds + + nbonds = lmp%extract_global("nbonds") + f_lammps_extract_global_nbonds = nbonds +END FUNCTION f_lammps_extract_global_nbonds +FUNCTION f_lammps_extract_global_nbonds_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: nbonds + INTEGER (C_int64_t) :: f_lammps_extract_global_nbonds_big + + nbonds = lmp%extract_global("nbonds") + f_lammps_extract_global_nbonds_big = nbonds +END FUNCTION f_lammps_extract_global_nbonds_big + +FUNCTION f_lammps_extract_global_nangles () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nangles + INTEGER (C_int) :: f_lammps_extract_global_nangles + + nangles = lmp%extract_global("nangles") + f_lammps_extract_global_nangles = nangles +END FUNCTION f_lammps_extract_global_nangles +FUNCTION f_lammps_extract_global_nangles_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: nangles + INTEGER (C_int64_t) :: f_lammps_extract_global_nangles_big + + nangles = lmp%extract_global("nangles") + f_lammps_extract_global_nangles_big = nangles +END FUNCTION f_lammps_extract_global_nangles_big + +FUNCTION f_lammps_extract_global_ndihedrals () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: ndihedrals + INTEGER (C_int) :: f_lammps_extract_global_ndihedrals + + ndihedrals = lmp%extract_global("ndihedrals") + f_lammps_extract_global_ndihedrals = ndihedrals +END FUNCTION f_lammps_extract_global_ndihedrals +FUNCTION f_lammps_extract_global_ndihedrals_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: ndihedrals + INTEGER (C_int64_t) :: f_lammps_extract_global_ndihedrals_big + + ndihedrals = lmp%extract_global("ndihedrals") + f_lammps_extract_global_ndihedrals_big = ndihedrals +END FUNCTION f_lammps_extract_global_ndihedrals_big + +FUNCTION f_lammps_extract_global_nimpropers () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nimpropers + INTEGER (C_int) :: f_lammps_extract_global_nimpropers + + nimpropers = lmp%extract_global("nimpropers") + f_lammps_extract_global_nimpropers = nimpropers +END FUNCTION f_lammps_extract_global_nimpropers +FUNCTION f_lammps_extract_global_nimpropers_big () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int64_t + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int64_t), POINTER :: nimpropers + INTEGER (C_int64_t) :: f_lammps_extract_global_nimpropers_big + + nimpropers = lmp%extract_global("nimpropers") + f_lammps_extract_global_nimpropers_big = nimpropers +END FUNCTION f_lammps_extract_global_nimpropers_big + + +FUNCTION f_lammps_extract_global_ntypes () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: ntypes + INTEGER (C_int) :: f_lammps_extract_global_ntypes + + ntypes = lmp%extract_global("ntypes") + f_lammps_extract_global_ntypes = ntypes +END FUNCTION f_lammps_extract_global_ntypes + +FUNCTION f_lammps_extract_global_nlocal () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nlocal + INTEGER (C_int) :: f_lammps_extract_global_nlocal + + nlocal = lmp%extract_global("nlocal") + f_lammps_extract_global_nlocal = nlocal +END FUNCTION f_lammps_extract_global_nlocal + +FUNCTION f_lammps_extract_global_nghost () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nghost + INTEGER (C_int) :: f_lammps_extract_global_nghost + + nghost = lmp%extract_global("nghost") + f_lammps_extract_global_nghost = nghost +END FUNCTION f_lammps_extract_global_nghost + +FUNCTION f_lammps_extract_global_nmax () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int), POINTER :: nmax + INTEGER (C_int) :: f_lammps_extract_global_nmax + + nmax = lmp%extract_global("nmax") + f_lammps_extract_global_nmax = nmax +END FUNCTION f_lammps_extract_global_nmax + +FUNCTION f_lammps_extract_global_boltz () BIND(C) RESULT(C_k_B) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_k_B + REAL (C_double), POINTER :: k_B + + k_B = lmp%extract_global("boltz") + C_k_B = k_B +END FUNCTION f_lammps_extract_global_boltz + +FUNCTION f_lammps_extract_global_hplanck () BIND(C) RESULT(C_h) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: C_h + REAL (C_double), POINTER :: h + + h = lmp%extract_global("boltz") + C_h = h +END FUNCTION f_lammps_extract_global_hplanck + +FUNCTION f_lammps_extract_global_angstrom () BIND(C) RESULT(Angstrom) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: Angstrom + REAL (C_double), POINTER :: A + + A = lmp%extract_global("angstrom") + Angstrom = A +END FUNCTION f_lammps_extract_global_angstrom + +FUNCTION f_lammps_extract_global_femtosecond () BIND(C) RESULT(fs) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE keepglobal, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + REAL (C_double) :: fs + REAL (C_double), POINTER :: femtosecond + + femtosecond = lmp%extract_global("femtosecond") + fs = femtosecond +END FUNCTION f_lammps_extract_global_femtosecond diff --git a/unittest/fortran/test_fortran_get_thermo.f90 b/unittest/fortran/test_fortran_get_thermo.f90 new file mode 100644 index 0000000000..e96c964e7c --- /dev/null +++ b/unittest/fortran/test_fortran_get_thermo.f90 @@ -0,0 +1,174 @@ +MODULE keepthermo + USE liblammps + IMPLICIT NONE + TYPE(LAMMPS) :: lmp + CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: demo_input = & + [ CHARACTER(len=40) :: & + 'region box block 0 $x 0 3 0 4', & + 'create_box 1 box', & + 'create_atoms 1 single 1.0 1.0 ${zpos}' ] + CHARACTER(LEN=40), DIMENSION(2), PARAMETER :: cont_input = & + [ CHARACTER(len=40) :: & + 'create_atoms 1 single &', & + ' 0.2 0.1 0.1' ] + CHARACTER(LEN=40), DIMENSION(3), PARAMETER :: pair_input = & + [ CHARACTER(LEN=40) :: & + 'pair_style lj/cut 2.5', & + 'pair_coeff 1 1 1.0 1.0', & + 'mass 1 1.0' ] +END MODULE keepthermo + +FUNCTION f_lammps_with_args() BIND(C) + USE ISO_C_BINDING, ONLY: c_ptr + USE liblammps + USE keepthermo, ONLY: lmp + IMPLICIT NONE + TYPE(c_ptr) :: f_lammps_with_args + + CHARACTER(len=12), DIMENSION(12), PARAMETER :: args = & + [ CHARACTER(len=12) :: 'liblammps', '-log', 'none', & + '-echo','screen','-nocite','-var','zpos','1.5','-var','x','2'] + + lmp = lammps(args) + f_lammps_with_args = lmp%handle +END FUNCTION f_lammps_with_args + +SUBROUTINE f_lammps_close() BIND(C) + USE ISO_C_BINDING, ONLY: c_null_ptr + USE liblammps + USE keepthermo, ONLY: lmp + IMPLICIT NONE + + CALL lmp%close() + lmp%handle = c_null_ptr +END SUBROUTINE f_lammps_close + +SUBROUTINE f_lammps_get_thermo_setup () BIND(C) + USE liblammps + USE keepthermo, ONLY : lmp, demo_input, cont_input, pair_input + IMPLICIT NONE + + CALL lmp%commands_list(demo_input) + CALL lmp%commands_list(cont_input) + CALL lmp%commands_list(pair_input) +END SUBROUTINE f_lammps_get_thermo_setup + +FUNCTION f_lammps_get_thermo_natoms () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_natoms + + f_lammps_get_thermo_natoms = lmp%get_thermo('atoms') +END FUNCTION f_lammps_get_thermo_natoms + +FUNCTION f_lammps_get_thermo_dt () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_dt + + f_lammps_get_thermo_dt = lmp%get_thermo('dt') +END FUNCTION f_lammps_get_thermo_dt + +FUNCTION f_lammps_get_thermo_vol () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_vol + + f_lammps_get_thermo_vol = lmp%get_thermo('vol') +END FUNCTION f_lammps_get_thermo_vol + +FUNCTION f_lammps_get_thermo_lx () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_lx + + f_lammps_get_thermo_lx = lmp%get_thermo('lx') +END FUNCTION f_lammps_get_thermo_lx + +FUNCTION f_lammps_get_thermo_ly () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_ly + + f_lammps_get_thermo_ly = lmp%get_thermo('ly') +END FUNCTION f_lammps_get_thermo_ly + +FUNCTION f_lammps_get_thermo_lz () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_lz + + f_lammps_get_thermo_lz = lmp%get_thermo('lz') +END FUNCTION f_lammps_get_thermo_lz + +FUNCTION f_lammps_get_thermo_xlo () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_xlo + + f_lammps_get_thermo_xlo = lmp%get_thermo('xlo') +END FUNCTION f_lammps_get_thermo_xlo + +FUNCTION f_lammps_get_thermo_xhi () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_xhi + + f_lammps_get_thermo_xhi = lmp%get_thermo('xhi') +END FUNCTION f_lammps_get_thermo_xhi + +FUNCTION f_lammps_get_thermo_ylo () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_ylo + + f_lammps_get_thermo_ylo = lmp%get_thermo('ylo') +END FUNCTION f_lammps_get_thermo_ylo + +FUNCTION f_lammps_get_thermo_yhi () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_yhi + + f_lammps_get_thermo_yhi = lmp%get_thermo('yhi') +END FUNCTION f_lammps_get_thermo_yhi + +FUNCTION f_lammps_get_thermo_zlo () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_zlo + + f_lammps_get_thermo_zlo = lmp%get_thermo('zlo') +END FUNCTION f_lammps_get_thermo_zlo + +FUNCTION f_lammps_get_thermo_zhi () BIND (C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_double + USE liblammps + USE keepthermo, ONLY : lmp + IMPLICIT NONE + REAL (c_double) :: f_lammps_get_thermo_zhi + + f_lammps_get_thermo_zhi = lmp%get_thermo('zhi') +END FUNCTION f_lammps_get_thermo_zhi diff --git a/unittest/fortran/test_fortran_properties.f90 b/unittest/fortran/test_fortran_properties.f90 new file mode 100644 index 0000000000..00e083b301 --- /dev/null +++ b/unittest/fortran/test_fortran_properties.f90 @@ -0,0 +1,52 @@ +FUNCTION f_lammps_version () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE liblammps + USE keepcmds, ONLY : lmp + IMPLICIT NONE + INTEGER (C_int) :: f_lammps_version + + f_lammps_version = lmp%version() +END FUNCTION f_lammps_version + +SUBROUTINE f_lammps_memory_usage (meminfo) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_double + USE liblammps + USE keepcmds, ONLY : lmp + IMPLICIT NONE + REAL (C_double), DIMENSION(3), INTENT(OUT) :: meminfo + + CALL lmp%memory_usage(meminfo) +END SUBROUTINE f_lammps_memory_usage + +FUNCTION f_lammps_get_mpi_comm () BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int + USE liblammps + USE keepcmds, ONLY : lmp + IMPLICIT NONE + INTEGER (C_int) :: f_lammps_get_mpi_comm + + f_lammps_get_mpi_comm = lmp%get_mpi_comm() +END FUNCTION f_lammps_get_mpi_comm + +FUNCTION f_lammps_extract_setting (Cstr) BIND(C) + USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_int, C_char + USE keepcmds, ONLY : lmp + USE LIBLAMMPS + IMPLICIT NONE + INTEGER (C_int) :: f_lammps_extract_setting + CHARACTER (KIND=C_char, LEN=1), DIMENSION(*), INTENT(IN) :: Cstr + INTEGER :: strlen, i + CHARACTER (LEN=:), ALLOCATABLE :: Fstr + + i = 1 + DO WHILE (Cstr(i) /= ACHAR(0)) + i = i + 1 + END DO + strlen = i + allocate ( CHARACTER(LEN=strlen) :: Fstr) + FORALL (i=1:strlen) + Fstr(i:i) = Cstr(i) + END FORALL + f_lammps_extract_setting = lmp%extract_setting(Fstr) + deallocate (Fstr) +END FUNCTION f_lammps_extract_setting diff --git a/unittest/fortran/wrap_box.cpp b/unittest/fortran/wrap_box.cpp new file mode 100644 index 0000000000..8678816658 --- /dev/null +++ b/unittest/fortran/wrap_box.cpp @@ -0,0 +1,64 @@ +// unit tests for extracting box dimensions fom a LAMMPS instance through the Fortran wrapper + +#include "lammps.h" +#include +#include + +#include "gtest/gtest.h" + +// prototypes for fortran reverse wrapper functions +extern "C" { +void *f_lammps_with_args(); +void f_lammps_close(); +void f_lammps_box_setup(); +double f_lammps_extract_box_xlo(); +double f_lammps_extract_box_xhi(); +double f_lammps_extract_box_ylo(); +double f_lammps_extract_box_yhi(); +double f_lammps_extract_box_zlo(); +double f_lammps_extract_box_zhi(); +void f_lammps_delete_everything(); +void f_lammps_reset_box_2x(); +} + +class LAMMPS_commands : public ::testing::Test { +protected: + LAMMPS_NS::LAMMPS *lmp; + LAMMPS_commands() = default; + ~LAMMPS_commands() override = default; + + void SetUp() override + { + ::testing::internal::CaptureStdout(); + lmp = (LAMMPS_NS::LAMMPS *)f_lammps_with_args(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 8).c_str(), "LAMMPS ("); + } + void TearDown() override + { + ::testing::internal::CaptureStdout(); + f_lammps_close(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 16).c_str(), "Total wall time:"); + lmp = nullptr; + } +}; + +TEST_F(LAMMPS_commands, get_thermo) +{ + f_lammps_box_setup(); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_xlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_xhi(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_ylo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_yhi(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_zlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_zhi(), 2.0); + f_lammps_delete_everything(); + f_lammps_reset_box_2x(); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_xlo(),-1.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_xhi(), 3.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_ylo(),-1.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_yhi(), 3.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_zlo(),-1.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_box_zhi(), 3.0); +}; diff --git a/unittest/fortran/wrap_extract_global.cpp b/unittest/fortran/wrap_extract_global.cpp new file mode 100644 index 0000000000..adf3986073 --- /dev/null +++ b/unittest/fortran/wrap_extract_global.cpp @@ -0,0 +1,177 @@ +// unit tests for extracting global data from a LAMMPS instance through the +// Fortran wrapper + +#include "lammps.h" +#include "library.h" +#include +#include +#include +#include + +#include "gtest/gtest.h" + +// prototypes for Fortran reverse wrapper functions +extern "C" { +void *f_lammps_with_args(); +void f_lammps_close(); +void f_lammps_setup_extract_global(); +void f_lammps_setup_full_extract_global(); +int f_lammps_extract_global_units(); +int f_lammps_extract_global_ntimestep(); +int64_t f_lammps_extract_global_ntimestep_big(); +double f_lammps_extract_global_dt(); +void f_lammps_extract_global_boxlo(double[3]); +void f_lammps_extract_global_boxhi(double[3]); +double f_lammps_extract_global_boxxlo(); +double f_lammps_extract_global_boxylo(); +double f_lammps_extract_global_boxzlo(); +double f_lammps_extract_global_boxxhi(); +double f_lammps_extract_global_boxyhi(); +double f_lammps_extract_global_boxzhi(); +void f_lammps_extract_global_periodicity(int[3]); +int f_lammps_extract_global_triclinic(); +double f_lammps_extract_global_xy(); +double f_lammps_extract_global_yz(); +double f_lammps_extract_global_xz(); +int f_lammps_extract_global_natoms(); +int64_t f_lammps_extract_global_natoms_big(); +int f_lammps_extract_global_nbonds(); +int64_t f_lammps_extract_global_nbonds_big(); +int f_lammps_extract_global_nangles(); +int64_t f_lammps_extract_global_nangles_big(); +int f_lammps_extract_global_ndihedrals(); +int64_t f_lammps_extract_global_ndihedrals_big(); +int f_lammps_extract_global_nimpropers(); +int64_t f_lammps_extract_global_nimpropers_big(); +int f_lammps_extract_global_ntypes(); +int f_lammps_extract_global_nlocal(); +int f_lammps_extract_global_nghost(); +int f_lammps_extract_global_nmax(); +double f_lammps_extract_global_boltz(); +double f_lammps_extract_global_hplanck(); +double f_lammps_extract_global_angstrom(); +double f_lammps_extract_global_femtosecond(); +} + +class LAMMPS_extract_global : public ::testing::Test { +protected: + LAMMPS_NS::LAMMPS *lmp; + LAMMPS_extract_global() = default; + ~LAMMPS_extract_global() override = default; + + void SetUp() override + { + ::testing::internal::CaptureStdout(); + lmp = (LAMMPS_NS::LAMMPS *)f_lammps_with_args(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 8).c_str(), "LAMMPS ("); + } + void TearDown() override + { + ::testing::internal::CaptureStdout(); + f_lammps_close(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 16).c_str(), "Total wall time:"); + lmp = nullptr; + } +}; + +TEST_F(LAMMPS_extract_global, units) +{ + f_lammps_setup_extract_global(); + EXPECT_EQ(f_lammps_extract_global_units(), 1); +}; + +TEST_F(LAMMPS_extract_global, ntimestep) +{ + f_lammps_setup_extract_global(); +#ifdef LAMMPS_SMALLSMALL + EXPECT_EQ(f_lammps_extract_global_ntimestep(), 0); +#else + EXPECT_EQ(f_lammps_extract_global_ntimestep_big(), 0l); +#endif +}; + +TEST_F(LAMMPS_extract_global, dt) +{ + f_lammps_setup_extract_global(); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_dt(), 0.005); +}; + +TEST_F(LAMMPS_extract_global, boxprops) +{ + f_lammps_setup_extract_global(); + double boxlo[3], boxhi[3]; + f_lammps_extract_global_boxlo(boxlo); + EXPECT_DOUBLE_EQ(boxlo[0], 0.0); + EXPECT_DOUBLE_EQ(boxlo[1], 0.0); + EXPECT_DOUBLE_EQ(boxlo[2], 0.0); + f_lammps_extract_global_boxhi(boxhi); + EXPECT_DOUBLE_EQ(boxhi[0], 2.0); + EXPECT_DOUBLE_EQ(boxhi[1], 3.0); + EXPECT_DOUBLE_EQ(boxhi[2], 4.0); + + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxxlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxxhi(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxylo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxyhi(), 3.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxzlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boxzhi(), 4.0); + + int periodicity[3]; + f_lammps_extract_global_periodicity(periodicity); + EXPECT_EQ(periodicity[0], 1); + EXPECT_EQ(periodicity[1], 1); + EXPECT_EQ(periodicity[2], 1); + + EXPECT_EQ(f_lammps_extract_global_triclinic(), 0); + + EXPECT_DOUBLE_EQ(f_lammps_extract_global_xy(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_yz(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_xz(), 0.0); +}; + +TEST_F(LAMMPS_extract_global, atomprops) +{ + f_lammps_setup_extract_global(); +#ifdef LAMMPS_SMALLSMALL + EXPECT_EQ(f_lammps_extract_global_natoms(), 2); + EXPECT_EQ(f_lammps_extract_global_nbonds(), 0); + EXPECT_EQ(f_lammps_extract_global_nangles(), 0); + EXPECT_EQ(f_lammps_extract_global_ndihedrals(), 0); +#else + EXPECT_EQ(f_lammps_extract_global_natoms_big(), 2l); + EXPECT_EQ(f_lammps_extract_global_nbonds_big(), 0l); + EXPECT_EQ(f_lammps_extract_global_nangles_big(), 0l); + EXPECT_EQ(f_lammps_extract_global_ndihedrals_big(), 0l); +#endif + + EXPECT_EQ(f_lammps_extract_global_ntypes(), 1); + EXPECT_EQ(f_lammps_extract_global_nlocal(), 2); + EXPECT_EQ(f_lammps_extract_global_nghost(), 41); + EXPECT_EQ(f_lammps_extract_global_nmax(), 16384); + + EXPECT_DOUBLE_EQ(f_lammps_extract_global_boltz(), 1.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_hplanck(), 1.0); + EXPECT_DOUBLE_EQ(f_lammps_extract_global_angstrom(), 1.0); + + EXPECT_DOUBLE_EQ(f_lammps_extract_global_femtosecond(), 1.0); +}; + +TEST_F(LAMMPS_extract_global, fullprops) +{ + if (! lammps_has_style(lmp, "atom", "full")) GTEST_SKIP(); + // This is not currently the world's most convincing test.... + f_lammps_setup_full_extract_global(); +#ifdef LAMMPS_SMALLSMALL + EXPECT_EQ(f_lammps_extract_global_natoms(), 2); + EXPECT_EQ(f_lammps_extract_global_nbonds(), 0); + EXPECT_EQ(f_lammps_extract_global_nangles(), 0); + EXPECT_EQ(f_lammps_extract_global_ndihedrals(), 0); +#else + EXPECT_EQ(f_lammps_extract_global_natoms_big(), 2l); + EXPECT_EQ(f_lammps_extract_global_nbonds_big(), 0l); + EXPECT_EQ(f_lammps_extract_global_nangles_big(), 0l); + EXPECT_EQ(f_lammps_extract_global_ndihedrals_big(), 0l); +#endif +} diff --git a/unittest/fortran/wrap_get_thermo.cpp b/unittest/fortran/wrap_get_thermo.cpp new file mode 100644 index 0000000000..71b51609eb --- /dev/null +++ b/unittest/fortran/wrap_get_thermo.cpp @@ -0,0 +1,67 @@ +// unit tests for getting thermodynamic output from a LAMMPS instance through the Fortran wrapper + +#include "lammps.h" +#include +#include + +#include "gtest/gtest.h" + +// prototypes for fortran reverse wrapper functions +extern "C" { +void *f_lammps_with_args(); +void f_lammps_close(); +void f_lammps_get_thermo_setup(); +double f_lammps_get_thermo_natoms(); +double f_lammps_get_thermo_dt(); +double f_lammps_get_thermo_vol(); +double f_lammps_get_thermo_lx(); +double f_lammps_get_thermo_ly(); +double f_lammps_get_thermo_lz(); +double f_lammps_get_thermo_xlo(); +double f_lammps_get_thermo_xhi(); +double f_lammps_get_thermo_ylo(); +double f_lammps_get_thermo_yhi(); +double f_lammps_get_thermo_zlo(); +double f_lammps_get_thermo_zhi(); +} + +class LAMMPS_thermo : public ::testing::Test { +protected: + LAMMPS_NS::LAMMPS *lmp; + LAMMPS_thermo() = default; + ~LAMMPS_thermo() override = default; + + void SetUp() override + { + ::testing::internal::CaptureStdout(); + lmp = (LAMMPS_NS::LAMMPS *)f_lammps_with_args(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 8).c_str(), "LAMMPS ("); + } + void TearDown() override + { + ::testing::internal::CaptureStdout(); + f_lammps_close(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 16).c_str(), "Total wall time:"); + lmp = nullptr; + } +}; + +TEST_F(LAMMPS_thermo, get_thermo) +{ + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_natoms(), 0.0); + f_lammps_get_thermo_setup(); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_natoms(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_dt(), 0.005); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_vol(), 24.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_lx(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_ly(), 3.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_lz(), 4.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_xlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_xhi(), 2.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_ylo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_yhi(), 3.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_zlo(), 0.0); + EXPECT_DOUBLE_EQ(f_lammps_get_thermo_zhi(), 4.0); +}; diff --git a/unittest/fortran/wrap_properties.cpp b/unittest/fortran/wrap_properties.cpp new file mode 100644 index 0000000000..8ecd9346dc --- /dev/null +++ b/unittest/fortran/wrap_properties.cpp @@ -0,0 +1,109 @@ +// unit tests for getting LAMMPS properties through the Fortran wrapper + +#include "lammps.h" +//#include // for stdin, stdout +#include "library.h" +#include +#include + +#include "gtest/gtest.h" + +// prototypes for fortran reverse wrapper functions +extern "C" { +void *f_lammps_with_args(); +void f_lammps_close(); +int f_lammps_version(); +void f_lammps_memory_usage(double*); +int f_lammps_get_mpi_comm(); +int f_lammps_extract_setting(const char*); +} + +class LAMMPS_properties : public ::testing::Test { +protected: + LAMMPS_NS::LAMMPS *lmp; + LAMMPS_properties() = default; + ~LAMMPS_properties() override = default; + + void SetUp() override + { + ::testing::internal::CaptureStdout(); + lmp = (LAMMPS_NS::LAMMPS *)f_lammps_with_args(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 8).c_str(), "LAMMPS ("); + } + void TearDown() override + { + ::testing::internal::CaptureStdout(); + f_lammps_close(); + std::string output = ::testing::internal::GetCapturedStdout(); + EXPECT_STREQ(output.substr(0, 16).c_str(), "Total wall time:"); + lmp = nullptr; + } +}; + +TEST_F(LAMMPS_properties, version) +{ + EXPECT_LT(20200917, f_lammps_version()); +}; + +TEST_F(LAMMPS_properties, memory_usage) +{ +// copied from c-library, with a two-character modification + double meminfo[3]; + f_lammps_memory_usage(meminfo); + EXPECT_GT(meminfo[0], 0.0); +#if defined(__linux__) || defined(_WIN32) + EXPECT_GE(meminfo[1], 0.0); +#endif +#if (defined(__linux__) || defined(__APPLE__) || defined(_WIN32)) && !defined(__INTEL_LLVM_COMPILER) + EXPECT_GT(meminfo[2], 0.0); +#endif +}; + +TEST_F(LAMMPS_properties, get_mpi_comm) +{ + int f_comm = f_lammps_get_mpi_comm(); + if ( lammps_config_has_mpi_support() ) + EXPECT_GE(f_comm, 0); + else + EXPECT_EQ(f_comm, -1); +}; + +TEST_F(LAMMPS_properties, extract_setting) +{ +#if defined(LAMMPS_SMALLSMALL) + EXPECT_EQ(f_lammps_extract_setting("bigint"), 4); +#else + EXPECT_EQ(f_lammps_extract_setting("bigint"), 8); +#endif +#if defined(LAMMPS_BIGBIG) + EXPECT_EQ(f_lammps_extract_setting("tagint"), 8); + EXPECT_EQ(f_lammps_extract_setting("imageint"), 8); +#else + EXPECT_EQ(f_lammps_extract_setting("tagint"), 4); + EXPECT_EQ(f_lammps_extract_setting("imageint"), 4); +#endif + + EXPECT_EQ(f_lammps_extract_setting("box_exist"), 0); + EXPECT_EQ(f_lammps_extract_setting("dimension"), 3); + EXPECT_EQ(f_lammps_extract_setting("world_size"), 1); + EXPECT_EQ(f_lammps_extract_setting("world_rank"), 0); + EXPECT_EQ(f_lammps_extract_setting("universe_size"), 1); + EXPECT_EQ(f_lammps_extract_setting("universe_rank"), 0); + EXPECT_GT(f_lammps_extract_setting("nthreads"), 0); + EXPECT_EQ(f_lammps_extract_setting("newton_pair"), 1); + EXPECT_EQ(f_lammps_extract_setting("newton_bond"), 1); + + EXPECT_EQ(f_lammps_extract_setting("ntypes"), 0); + EXPECT_EQ(f_lammps_extract_setting("nbondtypes"), 0); + EXPECT_EQ(f_lammps_extract_setting("nangletypes"), 0); + EXPECT_EQ(f_lammps_extract_setting("ndihedraltypes"), 0); + EXPECT_EQ(f_lammps_extract_setting("nimpropertypes"), 0); + + EXPECT_EQ(f_lammps_extract_setting("molecule_flag"), 0); + EXPECT_EQ(f_lammps_extract_setting("q_flag"), 0); + EXPECT_EQ(f_lammps_extract_setting("mu_flag"), 0); + EXPECT_EQ(f_lammps_extract_setting("rmass_flag"), 0); + EXPECT_EQ(f_lammps_extract_setting("UNKNOWN"), -1); + +}; diff --git a/unittest/python/python-open.py b/unittest/python/python-open.py index 328745ded0..3c9165b8c8 100644 --- a/unittest/python/python-open.py +++ b/unittest/python/python-open.py @@ -45,11 +45,21 @@ class PythonOpen(unittest.TestCase): def testWithArgs(self): """Create LAMMPS instance with a few arguments""" - lmp=lammps(name=self.machine, - cmdargs=['-nocite','-sf','opt','-log','none']) + lmp=lammps(name=self.machine,cmdargs=['-nocite','-sf','opt','-log','none']) self.assertIsNot(lmp.lmp,None) self.assertEqual(lmp.opened,1) + def testError(self): + """Print warning message through LAMMPS Error class""" + lmp=lammps(name=self.machine,cmdargs=['-nocite','-log','none','-screen','tmp.error.output']) + lmp.error(0,'test_warning') + lmp.close() + with open('tmp.error.output','r') as f: + output = f.read() + self.assertTrue('LAMMPS' in output) + self.assertTrue('Total wall time' in output) + self.assertTrue('WARNING: test_warning' in output) + def testContextManager(self): """Automatically clean up LAMMPS instance""" with lammps(name=self.machine) as lmp: