\(\renewcommand{\AA}{\text{Å}}\)

1.3.1. The :f:mod:`LIBLAMMPS` Fortran Module

The :f:mod:`LIBLAMMPS` module provides an interface to call LAMMPS from Fortran. It is based on the LAMMPS C library interface and requires a fully Fortran 2003-compatible compiler to be compiled. It is designed to be self-contained and not require any support functions written in C, C++, or Fortran other than those in the C library interface and the LAMMPS Fortran module itself.

While C libraries have a defined binary interface (ABI) and can thus be used from multiple compiler versions from different vendors as long as they are compatible with the hosting operating system, the same is not true for Fortran programs. Thus, the LAMMPS Fortran module needs to be compiled alongside the code using it from the source code in fortran/lammps.f90 and with the same compiler used to build the rest of the Fortran code that interfaces to LAMMPS. When linking, you also need to link to the LAMMPS library. A typical command line 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-parallelized program. Otherwise, using the plain Fortran compiler (gfortran, ifort, flang, etc.) will suffice, since there are no direct references to MPI library features, definitions and subroutine calls; MPI communicators are referred to by their integer index representation as required by the Fortran MPI interface. It may be necessary to link to additional libraries, depending on how LAMMPS was configured and whether the LAMMPS library was compiled as a static or dynamic library.

If the LAMMPS library itself has been compiled with MPI support, the resulting executable will be able to run LAMMPS in parallel with mpirun, mpiexec, or equivalent. This may be either on the “world” communicator or a sub-communicator created by the calling Fortran code. If, on the other hand, the LAMMPS library has been compiled without MPI support, each LAMMPS instance will run independently using just one processor.

Please also note that the order of the source files matters: the lammps.f90 file needs to be compiled first, since it provides the :f:mod:`LIBLAMMPS` module that would need to be imported by the calling Fortran code in order to uses the Fortran interface. A working example can be found together with equivalent examples in C and C++ in the examples/COUPLE/simple folder of the LAMMPS distribution.

Fortran compiler compatibility

A fully Fortran 2003 compatible Fortran compiler is required. This means that currently only GNU Fortran 9 and later are compatible and thus the default compilers of Red Hat or CentOS 7 and Ubuntu 18.04 LTS and not compatible. Either newer compilers need to be installed or the Linux updated.


1.3.2. Creating or deleting a LAMMPS object

With the Fortran interface, the creation of a 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 lammps_open_fortran() or lammps_open_no_mpi() from the C library API to create the class instance. All arguments are optional and lammps_mpi_init() will be called automatically if it is needed. Similarly, a possible call to lammps_mpi_finalize() is integrated into the :f:func:`close` function and triggered with the optional logical argument set to .TRUE.. Here is a simple example:

PROGRAM testlib
  USE LIBLAMMPS                 ! include the LAMMPS library interface
  IMPLICIT NONE
  TYPE(lammps) :: lmp           ! derived type to hold LAMMPS instance
  CHARACTER(LEN=12), PARAMETER :: args(3) = &
      [ 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 shutdown MPI)
  CALL lmp%close(.TRUE.)
END PROGRAM testlib

It is also possible to pass command line flags from Fortran to C/C++ and thus make the resulting executable behave similarly to the standalone executable (it will ignore the -in/-i flag, though). This allows using the command line to configure accelerator and suffix settings, configure screen and logfile output, or to set index style variables from the command line and more. Here is a correspondingly adapted version of the previous example:

PROGRAM testlib2
  USE LIBLAMMPS                 ! include the LAMMPS library interface
  IMPLICIT NONE
  TYPE(lammps) :: lmp           ! derived type to hold LAMMPS instance
  CHARACTER(LEN=128), ALLOCATABLE :: command_args(:)
  INTEGER :: i, argc

  ! copy command line flags to `command_args()`
  argc = COMMAND_ARGUMENT_COUNT()
  ALLOCATE(command_args(0:argc))
  DO i=0, argc
    CALL GET_COMMAND_ARGUMENT(i, command_args(i))
  END DO

  ! create a LAMMPS instance (and initialize MPI)
  lmp = lammps(command_args)
  ! get and print numerical version code
  PRINT*, 'Program name:   ', command_args(0)
  PRINT*, 'LAMMPS Version: ', lmp%version()
  ! delete LAMMPS instance (and shuts down MPI)
  CALL lmp%close(.TRUE.)
  DEALLOCATE(command_args)
END PROGRAM testlib2

1.3.3. 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 to pass individual or multiple commands from strings or lists of strings. This is done similarly to how it is implemented in the C library interface. Before handing off the calls to the C library interface, the corresponding Fortran versions of the calls (:f:func:`file`, :f:func:`command`, :f:func:`commands_list`, and :f:func:`commands_string`) have to make copies 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.

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(.TRUE.)
END PROGRAM testcmd

1.3.4. Accessing system properties

The C library interface allows the extraction of different kinds of information about the active simulation instance and also—in some cases—to apply modifications to it, and the Fortran interface provides access to the same data using Fortran-style, C-interoperable data types. In some cases, the Fortran library interface makes pointers to internal LAMMPS data structures accessible; when accessing them through the library interfaces, special care is needed to avoid data corruption and crashes. Please see the documentation of the individual type-bound procedures for details.

Below is an example demonstrating some of the possible uses.

PROGRAM testprop
  USE LIBLAMMPS
  USE, INTRINSIC :: ISO_C_BINDING, ONLY : c_double, c_int64_t, c_int
  USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY : OUTPUT_UNIT
  TYPE(lammps) :: lmp
  INTEGER(KIND=c_int64_t), POINTER :: natoms, ntimestep, bval
  REAL(KIND=c_double), POINTER :: dt, dval
  INTEGER(KIND=c_int), POINTER :: nfield, typ, ival
  INTEGER(KIND=c_int) :: i
  CHARACTER(LEN=11) :: key
  REAL(KIND=c_double) :: pe, ke

  lmp = lammps()
  CALL lmp%file('in.sysinit')
  natoms = lmp%extract_global('natoms')
  WRITE(OUTPUT_UNIT,'(A,I0,A)') 'Running a simulation with ', natoms, ' atoms'
  WRITE(OUTPUT_UNIT,'(I0,A,I0,A,I0,A)') lmp%extract_setting('nlocal'), &
      ' local and ', lmp%extract_setting('nghost'), ' ghost atoms. ', &
      lmp%extract_setting('ntypes'), ' atom types'

  CALL lmp%command('run 2 post no')

  ntimestep = lmp%last_thermo('step', 0)
  nfield = lmp%last_thermo('num', 0)
  WRITE(OUTPUT_UNIT,'(A,I0,A,I0)') 'Last thermo output on step: ', ntimestep, &
      ',  number of fields: ', nfield
  DO i=1, nfield
      key = lmp%last_thermo('keyword',i)
      typ = lmp%last_thermo('type',i)
      IF (typ == lmp%dtype%i32) THEN
          ival = lmp%last_thermo('data',i)
          WRITE(OUTPUT_UNIT,*) key, ':', ival
      ELSE IF (typ == lmp%dtype%i64) THEN
          bval = lmp%last_thermo('data',i)
          WRITE(OUTPUT_UNIT,*) key, ':', bval
      ELSE IF (typ == lmp%dtype%r64) THEN
          dval = lmp%last_thermo('data',i)
          WRITE(OUTPUT_UNIT,*) key, ':', dval
      END IF
  END DO

  dt = lmp%extract_global('dt')
  ntimestep = lmp%extract_global('ntimestep')
  WRITE(OUTPUT_UNIT,'(A,I0,A,F4.1,A)') 'At step: ', ntimestep, &
      '  Changing timestep from', dt, ' to 0.5'
  dt = 0.5_c_double
  CALL lmp%command('run 2 post no')

  WRITE(OUTPUT_UNIT,'(A,I0)') 'At step: ', ntimestep
  pe = lmp%get_thermo('pe')
  ke = lmp%get_thermo('ke')
  WRITE(OUTPUT_UNIT,*) 'PE = ', pe
  WRITE(OUTPUT_UNIT,*) 'KE = ', ke

  CALL lmp%close(.TRUE.)
END PROGRAM testprop

1.3.5. The :f:mod:`LIBLAMMPS` module API

Below are the detailed descriptions of definitions and interfaces of the contents of the :f:mod:`LIBLAMMPS` Fortran interface to LAMMPS.


Procedures Bound to the :f:type:`lammps` Derived Type










Note

Note that a frequent use case of this function is to extract only one or more of the options rather than all seven. For example, assuming “lmp” represents a properly-initialized LAMMPS instance, the following code will extract the periodic box settings into the variable “periodic”:

! code to start up
LOGICAL :: periodic(3)
! code to initialize LAMMPS / run things / etc.
CALL lmp%extract_box(pflags = periodic)









Note

LAMMPS cannot easily check if it is valid to access the data referenced by the variables (e.g., computes, fixes, or thermodynamic info), so it may fail with an error. The caller has to make certain that the data are extracted only when it is safe to evaluate the variable and thus an error and crash are avoided.