add unittest support for the fortran interface to LAMMPS

This commit is contained in:
Axel Kohlmeyer
2020-08-28 20:56:52 -04:00
parent 3e92647abb
commit e44707d5e1
7 changed files with 410 additions and 0 deletions

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:");
}