Merge remote-tracking branch 'github/master' into move-convenience-functions

This commit is contained in:
Axel Kohlmeyer 2020-08-31 11:41:18 -04:00
commit dd03c7232a
No known key found for this signature in database
GPG Key ID: D9B44E93BF0C375A
36 changed files with 2087 additions and 61 deletions

View File

@ -75,7 +75,7 @@ if(GPU_API STREQUAL "CUDA")
endif()
# Kepler (GPU Arch 3.5) is supported by CUDA 5 to CUDA 11
if((CUDA_VERSION VERSION_GREATER_EQUAL "5.0") AND (CUDA_VERSION VERSION_LESS "12.0"))
string(APPEND GPU_CUDA_GENCODE " -gencode arch=compute_30,code=[sm_30,compute_30] -gencode arch=compute_35,code=[sm_35,compute_35]")
string(APPEND GPU_CUDA_GENCODE " -gencode arch=compute_35,code=[sm_35,compute_35]")
endif()
# Maxwell (GPU Arch 5.x) is supported by CUDA 6 and later
if(CUDA_VERSION VERSION_GREATER_EQUAL "6.0")

View File

@ -422,6 +422,12 @@ INPUT = @LAMMPS_SOURCE_DIR@/utils.cpp \
@LAMMPS_SOURCE_DIR@/atom.h \
@LAMMPS_SOURCE_DIR@/input.cpp \
@LAMMPS_SOURCE_DIR@/input.h \
@LAMMPS_SOURCE_DIR@/tokenizer.cpp \
@LAMMPS_SOURCE_DIR@/tokenizer.h \
@LAMMPS_SOURCE_DIR@/text_file_reader.cpp \
@LAMMPS_SOURCE_DIR@/text_file_reader.h \
@LAMMPS_SOURCE_DIR@/potential_file_reader.cpp \
@LAMMPS_SOURCE_DIR@/potential_file_reader.h \
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded

91
doc/src/pg_cplusplus.rst Normal file
View File

@ -0,0 +1,91 @@
Using the C++ API directly
**************************
Using the C++ classes of the LAMMPS library is lacking some of the
convenience of the C library API, but it allows a more direct access to
simulation data and thus more low-level manipulations and tighter
integration of LAMMPS into another code. While for the complete C
library API is provided in the ``library.h`` header file, for using
the C++ API it is required to include the individual header files
defining the individual classes in use. Typically the name of the
class and the name of the header follow some simple rule. Examples
are given below.
Creating or deleting a LAMMPS object
*************************************
When using the LAMMPS library interfaces, the core task is to create an
instance of the :cpp:class:`LAMMPS_NS::LAMMPS` class. In C++ this can
be done directly through the ``new`` operator. All further operations
are then initiated through calling member functions of some of the
components of the LAMMPS class or accessing their data members. The
destruction of the LAMMPS instance is correspondingly initiated by using
the ``delete`` operator. Here is a simple example:
.. code-block:: c++
#include "lammps.h"
#include "universe.h"
#include <mpi.h>
#include <iostream>
int main(int argc, char **argv)
{
LAMMPS_NS::LAMMPS *lmp;
// custom argument vector for LAMMPS library
const char *lmpargv[] {"liblammps", "-log", "none"};
int lmpargc = sizeof(lmpargv)/sizeof(const char *);
// explicitly initialize MPI
MPI_Init(&argc, &argv);
// create LAMMPS instance
lmp = new LAMMPS_NS::LAMMPS(lmpargc, (char **)lmpargv, MPI_COMM_WORLD);
// output numerical version string
std::cout << "LAMMPS version: " << lmp->universe->num_ver << std::endl;
// delete LAMMPS instance
delete lmp;
// stop MPI environment
MPI_Finalize();
return 0;
}
Please note that this requires to include the ``lammps.h`` header for accessing
the members of the LAMMPS class and then the ``universe.h`` header for accessing the ``num_ver`` member of the :cpp:class:`Universe` class.
Executing LAMMPS commands
*************************
Once a LAMMPS instance is created by your C++ code, you need to set up a
simulation and that is most conveniently done by "driving" it through
issuing commands like you would do when running a LAMMPS simulation from
an input script. Processing of input in LAMMPS is handled by the
:cpp:class:`Input <LAMMPS_NS::Input>` class an instance of which is a
member of the :cpp:class:`LAMMPS <LAMMPS_NS::LAMMPS>` class. You have
two options: reading commands from a file, or executing a single
command from a string. See below for a small example:
.. code-block:: c++
#include "lammps.h"
#include "input.h"
#include <mpi.h>
using namespace LAMMPS_NS;
int main(int argc, char **argv)
{
const char *lmpargv[] {"liblammps", "-log", "none"};
int lmpargc = sizeof(lmpargv)/sizeof(const char *);
MPI_Init(&argc, &argv);
LAMMPS *lmp = new LAMMPS(lmpargc, (char **)lmpargv, MPI_COMM_WORLD);
lmp->input->file("in.melt");
lmp->input->one("run 100 post no");
delete lmp;
return 0;
}

View File

@ -762,6 +762,8 @@ on reading or an unexpected end-of-file state was reached. In that
case, the functions will stop the calculation with an error message,
indicating the name of the problematic file, if possible.
----------
.. doxygenfunction:: sfgets
:project: progguide
@ -787,6 +789,8 @@ and avoids a "hanging" calculation when run in parallel.
Please also see :cpp:func:`is_integer` and :cpp:func:`is_double` for
testing strings for compliance without conversion.
----------
.. doxygenfunction:: numeric
:project: progguide
@ -806,6 +810,8 @@ String processing
The following are functions to help with processing strings
and parsing files or arguments.
----------
.. doxygenfunction:: trim
:project: progguide
@ -895,3 +901,188 @@ Convenience functions
.. doxygenfunction:: timespec2seconds
:project: progguide
---------------------------
Tokenizer classes
=================
The purpose of the tokenizer classes is to simplify the recurring task
of breaking lines of text down into words and/or numbers.
Traditionally, LAMMPS code would be using the ``strtok()`` function from
the C library for that purpose, but that function has two significant
disadvantages: 1) it cannot be used concurrently from different LAMMPS
instances since it stores its status in a global variable and 2) it
modifies the string that it is processing. These classes were
implemented to avoid both of these issues and also to reduce the amount
of code that needs to be written.
The basic procedure is to create an instance of the tokenizer class with
the string to be processed as an argument and then do a loop until all
available tokens are read. The constructor has a default set of
separator characters, but that can be overridden. The default separators
are all "whitespace" characters, i.e. the space character, the tabulator
character, the carriage return character, the linefeed character, and
the form feed character.
.. code-block:: C++
:caption: Tokenizer class example listing entries of the PATH environment variable
#include "tokenizer.h"
#include <cstdlib>
#include <string>
#include <iostream>
using namespace LAMMPS_NS;
int main(int, char **)
{
const char *path = getenv("PATH");
if (path != nullptr) {
Tokenizer p(path,":");
while (p.has_next())
std::cout << "Entry: " << p.next() << "\n";
}
return 0;
}
Most tokenizer operations cannot fail except for
:cpp:func:`LAMMPS_NS::Tokenizer::next` (when used without first
checking with :cpp:func:`LAMMPS_NS::Tokenizer::has_next`) and
:cpp:func:`LAMMPS_NS::Tokenizer::skip`. In case of failure, the class
will throw an exception, so you may need to wrap the code using the
tokenizer into a ``try`` / ``catch`` block to handle errors. The
:cpp:class:`LAMMPS_NS::ValueTokenizer` class may also throw an exception
when a (type of) number is requested as next token that is not
compatible with the string representing the next word.
.. code-block:: C++
:caption: ValueTokenizer class example with exception handling
#include "tokenizer.h"
#include <cstdlib>
#include <string>
#include <iostream>
using namespace LAMMPS_NS;
int main(int, char **)
{
const char *text = "1 2 3 4 5 20.0 21 twentytwo 2.3";
double num1(0),num2(0),num3(0),num4(0);
ValueTokenizer t(text);
// read 4 doubles after skipping over 5 numbers
try {
t.skip(5);
num1 = t.next_double();
num2 = t.next_double();
num3 = t.next_double();
num4 = t.next_double();
} catch (TokenizerException &e) {
std::cout << "Reading numbers failed: " << e.what() << "\n";
}
std::cout << "Values: " << num1 << " " << num2 << " " << num3 << " " << num4 << "\n";
return 0;
}
This code example should produce the following output:
.. code-block::
Reading numbers failed: Not a valid floating-point number: 'twentytwo'
Values: 20 21 0 0
----------
.. doxygenclass:: LAMMPS_NS::Tokenizer
:project: progguide
:members:
.. doxygenclass:: LAMMPS_NS::TokenizerException
:project: progguide
:members:
.. doxygenclass:: LAMMPS_NS::ValueTokenizer
:project: progguide
:members:
.. doxygenclass:: LAMMPS_NS::InvalidIntegerException
:project: progguide
:members: what
.. doxygenclass:: LAMMPS_NS::InvalidFloatException
:project: progguide
:members: what
File reader classes
====================
The purpose of the file reader classes is to simplify the recurring task
of reading and parsing files. They can use the
:cpp:class:`LAMMPS_NS::ValueTokenizer` class to process the read in
text. The :cpp:class:`LAMMPS_NS::TextFileReader` is a more general
version while :cpp:class:`LAMMPS_NS::PotentialFileReader` is specialized
to implement the behavior expected for looking up and reading/parsing
files with potential parameters in LAMMPS. The potential file reader
class requires a LAMMPS instance, requires to be run on MPI rank 0 only,
will use the :cpp:func:`LAMMPS_NS::utils::get_potential_file_path`
function to look up and open the file, and will call the
:cpp:class:`LAMMPS_NS::Error` class in case of failures to read or to
convert numbers, so that LAMMPS will be aborted.
.. code-block:: C++
:caption: Use of PotentialFileReader class in pair style coul/streitz
PotentialFileReader reader(lmp, file, "coul/streitz");
char * line;
while((line = reader.next_line(NPARAMS_PER_LINE))) {
try {
ValueTokenizer values(line);
std::string iname = values.next_string();
int ielement;
for (ielement = 0; ielement < nelements; ielement++)
if (iname == elements[ielement]) break;
if (nparams == maxparam) {
maxparam += DELTA;
params = (Param *) memory->srealloc(params,maxparam*sizeof(Param),
"pair:params");
}
params[nparams].ielement = ielement;
params[nparams].chi = values.next_double();
params[nparams].eta = values.next_double();
params[nparams].gamma = values.next_double();
params[nparams].zeta = values.next_double();
params[nparams].zcore = values.next_double();
} catch (TokenizerException & e) {
error->one(FLERR, e.what());
}
nparams++;
}
A file that would be parsed by the reader code fragment looks like this:
# DATE: 2015-02-19 UNITS: metal CONTRIBUTOR: Ray Shan CITATION: Streitz and Mintmire, Phys Rev B, 50, 11996-12003 (1994)
#
# X (eV) J (eV) gamma (1/\AA) zeta (1/\AA) Z (e)
Al 0.000000 10.328655 0.000000 0.968438 0.763905
O 5.484763 14.035715 0.000000 2.143957 0.000000
----------
.. doxygenclass:: LAMMPS_NS::TextFileReader
:project: progguide
:members:
.. doxygenclass:: LAMMPS_NS::PotentialFileReader
:project: progguide
:members:

View File

@ -10,7 +10,7 @@ used from multiple compiler versions from different vendors for as long
as they are compatible with the hosting operating system, the same is
not true for Fortran codes. Thus the LAMMPS Fortran module needs to be
compiled alongside the code using it from the source code in
``examples/COUPLE/fortran/lammps.f90``. When linking, you also need to
``fortran/lammps.f90``. When linking, you also need to
:doc:`link to the LAMMPS library <Build_link>`. A typical command line
for a simple program using the Fortran interface would be:
@ -19,5 +19,184 @@ 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.
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.
.. versionadded:: 30Sep2020
.. admonition:: Work in Progress
This Fortran module is work in progress and only the documented
functionality is currently available. The final implementation should
cover the entire range of functionality available in the C and
Python library interfaces.
----------
Creating or deleting a LAMMPS object
************************************
With the Fortran interface the creation of a :cpp:class:`LAMMPS
<LAMMPS_NS::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
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:
.. code-block:: fortran
PROGRAM testlib
USE LIBLAMMPS ! include the LAMMPS library interface
TYPE(lammps) :: lmp ! derived type to hold LAMMPS instance
CHARACTER(len=*), DIMENSION(*), PARAMETER :: args = &
[ CHARACTER(len=12) :: 'liblammps', '-log', 'none' ]
! create a LAMMPS instance (and initialize MPI)
lmp = lammps(args)
! get and print numerical version code
PRINT*, 'LAMMPS Version: ', lmp%version()
! delete LAMMPS instance (and shuts down MPI)
CALL lmp%close(.true.)
END PROGRAM testlib
--------------------
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
individual or multiple commands from strings or lists of strings. This
is done similar to how it is implemented in the `C-library
<pg_lib_execute>` 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
arguments so that they can be modified to be compatible with the
requirements of strings in C without affecting the original strings.
Those copies are automatically deleted after the functions return.
Below is a small demonstration of the uses of the different functions:
.. code-block:: fortran
PROGRAM testcmd
USE LIBLAMMPS
TYPE(lammps) :: lmp
CHARACTER(len=512) :: cmds
CHARACTER(len=40),ALLOCATABLE :: cmdlist(:)
CHARACTER(len=10) :: trimmed
INTEGER :: i
lmp = lammps()
CALL lmp%file('in.melt')
CALL lmp%command('variable zpos index 1.0')
! define 10 groups of 10 atoms each
ALLOCATE(cmdlist(10))
DO i=1,10
WRITE(trimmed,'(I10)') 10*i
WRITE(cmdlist(i),'(A,I1,A,I10,A,A)') &
'group g',i-1,' id ',10*(i-1)+1,':',ADJUSTL(trimmed)
END DO
CALL lmp%commands_list(cmdlist)
! run multiple commands from multi-line string
cmds = 'clear' // NEW_LINE('A') // &
'region box block 0 2 0 2 0 2' // NEW_LINE('A') // &
'create_box 1 box' // NEW_LINE('A') // &
'create_atoms 1 single 1.0 1.0 ${zpos}'
CALL lmp%commands_string(cmds)
CALL lmp%close()
END PROGRAM testcmd
---------------
The ``LIBLAMMPS`` module API
****************************
Below are the detailed descriptions of definitions and interfaces
of the contents of the ``LIBLAMMPS`` Fortran interface to LAMMPS.
.. f:type:: lammps
Derived type that is the general class of the Fortran interface.
It holds a reference to the :cpp:class:`LAMMPS <LAMMPS_NS::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: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`
or :cpp:func:`lammps_open_no_mpi`. If the LAMMPS library has been
compiled with MPI support, it will also initialize MPI, if it has
not already been initialized before.
The *args* argument with the list of command line parameters is
optional and so it the *comm* argument with the MPI communicator.
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 integer comm [optional]: MPI communicator
:r lammps: an instance of the :f:type:`lammps` derived type
.. f:subroutine:: close([finalize])
This method will close down the LAMMPS instance through calling
:cpp:func:`lammps_close`. If the *finalize* argument is present and
has a value of ``.true.``, then this subroutine also calls
:cpp:func:`lammps_mpi_finalize`.
:o logical finalize [optional]: shut down the MPI environment of the LAMMPS library if true.
.. f:function:: version()
This method returns the numeric LAMMPS version like :cpp:func:`lammps_version`
:r integer: LAMMPS version
--------
.. f:subroutine:: file(filename)
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
.. 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
.. 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
.. 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

33
doc/src/pg_lib_add.rst Normal file
View File

@ -0,0 +1,33 @@
Adding code to the Library interface
====================================
The functionality of the LAMMPS library interface has historically
always been motivated by the needs of its users and functions were
added or expanded as they were needed and used. Contributions to
the interface are always welcome. However with a refactoring of
the library interface and its documentation that started in 2020,
there are now a few requirements for inclusion of changes.
- New functions should be orthogonal to existing ones and not
implement functionality that can already be achieved with the
existing APIs.
- All changes and additions should be documented with
`Doxygen <https://doxgygen.org>`_ style comments and references
to those functions added to the corresponding files in the
``doc/src`` folder.
- If possible, new unit tests to test those new features should
be added.
- The new feature should also be implemented and documented for
the Python and Fortran modules.
- All additions should work and be compatible with ``-DLAMMPS_BIGBIG``,
``-DLAMMPS_SMALLBIG``, ``-DLAMMPS_SMALLSMALL`` and compiling
with and without MPI support.
- The ``library.h`` file should be kept compatible to C code at
a level similar to C89. Its interfaces may not reference any
custom data types (e.g. ``bigint``, ``tagint``, and so on) only
known inside of LAMMPS.
- only C style comments, not C++ style
Please note, that these are *not* *strict* requirements, but the
LAMMPS developers appreciate if they are followed closely and will
assist with implementing what is missing.

67
doc/src/pg_lib_config.rst Normal file
View File

@ -0,0 +1,67 @@
Retrieving LAMMPS configuration information
===========================================
The following library functions can be used to query the
LAMMPS library about compile time settings and included
packages and styles.
-----------------------
.. doxygenfunction:: lammps_config_has_mpi_support
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_gzip_support
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_png_support
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_jpeg_support
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_ffmpeg_support
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_exceptions
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_has_package
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_package_count
:project: progguide
-----------------------
.. doxygenfunction:: lammps_config_package_name
:project: progguide
-----------------------
.. doxygenfunction:: lammps_has_style
:project: progguide
-----------------------
.. doxygenfunction:: lammps_style_count
:project: progguide
-----------------------
.. doxygenfunction:: lammps_style_name
:project: progguide

104
doc/src/pg_lib_create.rst Normal file
View File

@ -0,0 +1,104 @@
Creating or deleting a LAMMPS object
====================================
The :cpp:func:`lammps_open` and :cpp:func:`lammps_open_no_mpi`
functions are used to create and initialize a
:cpp:func:`LAMMPS` instance. The calling program has to
provide a handle where a reference to this instance can be stored and
which has to be used in all subsequent function calls until that
instance is destroyed by calling :cpp:func:`lammps_close`.
Here is a simple example demonstrating its use:
.. code-block:: C
#include "library.h"
#include <stdio.h>
int main(int argc, char **argv)
{
void *handle;
int version;
const char *lmpargv[] = { "liblammps", "-log", "none"};
int lmpargc = sizeof(lmpargv)/sizeof(const char *);
/* create LAMMPS instance */
handle = lammps_open_no_mpi(lmpargc, lmpargv, NULL);
if (handle == NULL) {
printf("LAMMPS initialization failed");
lammps_mpi_finalize();
return 1;
}
/* get and print numerical version code */
version = lammps_version(handle);
printf("LAMMPS Version: %d\n",version);
/* delete LAMMPS instance and shut down MPI */
lammps_close(handle);
lammps_mpi_finalize();
return 0;
}
The LAMMPS library will be using the MPI library it was compiled with
and will either run on all processors in the ``MPI_COMM_WORLD``
communicator or on the set of processors in the communicator given in
the ``comm`` argument of :cpp:func:`lammps_open`. This means
the calling code can run LAMMPS on all or a subset of processors. For
example, a wrapper code might decide to alternate between LAMMPS and
another code, allowing them both to run on all the processors. Or it
might allocate part of the processors to LAMMPS and the rest to the
other code by creating a custom communicator with ``MPI_Comm_split()``
and running both codes concurrently before syncing them up periodically.
Or it might instantiate multiple instances of LAMMPS to perform
different calculations and either alternate between them, run them
concurrently on split communicators, or run them one after the other.
The :cpp:func:`lammps_open` function may be called multiple
times for this latter purpose.
The :cpp:func:`lammps_close` function is used to shut down
the :cpp:class:`LAMMPS <LAMMPS_NS::LAMMPS>` class pointed to by the handle
passed as an argument and free all its memory. This has to be called for
every instance created with any of the :cpp:func:`lammps_open` functions. It will, however, **not** call
``MPI_Finalize()``, since that may only be called once. See
:cpp:func:`lammps_mpi_finalize` for an alternative to calling
``MPI_Finalize()`` explicitly in the calling program.
The :cpp:func:`lammps_free` function is a clean-up
function to free memory that the library allocated previously
via other function calls. See below for notes in the descriptions
of the individual commands where such memory buffers were allocated.
-----------------------
.. doxygenfunction:: lammps_open
:project: progguide
-----------------------
.. doxygenfunction:: lammps_open_no_mpi
:project: progguide
-----------------------
.. doxygenfunction:: lammps_open_fortran
:project: progguide
-----------------------
.. doxygenfunction:: lammps_close
:project: progguide
-----------------------
.. doxygenfunction:: lammps_mpi_init
:project: progguide
-----------------------
.. doxygenfunction:: lammps_mpi_finalize
:project: progguide
-----------------------
.. doxygenfunction:: lammps_free
:project: progguide

View File

@ -0,0 +1,69 @@
Executing LAMMPS commands
=========================
Once a LAMMPS instance is created, there are multiple ways to "drive" a
simulation. In most cases it is easiest to process single or multiple
LAMMPS commands like in an input file. This can be done through reading
a file or passing single commands or lists of commands or blocks of
commands with the following functions.
Via these functions, the calling code can have the LAMMPS instance act
on a series of :doc:`input file commands <Commands_all>` that are either
read from a file or passed as strings. This for, for example, allows to
setup a problem from a template file and then run it in stages while
performing other operations in between or concurrently. The caller can
interleave the LAMMPS function calls with operations it performs, calls
to extract information from or set information within LAMMPS, or calls
to another code's library.
Also equivalent to regular :doc:`input script parsing <Commands_parse>`
is the handling of comments and expansion of variables with ``${name}``
or ``$(expression)`` syntax before the commands are parsed and
executed. Below is a short example using some of these functions.
.. code-block:: C
#include "library.h"
#include <mpi.h>
#include <stdio.h>
int main(int argc, char **argv)
{
void *handle;
int i;
MPI_Init(&argc, &argv);
handle = lammps_open(0, NULL, MPI_COMM_WORLD, NULL);
lammps_file(handle,"in.sysinit");
lammps_command(handle,"run 1000 post no");
for (i=0; i < 100; ++i) {
lammps_commands_string(handle,"run 100 pre no post no\n"
"print 'PE = $(pe)'\n"
"print 'KE = $(ke)'\n");
}
lammps_close(handle);
MPI_Finalize();
return 0;
}
-----------------------
.. doxygenfunction:: lammps_file
:project: progguide
-----------------------
.. doxygenfunction:: lammps_command
:project: progguide
-----------------------
.. doxygenfunction:: lammps_commands_list
:project: progguide
-----------------------
.. doxygenfunction:: lammps_commands_string
:project: progguide

View File

@ -0,0 +1,30 @@
Accessing LAMMPS Neighbor lists
===============================
The following functions allow to access neighbor lists
generated by LAMMPS or query their properties.
-----------------------
.. doxygenfunction:: lammps_find_compute_neighlist
:project: progguide
-----------------------
.. doxygenfunction:: lammps_find_fix_neighlist
:project: progguide
-----------------------
.. doxygenfunction:: lammps_find_pair_neighlist
:project: progguide
-----------------------
.. doxygenfunction:: lammps_neighlist_num_elements
:project: progguide
-----------------------
.. doxygenfunction:: lammps_neighlist_element_neighbors
:project: progguide

View File

@ -0,0 +1,31 @@
Retrieving or setting properties of LAMMPS objects
==================================================
This section documents accessing or modifying data from objects like
computes, fixes, or variables in LAMMPS.
-----------------------
.. doxygenfunction:: lammps_extract_compute
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_fix
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_variable
:project: progguide
-----------------------
.. doxygenfunction:: lammps_set_variable
:project: progguide
-----------------------
.. doxygenenum:: _LMP_STYLE_CONST
.. doxygenenum:: _LMP_TYPE_CONST

View File

@ -0,0 +1,62 @@
Retrieving or setting LAMMPS system properties
==============================================
The library interface allows to extract different kinds of information
about the active simulation instance and also to modify some of them.
This allows to combine MD simulation steps with other processing and
simulation methods computed in the calling code or 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 to be able to access it, and you need to know the correct dimensions
and lengths. When accessing per-atom data, please note that this data
is the per-processor **local** data and indexed accordingly. These arrays
can change sizes and order at every neighbor list rebuild and atom sort
event as atoms are migrating between sub-domains.
-----------------------
.. doxygenfunction:: lammps_version
:project: progguide
-----------------------
.. doxygenfunction:: lammps_get_natoms
:project: progguide
-----------------------
.. doxygenfunction:: lammps_get_thermo
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_box
:project: progguide
-----------------------
.. doxygenfunction:: lammps_reset_box
:project: progguide
-------------------
.. doxygenfunction:: lammps_extract_setting
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_global
:project: progguide
-----------------------
.. doxygenfunction:: lammps_extract_atom
:project: progguide
-----------------------
.. doxygenfunction:: lammps_create_atoms(void *handle, int n, int *id, int *type, double *x, double *v, int *image, int bexpand)
:project: progguide

View File

@ -0,0 +1,29 @@
Library functions for scatter/gather operations
================================================
.. TODO add description
-----------------------
.. doxygenfunction:: lammps_gather_atoms
:project: progguide
-----------------------
.. doxygenfunction:: lammps_gather_atoms_concat
:project: progguide
-----------------------
.. doxygenfunction:: lammps_gather_atoms_subset
:project: progguide
-----------------------
.. doxygenfunction:: lammps_scatter_atoms
:project: progguide
-----------------------
.. doxygenfunction:: lammps_scatter_atoms_subset
:project: progguide

View File

@ -0,0 +1,30 @@
Library interface utility functions
===================================
To simplify some of the tasks, the library interface contains
some utility functions that are not directly calling LAMMPS.
-----------------------
.. doxygenfunction:: lammps_encode_image_flags
:project: progguide
-----------------------
.. doxygenfunction:: lammps_decode_image_flags(int image, int *flags)
:project: progguide
-----------------------
.. doxygenfunction:: lammps_set_fix_external_callback(void *, char *, FixExternalFnPtr, void*)
:project: progguide
-----------------------
.. doxygenfunction:: lammps_has_error
:project: progguide
-----------------------
.. doxygenfunction:: lammps_get_last_error_message
:project: progguide

View File

@ -91,15 +91,15 @@ and consequently the function :cpp:func:`lammps_open` may not be used.
.. toctree::
:maxdepth: 1
.. pg_lib_create
.. pg_lib_execute
.. pg_lib_properties
.. pg_lib_objects
.. pg_lib_scatter
.. pg_lib_neighbor
.. pg_lib_config
.. pg_lib_utility
.. pg_lib_add
pg_lib_create
pg_lib_execute
pg_lib_properties
pg_lib_objects
pg_lib_scatter
pg_lib_neighbor
pg_lib_config
pg_lib_utility
pg_lib_add
--------------------
@ -111,7 +111,7 @@ LAMMPS Python APIs
The LAMMPS Python module enables calling the LAMMPS C library API from
Python by dynamically loading functions in the LAMMPS shared library through
the `Python ctypes module <https://docs.python.org/3/library/ctypes.html>`_.
Because of the dynamics loading, it is **required** that LAMMPS is compiled
Because of the dynamic loading, it is **required** that LAMMPS is compiled
in :ref:`"shared" mode <exe>`. The Python interface is object oriented, but
otherwise trying to be very similar to the C library API. Three different
Python classes to run LAMMPS are available and they build on each other.
@ -153,6 +153,6 @@ The following links provide some examples and references to the C++ API.
.. toctree::
:maxdepth: 1
.. pg_cplusplus
pg_cplusplus

View File

@ -26,4 +26,163 @@ There are multiple Python interface classes in the :py:mod:`lammps` module:
.. _mpi4py_url: https://mpi4py.readthedocs.io
----------
Creating or deleting a LAMMPS object
************************************
With the Python interface the creation of a :cpp:class:`LAMMPS
<LAMMPS_NS::LAMMPS>` instance is included in the constructor for the
:py:func:`lammps <lammps.lammps>` class. Internally it will call either
:cpp:func:`lammps_open` or :cpp:func:`lammps_open_no_mpi` from the C
library API to create the class instance.
All arguments are optional. The *name* argument is to allow loading a
LAMMPS shared library that is named ``liblammps_machine.so`` instead of
the default name of ``liblammps.so``. In most cases the latter will be
installed or used. The *ptr* argument is for use of the
:py:mod:`lammps` module from inside a LAMMPS instance, e.g. with the
:doc:`python <python>` command, where a pointer to the already existing
:cpp:class:`LAMMPS <LAMMPS_NS::LAMMPS>` class instance can be passed
to the Python class and used instead of creating a new instance. The
*comm* argument may be used in combination with the `mpi4py <mpi4py_url_>`_
module to pass an MPI communicator to LAMMPS and thus it is possible
to run the Python module like the library interface on a subset of the
MPI ranks after splitting the communicator. Here is a simple example:
.. code-block:: python
from lammps import lammps
# NOTE: argv[0] is set by the Python module
args = ["-log", "none"]
# create LAMMPS instance
lmp = lammps(cmdargs=args)
# get and print numerical version code
print("LAMMPS Version: ", lmp.version())
# explicitly close and delete LAMMPS instance (optional)
lmp.close()
Same as with the :doc:`C library API <pg_lib_create>` this will use the
``MPI_COMM_WORLD`` communicator for the MPI library that LAMMPS was
compiled with. The :py:func:`lmp.close() <lammps.lammps.close>` call is
optional since the LAMMPS class instance will also be deleted
automatically during the :py:class:`lammps <lammps.lammps>` class
destructor.
Executing LAMMPS commands
*************************
Once an instance of the :py:class:`lammps <lammps.lammps>` class is
created, there are multiple ways to "feed" it commands. In a way that is
not very different from running a LAMMPS input script, except that
Python has many more facilities for structured programming than the
LAMMPS input script syntax. Furthermore it is possible to "compute"
what the next LAMMPS command should be. Same as in the equivalent `C
library functions <pg_lib_execute>`, commands can be read from a file, a
single string, a list of strings and a block of commands in a single
multi-line string. They are processed under the same boundary conditions
as the C library counterparts. The example below demonstrates the use
of :py:func:`lammps.file`, :py:func:`lammps.command`,
:py:func:`lammps.commands_list`, and :py:func:`lammps.commands_string`:
.. code-block:: python
from lammps import lammps
lmp = lammps()
# read commands from file 'in.melt'
lmp.file('in.melt')
# issue a single command
lmp.command('variable zpos index 1.0')
# create 10 groups with 10 atoms each
cmds = ["group g{} id {}:{}".format(i,10*i+1,10*(i+1)) for i in range(10)]
lmp.commands_list(cmds)
# run commands from a multi-line string
block = """
clear
region box block 0 2 0 2 0 2
create_box 1 box
create_atoms 1 single 1.0 1.0 ${zpos}
"""
lmp.commands_string(block)
----------
The ``lammps`` class API
************************
The :py:class:`lammps <lammps.lammps>` class is the core of the LAMMPS
Python interfaces. It is a wrapper around the :doc:`LAMMPS C library
API <pg_library>` using the `Python ctypes module
<https://docs.python.org/3/library/ctypes.html>`_ and a shared library
compiled from the LAMMPS sources code. The individual methods in this
class try to closely follow the corresponding C functions. The handle
argument that needs to be passed to the C functions is stored internally
in the class and automatically added when calling the C library
functions. Below is a detailed documentation of the API.
.. autoclass:: lammps.lammps
:members:
----------
The ``PyLammps`` class API
**************************
.. autoclass:: lammps.PyLammps
:members:
----------
The ``IPyLammps`` class API
***************************
.. autoclass:: lammps.IPyLammps
:members:
----------
Additional components of the ``lammps`` module
**********************************************
The :py:mod:`lammps` module additionally contains several constants
and the :py:class:`NeighList <lammps.NeighList>` class:
.. _py_data_constants:
.. py:data:: LAMMPS_INT, LAMMPS_DOUBLE, LAMMPS_BIGINT, LAMMPS_TAGINT, LAMMPS_STRING
:type: int
Constants in the :py:mod:`lammps` module to indicate how to
cast data when the C library function returns a void pointer.
Used in :py:func:`lammps.extract_global`.
.. _py_style_constants:
.. py:data:: LMP_STYLE_GLOBAL, LMP_STYLE_ATOM, LMP_STYLE_LOCAL
:type: int
Constants in the :py:mod:`lammps` module to select what style of data
to request from computes or fixes. See :cpp:enum:`_LMP_STYLE_CONST`
for the equivalent constants in the C library interface. Used in
:py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`.
.. _py_type_constants:
.. py:data:: LMP_TYPE_SCALAR, LMP_TYLE_VECTOR, LMP_TYPE_ARRAY, LMP_SIZE_VECTOR, LMP_SIZE_ROWS, LMP_SIZE_COLS
:type: int
Constants in the :py:mod:`lammps` module to select what type of data
to request from computes or fixes. See :cpp:enum:`_LMP_TYPE_CONST`
for the equivalent constants in the C library interface. Used in
:py:func:`lammps.extract_compute` and :py:func:`lammps.extract_fix`.
.. _py_var_constants:
.. py:data:: LMP_VAR_EQUAL, LMP_VAR_ATOM
:type: int
Constants in the :py:mod:`lammps` module to select what style of
variable to query when calling :py:func:`lammps.extract_variable`.
.. autoclass:: lammps.NeighList
:members:
:no-undoc-members:

View File

@ -115,6 +115,7 @@ Archlinux
arcsin
arg
args
argv
arrhenius
Arun
arXiv
@ -138,6 +139,8 @@ atc
AtC
ATC
athermal
atime
atimestep
athomps
atm
atomeye
@ -207,7 +210,6 @@ bcolor
bdiam
bdw
Beckman
behaviour
Belak
Bellott
benchmarking
@ -267,6 +269,7 @@ bodystyle
Bogaerts
Bogusz
Bohrs
boltz
Boltzman
BondAngle
BondBond
@ -285,6 +288,14 @@ Botu
Bouguet
Bourne
boxcolor
boxlo
boxhi
boxxlo
boxxhi
boxylo
boxyhi
boxzlo
boxzhi
bp
bpclermont
bpls
@ -411,6 +422,7 @@ cmap
Cmax
cmd
cmdlist
cmds
Cmin
cmm
CMM
@ -675,6 +687,8 @@ Donadio
dotc
Doty
doxygen
doxygenclass
doxygenfunction
downarrow
Doye
dpd
@ -983,6 +997,7 @@ Fock
Fogarty
Foiles
fopenmp
forceclear
forestgreen
formatarg
formulae
@ -1007,6 +1022,7 @@ fs
fsh
fstyle
fsw
ftm
ftol
fugacity
Fumi
@ -1177,6 +1193,7 @@ hexorder
Heyes
HfO
hgrid
hhmrr
Hibbs
Higdon
Hijazi
@ -1186,6 +1203,7 @@ histogrammed
histogramming
hma
hmaktulga
hplanck
hoc
Hochbruck
Hofling
@ -1228,6 +1246,7 @@ hyperspherical
hysteretic
hz
Ibanez
iatom
ibar
ibm
icc
@ -1270,6 +1289,7 @@ indices
inertiax
inertiay
inertiaz
infile
infty
inhomogeneities
inhomogeneous
@ -1310,6 +1330,7 @@ ipp
Ippolito
IPv
IPython
ipython
Isele
isenthalpic
ish
@ -1458,6 +1479,7 @@ Kloza
kmax
Kmax
KMP
kmu
Knizhnik
knl
Kofke
@ -1944,6 +1966,7 @@ muz
mv
mV
Mvapich
mvh
mvv
MxN
myCompute
@ -1956,11 +1979,13 @@ na
nabla
Nagaosa
Nakano
nall
namespace
namespaces
nan
NaN
Nandor
nangles
Nangletype
nangletypes
Nangletypes
@ -1989,6 +2014,7 @@ Nbin
Nbins
nbody
Nbody
nbonds
nbondtype
Nbondtype
nbondtypes
@ -2001,9 +2027,11 @@ Nc
nchunk
Nchunk
ncoeff
ncol
ncorr
ncount
nd
ndihedrals
Ndihedraltype
Ndirango
ndof
@ -2050,6 +2078,7 @@ NiAlH
Nicklas
Niklasson
Nikolskiy
nimpropers
Nimpropertype
Ninteger
Nissila
@ -2058,6 +2087,7 @@ nitride
nitrides
niu
Nk
nktv
nl
nlen
Nlines
@ -2124,6 +2154,7 @@ Nrepeat
nreset
Nrho
Nroff
nrow
nrun
Ns
Nsample
@ -2142,6 +2173,7 @@ Nt
Ntable
ntheta
nthreads
ntimestep
Ntptask
Ntriples
Ntype
@ -2371,6 +2403,7 @@ polydisperse
polydispersity
polyelectrolyte
polyhedra
polymorphism
popen
Popov
popstore
@ -2404,6 +2437,7 @@ proc
Proc
procs
Prony
progguide
ps
Ps
pscreen
@ -2450,7 +2484,9 @@ qbmsst
qcore
qdist
qE
qe
qeff
qelectron
qeq
QeQ
QEq
@ -2468,6 +2504,8 @@ qmol
qoffload
qopenmp
qoverride
qqr
qqrd
qtb
quadratically
quadrupolar
@ -2523,6 +2561,7 @@ rebo
recursing
Ree
refactored
refactoring
reflectionstyle
regoin
Reinders
@ -2608,6 +2647,7 @@ Rkouter
RkouterN
rmask
Rmask
rmass
rmax
Rmax
rmdir
@ -2742,6 +2782,7 @@ shlib
SHM
shm
shockvel
shrinkexceed
Shugaev
si
SiC
@ -2879,6 +2920,7 @@ strstr
Stukowski
Su
subbox
Subclassed
subcutoff
subcycle
subcycling
@ -3249,6 +3291,7 @@ vv
vx
Vx
vxcm
vxmu
vy
Vy
vycm
@ -3325,6 +3368,7 @@ Xmax
xmgrace
xMIC
xmin
xml
xmovie
Xmovie
xmu
@ -3339,6 +3383,7 @@ xsu
xtc
xu
Xu
xxt
xxxxx
xy
xyz

11
fortran/README Normal file
View File

@ -0,0 +1,11 @@
This directory contains Fortran code which interface LAMMPS as a library
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
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.

281
fortran/lammps.f90 Normal file
View File

@ -0,0 +1,281 @@
! -------------------------------------------------------------------------
! LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
! http://lammps.sandia.gov, Sandia National Laboratories
! Steve Plimpton, sjplimp@sandia.gov
!
! Copyright (2003) Sandia Corporation. Under the terms of Contract
! DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
! certain rights in this software. This software is distributed under
! the GNU General Public License.
!
! See the README file in the top-level LAMMPS directory.
! -------------------------------------------------------------------------
!
! Fortran interface to the LAMMPS library implemented as a Fortran 2003
! style module that wraps the C-style library interface in library.cpp
! and library.h using the ISO_C_BINDING module of the Fortran compiler.
!
! Based on the LAMMPS Fortran 2003 module contributed by:
! Karl D. Hammond <karlh@ugcs.caltech.edu>
! University of Tennessee, Knoxville (USA), 2012
!
! 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_".
!
MODULE LIBLAMMPS
USE, INTRINSIC :: ISO_C_BINDING, ONLY: c_ptr, c_null_ptr, c_loc, &
c_int, c_char, c_null_char, c_double
IMPLICIT NONE
PRIVATE
PUBLIC :: lammps
TYPE lammps
TYPE(c_ptr) :: handle
CONTAINS
PROCEDURE :: close => lmp_close
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
END TYPE lammps
INTERFACE lammps
MODULE PROCEDURE lmp_open
END INTERFACE lammps
! interface definitions for calling functions in library.cpp
INTERFACE
FUNCTION lammps_open(argc,argv,comm,handle) &
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), INTENT(out) :: handle
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), INTENT(out) :: 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_mpi_init(handle) BIND(C, name='lammps_mpi_init')
IMPORT :: c_ptr
TYPE(c_ptr), VALUE :: handle
END SUBROUTINE lammps_mpi_init
SUBROUTINE lammps_mpi_finalize(handle) &
BIND(C, name='lammps_mpi_finalize')
IMPORT :: c_ptr
TYPE(c_ptr), VALUE :: handle
END SUBROUTINE lammps_mpi_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_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_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_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_free(ptr) BIND(C, name='lammps_free')
IMPORT :: c_ptr
TYPE(c_ptr), VALUE :: ptr
END SUBROUTINE lammps_free
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_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
! Fortran wrappers and helper functions.
! 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(:)
TYPE(c_ptr), ALLOCATABLE :: argv(:)
TYPE(c_ptr) :: dummy=c_null_ptr
INTEGER :: i,argc
IF (PRESENT(args)) THEN
! convert argument list to c style
argc = SIZE(args)
ALLOCATE(argv(argc))
DO i=1,argc
argv(i) = f2c_string(args(i))
END DO
ELSE
argc = 1
ALLOCATE(argv(1))
argv(1) = f2c_string("liblammps")
ENDIF
IF (PRESENT(comm)) THEN
lmp_open%handle = lammps_open(argc,argv,comm,dummy)
ELSE
lmp_open%handle = lammps_open_no_mpi(argc,argv,dummy)
END IF
! Clean up allocated memory
DO i=1,argc
CALL lammps_free(argv(i))
END DO
DEALLOCATE(argv)
END FUNCTION lmp_open
! 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
CALL lammps_close(self%handle)
IF (PRESENT(finalize)) THEN
IF (finalize) THEN
CALL lammps_mpi_finalize(self%handle)
END IF
END IF
END SUBROUTINE lmp_close
INTEGER FUNCTION lmp_version(self)
IMPLICIT NONE
CLASS(lammps) :: self
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
SUBROUTINE lmp_file(self,filename)
IMPLICIT NONE
CLASS(lammps) :: self
CHARACTER(len=*) :: filename
TYPE(c_ptr) :: str
str = f2c_string(filename)
CALL lammps_file(self%handle,str)
CALL lammps_free(str)
END SUBROUTINE lmp_file
! equivalent function to lammps_command()
SUBROUTINE lmp_command(self,cmd)
IMPLICIT NONE
CLASS(lammps) :: self
CHARACTER(len=*) :: cmd
TYPE(c_ptr) :: str
str = f2c_string(cmd)
CALL lammps_command(self%handle,str)
CALL lammps_free(str)
END SUBROUTINE lmp_command
! equivalent function to lammps_commands_list()
SUBROUTINE lmp_commands_list(self,cmds)
IMPLICIT NONE
CLASS(lammps) :: self
CHARACTER(len=*), INTENT(in), OPTIONAL :: cmds(:)
TYPE(c_ptr), ALLOCATABLE :: cmdv(:)
INTEGER :: i,ncmd
! convert command list to c style
ncmd = SIZE(cmds)
ALLOCATE(cmdv(ncmd))
DO i=1,ncmd
cmdv(i) = f2c_string(cmds(i))
END DO
CALL lammps_commands_list(self%handle,ncmd,cmdv)
! Clean up allocated memory
DO i=1,ncmd
CALL lammps_free(cmdv(i))
END DO
DEALLOCATE(cmdv)
END SUBROUTINE lmp_commands_list
! equivalent function to lammps_commands_string()
SUBROUTINE lmp_commands_string(self,str)
IMPLICIT NONE
CLASS(lammps) :: self
CHARACTER(len=*) :: str
TYPE(c_ptr) :: tmp
tmp = f2c_string(str)
CALL lammps_commands_string(self%handle,tmp)
CALL lammps_free(tmp)
END SUBROUTINE lmp_commands_string
! ----------------------------------------------------------------------
! 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(:)
TYPE(c_ptr) :: ptr
INTEGER :: i, n
n = LEN_TRIM(f_string)
ALLOCATE(c_string(n+1))
DO i=1,n
c_string(i) = f_string(i:i)
END DO
c_string(n+1) = c_null_char
ptr = c_loc(c_string(1))
END FUNCTION f2c_string
END MODULE LIBLAMMPS

View File

@ -85,8 +85,8 @@ PACKUSERUC = $(call uppercase,$(PACKUSER))
YESDIR = $(call uppercase,$(@:yes-%=%))
NODIR = $(call uppercase,$(@:no-%=%))
LIBDIR = $($(@:lib-%=%))
LIBUSERDIR = $($(@:lib-user-%=%))
LIBDIR = $(@:lib-%=%)
LIBUSERDIR = $(@:lib-user-%=%)
# List of all targets

View File

@ -169,7 +169,7 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) :
} else if (strcmp(arg[iarg],"reset_mol_ids") == 0) {
if (iarg+2 > narg) error->all(FLERR,"Illegal fix bond/react command: "
"'reset_mol_ids' keyword has too few arguments");
if (strcmp(arg[iarg+1],"yes") == 0) ; // default
if (strcmp(arg[iarg+1],"yes") == 0) reset_mol_ids_flag = 1; // default
if (strcmp(arg[iarg+1],"no") == 0) reset_mol_ids_flag = 0;
iarg += 2;
} else if (strcmp(arg[iarg],"react") == 0) {

View File

@ -752,29 +752,29 @@ recognized, the function returns -1. Please also see :cpp:func:`lammps_extract_
* \param keyword string with the name of the thermo keyword
* \return value of the queried setting or -1 if unknown */
int lammps_extract_setting(void * handle, char *name)
int lammps_extract_setting(void * handle, char *keyword)
{
LAMMPS *lmp = (LAMMPS *) handle;
// This can be customized by adding keywords and documenting them in the section above.
if (strcmp(name,"bigint") == 0) return sizeof(bigint);
if (strcmp(name,"tagint") == 0) return sizeof(tagint);
if (strcmp(name,"imageint") == 0) return sizeof(imageint);
if (strcmp(keyword,"bigint") == 0) return sizeof(bigint);
if (strcmp(keyword,"tagint") == 0) return sizeof(tagint);
if (strcmp(keyword,"imageint") == 0) return sizeof(imageint);
if (strcmp(name,"dimension") == 0) return lmp->domain->dimension;
if (strcmp(name,"box_exist") == 0) return lmp->domain->box_exist;
if (strcmp(name,"triclinic") == 0) return lmp->domain->triclinic;
if (strcmp(keyword,"dimension") == 0) return lmp->domain->dimension;
if (strcmp(keyword,"box_exist") == 0) return lmp->domain->box_exist;
if (strcmp(keyword,"triclinic") == 0) return lmp->domain->triclinic;
if (strcmp(name,"nlocal") == 0) return lmp->atom->nlocal;
if (strcmp(name,"nghost") == 0) return lmp->atom->nghost;
if (strcmp(name,"nall") == 0) return lmp->atom->nlocal+lmp->atom->nghost;
if (strcmp(name,"nmax") == 0) return lmp->atom->nmax;
if (strcmp(name,"ntypes") == 0) return lmp->atom->ntypes;
if (strcmp(keyword,"nlocal") == 0) return lmp->atom->nlocal;
if (strcmp(keyword,"nghost") == 0) return lmp->atom->nghost;
if (strcmp(keyword,"nall") == 0) return lmp->atom->nlocal+lmp->atom->nghost;
if (strcmp(keyword,"nmax") == 0) return lmp->atom->nmax;
if (strcmp(keyword,"ntypes") == 0) return lmp->atom->ntypes;
if (strcmp(name,"molecule_flag") == 0) return lmp->atom->molecule_flag;
if (strcmp(name,"q_flag") == 0) return lmp->atom->q_flag;
if (strcmp(name,"mu_flag") == 0) return lmp->atom->mu_flag;
if (strcmp(name,"rmass_flag") == 0) return lmp->atom->rmass_flag;
if (strcmp(keyword,"molecule_flag") == 0) return lmp->atom->molecule_flag;
if (strcmp(keyword,"q_flag") == 0) return lmp->atom->q_flag;
if (strcmp(keyword,"mu_flag") == 0) return lmp->atom->mu_flag;
if (strcmp(keyword,"rmass_flag") == 0) return lmp->atom->rmass_flag;
return -1;
}

View File

@ -250,17 +250,23 @@ The typecast prevents compiler warnings about possible truncations.
// functions and avoid compiler warnings about variable tracking.
// Disable for broken -D_FORTIFY_SOURCE feature.
#if defined(_FORTIFY_SOURCE) && (_FORTIFY_SOURCE > 0)
#define _noopt
#elif defined(__clang__)
#if defined(__clang__)
# define _noopt __attribute__((optnone))
#elif defined(__INTEL_COMPILER)
# define _noopt
#elif defined(__GNUC__)
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9))
# define _noopt __attribute__((optimize("O0","no-var-tracking-assignments")))
# if defined(_FORTIFY_SOURCE) && (_FORTIFY_SOURCE > 0)
# define _noopt __attribute__((optimize("no-var-tracking-assignments")))
# else
# define _noopt __attribute__((optimize("O0","no-var-tracking-assignments")))
# endif
# else
# define _noopt __attribute__((optimize("O0")))
# if defined(_FORTIFY_SOURCE) && (_FORTIFY_SOURCE > 0)
# define _noopt
# else
# define _noopt __attribute__((optimize("O0")))
# endif
# endif
#else
# define _noopt

View File

@ -29,6 +29,19 @@
using namespace LAMMPS_NS;
/** Class for reading and parsing LAMMPS potential files
*
* The value of the class member variable *ignore_comments* controls
* whether any text following the pound sign (#) should be ignored (true)
* or not (false). Default: true, i.e. ignore.
*
* \param lmp Pointer to LAMMPS instance
* \param filename Name of file to be read
* \param potential_name Name of potential style for error messages
* \param auto_convert Bitmask of supported unit conversions
*
* \sa TextFileReader */
PotentialFileReader::PotentialFileReader(LAMMPS *lmp,
const std::string &filename,
const std::string &potential_name,
@ -53,14 +66,21 @@ PotentialFileReader::PotentialFileReader(LAMMPS *lmp,
}
}
/** Closes the file */
PotentialFileReader::~PotentialFileReader() {
delete reader;
}
/** Set comment (= text after '#') handling preference for the file to be read
*
* \param value Comment text is ignored if true, or not if false */
void PotentialFileReader::ignore_comments(bool value) {
reader->ignore_comments = value;
}
/** Read a line but ignore its content */
void PotentialFileReader::skip_line() {
try {
reader->skip_line();
@ -69,6 +89,17 @@ void PotentialFileReader::skip_line() {
}
}
/** Read the next line(s) until *nparams* words have been read.
*
* This reads a line and counts the words in it, if the number
* is less than the requested number, it will read the next
* line, as well. Output will be a string with all read lines
* combined. The purpose is to somewhat replicate the reading
* behavior of formatted files in Fortran.
*
* \param nparams Number of words that must be read. Default: 0
* \return String with the concatenated text */
char *PotentialFileReader::next_line(int nparams) {
try {
return reader->next_line(nparams);
@ -78,6 +109,15 @@ char *PotentialFileReader::next_line(int nparams) {
return nullptr;
}
/** Read lines until *n* doubles have been read and stored in array *list*
*
* This reads lines from the file using the next_line() function,
* and splits them into floating-point numbers using the
* ValueTokenizer class and stores the number is the provided list.
*
* \param list Pointer to array with suitable storage for *n* doubles
* \param n Number of doubles to be read */
void PotentialFileReader::next_dvector(double * list, int n) {
try {
return reader->next_dvector(list, n);
@ -86,6 +126,16 @@ void PotentialFileReader::next_dvector(double * list, int n) {
}
}
/** Read text until *nparams* words are read and passed to a tokenizer object for custom parsing.
*
* This reads lines from the file using the next_line() function,
* and splits them into floating-point numbers using the
* ValueTokenizer class and stores the number is the provided list.
*
* \param nparams Number of words to be read
* \param separators String with list of separators.
* \return ValueTokenizer object for read in text */
ValueTokenizer PotentialFileReader::next_values(int nparams, const std::string & separators) {
try {
return reader->next_values(nparams, separators);
@ -95,6 +145,10 @@ ValueTokenizer PotentialFileReader::next_values(int nparams, const std::string &
return ValueTokenizer("");
}
/** Read next line and convert first word to a double
*
* \return Value of first word in line as double */
double PotentialFileReader::next_double() {
try {
char * line = reader->next_line(1);
@ -105,6 +159,10 @@ double PotentialFileReader::next_double() {
return 0.0;
}
/** Read next line and convert first word to an int
*
* \return Value of first word in line as int */
int PotentialFileReader::next_int() {
try {
char * line = reader->next_line(1);
@ -115,6 +173,10 @@ int PotentialFileReader::next_int() {
return 0;
}
/** Read next line and convert first word to a tagint
*
* \return Value of first word in line as tagint */
tagint PotentialFileReader::next_tagint() {
try {
char * line = reader->next_line(1);
@ -125,6 +187,10 @@ tagint PotentialFileReader::next_tagint() {
return 0;
}
/** Read next line and convert first word to a bigint
*
* \return Value of first word in line as bigint */
bigint PotentialFileReader::next_bigint() {
try {
char * line = reader->next_line(1);
@ -135,6 +201,10 @@ bigint PotentialFileReader::next_bigint() {
return 0;
}
/** Read next line and return first word
*
* \return First word of read in line */
std::string PotentialFileReader::next_string() {
try {
char * line = reader->next_line(1);
@ -145,6 +215,12 @@ std::string PotentialFileReader::next_string() {
return "";
}
/** Look up and open the potential file
*
* \param path Path of the potential file to open
* \return Pointer to TextFileReader object created
* \sa TextFileReader */
TextFileReader *PotentialFileReader::open_potential(const std::string &path) {
std::string filepath = utils::get_potential_file_path(path);

View File

@ -28,6 +28,17 @@
using namespace LAMMPS_NS;
/** Class for reading and parsing text files
*
* The value of the class member variable *ignore_comments* controls
* whether any text following the pound sign (#) should be ignored (true)
* or not (false). Default: true, i.e. ignore.
*
* \param filename Name of file to be read
* \param filetype Description of file type for error messages
*
* \sa PotentialFileReader */
TextFileReader::TextFileReader(const std::string &filename, const std::string &filetype)
: filename(filename), filetype(filetype), ignore_comments(true)
{
@ -38,10 +49,14 @@ TextFileReader::TextFileReader(const std::string &filename, const std::string &f
}
}
/** Closes the file */
TextFileReader::~TextFileReader() {
fclose(fp);
}
/** Read the next line and ignore it */
void TextFileReader::skip_line() {
char *ptr = fgets(line, MAXLINE, fp);
if (ptr == nullptr) {
@ -50,6 +65,20 @@ void TextFileReader::skip_line() {
}
}
/** Read the next line(s) until *nparams* words have been read.
*
* This reads a line and counts the words in it, if the number
* is less than the requested number, it will read the next
* line, as well. Output will be a string with all read lines
* combined. The purpose is to somewhat replicate the reading
* behavior of formatted files in Fortran.
*
* If the *ignore_comments* class member has the value *true*,
* then any text read in is truncated at the first '#' character.
*
* \param nparams Number of words that must be read. Default: 0
* \return String with the concatenated text */
char *TextFileReader::next_line(int nparams) {
// concatenate lines until have nparams words
int n = 0;
@ -82,7 +111,6 @@ char *TextFileReader::next_line(int nparams) {
return nullptr;
}
// strip comment
if (ignore_comments && (ptr = strchr(line, '#'))) *ptr = '\0';
@ -97,6 +125,15 @@ char *TextFileReader::next_line(int nparams) {
return line;
}
/** Read lines until *n* doubles have been read and stored in array *list*
*
* This reads lines from the file using the next_line() function,
* and splits them into floating-point numbers using the
* ValueTokenizer class and stores the number is the provided list.
*
* \param list Pointer to array with suitable storage for *n* doubles
* \param n Number of doubles to be read */
void TextFileReader::next_dvector(double * list, int n) {
int i = 0;
while (i < n) {
@ -116,6 +153,16 @@ void TextFileReader::next_dvector(double * list, int n) {
}
}
/** Read text until *nparams* words are read and passed to a tokenizer object for custom parsing.
*
* This reads lines from the file using the next_line() function,
* and splits them into floating-point numbers using the
* ValueTokenizer class and stores the number is the provided list.
*
* \param nparams Number of words to be read
* \param separators String with list of separators.
* \return ValueTokenizer object for read in text */
ValueTokenizer TextFileReader::next_values(int nparams, const std::string & separators) {
return ValueTokenizer(next_line(nparams), separators);
}

View File

@ -33,7 +33,7 @@ namespace LAMMPS_NS
FILE *fp;
public:
bool ignore_comments;
bool ignore_comments; //!< Controls whether comments are ignored
TextFileReader(const std::string &filename, const std::string &filetype);
~TextFileReader();

View File

@ -30,6 +30,16 @@ TokenizerException::TokenizerException(const std::string & msg, const std::strin
}
}
/** Class for splitting text into words
*
* This tokenizer will break down a string into sub-strings (i.e words)
* separated by the given separator characters.
*
* \param str string to be processed
* \param separators string with separator characters (default: " \t\r\n\f")
*
* \sa ValueTokenizer TokenizerException */
Tokenizer::Tokenizer(const std::string & str, const std::string & separators) :
text(str), separators(separators), start(0), ntokens(std::string::npos)
{
@ -48,14 +58,23 @@ Tokenizer::Tokenizer(Tokenizer && rhs) :
reset();
}
/*! Re-position the tokenizer state to the first word,
* i.e. the first non-separator character */
void Tokenizer::reset() {
start = text.find_first_not_of(separators);
}
/*! Search the text to be processed for a sub-string.
*
* \param str string to be searched for
* \return true if string was found, false if not */
bool Tokenizer::contains(const std::string & str) const {
return text.find(str) != std::string::npos;
}
/*! Skip over a given number of tokens
*
* \param n number of tokens to skip over */
void Tokenizer::skip(int n) {
for(int i = 0; i < n; ++i) {
if(!has_next()) throw TokenizerException("No more tokens", "");
@ -70,10 +89,16 @@ void Tokenizer::skip(int n) {
}
}
/*! Indicate whether more tokens are available
*
* \return true if there are more tokens, false if not */
bool Tokenizer::has_next() const {
return start != std::string::npos;
}
/*! Retrieve next token.
*
* \return string with the next token */
std::string Tokenizer::next() {
if(!has_next()) throw TokenizerException("No more tokens", "");
@ -90,6 +115,9 @@ std::string Tokenizer::next() {
return token;
}
/*! Count number of tokens in text.
*
* \return number of counted tokens */
size_t Tokenizer::count() {
// lazy evaluation
if (ntokens == std::string::npos) {
@ -98,6 +126,9 @@ size_t Tokenizer::count() {
return ntokens;
}
/*! Retrieve the entire text converted to an STL vector of tokens.
*
* \return The STL vector */
std::vector<std::string> Tokenizer::as_vector() {
// store current state
size_t current = start;
@ -117,6 +148,12 @@ std::vector<std::string> Tokenizer::as_vector() {
return tokens;
}
/*! Class for reading text with numbers
*
* \param str String to be processed
* \param separators String with separator characters (default: " \t\r\n\f")
*
* \sa Tokenizer InvalidIntegerException InvalidFloatException */
ValueTokenizer::ValueTokenizer(const std::string & str, const std::string & separators) : tokens(str, separators) {
}
@ -127,14 +164,24 @@ ValueTokenizer::ValueTokenizer(const ValueTokenizer & rhs) : tokens(rhs.tokens)
ValueTokenizer::ValueTokenizer(ValueTokenizer && rhs) : tokens(std::move(rhs.tokens)) {
}
/*! Indicate whether more tokens are available
*
* \return true if there are more tokens, false if not */
bool ValueTokenizer::has_next() const {
return tokens.has_next();
}
/*! Search the text to be processed for a sub-string.
*
* \param value string with value to be searched for
* \return true if string was found, false if not */
bool ValueTokenizer::contains(const std::string & value) const {
return tokens.contains(value);
}
/*! Retrieve next token
*
* \return string with next token */
std::string ValueTokenizer::next_string() {
if (has_next()) {
std::string value = tokens.next();
@ -143,6 +190,9 @@ std::string ValueTokenizer::next_string() {
return "";
}
/*! Retrieve next token and convert to int
*
* \return value of next token */
int ValueTokenizer::next_int() {
if (has_next()) {
std::string current = tokens.next();
@ -155,6 +205,9 @@ int ValueTokenizer::next_int() {
return 0;
}
/*! Retrieve next token and convert to bigint
*
* \return value of next token */
bigint ValueTokenizer::next_bigint() {
if (has_next()) {
std::string current = tokens.next();
@ -167,6 +220,9 @@ bigint ValueTokenizer::next_bigint() {
return 0;
}
/*! Retrieve next token and convert to tagint
*
* \return value of next token */
tagint ValueTokenizer::next_tagint() {
if (has_next()) {
std::string current = tokens.next();
@ -179,6 +235,9 @@ tagint ValueTokenizer::next_tagint() {
return 0;
}
/*! Retrieve next token and convert to double
*
* \return value of next token */
double ValueTokenizer::next_double() {
if (has_next()) {
std::string current = tokens.next();
@ -191,10 +250,16 @@ double ValueTokenizer::next_double() {
return 0.0;
}
/*! Skip over a given number of tokens
*
* \param n number of tokens to skip over */
void ValueTokenizer::skip(int n) {
tokens.skip(n);
}
/*! Count number of tokens in text.
*
* \return number of counted tokens */
size_t ValueTokenizer::count() {
return tokens.count();
}

View File

@ -52,11 +52,17 @@ public:
class TokenizerException : public std::exception {
std::string message;
public:
/** Thrown during retrieving or skipping tokens
*
* \param msg String with error message
* \param token String of the token/word that caused the error */
TokenizerException(const std::string &msg, const std::string &token);
~TokenizerException() throw() {
}
/** Retrieve message describing the thrown exception
* \return string with error message */
virtual const char * what() const throw() {
return message.c_str();
}
@ -64,14 +70,20 @@ public:
class InvalidIntegerException : public TokenizerException {
public:
InvalidIntegerException(const std::string &token) : TokenizerException("Not a valid integer number", token) {
}
/** Thrown during converting string to integer number
*
* \param token String of the token/word that caused the error */
InvalidIntegerException(const std::string &token)
: TokenizerException("Not a valid integer number", token) {}
};
class InvalidFloatException : public TokenizerException {
public:
InvalidFloatException(const std::string &token) : TokenizerException("Not a valid floating-point number", token) {
}
/** Thrown during converting string to floating point number
*
* \param token String of the token/word that caused the error */
InvalidFloatException(const std::string &token)
: TokenizerException("Not a valid floating-point number", token) {}
};
class ValueTokenizer {

View File

@ -870,14 +870,10 @@ std::string utils::get_potential_date(const std::string &path, const std::string
reader.ignore_comments = false;
char *line = reader.next_line();
ValueTokenizer values(line);
while (values.has_next()) {
std::string word = values.next_string();
if (word == "DATE:") {
if (values.has_next()) {
std::string date = values.next_string();
return date;
}
Tokenizer words(line);
while (words.has_next()) {
if (words.next() == "DATE:") {
if (words.has_next()) return words.next();
}
}
return "";
@ -893,14 +889,10 @@ std::string utils::get_potential_units(const std::string &path, const std::strin
reader.ignore_comments = false;
char *line = reader.next_line();
ValueTokenizer values(line);
while (values.has_next()) {
std::string word = values.next_string();
if (word == "UNITS:") {
if (values.has_next()) {
std::string units = values.next_string();
return units;
}
Tokenizer words(line);
while (words.has_next()) {
if (words.next() == "UNITS:") {
if (words.has_next()) return words.next();
}
}
return "";

View File

@ -5,6 +5,7 @@ add_subdirectory(formats)
add_subdirectory(commands)
add_subdirectory(c-library)
add_subdirectory(cplusplus)
add_subdirectory(fortran)
add_subdirectory(python)
add_subdirectory(force-styles)

View File

@ -0,0 +1,30 @@
include(CheckGeneratorSupport)
if(NOT CMAKE_GENERATOR_SUPPORT_FORTRAN)
message(STATUS "Skipping Tests for the LAMMPS Fortran Module: no Fortran support in build tool")
return()
endif()
include(CheckLanguage)
check_language(Fortran)
if(CMAKE_Fortran_COMPILER)
enable_language(Fortran)
get_filename_component(LAMMPS_FORTRAN_MODULE ${LAMMPS_SOURCE_DIR}/../fortran/lammps.f90 ABSOLUTE)
if(BUILD_MPI)
find_package(MPI REQUIRED)
else()
add_library(fmpi_stubs STATIC mpi_stubs.f90)
add_library(MPI::MPI_Fortran ALIAS fmpi_stubs)
endif()
add_library(flammps STATIC ${LAMMPS_FORTRAN_MODULE})
add_executable(fortran-create wrap-create.cpp fortran-create.f90)
target_link_libraries(fortran-create PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTest GTest::GTestMain)
add_test(FortranOpen fortran-create)
add_executable(fortran-commands wrap-commands.cpp fortran-commands.f90)
target_link_libraries(fortran-commands PRIVATE flammps lammps MPI::MPI_Fortran GTest::GTest GTest::GTestMain)
add_test(FortranCommands fortran-commands)
else()
message(STATUS "Skipping Tests for the LAMMPS Fortran Module: no Fortran compiler")
endif()

View File

@ -0,0 +1,111 @@
MODULE keepcmds
USE liblammps
TYPE(LAMMPS) :: lmp
CHARACTER(len=*), DIMENSION(*), 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=*), DIMENSION(*), PARAMETER :: cont_input = &
[ CHARACTER(len=40) :: &
'create_atoms 1 single &', &
' 0.2 0.1 0.1' ]
END MODULE keepcmds
FUNCTION f_lammps_with_args() BIND(C, name="f_lammps_with_args")
USE ISO_C_BINDING, ONLY: c_ptr
USE liblammps
USE keepcmds, ONLY: lmp
IMPLICIT NONE
TYPE(c_ptr) :: f_lammps_with_args
CHARACTER(len=*), DIMENSION(*), 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 keepcmds, ONLY: lmp
IMPLICIT NONE
CALL lmp%close()
lmp%handle = c_null_ptr
END SUBROUTINE f_lammps_close
FUNCTION f_lammps_get_natoms() BIND(C, name="f_lammps_get_natoms")
USE ISO_C_BINDING, ONLY: c_null_ptr, c_double
USE liblammps
USE keepcmds, ONLY: lmp
IMPLICIT NONE
REAL(c_double) :: f_lammps_get_natoms
f_lammps_get_natoms = lmp%get_natoms()
END FUNCTION f_lammps_get_natoms
SUBROUTINE f_lammps_file() BIND(C, name="f_lammps_file")
USE ISO_C_BINDING, ONLY: c_null_ptr
USE liblammps
USE keepcmds, ONLY: lmp, demo_input, cont_input
IMPLICIT NONE
INTEGER :: i
CHARACTER(len=*), PARAMETER :: demo_file = 'in.test', cont_file = 'in.cont'
OPEN(10, file=demo_file, status='replace')
WRITE(10, fmt='(A)') (demo_input(i),i=1,SIZE(demo_input))
CLOSE(10)
OPEN(11, file=cont_file, status='replace')
WRITE(11, fmt='(A)') (cont_input(i),i=1,SIZE(cont_input))
CLOSE(11)
CALL lmp%file(demo_file)
CALL lmp%file(cont_file)
OPEN(12, file=demo_file, status='old')
CLOSE(12, status='delete')
OPEN(13, file=cont_file, status='old')
CLOSE(13, status='delete')
END SUBROUTINE f_lammps_file
SUBROUTINE f_lammps_command() BIND(C, name="f_lammps_command")
USE ISO_C_BINDING, ONLY: c_null_ptr
USE liblammps
USE keepcmds, ONLY: lmp, demo_input
IMPLICIT NONE
INTEGER :: i
DO i=1,SIZE(demo_input)
call lmp%command(demo_input(i))
END DO
END SUBROUTINE f_lammps_command
SUBROUTINE f_lammps_commands_list() BIND(C, name="f_lammps_commands_list")
USE ISO_C_BINDING, ONLY: c_null_ptr
USE liblammps
USE keepcmds, ONLY: lmp, demo_input, cont_input
IMPLICIT NONE
CALL lmp%commands_list(demo_input)
CALL lmp%commands_list(cont_input)
END SUBROUTINE f_lammps_commands_list
SUBROUTINE f_lammps_commands_string() BIND(C, name="f_lammps_commands_string")
USE ISO_C_BINDING, ONLY: c_null_ptr
USE liblammps
USE keepcmds, ONLY: lmp, demo_input, cont_input
IMPLICIT NONE
INTEGER :: i
CHARACTER(len=512) :: cmds
cmds = ''
DO i=1,SIZE(demo_input)
cmds = TRIM(cmds) // TRIM(demo_input(i)) // NEW_LINE('A')
END DO
DO i=1,SIZE(cont_input)
cmds = TRIM(cmds) // TRIM(cont_input(i)) // NEW_LINE('A')
END DO
CALL lmp%commands_string(cmds)
END SUBROUTINE f_lammps_commands_string

View File

@ -0,0 +1,86 @@
MODULE keepcreate
USE liblammps
TYPE(LAMMPS) :: lmp
INTEGER :: mycomm
END MODULE keepcreate
FUNCTION f_lammps_no_mpi_no_args() BIND(C, name="f_lammps_no_mpi_no_args")
USE ISO_C_BINDING, ONLY: c_ptr
USE liblammps
USE keepcreate, ONLY: lmp
IMPLICIT NONE
TYPE(c_ptr) :: f_lammps_no_mpi_no_args
lmp = lammps()
f_lammps_no_mpi_no_args = lmp%handle
END FUNCTION f_lammps_no_mpi_no_args
FUNCTION f_lammps_no_mpi_with_args() BIND(C, name="f_lammps_no_mpi_with_args")
USE ISO_C_BINDING, ONLY: c_ptr
USE liblammps
USE keepcreate, ONLY: lmp
IMPLICIT NONE
TYPE(c_ptr) :: f_lammps_no_mpi_with_args
CHARACTER(len=*), DIMENSION(*), PARAMETER :: args = &
[ CHARACTER(len=12) :: 'liblammps', '-log', 'none', '-nocite' ]
lmp = lammps(args)
f_lammps_no_mpi_with_args = lmp%handle
END FUNCTION f_lammps_no_mpi_with_args
FUNCTION f_lammps_open_no_args() BIND(C, name="f_lammps_open_no_args")
USE ISO_C_BINDING, ONLY: c_ptr
USE MPI, ONLY: MPI_COMM_WORLD, mpi_comm_split
USE liblammps
USE keepcreate, ONLY: lmp,mycomm
IMPLICIT NONE
TYPE(c_ptr) :: f_lammps_open_no_args
INTEGER :: color, key, ierr
color = 1
key = 1
CALL mpi_comm_split(MPI_COMM_WORLD, color, key, mycomm, ierr)
lmp = lammps(comm=mycomm)
f_lammps_open_no_args = lmp%handle
END FUNCTION f_lammps_open_no_args
FUNCTION f_lammps_open_with_args() BIND(C, name="f_lammps_open_with_args")
USE ISO_C_BINDING, ONLY: c_ptr
USE MPI, ONLY: MPI_COMM_WORLD, mpi_comm_split
USE liblammps
USE keepcreate, ONLY: lmp,mycomm
IMPLICIT NONE
TYPE(c_ptr) :: f_lammps_open_with_args
INTEGER :: color, key, ierr
CHARACTER(len=*), DIMENSION(*), PARAMETER :: args = &
[ CHARACTER(len=12) :: 'liblammps', '-log', 'none', '-nocite' ]
color = 2
key = 1
CALL mpi_comm_split(MPI_COMM_WORLD, color, key, mycomm, ierr)
lmp = lammps(args,mycomm)
f_lammps_open_with_args = lmp%handle
END FUNCTION f_lammps_open_with_args
SUBROUTINE f_lammps_close() BIND(C, name="f_lammps_close")
USE ISO_C_BINDING, ONLY: c_null_ptr
USE liblammps
USE keepcreate, ONLY: lmp
IMPLICIT NONE
CALL lmp%close()
lmp%handle = c_null_ptr
END SUBROUTINE f_lammps_close
FUNCTION f_lammps_get_comm() BIND(C, name="f_lammps_get_comm")
USE liblammps
USE keepcreate, ONLY: mycomm
IMPLICIT NONE
INTEGER :: f_lammps_get_comm
f_lammps_get_comm = mycomm
END FUNCTION f_lammps_get_comm

View File

@ -0,0 +1,20 @@
MODULE MPI
IMPLICIT NONE
PRIVATE
INTEGER, PARAMETER :: MPI_COMM_WORLD=0
INTEGER, PARAMETER :: MPI_SUCCESS=0
PUBLIC :: MPI_COMM_WORLD, MPI_SUCCESS, &
mpi_comm_split
CONTAINS
SUBROUTINE mpi_comm_split(comm,color,key,newcomm,ierr)
INTEGER, INTENT(in) :: comm,color,key
INTEGER, INTENT(out) :: newcomm,ierr
newcomm = comm + 1
ierr = 0
END SUBROUTINE mpi_comm_split
END MODULE MPI

View File

@ -0,0 +1,65 @@
// unit tests for issuing command to a LAMMPS instance through the Fortran wrapper
#include "lammps.h"
#include <mpi.h>
#include <cstdio> // for stdin, stdout
#include <string>
#include "gtest/gtest.h"
// prototypes for fortran reverse wrapper functions
extern "C" {
void *f_lammps_with_args();
void f_lammps_close();
void f_lammps_file();
void f_lammps_command();
void f_lammps_commands_list();
void f_lammps_commands_string();
double f_lammps_get_natoms();
}
class LAMMPS_commands : public ::testing::Test
{
protected:
LAMMPS_NS::LAMMPS *lmp;
LAMMPS_commands() {};
~LAMMPS_commands() override {};
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, from_file) {
EXPECT_EQ(f_lammps_get_natoms(),0);
f_lammps_file();
EXPECT_EQ(f_lammps_get_natoms(),2);
};
TEST_F(LAMMPS_commands, from_line) {
EXPECT_EQ(f_lammps_get_natoms(),0);
f_lammps_command();
EXPECT_EQ(f_lammps_get_natoms(),1);
};
TEST_F(LAMMPS_commands, from_list) {
EXPECT_EQ(f_lammps_get_natoms(),0);
f_lammps_commands_list();
EXPECT_EQ(f_lammps_get_natoms(),2);
};
TEST_F(LAMMPS_commands, from_string) {
EXPECT_EQ(f_lammps_get_natoms(),0);
f_lammps_commands_string();
EXPECT_EQ(f_lammps_get_natoms(),2);
};

View File

@ -0,0 +1,97 @@
// unit tests for the LAMMPS base class
#include "lammps.h"
#include <mpi.h>
#include <cstdio> // for stdin, stdout
#include <string>
#include "gtest/gtest.h"
// prototypes for fortran reverse wrapper functions
extern "C" {
void *f_lammps_open_no_args();
void *f_lammps_open_with_args();
void *f_lammps_no_mpi_no_args();
void *f_lammps_no_mpi_with_args();
void f_lammps_close();
int f_lammps_get_comm();
}
TEST(open_no_mpi, no_args) {
::testing::internal::CaptureStdout();
int mpi_init=0;
MPI_Initialized(&mpi_init);
EXPECT_EQ(mpi_init,0);
void *handle = f_lammps_no_mpi_no_args();
std::string output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,6).c_str(),"LAMMPS");
LAMMPS_NS::LAMMPS *lmp = (LAMMPS_NS::LAMMPS *)handle;
MPI_Initialized(&mpi_init);
EXPECT_NE(mpi_init,0);
EXPECT_EQ(lmp->world, MPI_COMM_WORLD);
EXPECT_EQ(lmp->infile, stdin);
EXPECT_EQ(lmp->screen, stdout);
EXPECT_NE(lmp->citeme, nullptr);
::testing::internal::CaptureStdout();
f_lammps_close();
output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,16).c_str(), "Total wall time:");
}
TEST(open_no_mpi, with_args) {
::testing::internal::CaptureStdout();
void *handle = f_lammps_no_mpi_with_args();
std::string output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,6).c_str(),"LAMMPS");
LAMMPS_NS::LAMMPS *lmp = (LAMMPS_NS::LAMMPS *)handle;
EXPECT_EQ(lmp->infile, stdin);
EXPECT_EQ(lmp->screen, stdout);
EXPECT_EQ(lmp->logfile, nullptr);
EXPECT_EQ(lmp->citeme, nullptr);
EXPECT_EQ(lmp->world, MPI_COMM_WORLD);
::testing::internal::CaptureStdout();
f_lammps_close();
output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,16).c_str(), "Total wall time:");
}
TEST(fortran_open, no_args) {
::testing::internal::CaptureStdout();
void *handle = f_lammps_open_no_args();
std::string output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,6).c_str(),"LAMMPS");
LAMMPS_NS::LAMMPS *lmp = (LAMMPS_NS::LAMMPS *)handle;
int f_comm = f_lammps_get_comm();
MPI_Comm mycomm = MPI_Comm_f2c(f_comm);
EXPECT_EQ(lmp->world, mycomm);
EXPECT_EQ(lmp->infile, stdin);
EXPECT_EQ(lmp->screen, stdout);
EXPECT_NE(lmp->citeme, nullptr);
::testing::internal::CaptureStdout();
f_lammps_close();
output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,16).c_str(), "Total wall time:");
}
TEST(fortran_open, with_args) {
::testing::internal::CaptureStdout();
void *handle = f_lammps_open_with_args();
std::string output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,6).c_str(),"LAMMPS");
LAMMPS_NS::LAMMPS *lmp = (LAMMPS_NS::LAMMPS *)handle;
int f_comm = f_lammps_get_comm();
MPI_Comm mycomm = MPI_Comm_f2c(f_comm);
EXPECT_EQ(lmp->world, mycomm);
EXPECT_EQ(lmp->infile, stdin);
EXPECT_EQ(lmp->screen, stdout);
EXPECT_EQ(lmp->logfile, nullptr);
EXPECT_EQ(lmp->citeme, nullptr);
::testing::internal::CaptureStdout();
f_lammps_close();
output = ::testing::internal::GetCapturedStdout();
EXPECT_STREQ(output.substr(0,16).c_str(), "Total wall time:");
}