Unverified Commit dd03c723 authored by Axel Kohlmeyer's avatar Axel Kohlmeyer
Browse files

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

parents cf11945e a4d3b21a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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")
+6 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
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;
   }
+191 −0
Original line number Diff line number Diff line
@@ -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:
+181 −2
Original line number Diff line number Diff line
@@ -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
Loading