Unverified Commit 3c71d300 authored by Axel Kohlmeyer's avatar Axel Kohlmeyer Committed by GitHub
Browse files

Merge pull request #2320 from akohlmey/programmer-guide

Add programmer guide part 2
parents 3be06474 c7360fb8
Loading
Loading
Loading
Loading
+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

@@ -785,6 +787,8 @@ return the result of a partial conversion or zero in cases where the
string is not a valid number.  This behavior allows to more easily detect
typos or issues when processing input files.

----------

.. doxygenfunction:: numeric
   :project: progguide

@@ -803,6 +807,8 @@ String processing functions
The following are functions to help with processing strings
and parsing files or arguments.

----------

.. doxygenfunction:: trim
   :project: progguide

@@ -880,3 +886,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:

doc/src/pg_lib_add.rst

0 → 100644
+33 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
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
Loading