From 9b969648d579202c903570a954e7e77668a12d03 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Thu, 7 Apr 2022 11:51:52 -0400 Subject: [PATCH] add kimplugin source and CMake based build support --- doc/src/Developer_plugins.rst | 21 ++++ doc/src/plugin.rst | 9 +- examples/kim/plugin/CMakeLists.txt | 109 +++++++++++++++++++ examples/kim/plugin/LAMMPSInterfaceCXX.cmake | 88 +++++++++++++++ examples/kim/plugin/kimplugin.cpp | 54 +++++++++ 5 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 examples/kim/plugin/CMakeLists.txt create mode 100644 examples/kim/plugin/LAMMPSInterfaceCXX.cmake create mode 100644 examples/kim/plugin/kimplugin.cpp diff --git a/doc/src/Developer_plugins.rst b/doc/src/Developer_plugins.rst index 96bb872929..1f698d27d5 100644 --- a/doc/src/Developer_plugins.rst +++ b/doc/src/Developer_plugins.rst @@ -262,3 +262,24 @@ A plugin may be registered under an existing style name. In that case the plugin will override the existing code. This can be used to modify the behavior of existing styles or to debug new versions of them without having to re-compile or re-install all of LAMMPS. + +Compiling plugins +^^^^^^^^^^^^^^^^^ + +Plugins need to be compiled with the same compilers and libraries +(e.g. MPI) and compilation settings (MPI on/off, OpenMP, integer sizes) +as the LAMMPS executable and library. Otherwise the plugin will likely +not load due to mismatches in the function signatures (LAMMPS is C++ so +scope, type, and number of arguments are encoded into the symbol names +and thus differences in them will lead to failed plugin load commands. +Compilation of the plugin can be done managed via both, CMake or +traditional GNU makefiles. Some examples that can be used as a template +are in the ``examples/plugins`` folder. The CMake script code has some +small adjustments to allow building he plugins for running unit tests +with them. Another example that converts the KIM package into a plugin +can be found in the ``examples/kim/plugin`` folder. No changes to the +sources of the KIM package themselves are needed; only the plugin +interface and loader code needs to be added. This example only supports +building with CMake, but is probably a more typical example. To compile +you need to run CMake with -DLAMMPS_SOURCE_DIR=. +Other configuration setting are identical to those for compiling LAMMPS. diff --git a/doc/src/plugin.rst b/doc/src/plugin.rst index 83eea789d1..1a10ab84ad 100644 --- a/doc/src/plugin.rst +++ b/doc/src/plugin.rst @@ -70,12 +70,11 @@ Restrictions """""""""""" The *plugin* command is part of the PLUGIN package. It is -only enabled if LAMMPS was built with that package. -See the :doc:`Build package ` page for -more info. Plugins are not available on Windows. +only enabled if LAMMPS was built with that package. See +the :doc:`Build package ` page for more info. -If plugins access functions or classes from a package, LAMMPS must -have been compiled with that package included. +If plugins access functions or classes from a package, +LAMMPS must have been compiled with that package included. Plugins are dependent on the LAMMPS binary interface (ABI) and particularly the MPI library used. So they are not guaranteed diff --git a/examples/kim/plugin/CMakeLists.txt b/examples/kim/plugin/CMakeLists.txt new file mode 100644 index 0000000000..1d955aa8a1 --- /dev/null +++ b/examples/kim/plugin/CMakeLists.txt @@ -0,0 +1,109 @@ +########################################## +# CMake build system for plugin examples. +# The is meant to be used as a template for plugins that are +# distributed independent from the LAMMPS package. +########################################## + +cmake_minimum_required(VERSION 3.10) + +# enforce out-of-source build +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "In-source builds are not allowed. You must create and use a build directory. " + "Please remove CMakeCache.txt and CMakeFiles first.") +endif() + +project(kimplugin VERSION 1.0 LANGUAGES CXX) + +set(LAMMPS_SOURCE_DIR "" CACHE PATH "Location of LAMMPS sources folder") +if(NOT LAMMPS_SOURCE_DIR) + message(FATAL_ERROR "Must set LAMMPS_SOURCE_DIR") +endif() + +# by default, install into $HOME/.local (not /usr/local), +# so that no root access (and sudo) is needed +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Default install path" FORCE) +endif() + +# ugly hacks for MSVC which by default always reports an old C++ standard in the __cplusplus macro +# and prints lots of pointless warnings about "unsafe" functions +if(MSVC) + add_compile_options(/Zc:__cplusplus) + add_compile_options(/wd4244) + add_compile_options(/wd4267) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +endif() + +# C++11 is required +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Need -restrict with Intel compilers +if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -restrict") +endif() + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +include(CheckIncludeFileCXX) +include(LAMMPSInterfaceCXX) + +########################## +# building the plugins + +add_library(kimplugin MODULE kimplugin.cpp ${LAMMPS_SOURCE_DIR}/KIM/pair_kim.cpp + ${LAMMPS_SOURCE_DIR}/KIM/fix_store_kim.cpp ${LAMMPS_SOURCE_DIR}/KIM/kim_command.cpp + ${LAMMPS_SOURCE_DIR}/KIM/kim_init.cpp ${LAMMPS_SOURCE_DIR}/KIM/kim_interactions.cpp + ${LAMMPS_SOURCE_DIR}/KIM/kim_param.cpp ${LAMMPS_SOURCE_DIR}/KIM/kim_property.cpp + ${LAMMPS_SOURCE_DIR}/KIM/kim_query.cpp ${LAMMPS_SOURCE_DIR}/KIM/kim_units.cpp) +target_link_libraries(kimplugin PRIVATE lammps) +target_include_directories(kimplugin PRIVATE ${LAMMPS_SOURCE_DIR}/KIM) +set_target_properties(kimplugin PROPERTIES PREFIX "" SUFFIX ".so") + +set(KIM-API_MIN_VERSION 2.1.3) +find_package(PkgConfig REQUIRED) +if(KIM-API_FOUND AND KIM-API_VERSION VERSION_GREATER_EQUAL 2.2.0) + # For kim-api >= 2.2.0 + find_package(KIM-API 2.2.0 CONFIG REQUIRED) + target_link_libraries(kimplugin PRIVATE KIM-API::kim-api) +else() + # For kim-api 2.1.3 (consistent with previous version of this file) + find_package(PkgConfig REQUIRED) + pkg_check_modules(KIM-API REQUIRED IMPORTED_TARGET libkim-api>=${KIM-API_MIN_VERSION}) + target_link_libraries(kimplugin PRIVATE PkgConfig::KIM-API) +endif() + +########################## +# need libcurl +find_package(CURL) +if(CURL_FOUND) + if(CMAKE_VERSION VERSION_LESS 3.12) + target_include_directories(kimplugin PRIVATE ${CURL_INCLUDE_DIRS}) + target_link_libraries(kimplugin PRIVATE ${CURL_LIBRARIES}) + else() + target_link_libraries(kimplugin PRIVATE CURL::libcurl) + endif() + target_compile_definitions(kimplugin PRIVATE -DLMP_KIM_CURL) + set(LMP_DEBUG_CURL OFF CACHE STRING "Set libcurl verbose mode on/off. If on, it displays a lot of verbose information about its operations.") + mark_as_advanced(LMP_DEBUG_CURL) + if(LMP_DEBUG_CURL) + target_compile_definitions(kimplugin PRIVATE -DLMP_DEBUG_CURL) + endif() + set(LMP_NO_SSL_CHECK OFF CACHE STRING "Tell libcurl to not verify the peer. If on, the connection succeeds regardless of the names in the certificate. Insecure - Use with caution!") + mark_as_advanced(LMP_NO_SSL_CHECK) + if(LMP_NO_SSL_CHECK) + target_compile_definitions(kimplugin PRIVATE -DLMP_NO_SSL_CHECK) + endif() +endif() + +# MacOS seems to need this +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set_target_properties(kimplugin PROPERTIES LINK_FLAGS "-Wl,-undefined,dynamic_lookup") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") +# tell CMake to export all symbols to a .dll on Windows with special case for MinGW cross-compilers + set_target_properties(kimplugin.so PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + if(CMAKE_CROSSCOMPILING) + set_target_properties(kimplugin PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") + endif() +else() + set_target_properties(kimplugin PROPERTIES LINK_FLAGS "-rdynamic") +endif() diff --git a/examples/kim/plugin/LAMMPSInterfaceCXX.cmake b/examples/kim/plugin/LAMMPSInterfaceCXX.cmake new file mode 100644 index 0000000000..dfbd77e28a --- /dev/null +++ b/examples/kim/plugin/LAMMPSInterfaceCXX.cmake @@ -0,0 +1,88 @@ +# Cmake script code to define the LAMMPS C++ interface +# settings required for building LAMMPS plugins + +################################################################################ +# helper function +function(validate_option name values) + string(TOLOWER ${${name}} needle_lower) + string(TOUPPER ${${name}} needle_upper) + list(FIND ${values} ${needle_lower} IDX_LOWER) + list(FIND ${values} ${needle_upper} IDX_UPPER) + if(${IDX_LOWER} LESS 0 AND ${IDX_UPPER} LESS 0) + list_to_bulletpoints(POSSIBLE_VALUE_LIST ${${values}}) + message(FATAL_ERROR "\n########################################################################\n" + "Invalid value '${${name}}' for option ${name}\n" + "\n" + "Possible values are:\n" + "${POSSIBLE_VALUE_LIST}" + "########################################################################") + endif() +endfunction(validate_option) + +################################################################################# +# LAMMPS C++ interface. We only need the header related parts. +add_library(lammps INTERFACE) +target_include_directories(lammps INTERFACE ${LAMMPS_SOURCE_DIR}) +if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING) + target_link_libraries(lammps INTERFACE ${CMAKE_BINARY_DIR}/../liblammps.dll.a) +endif() +################################################################################ +# MPI configuration +if(NOT CMAKE_CROSSCOMPILING) + set(MPI_CXX_SKIP_MPICXX TRUE) + find_package(MPI QUIET) + option(BUILD_MPI "Build MPI version" ${MPI_FOUND}) +else() + option(BUILD_MPI "Build MPI version" OFF) +endif() + +if(BUILD_MPI) + find_package(MPI REQUIRED) + option(LAMMPS_LONGLONG_TO_LONG "Workaround if your system or MPI version does not recognize 'long long' data types" OFF) + if(LAMMPS_LONGLONG_TO_LONG) + target_compile_definitions(lammps INTERFACE -DLAMMPS_LONGLONG_TO_LONG) + endif() + target_link_libraries(lammps INTERFACE MPI::MPI_CXX) +else() + target_include_directories(lammps INTERFACE "${LAMMPS_SOURCE_DIR}/STUBS") +endif() + +set(LAMMPS_SIZES "smallbig" CACHE STRING "LAMMPS integer sizes (smallsmall: all 32-bit, smallbig: 64-bit #atoms #timesteps, bigbig: also 64-bit imageint, 64-bit atom ids)") +set(LAMMPS_SIZES_VALUES smallbig bigbig smallsmall) +set_property(CACHE LAMMPS_SIZES PROPERTY STRINGS ${LAMMPS_SIZES_VALUES}) +validate_option(LAMMPS_SIZES LAMMPS_SIZES_VALUES) +string(TOUPPER ${LAMMPS_SIZES} LAMMPS_SIZES) +target_compile_definitions(lammps INTERFACE -DLAMMPS_${LAMMPS_SIZES}) + +################################################################################ +# detect if we may enable OpenMP support by default +set(BUILD_OMP_DEFAULT OFF) +find_package(OpenMP QUIET) +if(OpenMP_FOUND) + check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE) + if(HAVE_OMP_H_INCLUDE) + set(BUILD_OMP_DEFAULT ON) + endif() +endif() + +option(BUILD_OMP "Build with OpenMP support" ${BUILD_OMP_DEFAULT}) + +if(BUILD_OMP) + find_package(OpenMP REQUIRED) + check_include_file_cxx(omp.h HAVE_OMP_H_INCLUDE) + if(NOT HAVE_OMP_H_INCLUDE) + message(FATAL_ERROR "Cannot find the 'omp.h' header file required for full OpenMP support") + endif() + + if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 9.0)) OR + (CMAKE_CXX_COMPILER_ID STREQUAL "PGI") OR + ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)) OR + ((CMAKE_CXX_COMPILER_ID STREQUAL "Intel") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0))) + # GCC 9.x and later plus Clang 10.x and later implement strict OpenMP 4.0 semantics for consts. + # Intel 18.0 was tested to support both, so we switch to OpenMP 4+ from 19.x onward to be safe. + target_compile_definitions(lammps INTERFACE -DLAMMPS_OMP_COMPAT=4) + else() + target_compile_definitions(lammps INTERFACE -DLAMMPS_OMP_COMPAT=3) + endif() + target_link_libraries(lammps INTERFACE OpenMP::OpenMP_CXX) +endif() diff --git a/examples/kim/plugin/kimplugin.cpp b/examples/kim/plugin/kimplugin.cpp new file mode 100644 index 0000000000..45b1f75681 --- /dev/null +++ b/examples/kim/plugin/kimplugin.cpp @@ -0,0 +1,54 @@ + +#include "lammpsplugin.h" +#include "version.h" + +#include "pair_kim.h" +#include "fix_store_kim.h" +#include "kim_command.h" + +using namespace LAMMPS_NS; + +static Pair *pair_kim_creator(LAMMPS *lmp) +{ + return new PairKIM(lmp); +} + +static Fix *fix_store_kim_creator(LAMMPS *lmp, int argc, char **argv) +{ + return new FixStoreKIM(lmp, argc, argv); +} + +static Command *kim_command_creator(LAMMPS *lmp) +{ + return new KimCommand(lmp); +} + +extern "C" void lammpsplugin_init(void *lmp, void *handle, void *regfunc) +{ + lammpsplugin_t plugin; + lammpsplugin_regfunc register_plugin = (lammpsplugin_regfunc) regfunc; + + // register kim pair style + plugin.version = LAMMPS_VERSION; + plugin.style = "pair"; + plugin.name = "kim"; + plugin.info = "KIM plugin pair style v1.0"; + plugin.author = "Axel Kohlmeyer (akohlmey@gmail.com)"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &pair_kim_creator; + plugin.handle = handle; + (*register_plugin)(&plugin, lmp); + + // register fix STORE/KIM only need to update changed fields + plugin.style = "fix"; + plugin.name = "STORE/KIM"; + plugin.info = "Internal settings storage for KIM fix style v1.0"; + plugin.creator.v2 = (lammpsplugin_factory2 *) &fix_store_kim_creator; + (*register_plugin)(&plugin, lmp); + + // register KIM command + plugin.style = "command"; + plugin.name = "kim"; + plugin.info = "kim command style v1.0"; + plugin.creator.v1 = (lammpsplugin_factory1 *) &kim_command_creator; + (*register_plugin)(&plugin, lmp); +}