diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b53fe1b30..8b6e39108 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -22,7 +22,7 @@ Please include all the files you used to run the simulation. **Please complete the following information:** - OS: [e.g. Ubuntu, Fedora, CentOS, Windows, Cygwin, macOS, ...] - Ensemble: [e.g. NPT, NVT, GEMC, GCMC] - - Code version [e.g. 2.31] + - Code version [e.g. 2.40] **Additional context** Add any other context about the problem here. diff --git a/.gitignore b/.gitignore index ac2028b28..bd6af2574 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ *.app bin +bin_MPI lib/cub +.vscode .idea -cmake-build-debug \ No newline at end of file +cmake-build-debug +.vscode diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 69d93ca53..000000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "macFrameworkPath": [ - "/System/Library/Frameworks", - "/Library/Frameworks" - ] - }, - { - "name": "Linux", - "includePath": [ - "/opt/intel/compilers_and_libraries_2016.4.258/linux/ipp/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/mkl/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/tbb/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/daal/include", - "/usr/include/c++/6", - "/usr/include/x86_64-linux-gnu/c++/6", - "/usr/include/c++/6/backward", - "/usr/lib/gcc/x86_64-linux-gnu/6/include", - "/usr/local/include", - "/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed", - "/usr/include/x86_64-linux-gnu", - "/usr/include", - "${workspaceRoot}", - "${workspaceRoot}/src", - "${workspaceRoot}/lib", - "${workspaceRoot}/src/cbmc" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/opt/intel/compilers_and_libraries_2016.4.258/linux/ipp/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/mkl/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/tbb/include", - "/opt/intel/compilers_and_libraries_2016.4.258/linux/daal/include", - "/usr/include/c++/6", - "/usr/include/x86_64-linux-gnu/c++/6", - "/usr/include/c++/6/backward", - "/usr/lib/gcc/x86_64-linux-gnu/6/include", - "/usr/local/include", - "/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed", - "/usr/include/x86_64-linux-gnu", - "/usr/include", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "compilerPath": "/usr/bin/gcc", - "cStandard": "c11", - "cppStandard": "c++17" - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", - "${workspaceRoot}" - ], - "defines": [ - "_DEBUG", - "UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", - "${workspaceRoot}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3b62c5316..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "files.associations": { - "__tree": "cpp", - "ios": "cpp", - "__config": "cpp", - "__nullptr": "cpp", - "cstddef": "cpp", - "exception": "cpp", - "initializer_list": "cpp", - "new": "cpp", - "stdexcept": "cpp", - "type_traits": "cpp", - "typeinfo": "cpp", - "algorithm": "cpp", - "iostream": "cpp", - "cctype": "cpp", - "cmath": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "iomanip": "cpp" - } -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9f5522d..1221b11b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ # Change Log All notable changes to this project will be documented in this file. +## [2.50] - 1/20/2020 ++ Addes support for force biased multiparticle move ++ Added support for free energy calculations using TI and FEP methods ++ Support for multiple simulation with different temperatures using MPI ++ Added support for Exponential-6 forcefield ++ Support for restarting simulation using checkpoint ++ Added support for new GPU architectures 7.0 and 7.5 ++ Added an error message when GPU version was used but there was no GPU present (#126) ++ Read PDB file more efficiently when recalculating trajectory (#131) ++ Fixed detecting simulation box shape (orthogonal and non-orthogonal) (#134) ++ Fixed bugs in PDB reader and support for HETATM field in PDB file ++ Fixed bugs in PSF reader ++ Fixed Case sensitive keywords (#100) ++ Fixed the timing report on Windows (#89) ++ Added minimum volume in case the difference in box sizes were large (#94) ++ Fixed the issue where cutoff value is larger than half of minimum box length (#98) ++ Added an error message when charges detected and ewald was set to false (#99) ++ Fixed compiling issue on Visual Studio (#110) ++ Fixed the issue where GOMC did not read CHARMM parameter files missing Urey-Bradley terms (#147) ++ Reduced GOMC reposity size + ## [2.40] - 3/15/2019 + Added support for Cyclic molecules. + Added Inter Molecular Exchange Monte Carlo move in GCMC and GEMC simulation. diff --git a/CMake/FileLists.cmake b/CMake/FileLists.cmake index 892076250..c518849a1 100644 --- a/CMake/FileLists.cmake +++ b/CMake/FileLists.cmake @@ -19,6 +19,7 @@ set(sources src/FFParticle.cpp src/FFSetup.cpp src/Forcefield.cpp + src/FreeEnergyOutput.cpp src/Geometry.cpp src/HistOutput.cpp src/InputFileReader.cpp @@ -31,6 +32,7 @@ set(sources src/NoEwald.cpp src/OutConst.cpp src/OutputVars.cpp + src/ParallelTemperingPreprocessor.cpp src/PDBSetup.cpp src/PDBOutput.cpp src/PRNGSetup.cpp @@ -84,6 +86,7 @@ set(headers src/FFBonds.h src/FFConst.h src/FFDihedrals.h + src/FFExp6.h src/FFParticle.h src/FFSetup.h src/FFShift.h @@ -91,6 +94,7 @@ set(headers src/FFSwitchMartini.h src/FixedWidthReader.h src/Forcefield.h + src/FreeEnergyOutput.h src/FxdWidthWrtr.h src/Geometry.h src/HistOutput.h @@ -108,6 +112,7 @@ set(headers src/OutConst.h src/OutputAbstracts.h src/OutputVars.h + src/ParallelTemperingPreprocessor.h src/PDBConst.h src/PDBOutput.h src/PDBSetup.h @@ -145,6 +150,7 @@ set(headers src/cbmc/DCRotateOnAtom.h src/cbmc/DCSingle.h src/cbmc/TrialMol.h + src/moves/CFCMC.h src/moves/CrankShaft.h src/moves/IntraMoleculeExchange1.h src/moves/IntraMoleculeExchange2.h @@ -155,6 +161,7 @@ set(headers src/moves/MoleculeExchange3.h src/moves/MoleculeTransfer.h src/moves/MoveBase.h + src/moves/MultiParticle.h src/moves/Regrowth.h src/moves/Rotation.h src/moves/Translate.h @@ -164,6 +171,7 @@ set(libHeaders lib/BasicTypes.h lib/BitLib.h lib/GeomLib.h + lib/Lambda.h lib/NumLib.h lib/StrLib.h lib/StrStrmLib.h diff --git a/CMake/GOMCCPUSetup.cmake b/CMake/GOMCCPUSetup.cmake index e96e5d38a..a51b741a9 100644 --- a/CMake/GOMCCPUSetup.cmake +++ b/CMake/GOMCCPUSetup.cmake @@ -7,6 +7,9 @@ if(ENSEMBLE_NVT) #needed for hostname target_link_libraries(NVT ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(NVT ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_GEMC) @@ -18,6 +21,9 @@ if(ENSEMBLE_GEMC) #needed for hostname target_link_libraries(GEMC ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GEMC ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_GCMC) @@ -29,6 +35,9 @@ if(ENSEMBLE_GCMC) #needed for hostname target_link_libraries(GCMC ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GCMC ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_NPT) @@ -40,5 +49,8 @@ if(ENSEMBLE_NPT) #needed for hostname target_link_libraries(NPT ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(NPT ${MPI_LIBRARIES}) + endif() endif() diff --git a/CMake/GOMCCUDASetup.cmake b/CMake/GOMCCUDASetup.cmake index b07de57c9..8333f8e2a 100644 --- a/CMake/GOMCCUDASetup.cmake +++ b/CMake/GOMCCUDASetup.cmake @@ -7,7 +7,13 @@ set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-Xcompiler -D__CORRECT_ISO_CPP11_MATH_H_P set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode arch=compute_30,code=sm_30) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode arch=compute_35,code=sm_35) set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode arch=compute_50,code=sm_50) +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode=arch=compute_70,code=sm_70) +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode=arch=compute_70,code=compute_70) +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode=arch=compute_75,code=sm_75) +set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS};-gencode=arch=compute_75,code=compute_75) include_directories(src/GPU) +#set(CUDA_VERBOSE_BUILD ON) +set(CUDA_SEPARABLE_COMPILATION ON) set(GPU_NPT_flags "-DENSEMBLE=4 -DGOMC_CUDA") set(GPU_NPT_name "GOMC_GPU_NPT") @@ -29,6 +35,9 @@ if(ENSEMBLE_GPU_NVT) #needed for hostname target_link_libraries(GPU_NVT ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GPU_NVT ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_GPU_GEMC) @@ -41,6 +50,9 @@ if(ENSEMBLE_GPU_GEMC) #needed for hostname target_link_libraries(GPU_GEMC ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GPU_GEMC ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_GPU_GCMC) @@ -53,6 +65,9 @@ if(ENSEMBLE_GPU_GCMC) #needed for hostname target_link_libraries(GPU_GCMC ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GPU_GCMC ${MPI_LIBRARIES}) + endif() endif() if(ENSEMBLE_GPU_NPT) @@ -65,4 +80,7 @@ if(ENSEMBLE_GPU_NPT) #needed for hostname target_link_libraries(GPU_NPT ws2_32) endif() + if(MPI_FOUND) + target_link_libraries(GPU_NPT ${MPI_LIBRARIES}) + endif() endif() diff --git a/CMake/GOMCManageMPI.cmake b/CMake/GOMCManageMPI.cmake new file mode 100644 index 000000000..3da7dd03e --- /dev/null +++ b/CMake/GOMCManageMPI.cmake @@ -0,0 +1,176 @@ +# +# This file is part of the GROMACS molecular simulation package. +# +# Copyright (c) 2012,2013,2014,2015,2016,2019, by the GROMACS development team, led by +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, +# and including many others, as listed in the AUTHORS file in the +# top-level source directory and at http://www.gromacs.org. +# +# GROMACS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# GROMACS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with GROMACS; if not, see +# http://www.gnu.org/licenses, or write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# If you want to redistribute modifications to GROMACS, please +# consider that scientific software is very special. Version +# control is crucial - bugs must be traceable. We will be happy to +# consider code for inclusion in the official distribution, but +# derived work must not be called official GROMACS. Details are found +# in the README & COPYING files - if they are missing, get the +# official version at http://www.gromacs.org. +# +# To help us fund GROMACS development, we humbly ask that you cite +# the research papers on the package. Check out http://www.gromacs.org. + +# Manage the MPI setup, assuming that CMAKE_C_COMPILER is an MPI +# (wrapper) compiler. +if(GOMC_MPI) + if(GOMC_THREAD_MPI) + message(STATUS "MPI is not compatible with thread-MPI. Disabling thread-MPI.") + set(GOMC_THREAD_MPI OFF CACHE BOOL + "Build a thread-MPI-based multithreaded version of GOMC (not compatible with MPI)" FORCE) + endif() + + # Test the CMAKE_C_COMPILER for being an MPI (wrapper) compiler + TRY_COMPILE(MPI_FOUND ${CMAKE_BINARY_DIR} + "${CMAKE_SOURCE_DIR}/CMake/TestMPI.cpp" + COMPILE_DEFINITIONS ) + + # If CMAKE_C_COMPILER is not a MPI wrapper. Try to find MPI using cmake module as fall-back. + if(NOT MPI_FOUND) + find_package(MPI) + if(MPI_C_FOUND) + set(MPI_COMPILE_FLAGS ${MPI_C_COMPILE_FLAGS}) + set(MPI_LINKER_FLAGS ${MPI_C_LINK_FLAGS}) + include_directories(SYSTEM ${MPI_C_INCLUDE_PATH}) + list(APPEND GOMC_COMMON_LIBRARIES ${MPI_C_LIBRARIES}) + endif() + set(MPI_FOUND ${MPI_C_FOUND}) + else() + # The following defaults are based on FindMPI.cmake in cmake + # 3.1.2. (That package does not actually do any detection of the + # flags, but if it ever does then we should re-visit how we use + # the package.) If we are compiling with an MPI wrapper + # compiler, then MPI_FOUND will be set above, and will mean that + # none of these cache variables are populated by the package. We + # need to do it manually so that test drivers can work using the + # standard machinery for CMake + FindMPI.cmake. Users will need + # to set these to suit their MPI setup in order for tests to + # work. + + find_program(MPIEXEC + NAMES mpiexec mpirun lamexec srun aprun poe + HINTS ${MPI_HOME} $ENV{MPI_HOME} + PATH_SUFFIXES bin + DOC "Executable for running MPI programs.") + + set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC; the next option will be the number of processes.") + set(MPIEXEC_PREFLAGS "" CACHE STRING "These flags will be directly before the executable that is being run by MPIEXEC.") + set(MPIEXEC_POSTFLAGS "" CACHE STRING "These flags will come after all flags given to MPIEXEC.") + set(MPIEXEC_MAX_NUMPROCS "2" CACHE STRING "Maximum number of processors available to run MPI applications.") + mark_as_advanced(MPIEXEC MPIEXEC_NUMPROC_FLAG MPIEXEC_PREFLAGS MPIEXEC_POSTFLAGS MPIEXEC_MAX_NUMPROCS) + endif() + + if(MPI_FOUND) + include(${PROJECT_SOURCE_DIR}/CMake/GOMCTestMPI_IN_PLACE.cmake) + if (GOMC_MPI_IN_PLACE) + GOMC_test_mpi_in_place(MPI_IN_PLACE_EXISTS) + endif() + + # Find path of the mpi compilers + if (${MPI_C_FOUND}) + get_filename_component(_mpi_c_compiler_path "${MPI_C_COMPILER}" PATH) + get_filename_component(_mpiexec_path "${MPIEXEC}" PATH) + else() + get_filename_component(_cmake_c_compiler_path "${CMAKE_C_COMPILER}" PATH) + get_filename_component(_cmake_cxx_compiler_path "${CMAKE_CXX_COMPILER}" PATH) + endif() + + # Test for and warn about unsuitable MPI versions + # + # Execute the ompi_info binary with the full path of the compiler wrapper + # found, otherwise we run the risk of false positives. + find_file(MPI_INFO_BIN ompi_info + HINTS ${_mpi_c_compiler_path} ${_mpiexec_path} + ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path} + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + if (MPI_INFO_BIN) + exec_program(${MPI_INFO_BIN} + ARGS -v ompi full + OUTPUT_VARIABLE OPENMPI_TYPE + RETURN_VALUE OPENMPI_EXEC_RETURN) + if(OPENMPI_EXEC_RETURN EQUAL 0) + string(REGEX REPLACE ".*Open MPI: \([0-9]+\\.[0-9]*\\.?[0-9]*\).*" "\\1" OPENMPI_VERSION ${OPENMPI_TYPE}) + if(OPENMPI_VERSION VERSION_LESS "1.4.1") + MESSAGE(WARNING + "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. " + "There are known problems with GOMC and OpenMPI version < 1.4.1. " + "Please consider updating your OpenMPI if your MPI wrapper compilers " + "are using the above OpenMPI version.") + endif() + if(OPENMPI_VERSION VERSION_EQUAL "1.8.6") + MESSAGE(WARNING + "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. " + "This OpenMPI version is known to leak memory with GOMC," + "please update to a more recent version. ") + endif() + unset(OPENMPI_VERSION) + unset(OPENMPI_TYPE) + unset(OPENMPI_EXEC_RETURN) + endif() + endif() + unset(MPI_INFO_BIN CACHE) + + # Execute the mpiname binary with the full path of the compiler wrapper + # found, otherwise we run the risk of false positives. + find_file(MPINAME_BIN mpiname + HINTS ${_mpi_c_compiler_path} + ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path} + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + if (MPINAME_BIN) + exec_program(${MPINAME_BIN} + ARGS -n -v + OUTPUT_VARIABLE MVAPICH2_TYPE + RETURN_VALUE MVAPICH2_EXEC_RETURN) + if(MVAPICH2_EXEC_RETURN EQUAL 0) + string(REGEX MATCH "MVAPICH2" MVAPICH2_NAME ${MVAPICH2_TYPE}) + # Want to check for MVAPICH2 in case some other library supplies mpiname + string(REGEX REPLACE "MVAPICH2 \([0-9]+\\.[0-9]*[a-z]?\\.?[0-9]*\)" "\\1" MVAPICH2_VERSION ${MVAPICH2_TYPE}) + if(${MVAPICH2_NAME} STREQUAL "MVAPICH2" AND MVAPICH2_VERSION VERSION_LESS "1.5") + # This test works correctly even with 1.5a1 + MESSAGE(WARNING + "CMake found MVAPICH2 version ${MVAPICH2_VERSION} on your system. " + "There are known problems with GOMC and MVAPICH2 version < 1.5. " + "Please consider updating your MVAPICH2 if your MPI wrapper compilers " + "are using the above MVAPICH2 version.") + endif() + unset(MVAPICH2_VERSION) + unset(MVAPICH2_NAME) + unset(MVAPICH2_TYPE) + unset(MVAPICH2_EXEC_RETURN) + endif() + endif() + unset(MPINAME_BIN CACHE) + else() + message(FATAL_ERROR + "MPI support requested, but no MPI compiler found. Either set the " + "C-compiler (CMAKE_C_COMPILER) to the MPI compiler (often called mpicc), " + "or set the variables reported missing for MPI_C above.") + endif() + + set(GOMC_LIB_MPI 1) +endif() diff --git a/CMake/GOMCOptionUtilities.cmake b/CMake/GOMCOptionUtilities.cmake new file mode 100644 index 000000000..2b92e0df9 --- /dev/null +++ b/CMake/GOMCOptionUtilities.cmake @@ -0,0 +1,231 @@ +# +# This file is part of the GROMACS molecular simulation package. +# +# Copyright (c) 2013,2014,2015,2017, by the GROMACS development team, led by +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, +# and including many others, as listed in the AUTHORS file in the +# top-level source directory and at http://www.gromacs.org. +# +# GROMACS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# GROMACS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with GROMACS; if not, see +# http://www.gnu.org/licenses, or write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# If you want to redistribute modifications to GROMACS, please +# consider that scientific software is very special. Version +# control is crucial - bugs must be traceable. We will be happy to +# consider code for inclusion in the official distribution, but +# derived work must not be called official GROMACS. Details are found +# in the README & COPYING files - if they are missing, get the +# official version at http://www.gromacs.org. +# +# To help us fund GROMACS development, we humbly ask that you cite +# the research papers on the package. Check out http://www.gromacs.org. + +# Helper functions for managing more complex options +# + +# Creates a string cache variable with multiple choices +# +# Usage: +# GOMC_option_multichoice(VAR "Description" DEFAULT_VALUE +# Value1 Value2 ... ValueN) +# Output: +# VAR is set in the cache and in the caller's scope. The caller can assume +# that it is always one of the provided values, converted to uppercase. +# +# Main benefit is that the list of allowed values only needs to be provided +# once, and gets used in multiple contexts: +# 1. It is automatically added to the description. +# 2. It is set as the STRINGS property of the created cache variable for use +# with CMake GUIs. +# 3. The user-provided value is checked against the list, and a fatal error +# is produced if the value is not known. The caller does not need to +# produce good error messages in cases where it may be necessary to check +# for the validity again. +# As a special case, any "[built-in]" string in the allowed values is ignored +# when checking the user-provided value, but is added to all user-visible +# messages. +# +# It appears that ccmake does not use the STRINGS property, but perhaps some +# day... +# +function(GOMC_OPTION_MULTICHOICE NAME DESCRIPTION DEFAULT) + # Some processing of the input values + string(REPLACE ";" ", " _allowed_comma_separated "${ARGN}") + set(_description "${DESCRIPTION}. Pick one of: ${_allowed_comma_separated}") + string(REPLACE "[built-in]" "" _allowed "${ARGN}") + + # Set the cache properties + set(${NAME} ${DEFAULT} CACHE STRING "${_description}") + set_property(CACHE ${NAME} PROPERTY STRINGS ${_allowed}) + + # Check that the value is one of the allowed + set(_org_value "${${NAME}}") + string(TOUPPER "${${NAME}}" ${NAME}) + string(TOUPPER "${_allowed}" _allowed_as_upper) + list(FIND _allowed_as_upper "${${NAME}}" _found_index) + if (_found_index EQUAL -1) + message(FATAL_ERROR "Invalid value for ${NAME}: ${_org_value}. " + "Pick one of: ${_allowed_comma_separated}") + endif() + # Always provide the upper-case value to the caller + set(${NAME} "${${NAME}}" PARENT_SCOPE) +endfunction() + +# Convenience function for reporting a fatal error for an invalid input value +function(GOMC_INVALID_OPTION_VALUE NAME) + message(FATAL_ERROR "Invalid value for ${NAME}: ${${NAME}}") +endfunction() + +# Declares a cache variable with ON/OFF/AUTO values +# +# Usage: +# GOMC_option_trivalue(VAR "Description" DEFAULT) +# +# Output: +# VAR is created in the cache, and the caller can assume that the value is +# always one of ON/OFF/AUTO. Additionally, VAR_AUTO is set if value is AUTO, +# and VAR_FORCE is set if value is ON. +# These make it convenient to check for any combination of states with simple +# if() statements (simple if(VAR) matches AUTO and ON). +function(GOMC_OPTION_TRIVALUE NAME DESCRIPTION DEFAULT) + set(_description "${DESCRIPTION}. ON/OFF/AUTO") + set(${NAME} ${DEFAULT} CACHE STRING "${_description}") + set_property(CACHE ${NAME} PROPERTY STRINGS ON OFF AUTO) + + set(${NAME}_AUTO OFF) + set(${NAME}_FORCE OFF) + string(TOUPPER "${${NAME}}" ${NAME}) + if ("${${NAME}}" STREQUAL "AUTO") + set(${NAME}_AUTO ON) + elseif (${NAME}) + set(${NAME}_FORCE ON) + set(${NAME} ON) + else() + set(${NAME} OFF) + endif() + # Always provide the sanitized value to the caller + set(${NAME} "${${NAME}}" PARENT_SCOPE) + set(${NAME}_AUTO "${${NAME}_AUTO}" PARENT_SCOPE) + set(${NAME}_FORCE "${${NAME}_FORCE}" PARENT_SCOPE) +endfunction() + +# Hides or shows a cache value based on conditions +# +# Usage: +# GOMC_add_cache_dependency(VAR TYPE CONDITIONS VALUE) +# where +# VAR is a name of a cached variable +# TYPE is the type of VAR +# CONDITIONS is a list of conditional expressions (see below) +# VALUE is a value that is set to VAR if CONDITIONS is not satisfied +# +# Evaluates each condition in CONDITIONS, and if any of them is false, +# VAR is marked internal (hiding it from the user) and its value is set to +# VALUE. The previous user-set value of VAR is still remembered in the cache, +# and used when CONDITIONS become true again. +# +# The conditions is a semicolon-separated list of conditions as specified for +# CMake if() statements, such as "GOMC_FFT_LIBRARY STREQUAL FFTW3", +# "NOT GOMC_MPI" or "GOMC_MPI;NOT GOMC_DOUBLE". Note that quotes within the +# expressions don't work for some reason (even if escaped). +# +# The logic is adapted from cmake_dependent_option(). +# +function(GOMC_ADD_CACHE_DEPENDENCY NAME TYPE CONDITIONS FORCED_VALUE) + set(_available TRUE) + foreach(_cond ${CONDITIONS}) + string(REGEX REPLACE " +" ";" _cond_as_list ${_cond}) + if (${_cond_as_list}) + else() + set(_available FALSE) + endif() + endforeach() + if (_available) + set_property(CACHE ${NAME} PROPERTY TYPE ${TYPE}) + else() + set(${NAME} "${FORCED_VALUE}" PARENT_SCOPE) + set_property(CACHE ${NAME} PROPERTY TYPE INTERNAL) + endif() +endfunction() + +# Works like cmake_dependent_option(), but allows for an arbitrary cache value +# instead of only an ON/OFF option +# +# Usage: +# GOMC_dependent_cache_variable(VAR "Description" TYPE DEFAULT CONDITIONS) +# +# Creates a cache variable VAR with the given description, type and default +# value. If any of the conditions listed in CONDITIONS is not true, then +# the cache variable is marked internal (hiding it from the user) and the +# value of VAR is set to DEFAULT. The previous user-set value of VAR is still +# remembered in the cache, and used when CONDITIONS become true again. +# Any further changes to the variable can be done with simple set() +# (or set_property(CACHE VAR PROPERTY VALUE ...) if the cache needs to be +# modified). +# +# See GOMC_add_cache_dependency() on how to specify the conditions. +# +macro(GOMC_DEPENDENT_CACHE_VARIABLE NAME DESCRIPTION TYPE DEFAULT CONDITIONS) + set(${NAME} "${DEFAULT}" CACHE ${TYPE} "${DESCRIPTION}") + GOMC_add_cache_dependency(${NAME} ${TYPE} "${CONDITIONS}" "${DEFAULT}") +endmacro() + +# Works like cmake_dependent_option(), but reuses the code from +# GOMC_dependent_cache_variable() to make sure both behave the same way. +macro(GOMC_DEPENDENT_OPTION NAME DESCRIPTION DEFAULT CONDITIONS) + GOMC_dependent_cache_variable(${NAME} "${DESCRIPTION}" BOOL "${DEFAULT}" "${CONDITIONS}") +endmacro() + +# Sets a boolean variable based on conditions +# +# Usage: +# GOMC_set_boolean(VAR CONDITIONS) +# +# Sets VAR to ON if all conditions listed in CONDITIONS are true, otherwise +# VAR is set OFF. +# +# See GOMC_add_cache_dependency() on how to specify the conditions. +# +function (GOMC_SET_BOOLEAN NAME CONDITIONS) + set(${NAME} ON) + foreach(_cond ${CONDITIONS}) + string(REGEX REPLACE " +" ";" _cond_as_list ${_cond}) + if (${_cond_as_list}) + else() + set(${NAME} OFF) + endif() + endforeach() + set(${NAME} ${${NAME}} PARENT_SCOPE) +endfunction() + +# Checks if one or more variables have changed since last call to this function +# +# Usage: +# GOMC_check_if_changed(RESULT VAR1 VAR2 ... VARN) +# +# Sets RESULT to true if any of the given variables VAR1 ... VARN has +# changed since the last call to this function for that variable. +# Changes are tracked also across CMake runs. +function(GOMC_CHECK_IF_CHANGED RESULT) + set(_result FALSE) + foreach (_var ${ARGN}) + if (NOT "${${_var}}" STREQUAL "${${_var}_PREVIOUS_VALUE}") + set(_result TRUE) + endif() + set(${_var}_PREVIOUS_VALUE "${${_var}}" CACHE INTERNAL + "Previous value of ${_var} for change tracking") + endforeach() + set(${RESULT} ${_result} PARENT_SCOPE) +endfunction() diff --git a/CMake/GOMCTestMPI_IN_PLACE.cmake b/CMake/GOMCTestMPI_IN_PLACE.cmake new file mode 100644 index 000000000..b5be1bced --- /dev/null +++ b/CMake/GOMCTestMPI_IN_PLACE.cmake @@ -0,0 +1,74 @@ +# +# This file is part of the GROMACS molecular simulation package. +# +# Copyright (c) 2009,2011,2012,2014,2015,2016, by the GROMACS development team, led by +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, +# and including many others, as listed in the AUTHORS file in the +# top-level source directory and at http://www.gromacs.org. +# +# GROMACS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# GROMACS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with GROMACS; if not, see +# http://www.gnu.org/licenses, or write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# If you want to redistribute modifications to GROMACS, please +# consider that scientific software is very special. Version +# control is crucial - bugs must be traceable. We will be happy to +# consider code for inclusion in the official distribution, but +# derived work must not be called official GROMACS. Details are found +# in the README & COPYING files - if they are missing, get the +# official version at http://www.gromacs.org. +# +# To help us fund GROMACS development, we humbly ask that you cite +# the research papers on the package. Check out http://www.gromacs.org. + +# - Define macro to check if MPI_IN_PLACE exists +# +# GOMC_TEST_MPI_IN_PLACE(VARIABLE) +# +# VARIABLE will be set to true if MPI_IN_PLACE exists +# + +include(CheckCSourceCompiles) +MACRO(GOMC_TEST_MPI_IN_PLACE VARIABLE) + if(NOT DEFINED MPI_IN_PLACE_COMPILE_OK) + MESSAGE(STATUS "Checking for MPI_IN_PLACE") + + set(CMAKE_REQUIRED_FLAGS ${MPI_COMPILE_FLAGS}) + set(CMAKE_REQUIRED_INCLUDES ${MPI_INCLUDE_PATH}) + set(CMAKE_REQUIRED_LIBRARIES ${MPI_LIBRARIES}) + check_cxx_source_compiles( + "#include +int main(void) { + void* buf; + MPI_Allreduce(MPI_IN_PLACE, buf, 10, MPI_FLOAT, MPI_SUM, MPI_COMM_WORLD); +}" MPI_IN_PLACE_COMPILE_OK) + + if(MPI_IN_PLACE_COMPILE_OK) + MESSAGE(STATUS "Checking for MPI_IN_PLACE - yes") + else() + MESSAGE(STATUS "Checking for MPI_IN_PLACE - no") + endif() + set(MPI_IN_PLACE_COMPILE_OK "${MPI_IN_PLACE_COMPILE_OK}" CACHE INTERNAL "Result of mpi_in_place check") + set(CMAKE_REQUIRED_FLAGS) + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) + endif() + if (MPI_IN_PLACE_COMPILE_OK) + set(${VARIABLE} ${MPI_IN_PLACE_COMPILE_OK} + "Result of test for MPI_IN_PLACE") + endif() +ENDMACRO(GOMC_TEST_MPI_IN_PLACE VARIABLE) + + + diff --git a/CMake/TestMPI.cpp b/CMake/TestMPI.cpp new file mode 100644 index 000000000..ea22249f0 --- /dev/null +++ b/CMake/TestMPI.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) +{ + MPI_Init(&argc, &argv); +} diff --git a/CMake/TestWinProcNum.cpp b/CMake/TestWinProcNum.cpp new file mode 100644 index 000000000..20dd38a22 --- /dev/null +++ b/CMake/TestWinProcNum.cpp @@ -0,0 +1,7 @@ +#define _WIN32_WINNT 0x0601 /*Require Windows7 (needed for MingW)*/ +#include +int main() +{ + PROCESSOR_NUMBER p; + return 0; +} diff --git a/CMake/ThreadMPI.cmake b/CMake/ThreadMPI.cmake new file mode 100644 index 000000000..411b5566b --- /dev/null +++ b/CMake/ThreadMPI.cmake @@ -0,0 +1,205 @@ +# This source code file is part of thread_mpi. +# Written by Sander Pronk, Erik Lindahl, and possibly others. +# +# Copyright (c) 2009, Sander Pronk, Erik Lindahl. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1) Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2) Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3) Neither the name of the copyright holders nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# If you want to redistribute modifications, please consider that +# scientific software is very special. Version control is crucial - +# bugs must be traceable. We will be happy to consider code for +# inclusion in the official distribution, but derived work should not +# be called official thread_mpi. Details are found in the README & COPYING +# files. + +include(CheckIncludeFileCXX) +include(CheckCXXSymbolExists) +include(CheckCXXSourceCompiles) + +# sets TMPI_ATOMICS to 1 if atomic operations are found, unset otherwise +# Options: +# include directory for thread_mpi/atomic.h +MACRO(TMPI_TEST_ATOMICS INCDIR) + + if (NOT DEFINED TMPI_ATOMICS) + try_compile(TEST_ATOMICS "${CMAKE_BINARY_DIR}" + "${CMAKE_SOURCE_DIR}/CMake/TestAtomics.cpp" + COMPILE_DEFINITIONS "-I${INCDIR} -DTMPI_ATOMICS") + if (TEST_ATOMICS) + message(STATUS "Atomic operations found") + # If the check fails, we want to be able to check again, + # in case the user has been able to fix this without + # needing to delete the cache. Thus we only cache + # positive results. + set(TMPI_ATOMICS ${TEST_ATOMICS} CACHE INTERNAL "Whether atomic operations are found") + set(TMPI_ATOMICS_INCDIR ${INCDIR} CACHE INTERNAL "Atomic operations check include dir") + else () + message(STATUS "Atomic operations not found") + unset(TEST_ATOMICS) + endif() + endif() + +ENDMACRO(TMPI_TEST_ATOMICS VARIABLE) + +try_compile(HAVE_PROCESSOR_NUMBER ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/CMake/TestWinProcNum.cpp") + +include(FindThreads) + +if(CMAKE_USE_WIN32_THREADS_INIT AND NOT HAVE_PROCESSOR_NUMBER) + message(WARNING "Incomplete Windows Processor Group API. If you want GOMC to be able to set thread affinity, choose a Mingw distribution with a complete API (e.g. Mingw-w64).") +endif() + +if (CMAKE_USE_WIN32_THREADS_INIT AND HAVE_PROCESSOR_NUMBER) + set(THREAD_WINDOWS 1) + set(THREAD_LIB) +elseif (CMAKE_USE_PTHREADS_INIT) + check_include_file_cxx(pthread.h HAVE_PTHREAD_H) + set(THREAD_PTHREADS 1) + set(THREAD_LIB ${CMAKE_THREAD_LIBS_INIT}) +else() + message(FATAL_ERROR "Thread support required") +endif () + +# Turns on thread_mpi core threading functions. +MACRO(TMPI_ENABLE_CORE INCDIR) + TMPI_TEST_ATOMICS(${INCDIR}) + +# affinity checks + include(CheckFunctionExists) + if (THREAD_PTHREADS) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + # check for sched_setaffinity + check_cxx_source_compiles( + "#define _GNU_SOURCE +#include +#include +#include +#include + int main(void) { cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(0, &set); + pthread_setaffinity_np(pthread_self(), sizeof(set), &set); + return 0; + }" + PTHREAD_SETAFFINITY + ) + if (PTHREAD_SETAFFINITY) + set(HAVE_PTHREAD_SETAFFINITY 1) + endif () + set(CMAKE_REQUIRED_LIBRARIES) + endif () + + +# this runs on POSIX systems + check_include_file_cxx(unistd.h HAVE_UNISTD_H) + check_include_file_cxx(sched.h HAVE_SCHED_H) + check_include_file_cxx(sys/time.h HAVE_SYS_TIME_H) + check_cxx_symbol_exists(sysconf unistd.h HAVE_SYSCONF) +# this runs on windows +#check_include_files(windows.h HAVE_WINDOWS_H) +ENDMACRO(TMPI_ENABLE_CORE) + +# enable C++ library build. +set(TMPI_CXX_LIB 1) + +# Turns on thread_mpi MPI functions. +MACRO(TMPI_ENABLE) + TMPI_TEST_ATOMICS(TMPI_ATOMICS_INCDIR) + if(NOT DEFINED TMPI_ATOMICS) + message(WARNING "Atomic operations not found for this CPU+compiler combination. Thread support will be unbearably slow: disable threads. Atomic operations should work on all but the most obscure CPU+compiler combinations; if your system is not obscure -- like, for example, x86 with gcc -- please contact the developers.") + endif() + + set(TMPI_ENABLED 1) + +# the spin-waiting option + option(THREAD_MPI_WAIT_FOR_NO_ONE "Use busy waits without yielding to the OS scheduler. Turning this on might improve performance (very) slightly at the cost of very poor performance if the threads are competing for CPU time." OFF) + mark_as_advanced(THREAD_MPI_WAIT_FOR_NO_ONE) + if (THREAD_MPI_WAIT_FOR_NO_ONE) + set(TMPI_WAIT_FOR_NO_ONE 1) + else () + set(TMPI_WAIT_FOR_NO_ONE 0) + endif () + +# the copy buffer option + option(THREAD_MPI_COPY_BUFFER "Use an intermediate copy buffer for small message sizes, to allow blocking sends to return quickly. Only useful in programs with relatively uncoupled threads (infrequent MPI communication)" OFF) + mark_as_advanced(THREAD_MPI_COPY_BUFFER) + if (THREAD_MPI_COPY_BUFFER) + set(TMPI_COPY_BUFFER 1) + else () + set(TMPI_COPY_BUFFER 0) + endif () + +# the profiling option + option(THREAD_MPI_PROFILING "Turn on simple MPI profiling." OFF) + mark_as_advanced(THREAD_MPI_PROFILING) + if (THREAD_MPI_PROFILING) + set(TMPI_PROFILE 1) + else () + set(TMPI_PROFILE 0) + endif () + +# tmpi warnings for testing + option(THREAD_MPI_WARNINGS "Turn thread_mpi warnings for testing." OFF) + mark_as_advanced(THREAD_MPI_WARNINGS) + if (THREAD_MPI_WARNINGS) + set(TMPI_WARNINGS 1) + else () + set(TMPI_WARNINGS 0) + endif () +ENDMACRO(TMPI_ENABLE) + + +MACRO(TMPI_GET_SOURCE_LIST SRC_VARIABLE SRC_ROOT) + set(${SRC_VARIABLE} + ${SRC_ROOT}/errhandler.cpp + ${SRC_ROOT}/tmpi_malloc.cpp + ${SRC_ROOT}/atomic.cpp + ${SRC_ROOT}/lock.cpp) + + if (THREAD_PTHREADS) + list(APPEND ${SRC_VARIABLE} ${SRC_ROOT}/pthreads.cpp) + elseif (THREAD_WINDOWS) + list(APPEND ${SRC_VARIABLE} ${SRC_ROOT}/winthreads.cpp) + endif () + + if (TMPI_CXX_LIB) + list(APPEND ${SRC_VARIABLE} ${SRC_ROOT}/system_error.cpp) + endif () + + if (TMPI_ENABLED) + list(APPEND ${SRC_VARIABLE} + ${SRC_ROOT}/alltoall.cpp ${SRC_ROOT}/p2p_protocol.cpp + ${SRC_ROOT}/barrier.cpp ${SRC_ROOT}/p2p_send_recv.cpp + ${SRC_ROOT}/bcast.cpp ${SRC_ROOT}/p2p_wait.cpp + ${SRC_ROOT}/collective.cpp ${SRC_ROOT}/profile.cpp + ${SRC_ROOT}/comm.cpp ${SRC_ROOT}/reduce.cpp + ${SRC_ROOT}/event.cpp ${SRC_ROOT}/reduce_fast.cpp + ${SRC_ROOT}/gather.cpp ${SRC_ROOT}/scatter.cpp + ${SRC_ROOT}/group.cpp ${SRC_ROOT}/tmpi_init.cpp + ${SRC_ROOT}/topology.cpp ${SRC_ROOT}/list.cpp + ${SRC_ROOT}/type.cpp ${SRC_ROOT}/scan.cpp + ${SRC_ROOT}/numa_malloc.cpp ${SRC_ROOT}/once.cpp) + endif() +ENDMACRO(TMPI_GET_SOURCE_LIST) + diff --git a/CMakeLists.txt b/CMakeLists.txt index 90bfa2619..a85ac82e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) #Versioning set (GOMC_VERSION_MAJOR 2) -set (GOMC_VERSION_MINOR 40) +set (GOMC_VERSION_MINOR 50) IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE Release CACHE STRING @@ -34,6 +34,27 @@ set(ENSEMBLE_GPU_GEMC ON CACHE BOOL "Build GPU GEMC version") set(ENSEMBLE_GPU_GCMC ON CACHE BOOL "Build GPU GCMC version") set(ENSEMBLE_GPU_NPT ON CACHE BOOL "Build GPU NPT version") + +######################## MPI ################################## + +include(${PROJECT_SOURCE_DIR}/CMake/GOMCOptionUtilities.cmake) + +option(GOMC_MPI "Build a parallel (message-passing) version of GOMC" OFF) +option(GOMC_THREAD_MPI "Build a thread-MPI-based multithreaded version of GOMC (not compatible with MPI)" ON) + +include(${PROJECT_SOURCE_DIR}/CMake/GOMCManageMPI.cmake) +include(${PROJECT_SOURCE_DIR}/CMake/ThreadMPI.cmake) + +set(GOMC_COMMON_LIBRARIES "") +GOMC_dependent_option( + GOMC_MPI_IN_PLACE + "Enable MPI_IN_PLACE for MPIs that have it defined" + ON + GOMC_MPI) +mark_as_advanced(GOMC_MPI_IN_PLACE) + +######################## MPI ################################## + #enable config header configure_file( "${PROJECT_SOURCE_DIR}/GOMC_Config.h.in" @@ -83,7 +104,6 @@ include(${PROJECT_SOURCE_DIR}/CMake/FileLists.cmake) include(${PROJECT_SOURCE_DIR}/CMake/GOMCCPUSetup.cmake) # find CUDA and set it up -set(CUDA_SEPARABLE_COMPILATION ON) find_package(CUDA) IF(CUDA_FOUND) include(${PROJECT_SOURCE_DIR}/CMake/GOMCCUDASetup.cmake) diff --git a/GOMC_Config.h.in b/GOMC_Config.h.in index 8d78494da..33d8b5925 100644 --- a/GOMC_Config.h.in +++ b/GOMC_Config.h.in @@ -1,4 +1,17 @@ // Configuration file generated by CMake from GOMC_Config.h.in #define GOMC_VERSION_MAJOR @GOMC_VERSION_MAJOR@ -#define GOMC_VERSION_MINOR @GOMC_VERSION_MINOR@ \ No newline at end of file +#define GOMC_VERSION_MINOR @GOMC_VERSION_MINOR@ + +/* Use MPI (with mpicc) for parallelization */ +#cmakedefine01 GOMC_LIB_MPI + +/* Use threads_mpi for parallelization */ +#cmakedefine01 GOMC_THREAD_MPI + +/* Make a parallel version of GOMC using message passing + (MPI or thread_mpi) */ +#define GOMC_MPI (GOMC_LIB_MPI || GOMC_THREAD_MPI) + +/* MPI_IN_PLACE exists for collective operations */ +#cmakedefine01 MPI_IN_PLACE_EXISTS \ No newline at end of file diff --git a/README.md b/README.md index c03e65776..509c63a8f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GOMC - GPU Optimized Monte Carlo -Current Release: 2.40 (5/9/2019) +Current Release: 2.50 (1/20/2020) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/GOMC_WSU/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) [![Build Status](https://travis-ci.org/GOMC-WSU/GOMC.svg?branch=master)](https://travis-ci.org/GOMC-WSU/GOMC) diff --git a/lib/BasicTypes.h b/lib/BasicTypes.h index 9d361b9b2..efbdd8d91 100644 --- a/lib/BasicTypes.h +++ b/lib/BasicTypes.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -30,6 +30,12 @@ struct XYZ { z = rhs.z; return *this; } + bool operator!=(XYZ const& rhs) + { + if(x != rhs.x || y != rhs.y || z != rhs.z) + return true; + return false; + } XYZ& operator+=(XYZ const& rhs) { x += rhs.x; diff --git a/lib/BitLib.h b/lib/BitLib.h index f8e6240e3..a59ba2414 100644 --- a/lib/BitLib.h +++ b/lib/BitLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/lib/GeomLib.h b/lib/GeomLib.h index c0fa7a4bf..bb79899f5 100644 --- a/lib/GeomLib.h +++ b/lib/GeomLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/lib/Lambda.h b/lib/Lambda.h new file mode 100644 index 000000000..0d62b6673 --- /dev/null +++ b/lib/Lambda.h @@ -0,0 +1,144 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef LAMBDA_H +#define LAMBDA_H + +#include "BasicTypes.h" //For ulong, uint + +//Defining lambda class to handle fractional molecule +class Lambda +{ +public: + Lambda() + { + std::fill_n(isFraction, BOX_TOTAL, false); + std::fill_n(lambdaVDW, BOX_TOTAL, 1.0); + std::fill_n(lambdaCoulomb, BOX_TOTAL, 1.0); + } + + void Set(const double vdw, const double coulomb, const uint mol, + const uint kind, const uint box); + + void UnSet(const uint sourceBox, const uint destBox); + + double GetLambdaVDW(const uint mol, const uint kind, const uint box) const; + + double GetLambdaVDW(const uint kind, const uint box) const; + + double GetLambdaCoulomb(const uint mol, const uint kind, const uint box) const; + + double GetLambdaCoulomb(const uint kind, const uint box) const; + + bool KindIsFractional(const uint kind, const uint box) const; + + uint GetKind(const uint box) const; + + uint GetMolIndex(const uint box) const; + + +protected: + uint molIndex[BOX_TOTAL]; + uint kindIndex[BOX_TOTAL]; + double lambdaVDW[BOX_TOTAL], lambdaCoulomb[BOX_TOTAL]; + bool isFraction[BOX_TOTAL]; +}; + +inline void Lambda::Set(const double vdw, const double coulomb, const uint mol, + const uint kind, const uint box) +{ + molIndex[box] = mol; + kindIndex[box] = kind; + lambdaVDW[box] = vdw; + lambdaCoulomb[box] = coulomb; + isFraction[box] = true; +} + +inline void Lambda::UnSet(const uint sourceBox, const uint destBox) +{ + isFraction[sourceBox] = isFraction[destBox] = false; + lambdaVDW[sourceBox] = lambdaCoulomb[sourceBox] = 1.0; + lambdaVDW[destBox] = lambdaCoulomb[destBox] = 0.0; +} + +inline double Lambda::GetLambdaVDW(const uint mol, const uint kind, + const uint box) const +{ + double val = 1.0; + if(isFraction[box]) { + if((molIndex[box] == mol) && (kindIndex[box] == kind)) { + val = lambdaVDW[box]; + } + } + return val; +} + +inline double Lambda::GetLambdaVDW(const uint kind, const uint box) const +{ + double val = 1.0; + if(isFraction[box]) { + if(kindIndex[box] == kind) { + val = lambdaVDW[box]; + } + } + return val; +} + +inline double Lambda::GetLambdaCoulomb(const uint mol, const uint kind, + const uint box) const +{ + double val = 1.0; + if(isFraction[box]) { + if((molIndex[box] == mol) && (kindIndex[box] == kind)) { + val = lambdaCoulomb[box]; + } + } + return val; +} + +inline double Lambda::GetLambdaCoulomb(const uint kind, const uint box) const +{ + double val = 1.0; + if(isFraction[box]) { + if(kindIndex[box] == kind) { + val = lambdaCoulomb[box]; + } + } + return val; +} + +inline bool Lambda::KindIsFractional(const uint kind, const uint box) const +{ + bool result = false; + if(isFraction[box]) { + if(kindIndex[box] == kind) { + result = true; + } + } + return result; +} + +inline uint Lambda::GetKind(const uint box) const +{ + if(isFraction[box]) { + return kindIndex[box]; + } else { + std::cout << "Error: Lambda.h, calling GetKind\n"; + exit(EXIT_FAILURE); + } +} + +inline uint Lambda::GetMolIndex(const uint box) const +{ + if(isFraction[box]) { + return molIndex[box]; + } else { + std::cout << "Error: Lambda.h, calling GetKind\n"; + exit(EXIT_FAILURE); + } +} + +#endif \ No newline at end of file diff --git a/lib/NumLib.h b/lib/NumLib.h index 2722a5ef8..aca1a75d4 100644 --- a/lib/NumLib.h +++ b/lib/NumLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -10,8 +10,12 @@ along with this program, also can be found at . #include //for double limits #include //for vector average #include "BasicTypes.h" //For uint, XYZ +#include +#include +#ifndef DBL_MAX #define DBL_MAX 1.7976931348623158e+308 +#endif namespace num { @@ -81,6 +85,13 @@ inline double MeanG(std::vector const& v1, return sqrt(v1[ix1] * v2[ix2]); } +inline double MeanG(std::vector const& v1, + std::vector const& v2, + const uint ix1, const uint ix2) +{ + return sqrt(v1[ix1] * v2[ix2]); +} + //return n! inline double Factorial(const uint n) { @@ -204,6 +215,140 @@ inline double POW(const double d2, const double d4, const double d6, } return result; } + + +//Class to define the function used in Zbrent +class Exp6Fun +{ +public: + Exp6Fun(const double a, const double s) : alpha(a), sigma(s) {} + virtual ~Exp6Fun() {}; + virtual float operator() (float x) = 0; + + float sigma, alpha, rmin; +}; + +class RminFun : public Exp6Fun +{ +public: + RminFun(double a, double s) : Exp6Fun(a, s) {} + virtual ~RminFun() {}; + virtual float operator() (float x) + { + double rep = (6.0 / alpha) * exp(alpha * (1 - sigma / x)); + double at = pow(x / sigma, 6); + return (float)(rep - at); + } +}; + +class RmaxFun : public Exp6Fun +{ +public: + RmaxFun(double a, double s, double r) : Exp6Fun(a, s) + { + rmin = r; + } + virtual ~RmaxFun() {}; + virtual float operator() (float x) + { + double rep = (-1.0 / rmin) * exp(alpha * (1 - x / rmin)); + double at = pow(rmin / x, 6) / x; + return (float)(rep + at); + } +}; + +//Using Brent’s method, find the root of a function func known to lie between x1 and x2. +//The root, returned as zbrent, will be refined until its accuracy is tol. +//Brent, R.P. 1973, Algorithms for Minimization without Derivatives (Englewood Cliffs, NJ: PrenticeHall) +//Forsythe, G.E., Malcolm, M.A., and Moler, C.B. 1977, Computer Methods for Mathematical Computations (Englewood Cliffs, NJ: Prentice-Hall) +inline double Zbrent(Exp6Fun* func, float x1, float x2, float tol) +{ + const int ITMAX = 100; //Maximum allowed number of iterations. + const float EPS = 3.0e-8; //Machine floating-point precision. + int iter; + float a = x1, b = x2, c = x2; + float d, e, min1, min2; + float fa = (*func)(a), fb = (*func)(b); + float fc, p, q, r, s, tol1, xm; + + if((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0)) { + std::cout << "Root must be bracketed in zbrent!\n"; + exit(EXIT_FAILURE); + } + + fc = fb; + + for(iter = 1; iter <= ITMAX; iter++) { + if((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) { + c = a; //Rename a, b, c and adjust bounding interval + fc = fa; + e = d = b - a; + } + + if(fabs(fc) < fabs(fb)) { + a = b; + b = c; + c = a; + fa = fb; + fb = fc; + fc = fa; + } + + tol1 = 2.0 * EPS * fabs(b) + 0.5 * tol; //Convergence check. + xm = 0.5 * (c - b); + if(fabs(xm) <= tol1 || fb == 0.0) { + return (double)b; + } + + if(fabs(e) >= tol1 && fabs(fa) > fabs(fb)) { + s = fb / fa; //Attempt inverse quadratic interpolation. + if(a == c) { + p = 2.0 * xm * s; + q = 1.0 - s; + } else { + q = fa / fc; + r = fb / fc; + p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0)); + q = (q - 1.0) * (r - 1.0) * (s - 1.0); + } + + if(p > 0.0) { + q = -q; //Check whether in bounds. + } + + p = fabs(p); + min1 = 3.0 * xm * q - fabs(tol1 * q); + min2 = fabs(e * q); + if(2.0 * p < (min1 < min2 ? min1 : min2)) { + e = d; //Accept interpolation. + d = p / q; + } else { + d = xm; //Interpolation failed, use bisection. + e = d; + } + } else { + //Bounds decreasing too slowly, use bisection. + d = xm; + e = d; + } + + a = b; //Move last best guess to a. + fa = fb; + if(fabs(d) > tol1) { //Evaluate new trial root. + b += d; + } else { + //b += SIGN(tol1, xm); + b += ((xm) >= 0.0 ? fabs(tol1) : -fabs(tol1)); + } + + fb = (*func)(b); + } + std::cout << "Maximum number of iterations exceeded in zbrent!\n"; + exit(EXIT_FAILURE); + + return 0.0; //Never get here. +} + } #endif /*NUMERIC_LIB_H*/ diff --git a/lib/StrLib.h b/lib/StrLib.h index ba5a7fb93..61737dab5 100644 --- a/lib/StrLib.h +++ b/lib/StrLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/lib/StrStrmLib.h b/lib/StrStrmLib.h index a779e2a54..17f76d188 100644 --- a/lib/StrStrmLib.h +++ b/lib/StrStrmLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/lib/VectorLib.h b/lib/VectorLib.h index 6d6fa5a54..ffc5b5d63 100644 --- a/lib/VectorLib.h +++ b/lib/VectorLib.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/metamakeMPI.sh b/metamakeMPI.sh new file mode 100755 index 000000000..91fda9459 --- /dev/null +++ b/metamakeMPI.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +if [ ! -d "./lib/cub" ]; then + cd lib + mkdir -p temp + cd temp + echo "==== GOMC needs CUB library to run..." + echo "==== Finding latest CUB library..." + + # download the download html page + wget https://nvlabs.github.io/cub/download_cub.html > /dev/null 2>&1 + + # find the lines that have the link + grep "https://github.com/NVlabs/" download_cub.html > link_lines + + # the last line is the easiest to find the link + awk '/./{line=$0} END{print line}' link_lines > last_line + + # the substring between two quotes is the link!!!! + LINK="$(awk -F'"' '{ print $2 }' last_line)" + echo "==== Link found at ${LINK}" + + # remove any temporary files + rm link_lines + rm download_cub.html + rm last_line + + # download the zip file + echo "==== Downloading the CUB library... (Shouldn't take too long)" + wget "${LINK}" > /dev/null 2>&1 + + #unzip + echo "==== Extracting the CUB library..." + for z in *.zip; do + unzip "$z" > /dev/null 2>&1 + rm "$z" > /dev/null 2>&1 + done + + # move the cub directory to and remove the rest + for d in */ ; do + mv "$d"/cub ../cub > /dev/null 2>&1 + rm -r "$d" > /dev/null 2>&1 + done + cd .. + rmdir temp + cd .. +else + echo "==== cub library already exists. Skipping..." +fi + +mkdir -p bin_MPI +cd bin_MPI +ICC_PATH="$(which icc)" +ICPC_PATH="$(which icpc)" +export CC=${ICC_PATH} +export CXX=${ICPC_PATH} +cmake .. -DGOMC_MPI=on +make diff --git a/src/BlockOutput.cpp b/src/BlockOutput.cpp index f841ea022..5d42699f9 100644 --- a/src/BlockOutput.cpp +++ b/src/BlockOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -88,10 +88,18 @@ void BlockAverage::DoWrite(const ulong step, uint precision) void BlockAverages::Init(pdb_setup::Atoms const& atoms, config_setup::Output const& output) { +#if GOMC_LIB_MPI + std::string name = pathToReplicaDirectory + "Blk_" + uniqueName + "_BOX_0.dat"; +#else std::string name = "Blk_" + uniqueName + "_BOX_0.dat"; +#endif outBlock0.open(name.c_str(), std::ofstream::out); if(BOXES_WITH_U_NB >= 2) { +#if GOMC_LIB_MPI + name = pathToReplicaDirectory + "Blk_" + uniqueName + "_BOX_1.dat"; +#else name = "Blk_" + uniqueName + "_BOX_1.dat"; +#endif outBlock1.open(name.c_str(), std::ofstream::out); } InitVals(output.statistics.settings.block); @@ -255,4 +263,4 @@ void BlockAverage::printTitle(std::string output, uint boxes) std::cerr << "Unable to write to Block_1 output file!" << std::endl; } } -} +} \ No newline at end of file diff --git a/src/BlockOutput.h b/src/BlockOutput.h index 016a0dbf7..25fd5ce56 100644 --- a/src/BlockOutput.h +++ b/src/BlockOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/BoxDimensions.cpp b/src/BoxDimensions.cpp index f401fc2ee..4d66dccc5 100644 --- a/src/BoxDimensions.cpp +++ b/src/BoxDimensions.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -67,7 +67,7 @@ void BoxDimensions::Init(config_setup::RestartSettings const& restart, cellBasis[b].Length(2)); if(axis.Get(b).Min() < 2.0 * rCut[b]) { - printf("Error: Cutoff value is large than half of minimum BOX%d length!\n", b); + printf("Error: Cutoff value is larger than half of minimum BOX%d length!\n", b); exit(EXIT_FAILURE); } //Find Cosine Angle of alpha, beta and gamma @@ -204,7 +204,6 @@ void BoxDimensions::SetVolume(const uint b, const double vol) } volume[b] = vol; volInv[b] = 1.0 / vol; - } diff --git a/src/BoxDimensions.h b/src/BoxDimensions.h index 9f37d9907..10834c564 100644 --- a/src/BoxDimensions.h +++ b/src/BoxDimensions.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/BoxDimensionsNonOrth.cpp b/src/BoxDimensionsNonOrth.cpp index 7e51c2f25..7abd28102 100644 --- a/src/BoxDimensionsNonOrth.cpp +++ b/src/BoxDimensionsNonOrth.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/BoxDimensionsNonOrth.h b/src/BoxDimensionsNonOrth.h index 8c8539e08..3131365f4 100644 --- a/src/BoxDimensionsNonOrth.h +++ b/src/BoxDimensionsNonOrth.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/CBMC.cpp b/src/CBMC.cpp index a2b84ceb5..40b2aa238 100644 --- a/src/CBMC.cpp +++ b/src/CBMC.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/CBMC.h b/src/CBMC.h index 8f14a513b..a0d954dca 100644 --- a/src/CBMC.h +++ b/src/CBMC.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/COM.h b/src/COM.h index bcbd04a8d..22a22f617 100644 --- a/src/COM.h +++ b/src/COM.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/CPUSide.cpp b/src/CPUSide.cpp index 068e05b32..784e862d5 100644 --- a/src/CPUSide.cpp +++ b/src/CPUSide.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -14,6 +14,9 @@ CPUSide::CPUSide(System & sys, StaticVals & statV) : #if ENSEMBLE == GCMC , sample_N_E(varRef) #endif +#if ENSEMBLE == NVT || ENSEMBLE == NPT + , freeEnergy(varRef, sys) +#endif {} void CPUSide::Init(PDBSetup const& pdbSet, config_setup::Output const& out, @@ -34,6 +37,9 @@ void CPUSide::Init(PDBSetup const& pdbSet, config_setup::Output const& out, #if ENSEMBLE == GCMC outObj.push_back(&hist); outObj.push_back(&sample_N_E); +#endif +#if ENSEMBLE == NVT || ENSEMBLE == NPT + outObj.push_back(&freeEnergy); #endif //Calculate pressure, heat of vap. (if applicable), etc. varRef.CalcAndConvert(0); diff --git a/src/CPUSide.h b/src/CPUSide.h index 04332dfdb..36c74162a 100644 --- a/src/CPUSide.h +++ b/src/CPUSide.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -17,6 +17,7 @@ along with this program, also can be found at . #include "OutputVars.h" #include "CheckpointOutput.h" #include "EnPartCntSampleOutput.h" +#include "FreeEnergyOutput.h" #include @@ -41,6 +42,9 @@ struct CPUSide { CheckpointOutput checkpoint; #if ENSEMBLE == GCMC EnPartCntSample sample_N_E; +#endif +#if ENSEMBLE == NVT || ENSEMBLE == NPT + FreeEnergyOutput freeEnergy; #endif OutputVars varRef; }; diff --git a/src/CalculateEnergy.cpp b/src/CalculateEnergy.cpp index d634d7721..278a29ae6 100644 --- a/src/CalculateEnergy.cpp +++ b/src/CalculateEnergy.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -44,6 +44,9 @@ using namespace geom; CalculateEnergy::CalculateEnergy(StaticVals & stat, System & sys) : forcefield(stat.forcefield), mols(stat.mol), currentCoords(sys.coordinates), currentCOM(sys.com), + atomForceRef(sys.atomForceRef), + molForceRef(sys.molForceRef), + lambdaRef(sys.lambdaRef), #ifdef VARIABLE_PARTICLE_NUMBER molLookup(sys.molLookup), #else @@ -54,7 +57,9 @@ CalculateEnergy::CalculateEnergy(StaticVals & stat, System & sys) : #else currentAxes(*stat.GetBoxDim()) #endif - , cellList(sys.cellList) {} + , cellList(sys.cellList) +{ +} void CalculateEnergy::Init(System & sys) @@ -62,6 +67,8 @@ void CalculateEnergy::Init(System & sys) uint maxAtomInMol = 0; calcEwald = sys.GetEwald(); electrostatic = forcefield.electrostatic; + ewald = forcefield.ewald; + multiParticleEnabled = sys.statV.multiParticleEnabled; for(uint m = 0; m < mols.count; ++m) { const MoleculeKind& molKind = mols.GetKind(m); if(molKind.NumAtoms() > maxAtomInMol) @@ -85,7 +92,6 @@ SystemPotential CalculateEnergy::SystemTotal() //system intra for (uint b = 0; b < BOX_TOTAL; ++b) { - pot.boxVirial[b] = ForceCalc(b); int i; double bondEnergy[2] = {0}; double bondEn = 0.0, nonbondEn = 0.0, self = 0.0, correction = 0.0; @@ -99,7 +105,8 @@ SystemPotential CalculateEnergy::SystemTotal() } #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, bondEnergy) reduction(+:bondEn, nonbondEn, correction) + #pragma omp parallel for default(shared) private(i, bondEnergy) \ + reduction(+:bondEn, nonbondEn, correction) #endif for (i = 0; i < molID.size(); i++) { //calculate nonbonded energy @@ -114,7 +121,10 @@ SystemPotential CalculateEnergy::SystemTotal() pot.boxEnergy[b].intraNonbond = nonbondEn; //calculate self term of electrostatic interaction pot.boxEnergy[b].self = calcEwald->BoxSelf(currentAxes, b); - pot.boxEnergy[b].correction = -1 * correction * num::qqFact; + pot.boxEnergy[b].correction = correction; + + //Calculate Virial + pot.boxVirial[b] = VirialCalc(b); } pot.Total(); @@ -131,15 +141,14 @@ SystemPotential CalculateEnergy::SystemTotal() } -SystemPotential CalculateEnergy::SystemInter -(SystemPotential potential, - XYZArray const& coords, - XYZArray const& com, - BoxDimensions const& boxAxes) +SystemPotential CalculateEnergy::SystemInter(SystemPotential potential, + XYZArray const& coords, + XYZArray const& com, + BoxDimensions const& boxAxes) { for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { //calculate LJ interaction and real term of electrostatic interaction - potential = BoxInter(potential, coords, com, boxAxes, b); + potential = BoxInter(potential, coords, boxAxes, b); //calculate reciprocate term of electrostatic interaction potential.boxEnergy[b].recip = calcEwald->BoxReciprocal(b); } @@ -150,10 +159,10 @@ SystemPotential CalculateEnergy::SystemInter } - +// Calculate the inter energy for Box. Fractional molecule are not allowed in +// this function. Need to implement the GPU function SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, XYZArray const& coords, - XYZArray const& com, BoxDimensions const& boxAxes, const uint box) { @@ -163,9 +172,9 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, return potential; double tempREn = 0.0, tempLJEn = 0.0; - double distSq, qi_qj_fact; - int i; - XYZ virComponents; + double distSq, qi_qj_fact, lambdaVDW, lambdaCoulomb; + int i, pairSize; + XYZ virComponents, forceLJ, forceReal; std::vector pair1, pair2; CellList::Pairs pair = cellList.EnumeratePairs(box); @@ -177,9 +186,21 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, } pair.Next(); } + pairSize = pair1.size(); + + // store lambda values in array + // this way GPU can access the same data + double *arr_lambdaVDW = new double[pairSize]; + double *arr_lambdaCoulomb = new double[pairSize]; + for(i = 0; i < pairSize; i++) { + arr_lambdaVDW[i] = GetLambdaVDW(particleMol[pair1[i]], particleMol[pair2[i]], box); + if(electrostatic) { + arr_lambdaCoulomb[i] = GetLambdaCoulomb(particleMol[pair1[i]], + particleMol[pair2[i]], box); + } + } #ifdef GOMC_CUDA - uint pairSize = pair1.size(); uint currentIndex = 0; double REn = 0.0, LJEn = 0.0; //update unitcell in GPU @@ -190,7 +211,8 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, if(!boxAxes.orthogonal[box]) { BoxDimensionsNonOrth newAxes = *((BoxDimensionsNonOrth*)(&boxAxes)); UpdateInvCellBasisCUDA(forcefield.particles->getCUDAVars(), box, - newAxes.cellBasis_Inv[box].x, newAxes.cellBasis_Inv[box].y, + newAxes.cellBasis_Inv[box].x, + newAxes.cellBasis_Inv[box].y, newAxes.cellBasis_Inv[box].z); } @@ -207,7 +229,10 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, CallBoxInterGPU(forcefield.particles->getCUDAVars(), subPair1, subPair2, coords, boxAxes, electrostatic, particleCharge, - particleKind, REn, LJEn, box); + particleKind, particleMol, REn, LJEn, arr_lambdaVDW, + arr_lambdaCoulomb, forcefield.sc_coul, + forcefield.sc_sigma_6, forcefield.sc_alpha, + forcefield.sc_power, box); tempREn += REn; tempLJEn += LJEn; currentIndex += MAX_PAIR_SIZE; @@ -215,23 +240,24 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, virComponents) reduction(+:tempREn, tempLJEn) + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, \ + virComponents, forceReal, forceLJ) \ +reduction(+:tempREn, tempLJEn) #endif for (i = 0; i < pair1.size(); i++) { if(boxAxes.InRcut(distSq, virComponents, coords, pair1[i], pair2[i], box)) { if (electrostatic) { - qi_qj_fact = particleCharge[pair1[i]] * - particleCharge[pair2[i]] * num::qqFact; - - tempREn += forcefield.particles->CalcCoulomb(distSq, qi_qj_fact, box); + qi_qj_fact = particleCharge[pair1[i]] * particleCharge[pair2[i]] * + num::qqFact; + tempREn += forcefield.particles->CalcCoulomb(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], qi_qj_fact, + arr_lambdaCoulomb[i], box); } - tempLJEn += forcefield.particles->CalcEn(distSq, particleKind[pair1[i]], - particleKind[pair2[i]]); + particleKind[pair2[i]], arr_lambdaVDW[i]); } } #endif - // setting energy and virial of LJ interaction potential.boxEnergy[box].inter = tempLJEn; // setting energy and virial of coulomb interaction @@ -244,13 +270,175 @@ SystemPotential CalculateEnergy::BoxInter(SystemPotential potential, potential.Total(); + delete[] arr_lambdaVDW; + delete[] arr_lambdaCoulomb; + return potential; +} + +SystemPotential CalculateEnergy::BoxForce(SystemPotential potential, + XYZArray const& coords, + XYZArray& atomForce, + XYZArray& molForce, + BoxDimensions const& boxAxes, + const uint box) +{ + //Handles reservoir box case, returning zeroed structure if + //interactions are off. + if (box >= BOXES_WITH_U_NB) + return potential; + + double tempREn = 0.0, tempLJEn = 0.0; + double distSq, qi_qj_fact, lambdaVDW, lambdaCoulomb; + int i; + XYZ virComponents, forceLJ, forceReal; + std::vector pair1, pair2; + CellList::Pairs pair = cellList.EnumeratePairs(box); + + // make a pointer to atom force and mol force for openmp + double *aForcex = atomForce.x; + double *aForcey = atomForce.y; + double *aForcez = atomForce.z; + double *mForcex = molForce.x; + double *mForcey = molForce.y; + double *mForcez = molForce.z; + int atomCount = atomForce.Count(); + int molCount = molForce.Count(); + + // Reset Force Arrays + ResetForce(atomForce, molForce, box); + + uint pairSize = pair1.size(); + //store atom pair index + while (!pair.Done()) { + if(!SameMolecule(pair.First(), pair.Second())) { + pair1.push_back(pair.First()); + pair2.push_back(pair.Second()); + } + pair.Next(); + } + + // store lambda values in array + // this way GPU can access the same data + double *arr_lambdaVDW = new double[pairSize]; + double *arr_lambdaCoulomb = new double[pairSize]; + for(i = 0; i < pairSize; i++) { + arr_lambdaVDW[i] = GetLambdaVDW(particleMol[pair1[i]], particleMol[pair2[i]], box); + if(electrostatic) { + arr_lambdaCoulomb[i] = GetLambdaCoulomb(particleMol[pair1[i]], + particleMol[pair2[i]], box); + } + } + +#ifdef GOMC_CUDA + uint currentIndex = 0; + double REn = 0.0, LJEn = 0.0; + //update unitcell in GPU + UpdateCellBasisCUDA(forcefield.particles->getCUDAVars(), box, + boxAxes.cellBasis[box].x, boxAxes.cellBasis[box].y, + boxAxes.cellBasis[box].z); + + if(!boxAxes.orthogonal[box]) { + BoxDimensionsNonOrth newAxes = *((BoxDimensionsNonOrth*)(&boxAxes)); + UpdateInvCellBasisCUDA(forcefield.particles->getCUDAVars(), box, + newAxes.cellBasis_Inv[box].x, + newAxes.cellBasis_Inv[box].y, + newAxes.cellBasis_Inv[box].z); + } + + while(currentIndex < pairSize) { + uint max = currentIndex + MAX_PAIR_SIZE; + max = (max < pairSize ? max : pairSize); + + std::vector::const_iterator first1 = pair1.begin() + currentIndex; + std::vector::const_iterator last1 = pair1.begin() + max; + std::vector::const_iterator first2 = pair2.begin() + currentIndex; + std::vector::const_iterator last2 = pair2.begin() + max; + std::vector subPair1(first1, last1); + std::vector subPair2(first2, last2); + + // Reset forces on GPU for the first iteration + bool reset_force = currentIndex == 0; + + // Copy back the result if it is the last iteration + bool copy_back = max == pairSize; + + CallBoxForceGPU(forcefield.particles->getCUDAVars(), subPair1, subPair2, + coords, boxAxes, electrostatic, particleCharge, + particleKind, particleMol, REn, LJEn, + aForcex, aForcey, aForcez, mForcex, mForcey, mForcez, + atomCount, molCount, reset_force, copy_back, arr_lambdaVDW, + arr_lambdaCoulomb, forcefield.sc_coul, + forcefield.sc_sigma_6, forcefield.sc_alpha, + forcefield.sc_power, box); + tempREn += REn; + tempLJEn += LJEn; + currentIndex += MAX_PAIR_SIZE; + } + +#else +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, \ + virComponents, forceReal, forceLJ, lambdaVDW, lambdaCoulomb) \ +reduction(+:tempREn, tempLJEn, aForcex[:atomCount], aForcey[:atomCount], \ + aForcez[:atomCount], mForcex[:molCount], mForcey[:molCount], mForcez[:molCount]) +#endif + for (i = 0; i < pair1.size(); i++) { + if(boxAxes.InRcut(distSq, virComponents, coords, pair1[i], pair2[i], box)) { + lambdaVDW = GetLambdaVDW(particleMol[pair1[i]], particleMol[pair2[i]], box); + + if (electrostatic) { + lambdaCoulomb = GetLambdaCoulomb(particleMol[pair1[i]], + particleMol[pair2[i]], box); + qi_qj_fact = particleCharge[pair1[i]] * particleCharge[pair2[i]] * + num::qqFact; + tempREn += forcefield.particles->CalcCoulomb(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], qi_qj_fact, lambdaCoulomb, box); + } + tempLJEn += forcefield.particles->CalcEn(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], lambdaVDW); + + // Calculating the force + if(multiParticleEnabled) { + if(electrostatic) { + forceReal = virComponents * + forcefield.particles->CalcCoulombVir(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], qi_qj_fact, lambdaCoulomb, box); + } + forceLJ = virComponents * + forcefield.particles->CalcVir(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], lambdaVDW); + aForcex[pair1[i]] += forceLJ.x + forceReal.x; + aForcey[pair1[i]] += forceLJ.y + forceReal.y; + aForcez[pair1[i]] += forceLJ.z + forceReal.z; + aForcex[pair2[i]] += -(forceLJ.x + forceReal.x); + aForcey[pair2[i]] += -(forceLJ.y + forceReal.y); + aForcez[pair2[i]] += -(forceLJ.z + forceReal.z); + mForcex[particleMol[pair1[i]]] += (forceLJ.x + forceReal.x); + mForcey[particleMol[pair1[i]]] += (forceLJ.y + forceReal.y); + mForcez[particleMol[pair1[i]]] += (forceLJ.z + forceReal.z); + mForcex[particleMol[pair2[i]]] += -(forceLJ.x + forceReal.x); + mForcey[particleMol[pair2[i]]] += -(forceLJ.y + forceReal.y); + mForcez[particleMol[pair2[i]]] += -(forceLJ.z + forceReal.z); + } + } + } +#endif + // setting energy and virial of LJ interaction + potential.boxEnergy[box].inter = tempLJEn; + // setting energy and virial of coulomb interaction + potential.boxEnergy[box].real = tempREn; + + potential.Total(); + + delete[] arr_lambdaVDW; + delete[] arr_lambdaCoulomb; return potential; } // NOTE: The calculation of W12, W13, W23 is expensive and would not be // requied for pressure and surface tension calculation. So, they have been // commented out. In case you need to calculate them, uncomment them. -Virial CalculateEnergy::ForceCalc(const uint box) +Virial CalculateEnergy::VirialCalc(const uint box) { //store virial and energy of reference and modify the virial Virial tempVir; @@ -261,7 +449,7 @@ Virial CalculateEnergy::ForceCalc(const uint box) double rT11 = 0.0, rT12 = 0.0, rT13 = 0.0; double rT22 = 0.0, rT23 = 0.0, rT33 = 0.0; - double distSq, pVF, pRF, qi_qj; + double distSq, pVF, pRF, qi_qj, lambdaVDW, lambdaCoulomb; int i; XYZ virC, comC; std::vector pair1, pair2; @@ -275,8 +463,20 @@ Virial CalculateEnergy::ForceCalc(const uint box) pair.Next(); } -#ifdef GOMC_CUDA uint pairSize = pair1.size(); + // store lambda values in array + // this way GPU can access the same data + double *arr_lambdaVDW = new double[pairSize]; + double *arr_lambdaCoulomb = new double[pairSize]; + for(i = 0; i < pairSize; i++) { + arr_lambdaVDW[i] = GetLambdaVDW(particleMol[pair1[i]], particleMol[pair2[i]], box); + if(electrostatic) { + arr_lambdaCoulomb[i] = GetLambdaCoulomb(particleMol[pair1[i]], + particleMol[pair2[i]], box); + } + } + +#ifdef GOMC_CUDA //update unitcell in GPU UpdateCellBasisCUDA(forcefield.particles->getCUDAVars(), box, currentAxes.cellBasis[box].x, @@ -309,7 +509,10 @@ Virial CalculateEnergy::ForceCalc(const uint box) subPair2, currentCoords, currentCOM, currentAxes, electrostatic, particleCharge, particleKind, particleMol, rT11t, rT12t, rT13t, rT22t, rT23t, rT33t, - vT11t, vT12t, vT13t, vT22t, vT23t, vT33t, box); + vT11t, vT12t, vT13t, vT22t, vT23t, vT33t, + arr_lambdaVDW, arr_lambdaCoulomb, forcefield.sc_coul, + forcefield.sc_sigma_6, forcefield.sc_alpha, + forcefield.sc_power, box); rT11 += rT11t; rT12 += rT12t; rT13 += rT13t; @@ -326,7 +529,9 @@ Virial CalculateEnergy::ForceCalc(const uint box) } #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, distSq, pVF, pRF, qi_qj, virC, comC) reduction(+:vT11, vT12, vT13, vT22, vT23, vT33, rT11, rT12, rT13, rT22, rT23, rT33) + #pragma omp parallel for default(shared) private(i, distSq, pVF, pRF, qi_qj, \ +virC, comC, lambdaVDW, lambdaCoulomb) reduction(+:vT11, vT12, vT13, vT22, \ + vT23, vT33, rT11, rT12, rT13, rT22, rT23, rT33) #endif for (i = 0; i < pair1.size(); i++) { if (currentAxes.InRcut(distSq, virC, currentCoords, pair1[i], @@ -334,16 +539,21 @@ Virial CalculateEnergy::ForceCalc(const uint box) pVF = 0.0; pRF = 0.0; - //calculate the distance between com of two molecule + //calculate the distance between com of two molecules comC = currentCOM.Difference(particleMol[pair1[i]], particleMol[pair2[i]]); - //calculate the minimum image between com of two molecule + //calculate the minimum image between com of two molecules comC = currentAxes.MinImage(comC, box); + lambdaVDW = GetLambdaVDW(particleMol[pair1[i]], particleMol[pair2[i]], box); if (electrostatic) { + lambdaCoulomb = GetLambdaCoulomb(particleMol[pair1[i]], + particleMol[pair2[i]], box); qi_qj = particleCharge[pair1[i]] * particleCharge[pair2[i]]; - pRF = forcefield.particles->CalcCoulombVir(distSq, qi_qj, box); + pRF = forcefield.particles->CalcCoulombVir(distSq, particleKind[pair1[i]], + particleKind[pair2[i]], qi_qj, + lambdaCoulomb, box); //calculate the top diagonal of pressure tensor rT11 += pRF * (virC.x * comC.x); //rT12 += pRF * (0.5 * (virC.x * comC.y + virC.y * comC.x)); @@ -356,7 +566,7 @@ Virial CalculateEnergy::ForceCalc(const uint box) } pVF = forcefield.particles->CalcVir(distSq, particleKind[pair1[i]], - particleKind[pair2[i]]); + particleKind[pair2[i]], lambdaVDW); //calculate the top diagonal of pressure tensor vT11 += pVF * (virC.x * comC.x); //vT12 += pVF * (0.5 * (virC.x * comC.y + virC.y * comC.x)); @@ -404,19 +614,19 @@ Virial CalculateEnergy::ForceCalc(const uint box) tempVir.real = (rT11 + rT22 + rT33) * num::qqFact; if (forcefield.useLRC) { - ForceCorrection(tempVir, currentAxes, box); + VirialCorrection(tempVir, currentAxes, box); } //calculate reciprocate term of force - tempVir = calcEwald->ForceReciprocal(tempVir, box); + tempVir = calcEwald->VirialReciprocal(tempVir, box); tempVir.Total(); + delete[] arr_lambdaVDW; + delete[] arr_lambdaCoulomb; return tempVir; } - - bool CalculateEnergy::MoleculeInter(Intermolecular &inter_LJ, Intermolecular &inter_coulomb, XYZArray const& molCoords, @@ -425,6 +635,7 @@ bool CalculateEnergy::MoleculeInter(Intermolecular &inter_LJ, { double tempREn = 0.0, tempLJEn = 0.0; bool overlap = false; + if (box < BOXES_WITH_U_NB) { uint length = mols.GetKind(molIndex).NumAtoms(); uint start = mols.MolStart(molIndex); @@ -435,9 +646,9 @@ bool CalculateEnergy::MoleculeInter(Intermolecular &inter_LJ, box); n = cellList.EnumerateLocal(currentCoords[atom], box); - double qi_qj_fact, distSq; + double qi_qj_fact, distSq, lambdaVDW, lambdaCoulomb; int i; - XYZ virComponents; + XYZ virComponents, forceLJ, forceReal; std::vector nIndex; //store atom index in neighboring cell @@ -447,23 +658,29 @@ bool CalculateEnergy::MoleculeInter(Intermolecular &inter_LJ, } #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, virComponents) reduction(+:tempREn, tempLJEn) + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, \ + virComponents, forceLJ, forceReal, lambdaVDW, lambdaCoulomb) \ +reduction(+:tempREn, tempLJEn) #endif for(i = 0; i < nIndex.size(); i++) { distSq = 0.0; //Subtract old energy - if (currentAxes.InRcut(distSq, virComponents, - currentCoords, atom, nIndex[i], box)) { + if (currentAxes.InRcut(distSq, virComponents, currentCoords, atom, + nIndex[i], box)) { + lambdaVDW = GetLambdaVDW(molIndex, particleMol[nIndex[i]], box); if (electrostatic) { + lambdaCoulomb = GetLambdaCoulomb(molIndex, particleMol[nIndex[i]], + box); qi_qj_fact = particleCharge[atom] * particleCharge[nIndex[i]] * num::qqFact; - tempREn -= forcefield.particles->CalcCoulomb(distSq, qi_qj_fact, box); + tempREn -= forcefield.particles->CalcCoulomb(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, lambdaCoulomb, box); } tempLJEn -= forcefield.particles->CalcEn(distSq, particleKind[atom], - particleKind[nIndex[i]]); + particleKind[nIndex[i]], lambdaVDW); } } @@ -477,27 +694,34 @@ bool CalculateEnergy::MoleculeInter(Intermolecular &inter_LJ, } #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, virComponents) reduction(+:tempREn, tempLJEn) + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, \ + virComponents, forceReal, forceLJ, lambdaVDW, lambdaCoulomb) \ +reduction(+:tempREn, tempLJEn) #endif for(i = 0; i < nIndex.size(); i++) { - distSq = 0.0; - if (currentAxes.InRcut(distSq, virComponents, - molCoords, p, currentCoords, nIndex[i], box)) { + distSq = 0.0; + if (currentAxes.InRcut(distSq, virComponents, molCoords, p, + currentCoords, nIndex[i], box)) { + lambdaVDW = GetLambdaVDW(molIndex, particleMol[nIndex[i]], box); + if(distSq < forcefield.rCutLowSq) { overlap |= true; } if (electrostatic) { + lambdaCoulomb = GetLambdaCoulomb(molIndex, particleMol[nIndex[i]], + box); qi_qj_fact = particleCharge[atom] * particleCharge[nIndex[i]] * num::qqFact; tempREn += forcefield.particles->CalcCoulomb(distSq, - qi_qj_fact, box); + particleKind[atom], particleKind[nIndex[i]], + qi_qj_fact, lambdaCoulomb, box); } tempLJEn += forcefield.particles->CalcEn(distSq, particleKind[atom], - particleKind[nIndex[i]]); + particleKind[nIndex[i]], lambdaVDW); } } } @@ -532,7 +756,7 @@ void CalculateEnergy::ParticleNonbonded(double* inter, *partner, box)) { inter[t] += forcefield.particles->CalcEn(distSq, kind.AtomKind(partIndex), - kind.AtomKind(*partner)); + kind.AtomKind(*partner), 1.0); if (electrostatic) { double qi_qj_Fact = kind.AtomCharge(partIndex) * kind.AtomCharge(*partner) * num::qqFact; @@ -556,7 +780,7 @@ void CalculateEnergy::ParticleInter(double* en, double *real, { if(box >= BOXES_WITH_U_NB) return; - double distSq, qi_qj_Fact, tempLJ, tempReal; + double distSq, qi_qj_Fact, tempLJ, tempReal, lambdaVDW, lambdaCoulomb; int i; MoleculeKind const& thisKind = mols.GetKind(molIndex); uint kindI = thisKind.AtomKind(partIndex); @@ -574,20 +798,26 @@ void CalculateEnergy::ParticleInter(double* en, double *real, } #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, distSq, qi_qj_Fact) reduction(+:tempLJ, tempReal) + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_Fact, \ +lambdaVDW, lambdaCoulomb) reduction(+:tempLJ, tempReal) #endif for(i = 0; i < nIndex.size(); i++) { distSq = 0.0; - if(currentAxes.InRcut(distSq, trialPos, t, currentCoords, nIndex[i], box)) { + lambdaVDW = GetLambdaVDW(molIndex, particleMol[nIndex[i]], box); + if(distSq < forcefield.rCutLowSq) { overlap[t] |= true; } tempLJ += forcefield.particles->CalcEn(distSq, kindI, - particleKind[nIndex[i]]); + particleKind[nIndex[i]], + lambdaVDW); if(electrostatic) { + lambdaCoulomb = GetLambdaCoulomb(molIndex, particleMol[nIndex[i]], + box); qi_qj_Fact = particleCharge[nIndex[i]] * kindICharge * num::qqFact; - tempReal += forcefield.particles->CalcCoulomb(distSq, qi_qj_Fact, box); + tempReal += forcefield.particles->CalcCoulomb(distSq, kindI, + particleKind[nIndex[i]], qi_qj_Fact, lambdaCoulomb, box); } } } @@ -610,8 +840,8 @@ Intermolecular CalculateEnergy::MoleculeTailChange(const uint box, uint mkIdxII = kind * mols.GetKindsCount() + kind; for (uint j = 0; j < mols.GetKindsCount(); ++j) { uint mkIdxIJ = j * mols.GetKindsCount() + kind; - double rhoDeltaIJ_2 = sign * 2.0 * - (double)(molLookup.NumKindInBox(j, box)) * currentAxes.volInv[box]; + double rhoDeltaIJ_2 = sign * 2.0 * (double)(molLookup.NumKindInBox(j, box)) * + currentAxes.volInv[box]; delta.energy += mols.pairEnCorrections[mkIdxIJ] * rhoDeltaIJ_2; } @@ -622,6 +852,31 @@ Intermolecular CalculateEnergy::MoleculeTailChange(const uint box, return delta; } +//Calculates the change in the Virial TC from adding numChange atoms of a kind +Intermolecular CalculateEnergy::MoleculeTailVirChange(const uint box, + const uint kind, + const bool add) const +{ + Intermolecular delta; + + if (box < BOXES_WITH_U_NB) { + + double sign = (add ? 1.0 : -1.0); + uint mkIdxII = kind * mols.GetKindsCount() + kind; + for (uint j = 0; j < mols.GetKindsCount(); ++j) { + uint mkIdxIJ = j * mols.GetKindsCount() + kind; + double rhoDeltaIJ_2 = sign * 2.0 * (double)(molLookup.NumKindInBox(j, box)) * + currentAxes.volInv[box]; + delta.virial += mols.pairVirCorrections[mkIdxIJ] * rhoDeltaIJ_2; + } + + //We already calculated part of the change for this type in the loop + delta.virial += mols.pairVirCorrections[mkIdxII] * + currentAxes.volInv[box]; + } + return delta; +} + //Calculates intramolecular energy of a full molecule void CalculateEnergy::MoleculeIntra(const uint molIndex, @@ -838,7 +1093,7 @@ void CalculateEnergy::MolNonbond(double & energy, energy += forcefield.particles->CalcEn(distSq, molKind.AtomKind (molKind.nonBonded.part1[i]), molKind.AtomKind - (molKind.nonBonded.part2[i])); + (molKind.nonBonded.part2[i]), 1.0); if (electrostatic) { qi_qj_Fact = num::qqFact * molKind.AtomCharge(molKind.nonBonded.part1[i]) * @@ -870,7 +1125,7 @@ void CalculateEnergy::MolNonbond(double & energy, cbmc::TrialMol const &mol, currentAxes.InRcut(distSq, mol.GetCoords(), p1, p2, mol.GetBox()); if (forcefield.rCutSq > distSq) { energy += forcefield.particles->CalcEn(distSq, molKind.AtomKind(p1), - molKind.AtomKind(p2)); + molKind.AtomKind(p2), 1.0); if (electrostatic) { qi_qj_Fact = num::qqFact * molKind.AtomCharge(1) * molKind.AtomCharge(p2); @@ -1079,24 +1334,52 @@ double CalculateEnergy::IntraEnergy_1_4(const double distSq, const uint atom1, } -//!Calculates energy and virial tail corrections for the box +//!Calculates energy tail corrections for the box void CalculateEnergy::EnergyCorrection(SystemPotential& pot, BoxDimensions const& boxAxes, const uint box) const { - if (box < BOXES_WITH_U_NB) { - double en = 0.0; + if(box >= BOXES_WITH_U_NB) { + return; + } + + double en = 0.0; + for (uint i = 0; i < mols.GetKindsCount(); ++i) { + uint numI = molLookup.NumKindInBox(i, box); + for (uint j = 0; j < mols.GetKindsCount(); ++j) { + uint numJ = molLookup.NumKindInBox(j, box); + en += mols.pairEnCorrections[i * mols.GetKindsCount() + j] * numI * numJ + * boxAxes.volInv[box]; + } + } + if(!forcefield.freeEnergy) { + pot.boxEnergy[box].tc = en; + } +#if ENSEMBLE == NVT || ENSEMBLE == NPT + else { + //Get the kind and lambda value + uint fk = lambdaRef.GetKind(box); + double lambdaVDW = lambdaRef.GetLambdaVDW(fk, box); + //remove the LRC for one molecule with lambda = 1 + en += MoleculeTailChange(box, fk, false).energy; + + //Add the LRC for fractional molecule for (uint i = 0; i < mols.GetKindsCount(); ++i) { - uint numI = molLookup.NumKindInBox(i, box); - for (uint j = 0; j < mols.GetKindsCount(); ++j) { - uint numJ = molLookup.NumKindInBox(j, box); - en += mols.pairEnCorrections[i * mols.GetKindsCount() + j] * numI * numJ - * boxAxes.volInv[box]; + uint molNum = molLookup.NumKindInBox(i, box); + if(i == fk) { + --molNum; // We have one less molecule (it is fractional molecule) } + double rhoDeltaIJ_2 = 2.0 * (double)(molNum) * currentAxes.volInv[box]; + en += lambdaVDW * mols.pairEnCorrections[fk * mols.GetKindsCount() + i] * + rhoDeltaIJ_2; } + //We already calculated part of the change for this type in the loop + en += lambdaVDW * mols.pairEnCorrections[fk * mols.GetKindsCount() + fk] * + currentAxes.volInv[box]; pot.boxEnergy[box].tc = en; } +#endif } //!Calculates energy corrections for the box @@ -1117,23 +1400,122 @@ double CalculateEnergy::EnergyCorrection(const uint box, return tc; } -void CalculateEnergy::ForceCorrection(Virial& virial, - BoxDimensions const& boxAxes, - const uint box) const +void CalculateEnergy::VirialCorrection(Virial& virial, + BoxDimensions const& boxAxes, + const uint box) const { - if (box < BOXES_WITH_U_NB) { - double vir = 0.0; + if(box >= BOXES_WITH_U_NB) { + return; + } + double vir = 0.0; + for (uint i = 0; i < mols.GetKindsCount(); ++i) { + uint numI = molLookup.NumKindInBox(i, box); + for (uint j = 0; j < mols.GetKindsCount(); ++j) { + uint numJ = molLookup.NumKindInBox(j, box); + vir += mols.pairVirCorrections[i * mols.GetKindsCount() + j] * + numI * numJ * boxAxes.volInv[box]; + } + } + + if(!forcefield.freeEnergy) { + virial.tc = vir; + } +#if ENSEMBLE == NVT || ENSEMBLE == NPT + else { + //Get the kind and lambda value + uint fk = lambdaRef.GetKind(box); + double lambdaVDW = lambdaRef.GetLambdaVDW(fk, box); + //remove the LRC for one molecule with lambda = 1 + vir += MoleculeTailVirChange(box, fk, false).virial; + + //Add the LRC for fractional molecule for (uint i = 0; i < mols.GetKindsCount(); ++i) { - uint numI = molLookup.NumKindInBox(i, box); - for (uint j = 0; j < mols.GetKindsCount(); ++j) { - uint numJ = molLookup.NumKindInBox(j, box); - vir += mols.pairVirCorrections[i * mols.GetKindsCount() + j] * - numI * numJ * boxAxes.volInv[box]; + uint molNum = molLookup.NumKindInBox(i, box); + if(i == fk) { + --molNum; // We have one less molecule (it is fractional molecule) } + double rhoDeltaIJ_2 = 2.0 * (double)(molNum) * currentAxes.volInv[box]; + vir += mols.pairVirCorrections[fk * mols.GetKindsCount() + i] * rhoDeltaIJ_2; } + //We already calculated part of the change for this type in the loop + vir += mols.pairVirCorrections[fk * mols.GetKindsCount() + fk] * + currentAxes.volInv[box]; virial.tc = vir; } +#endif +} + +//! Calculate Torque +void CalculateEnergy::CalculateTorque(vector& moleculeIndex, + XYZArray const& coordinates, + XYZArray const& com, + XYZArray const& atomForce, + XYZArray const& atomForceRec, + XYZArray& molTorque, + vector& moveType, + const uint box) +{ + if(multiParticleEnabled && (box < BOXES_WITH_U_NB)) { + uint m, p, length, start; + XYZ tempTorque, distFromCOM; + + // make a pointer to atom force and mol force for openmp + double *torquex = molTorque.x; + double *torquey = molTorque.y; + double *torquez = molTorque.z; + int torqueCount = molTorque.Count(); + + molTorque.Reset(); + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(m, p, length, start, \ +distFromCOM, tempTorque) reduction(+: torquex[:torqueCount], \ + torquey[:torqueCount], torquez[:torqueCount]) +#endif + for(m = 0; m < moleculeIndex.size(); m++) { + length = mols.GetKind(moleculeIndex[m]).NumAtoms(); + start = mols.MolStart(moleculeIndex[m]); + + //Only if move is rotation + if(moveType[moleculeIndex[m]]) { + // atom iterator + for(p = start; p < start + length; p++) { + distFromCOM = coordinates.Difference(p, com, (moleculeIndex[m])); + distFromCOM = currentAxes.MinImage(distFromCOM, box); + tempTorque = Cross(distFromCOM, atomForce[p] + atomForceRec[p]); + + torquex[moleculeIndex[m]] += tempTorque.x; + torquey[moleculeIndex[m]] += tempTorque.y; + torquez[moleculeIndex[m]] += tempTorque.z; + } + } + } + + } +} + +void CalculateEnergy::ResetForce(XYZArray& atomForce, XYZArray& molForce, + uint box) +{ + if(multiParticleEnabled) { + uint length, start; + + // molecule iterator + MoleculeLookup::box_iterator thisMol = molLookup.BoxBegin(box); + MoleculeLookup::box_iterator end = molLookup.BoxEnd(box); + + while(thisMol != end) { + length = mols.GetKind(*thisMol).NumAtoms(); + start = mols.MolStart(*thisMol); + + molForce.Set(*thisMol, 0.0, 0.0, 0.0); + for(uint p = start; p < start + length; p++) { + atomForce.Set(p, 0.0, 0.0, 0.0); + } + thisMol++; + } + } } bool CalculateEnergy::FindMolInCavity(std::vector< std::vector > &mol, @@ -1188,3 +1570,263 @@ bool CalculateEnergy::FindMolInCavity(std::vector< std::vector > &mol, else return false; } + + +void CalculateEnergy::SingleMoleculeInter(Energy &interEnOld, + Energy &interEnNew, + const double lambdaOldVDW, + const double lambdaNewVDW, + const double lambdaOldCoulomb, + const double lambdaNewCoulomb, + const uint molIndex, + const uint box) const +{ + double tempREnOld = 0.0, tempLJEnOld = 0.0; + double tempREnNew = 0.0, tempLJEnNew = 0.0; + if (box < BOXES_WITH_U_NB) { + uint length = mols.GetKind(molIndex).NumAtoms(); + uint start = mols.MolStart(molIndex); + + for (uint p = 0; p < length; ++p) { + uint atom = start + p; + CellList::Neighbors n = cellList.EnumerateLocal(currentCoords[atom], + box); + n = cellList.EnumerateLocal(currentCoords[atom], box); + + double qi_qj_fact, distSq; + int i; + XYZ virComponents; + std::vector nIndex; + + //store atom index in neighboring cell + while (!n.Done()) { + if(particleMol[*n] != molIndex) { + nIndex.push_back(*n); + } + n.Next(); + } + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, distSq, qi_qj_fact, \ +virComponents) reduction(+:tempREnOld, tempLJEnOld, tempREnNew, tempLJEnNew) +#endif + for(i = 0; i < nIndex.size(); i++) { + distSq = 0.0; + if (currentAxes.InRcut(distSq, virComponents, currentCoords, atom, + nIndex[i], box)) { + + if (electrostatic) { + qi_qj_fact = particleCharge[atom] * particleCharge[nIndex[i]] * + num::qqFact; + tempREnNew += forcefield.particles->CalcCoulomb(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, lambdaNewCoulomb, box); + tempREnOld += forcefield.particles->CalcCoulomb(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, lambdaOldCoulomb, box); + } + + tempLJEnNew += forcefield.particles->CalcEn(distSq, particleKind[atom], + particleKind[nIndex[i]], lambdaNewVDW); + tempLJEnOld += forcefield.particles->CalcEn(distSq, particleKind[atom], + particleKind[nIndex[i]], lambdaOldVDW); + } + } + } + } + + interEnNew.inter = tempLJEnNew; + interEnNew.real = tempREnNew; + interEnOld.inter = tempLJEnOld; + interEnOld.real = tempREnOld; +} + +double CalculateEnergy::GetLambdaVDW(uint molA, uint molB, uint box) const +{ + double lambda = 1.0; + lambda *= lambdaRef.GetLambdaVDW(molA, mols.GetMolKind(molA), box); + lambda *= lambdaRef.GetLambdaVDW(molB, mols.GetMolKind(molB), box); + return lambda; +} + +double CalculateEnergy::GetLambdaCoulomb(uint molA, uint molB, uint box) const +{ + double lambda = 1.0; + lambda *= lambdaRef.GetLambdaCoulomb(molA, mols.GetMolKind(molA), box); + lambda *= lambdaRef.GetLambdaCoulomb(molB, mols.GetMolKind(molB), box); + //no need for sq root for inter energy. Alwayse one of the molecule has + // lambda 1 + return lambda; +} + +//Calculates the change in the TC from adding numChange atoms of a kind +double CalculateEnergy::MoleculeTailChange(const uint box, const uint kind, + const std::vector &kCount, + const double lambdaOld, + const double lambdaNew) const +{ + if (box >= BOXES_WITH_U_NB) { + return 0.0; + } + + double tcDiff = 0.0; + uint ktot = mols.GetKindsCount(); + for (uint i = 0; i < ktot; ++i) { + //We should have only one molecule of fractional kind + double rhoDeltaIJ_2 = 2.0 * (double)(kCount[i]) * currentAxes.volInv[box]; + uint index = kind * ktot + i; + tcDiff += (lambdaNew - lambdaOld) * mols.pairEnCorrections[index] * + rhoDeltaIJ_2; + } + uint index = kind * ktot + kind; + tcDiff += (lambdaNew - lambdaOld) * mols.pairEnCorrections[index] * + currentAxes.volInv[box]; + + return tcDiff; +} + +//Calculate the change in energy due to lambda +void CalculateEnergy::EnergyChange(Energy *energyDiff, Energy &dUdL_VDW, + Energy &dUdL_Coul, + const std::vector &lambda_VDW, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + if (box >= BOXES_WITH_U_NB) { + return; + } + + uint length = mols.GetKind(molIndex).NumAtoms(); + uint start = mols.MolStart(molIndex); + uint lambdaSize = lambda_VDW.size(); + int i, s; + double energyOldVDW = 0.0, energyOldCoul = 0.0; + double *tempLJEnDiff = new double [lambdaSize]; + double *tempREnDiff = new double [lambdaSize]; + double dudl_VDW = 0.0, dudl_Coul = 0.0; + std::fill_n(tempLJEnDiff, lambdaSize, 0.0); + std::fill_n(tempREnDiff, lambdaSize, 0.0); + + // Calculate the vdw, short range electrostatic energy + for (uint p = 0; p < length; ++p) { + uint atom = start + p; + CellList::Neighbors n = cellList.EnumerateLocal(currentCoords[atom], box); + n = cellList.EnumerateLocal(currentCoords[atom], box); + + double qi_qj_fact, distSq; + XYZ virComponents; + std::vector nIndex; + + //store atom index in neighboring cell + while (!n.Done()) { + if(particleMol[*n] != molIndex) { + nIndex.push_back(*n); + } + n.Next(); + } + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, s, distSq, qi_qj_fact, \ +virComponents, energyOldVDW, energyOldCoul) reduction(+:dudl_VDW, dudl_Coul, \ + tempREnDiff[:lambdaSize], tempLJEnDiff[:lambdaSize]) +#endif + for(i = 0; i < nIndex.size(); i++) { + distSq = 0.0; + if(currentAxes.InRcut(distSq, virComponents, currentCoords, atom, + nIndex[i], box)) { + + //Calculate the energy of current state + energyOldVDW = forcefield.particles->CalcEn(distSq, particleKind[atom], + particleKind[nIndex[i]], + lambda_VDW[iState]); + //Calculate du/dl in VDW for current state + dudl_VDW += forcefield.particles->CalcdEndL(distSq, particleKind[atom], + particleKind[nIndex[i]], lambda_VDW[iState]); + + if(electrostatic) { + qi_qj_fact = particleCharge[atom] * particleCharge[nIndex[i]] * + num::qqFact; + energyOldCoul = forcefield.particles->CalcCoulomb(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, + lambda_Coul[iState], box); + //Calculate du/dl in Coulomb for current state. + dudl_Coul += forcefield.particles->CalcCoulombdEndL(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, + lambda_Coul[iState], box); + } + + for(s = 0; s < lambdaSize; s++) { + //Calculate the energy of other state + tempLJEnDiff[s] += forcefield.particles->CalcEn(distSq, particleKind[atom], + particleKind[nIndex[i]], lambda_VDW[s]); + tempLJEnDiff[s] += (-energyOldVDW); + if(electrostatic) { + tempREnDiff[s] += forcefield.particles->CalcCoulomb(distSq, particleKind[atom], + particleKind[nIndex[i]], qi_qj_fact, + lambda_Coul[s], box); + tempREnDiff[s] += (-energyOldCoul); + } + } + } + } + } + + dUdL_VDW.inter = dudl_VDW; + dUdL_Coul.real = dudl_Coul; + for(s = 0; s < lambdaSize; s++) { + energyDiff[s].inter += tempLJEnDiff[s]; + energyDiff[s].real += tempREnDiff[s]; + } + delete [] tempLJEnDiff; + delete [] tempREnDiff; + + if (forcefield.useLRC) { + //Need to calculate change in LRC + ChangeLRC(energyDiff, dUdL_VDW, lambda_VDW, iState, molIndex, box); + } + //Need to calculate change in self + calcEwald->ChangeSelf(energyDiff, dUdL_Coul, lambda_Coul, iState, molIndex, + box); + //Need to calculate change in correction + calcEwald->ChangeCorrection(energyDiff, dUdL_Coul, lambda_Coul, iState, + molIndex, box); + //Need to calculate change in Reciprocal + calcEwald->ChangeRecip(energyDiff, dUdL_Coul, lambda_Coul, iState, molIndex, + box); +} + +//Calculate the change in LRC for each state +void CalculateEnergy::ChangeLRC(Energy *energyDiff, Energy &dUdL_VDW, + const std::vector &lambda_VDW, + const uint iState, const uint molIndex, + const uint box) const +{ + //Get the kind and lambda value + uint fk = mols.GetMolKind(molIndex); + double lambda_istate = lambda_VDW[iState]; + + //Add the LRC for fractional molecule + for(uint s = 0; s < lambda_VDW.size(); s++) { + double lambdaVDW = lambda_VDW[s]; + for (uint i = 0; i < mols.GetKindsCount(); ++i) { + uint molNum = molLookup.NumKindInBox(i, box); + if(i == fk) { + --molNum; // We have one less molecule (it is fractional molecule) + } + double rhoDeltaIJ_2 = 2.0 * (double)(molNum) * currentAxes.volInv[box]; + energyDiff[s].tc += mols.pairEnCorrections[fk * mols.GetKindsCount() + i] * + rhoDeltaIJ_2 * (lambdaVDW - lambda_istate); + if(s == iState) { + //Calculate du/dl in VDW LRC for current state + dUdL_VDW.tc += mols.pairEnCorrections[fk * mols.GetKindsCount() + i] * + rhoDeltaIJ_2; + } + } + energyDiff[s].tc += mols.pairEnCorrections[fk * mols.GetKindsCount() + fk] * + currentAxes.volInv[box] * (lambdaVDW - lambda_istate); + if(s == iState) { + //Calculate du/dl in VDW LRC for current state + dUdL_VDW.tc += mols.pairEnCorrections[fk * mols.GetKindsCount() + fk] * + currentAxes.volInv[box]; + } + } +} diff --git a/src/CalculateEnergy.h b/src/CalculateEnergy.h index ff6bc9065..63bc3a572 100644 --- a/src/CalculateEnergy.h +++ b/src/CalculateEnergy.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -40,6 +40,7 @@ class Coordinates; class COM; class XYZArray; class BoxDimensions; +class Lambda; namespace cbmc { @@ -59,12 +60,22 @@ class CalculateEnergy //! Calculates total energy/virial of a single box in the system SystemPotential BoxInter(SystemPotential potential, XYZArray const& coords, - XYZArray const& com, BoxDimensions const& boxAxes, - const uint box) ; + const uint box); + + //! Calculates force of a single box in the system + SystemPotential BoxForce(SystemPotential potential, + XYZArray const& coords, + XYZArray& atomForce, + XYZArray& molForce, + BoxDimensions const& boxAxes, + const uint box); //! Calculate force and virial for the box - Virial ForceCalc(const uint box); + Virial VirialCalc(const uint box); + + //! Set the force for atom and mol to zero for box + void ResetForce(XYZArray& atomForce, XYZArray& molForce, uint box); //! Calculates intermolecule energy of all boxes in the system @@ -122,7 +133,7 @@ class CalculateEnergy const uint trials) const; - //! Calculates the change in the TC from adding numChange atoms of a kind + //! Calculates change in the ENergyTC from adding numChange atoms of a kind //! @param box Index of box under consideration //! @param kind Kind of particle being added or removed //! @param add If removed: false (sign=-1); if added: true (sign=+1) @@ -130,7 +141,23 @@ class CalculateEnergy const uint kind, const bool add) const; - //! Calculates intramolecular energy of a full molecule + //! Calculates change in the Virial TC from adding numChange atoms of a kind + //! @param box Index of box under consideration + //! @param kind Kind of particle being added or removed + //! @param add If removed: false (sign=-1); if added: true (sign=+1) + Intermolecular MoleculeTailVirChange(const uint box, + const uint kind, + const bool add) const; + + //! Calculates the change in the TC from chaning the lambdaOld -> lambdaNew + //! @param box Index of box under consideration + //! @param kind Kind of particle being transfrom in lambda + double MoleculeTailChange(const uint box, const uint kind, + const std::vector &kCount, + const double lambdaOld, + const double lambdaNew) const; + + //! Calculates voidintramolecular energy of a full molecule void MoleculeIntra(const uint molIndex, const uint box, double *bondEn) const; //used in molecule exchange for calculating bonded and intraNonbonded energy @@ -146,6 +173,15 @@ class CalculateEnergy //for Martini forcefield double IntraEnergy_1_4(const double distSq, const uint atom1, const uint atom2, const uint molIndex) const; + //! Calculate Torque + void CalculateTorque(vector& moleculeIndex, + XYZArray const& coordinates, + XYZArray const& com, + XYZArray const& atomForce, + XYZArray const& atomForceRec, + XYZArray& molTorque, + vector& moveType, + const uint box); //Finding the molecule inside cavity and store the molecule Index. bool FindMolInCavity(std::vector< std::vector > &mol, const XYZ& center, @@ -155,6 +191,20 @@ class CalculateEnergy //!Calculates energy corrections for the box double EnergyCorrection(const uint box, const uint *kCount) const; + //Calculate inter energy for single molecule in the system. + void SingleMoleculeInter(Energy &interEnOld, Energy &interEnNew, + const double lambdaOldVDW, + const double lambdaNewVDW, + const double lambdaOldCoulomb, + const double lambdaNewCoulomb, + const uint molIndex, const uint box) const; + + //Calculate the change in energy due to lambda + void EnergyChange(Energy *energyDiff, Energy &dUdL_VDW, Energy &dUdL_Coul, + const std::vector &lambda_VDW, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; private: //! Calculates full TC energy for one box in current system @@ -162,8 +212,8 @@ class CalculateEnergy const uint box) const; //! Calculates full TC virial for one box in current system - void ForceCorrection(Virial& virial, BoxDimensions const& boxAxes, - const uint box) const; + void VirialCorrection(Virial& virial, BoxDimensions const& boxAxes, + const uint box) const; //! Calculates bond vectors of a full molecule, stores them in vecs @@ -230,6 +280,12 @@ class CalculateEnergy void MolNonbond_1_3(double & energy, cbmc::TrialMol const &mol, MoleculeKind const& molKind) const; + //Calculate the change in LRC for each state + void ChangeLRC(Energy *energyDiff, Energy &dUdL_VDW, + const std::vector &lambda_VDW, + const uint iState, const uint molIndex, + const uint box) const; + //! For particles in main coordinates array determines if they belong //! to same molecule, using internal arrays. bool SameMolecule(const uint p1, const uint p2) const @@ -240,6 +296,9 @@ class CalculateEnergy return (pair1 == pair2); } + double GetLambdaVDW(uint molA, uint molB, uint box) const; + double GetLambdaCoulomb(uint molA, uint molB, uint box) const; + const Forcefield& forcefield; const Molecules& mols; @@ -247,8 +306,12 @@ class CalculateEnergy const MoleculeLookup& molLookup; const BoxDimensions& currentAxes; const COM& currentCOM; - const Ewald *calcEwald; - bool electrostatic; + const Ewald *calcEwald; + const Lambda& lambdaRef; + XYZArray& atomForceRef; + XYZArray& molForceRef; + bool multiParticleEnabled; + bool electrostatic, ewald; std::vector particleKind; std::vector particleMol; diff --git a/src/CellList.cpp b/src/CellList.cpp index 8e3801fc5..98532d8e7 100644 --- a/src/CellList.cpp +++ b/src/CellList.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/CellList.h b/src/CellList.h index 88c9748b3..d04fc2d6b 100644 --- a/src/CellList.h +++ b/src/CellList.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -85,6 +85,12 @@ inline int CellList::PositionToCell(const XYZ& posRef, int box) const int x = (int)(pos.x / cellSize[box].x); int y = (int)(pos.y / cellSize[box].y); int z = (int)(pos.z / cellSize[box].z); + //Check the cell number to avoid segfult for coordinates close to axis + //x, y, and z should never be equal or greater than number of cells in x, y, + // and z axis, respectively. + x -= (x == edgeCells[box][0] ? 1 : 0); + y -= (y == edgeCells[box][1] ? 1 : 0); + z -= (z == edgeCells[box][2] ? 1 : 0); return x * edgeCells[box][1] * edgeCells[box][2] + y * edgeCells[box][2] + z; } diff --git a/src/CheckpointOutput.cpp b/src/CheckpointOutput.cpp index a1c531aaf..f1fe1090b 100644 --- a/src/CheckpointOutput.cpp +++ b/src/CheckpointOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -26,7 +26,11 @@ union uint32_output_union { CheckpointOutput::CheckpointOutput(System & sys, StaticVals const& statV) : moveSetRef(sys.moveSettings), molLookupRef(sys.molLookupRef), boxDimRef(sys.boxDimRef), molRef(statV.mol), prngRef(sys.prng), +#if GOMC_LIB_MPI + coordCurrRef(sys.coordinates), filename(pathToReplicaDirectory + "checkpoint.dat") +#else coordCurrRef(sys.coordinates), filename("checkpoint.dat") +#endif { outputFile = NULL; } @@ -143,108 +147,83 @@ void CheckpointOutput::printMoleculeLookupData() void CheckpointOutput::printMoveSettingsData() { - uint size_x, size_y, size_z; - - // print size of scale - size_x = moveSetRef.scale.size(); - size_y = moveSetRef.scale[0].size(); - size_z = moveSetRef.scale[0][0].size(); - outputUintIn8Chars(size_x); - outputUintIn8Chars(size_y); - outputUintIn8Chars(size_z); - - // print scale array - for(int i = 0; i < size_x; i++) { - for(int j = 0; j < size_y; j++) { - for(int k = 0; k < size_z; k++) { - outputDoubleIn8Chars(moveSetRef.scale[i][j][k]); - } - } - } - - // print size of acceptPercent - size_x = moveSetRef.acceptPercent.size(); - size_y = moveSetRef.acceptPercent[0].size(); - size_z = moveSetRef.acceptPercent[0][0].size(); - outputUintIn8Chars(size_x); - outputUintIn8Chars(size_y); - outputUintIn8Chars(size_z); - - // print acceptPercent array - for(int i = 0; i < size_x; i++) { - for(int j = 0; j < size_y; j++) { - for(int k = 0; k < size_z; k++) { - outputDoubleIn8Chars(moveSetRef.acceptPercent[i][j][k]); - } - } - } + printVector3DDouble(moveSetRef.scale); + printVector3DDouble(moveSetRef.acceptPercent); + printVector3DUint(moveSetRef.accepted); + printVector3DUint(moveSetRef.tries); + printVector3DUint(moveSetRef.tempAccepted); + printVector3DUint(moveSetRef.tempTries); + printVector2DUint(moveSetRef.mp_tries); + printVector2DUint(moveSetRef.mp_accepted); + printVector1DDouble(moveSetRef.mp_t_max); + printVector1DDouble(moveSetRef.mp_r_max); +} - // print size of accepted - size_x = moveSetRef.accepted.size(); - size_y = moveSetRef.accepted[0].size(); - size_z = moveSetRef.accepted[0][0].size(); +void CheckpointOutput::printVector3DDouble(vector< vector< vector > > data) +{ + // print size of tempTries + ulong size_x = data.size(); + ulong size_y = data[0].size(); + ulong size_z = data[0][0].size(); outputUintIn8Chars(size_x); outputUintIn8Chars(size_y); outputUintIn8Chars(size_z); - // print accepted array + // print tempTries array for(int i = 0; i < size_x; i++) { for(int j = 0; j < size_y; j++) { for(int k = 0; k < size_z; k++) { - outputUintIn8Chars(moveSetRef.accepted[i][j][k]); + outputDoubleIn8Chars(data[i][j][k]); } } } +} - // print size of tries - size_x = moveSetRef.tries.size(); - size_y = moveSetRef.tries[0].size(); - size_z = moveSetRef.tries[0][0].size(); +void CheckpointOutput::printVector3DUint(vector< vector< vector > > data) +{ + // print size of tempTries + ulong size_x = data.size(); + ulong size_y = data[0].size(); + ulong size_z = data[0][0].size(); outputUintIn8Chars(size_x); outputUintIn8Chars(size_y); outputUintIn8Chars(size_z); - // print tries array + // print tempTries array for(int i = 0; i < size_x; i++) { for(int j = 0; j < size_y; j++) { for(int k = 0; k < size_z; k++) { - outputUintIn8Chars(moveSetRef.tries[i][j][k]); + outputUintIn8Chars(data[i][j][k]); } } } +} - // print size of tempAccepted - size_x = moveSetRef.tempAccepted.size(); - size_y = moveSetRef.tempAccepted[0].size(); - size_z = moveSetRef.tempAccepted[0][0].size(); +void CheckpointOutput::printVector2DUint(vector< vector< uint > > data) +{ + // print size of array + ulong size_x = data.size(); + ulong size_y = data[0].size(); outputUintIn8Chars(size_x); outputUintIn8Chars(size_y); - outputUintIn8Chars(size_z); - // print tempAccepted array + // print array iteself for(int i = 0; i < size_x; i++) { for(int j = 0; j < size_y; j++) { - for(int k = 0; k < size_z; k++) { - outputUintIn8Chars(moveSetRef.tempAccepted[i][j][k]); - } + outputUintIn8Chars(data[i][j]); } } +} - // print size of tempTries - size_x = moveSetRef.tempTries.size(); - size_y = moveSetRef.tempTries[0].size(); - size_z = moveSetRef.tempTries[0][0].size(); +void CheckpointOutput::printVector1DDouble(vector< double > data) +{ + // print size of array + ulong size_x = data.size(); outputUintIn8Chars(size_x); - outputUintIn8Chars(size_y); - outputUintIn8Chars(size_z); - // print tempTries array + // print array iteself for(int i = 0; i < size_x; i++) { - for(int j = 0; j < size_y; j++) { - for(int k = 0; k < size_z; k++) { - outputUintIn8Chars(moveSetRef.tempTries[i][j][k]); - } - } + outputDoubleIn8Chars(data[i]); } } diff --git a/src/CheckpointOutput.h b/src/CheckpointOutput.h index 8562b3994..8ae966bbe 100644 --- a/src/CheckpointOutput.h +++ b/src/CheckpointOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -9,7 +9,9 @@ along with this program, also can be found at . #include "OutputAbstracts.h" #include "MoveSettings.h" #include "Coordinates.h" +#include "MoveBase.h" #include +#include "GOMC_Config.h" class CheckpointOutput : public OutputableBase { @@ -51,13 +53,18 @@ class CheckpointOutput : public OutputableBase ulong stepsPerCheckpoint; void openOutputFile(); - void printStepNumber(const ulong step); + void printStepNumber(ulong step); void printRandomNumbers(); void printCoordinates(); void printMoleculeLookupData(); void printMoveSettingsData(); void printBoxDimensionsData(); + void printVector3DDouble(vector< vector< vector > > data); + void printVector3DUint(vector< vector< vector > > data); + void printVector2DUint(vector< vector< uint > > data); + void printVector1DDouble(vector< double > data); void outputDoubleIn8Chars(double data); void outputUintIn8Chars(uint32_t data); + }; diff --git a/src/CheckpointSetup.cpp b/src/CheckpointSetup.cpp index 2b8e5ba50..46df101bc 100644 --- a/src/CheckpointSetup.cpp +++ b/src/CheckpointSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -139,109 +139,16 @@ void CheckpointSetup::readMoleculeLookupData() void CheckpointSetup::readMoveSettingsData() { - uint size_x, size_y, size_z; - - // read size of scale - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // read scale array - scaleVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - scaleVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - scaleVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - scaleVec[i][j][k] = readDoubleIn8Chars(); - } - } - } - - // read size of acceptPercent - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // read acceptPercent array - acceptPercentVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - acceptPercentVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - acceptPercentVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - acceptPercentVec[i][j][k] = readDoubleIn8Chars(); - } - } - } - - // read size of accepted - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // read accepted array - acceptedVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - acceptedVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - acceptedVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - acceptedVec[i][j][k] = readUintIn8Chars(); - } - } - } - - // print size of tries - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // print tries array - triesVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - triesVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - triesVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - triesVec[i][j][k] = readUintIn8Chars(); - } - } - } - - // print size of tempAccepted - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // print tempAccepted array - tempAcceptedVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - tempAcceptedVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - tempAcceptedVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - tempAcceptedVec[i][j][k] = readUintIn8Chars(); - } - } - } - - // print size of tempTries - size_x = readUintIn8Chars(); - size_y = readUintIn8Chars(); - size_z = readUintIn8Chars(); - - // print tempTries array - tempTriesVec.resize(size_x); - for(int i = 0; i < size_x; i++) { - tempTriesVec[i].resize(size_y); - for(int j = 0; j < size_y; j++) { - tempTriesVec[i][j].resize(size_z); - for(int k = 0; k < size_z; k++) { - tempTriesVec[i][j][k] = readUintIn8Chars(); - } - } - } + readVector3DDouble(scaleVec); + readVector3DDouble(acceptPercentVec); + readVector3DUint(acceptedVec); + readVector3DUint(triesVec); + readVector3DUint(tempAcceptedVec); + readVector3DUint(tempTriesVec); + readVector2DUint(mp_triesVec); + readVector2DUint(mp_acceptedVec); + readVector1DDouble(mp_t_maxVec); + readVector1DDouble(mp_r_maxVec); } void CheckpointSetup::openInputFile() @@ -351,4 +258,77 @@ void CheckpointSetup::SetMoveSettings(MoveSettings & moveSettings) moveSettings.tries = this->triesVec; moveSettings.tempAccepted = this->tempAcceptedVec; moveSettings.tempTries = this->tempTriesVec; + moveSettings.mp_tries = this->mp_triesVec; + moveSettings.mp_accepted = this->mp_acceptedVec; + moveSettings.mp_t_max = this->mp_t_maxVec; + moveSettings.mp_r_max = this->mp_r_maxVec; +} + +void +CheckpointSetup::readVector3DDouble(vector > > &data) +{ + // read size of data + ulong size_x = readUintIn8Chars(); + ulong size_y = readUintIn8Chars(); + ulong size_z = readUintIn8Chars(); + + // read array + data.resize(size_x); + for(int i = 0; i < size_x; i++) { + data[i].resize(size_y); + for(int j = 0; j < size_y; j++) { + data[i][j].resize(size_z); + for(int k = 0; k < size_z; k++) { + data[i][j][k] = readDoubleIn8Chars(); + } + } + } +} + +void CheckpointSetup::readVector3DUint(vector > > &data) +{ + // read size of data + ulong size_x = readUintIn8Chars(); + ulong size_y = readUintIn8Chars(); + ulong size_z = readUintIn8Chars(); + + // read array + data.resize(size_x); + for(int i = 0; i < size_x; i++) { + data[i].resize(size_y); + for(int j = 0; j < size_y; j++) { + data[i][j].resize(size_z); + for(int k = 0; k < size_z; k++) { + data[i][j][k] = readUintIn8Chars(); + } + } + } +} + +void CheckpointSetup::readVector2DUint(vector > &data) +{ + // read size of data + ulong size_x = readUintIn8Chars(); + ulong size_y = readUintIn8Chars(); + + // read array + data.resize(size_x); + for(int i = 0; i < size_x; i++) { + data[i].resize(size_y); + for(int j = 0; j < size_y; j++) { + data[i][j] = readUintIn8Chars(); + } + } +} + +void CheckpointSetup::readVector1DDouble(vector &data) +{ +// read size of data + ulong size_x = readUintIn8Chars(); + + // read array + data.resize(size_x); + for(int i = 0; i < size_x; i++) { + data[i] = readDoubleIn8Chars(); + } } diff --git a/src/CheckpointSetup.h b/src/CheckpointSetup.h index 21aecd910..4576ec258 100644 --- a/src/CheckpointSetup.h +++ b/src/CheckpointSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -62,6 +62,9 @@ class CheckpointSetup vector > > scaleVec, acceptPercentVec; vector > > acceptedVec, triesVec, tempAcceptedVec, tempTriesVec; + vector< vector< uint > > mp_acceptedVec, mp_triesVec; + vector< double > mp_r_maxVec; + vector< double > mp_t_maxVec; // private functions used by ReadAll and Get functions void openInputFile(); @@ -73,6 +76,11 @@ class CheckpointSetup void readBoxDimensionsData(); void closeInputFile(); + void readVector3DDouble(vector< vector< vector > > & data); + void readVector3DUint(vector< vector< vector > > & data); + void readVector2DUint(vector< vector< uint > > & data); + void readVector1DDouble(vector< double > & data); double readDoubleIn8Chars(); uint32_t readUintIn8Chars(); + }; diff --git a/src/Clock.h b/src/Clock.h index 67daa442c..a83fc8ecb 100644 --- a/src/Clock.h +++ b/src/Clock.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/ConfigSetup.cpp b/src/ConfigSetup.cpp index 03d7474ff..1380c061b 100644 --- a/src/ConfigSetup.cpp +++ b/src/ConfigSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -12,7 +12,9 @@ along with this program, also can be found at . #include "ConfigSetup.h" +#ifndef DBL_MAX #define DBL_MAX 1.7976931348623158e+308 +#endif int stringtoi(const std::string& s) { @@ -48,6 +50,7 @@ ConfigSetup::ConfigSetup(void) sys.elect.oneFourScale = DBL_MAX; sys.elect.dielectric = DBL_MAX; sys.memcVal.enable = false; + sys.cfcmcVal.enable = false; sys.intraMemcVal.enable = false; sys.step.total = ULONG_MAX; sys.step.equil = ULONG_MAX; @@ -81,9 +84,10 @@ ConfigSetup::ConfigSetup(void) sys.moves.displace = DBL_MAX; sys.moves.rotate = DBL_MAX; sys.moves.intraSwap = DBL_MAX; + sys.moves.multiParticleEnabled = false; + sys.moves.multiParticle = DBL_MAX; sys.moves.regrowth = DBL_MAX; sys.moves.crankShaft = DBL_MAX; - sys.moves.memc = DBL_MAX; sys.moves.intraMemc = DBL_MAX; out.state.settings.enable = true; out.restart.settings.enable = true; @@ -114,6 +118,8 @@ ConfigSetup::ConfigSetup(void) out.statistics.vars.surfaceTension.fluct = false; #ifdef VARIABLE_PARTICLE_NUMBER sys.moves.transfer = DBL_MAX; + sys.moves.memc = DBL_MAX; + sys.moves.cfcmc = DBL_MAX; sys.cbmcTrials.bonded.ang = UINT_MAX; sys.cbmcTrials.bonded.dih = UINT_MAX; sys.cbmcTrials.nonbonded.first = UINT_MAX; @@ -160,7 +166,7 @@ bool ConfigSetup::CheckString(string str1, string str2) return (str1 == str2); } -void ConfigSetup::Init(const char *fileName) +void ConfigSetup::Init(const char *fileName, MultiSim const*const& multisim) { std::vector line; @@ -259,7 +265,11 @@ void ConfigSetup::Init(const char *fileName) } #endif else if(CheckString(line[0], "Temperature")) { - sys.T.inKelvin = stringtod(line[1]); + if (line.size() > 2 && multisim != NULL) { + sys.T.inKelvin = stringtod(line[multisim->worldRank + 1]); + } else { + sys.T.inKelvin = stringtod(line[1]); + } printf("%-40s %-4.4f K\n", "Info: Input Temperature", sys.T.inKelvin); } else if(CheckString(line[0], "Potential")) { if(CheckString(line[1], "VDW")) { @@ -271,6 +281,9 @@ void ConfigSetup::Init(const char *fileName) } else if(CheckString(line[1], "SWITCH")) { sys.ff.VDW_KIND = sys.ff.VDW_SWITCH_KIND; printf("%-40s %-s \n", "Info: Switch truncated potential", "Active"); + } else if(CheckString(line[1], "EXP6")) { + sys.ff.VDW_KIND = sys.ff.VDW_EXP6_KIND; + printf("%-40s %-s \n", "Info: Exp-6 Non-truncated potential", "Active"); } } else if(CheckString(line[0], "LRC")) { sys.ff.doTailCorr = checkBool(line[1]); @@ -335,11 +348,11 @@ void ConfigSetup::Init(const char *fileName) } } else if(CheckString(line[0], "SmallKindBackBone")) { if((line.size() % 2) == 0) { - std::cout << "Error: Atom Names in Small Kind BackBone must be in pair!\n"; + std::cout << "Error: Two atom names must be defined for the backbone of each small molecule kind!\n"; exit(EXIT_FAILURE); } if(line.size() >= 3) { - printf("%-41s", "Info: Atom Names in Small Kind BackBone"); + printf("%-41s", "Info: Atom Names in BackBone of Small Molecule Kind "); for(uint i = 1; i < line.size() - 1; i += 2) { if(i != 1) { printf(" , "); @@ -358,11 +371,11 @@ void ConfigSetup::Init(const char *fileName) } } else if(CheckString(line[0], "LargeKindBackBone")) { if((line.size() % 2) == 0) { - std::cout << "Error: Atom Names in Large Kind BackBone must be in pair!\n"; + std::cout << "Error: Two atom names must be defined for the backbone of each large molecule kind!\n"; exit(EXIT_FAILURE); } if(line.size() >= 3) { - printf("%-41s", "Info: Atom Names in Large Kind BackBone"); + printf("%-41s", "Info: Atom Names in BackBone of Large Molecule Kind "); for(uint i = 1; i < line.size() - 1; i += 2) { if(i != 1) { printf(" , "); @@ -473,6 +486,14 @@ void ConfigSetup::Init(const char *fileName) sys.moves.displace = stringtod(line[1]); printf("%-40s %-4.4f \n", "Info: Displacement move frequency", sys.moves.displace); + } else if(CheckString(line[0], "MultiParticleFreq")) { + sys.moves.multiParticle = stringtod(line[1]); + if(sys.moves.multiParticle > 0.00) { + sys.moves.multiParticleEnabled = true; + } + printf("%-40s %-4.4f \n", + "Info: Multi-Particle move frequency", + sys.moves.multiParticle); } else if(CheckString(line[0], "IntraSwapFreq")) { sys.moves.intraSwap = stringtod(line[1]); printf("%-40s %-4.4f \n", "Info: Intra-Swap move frequency", @@ -531,13 +552,9 @@ void ConfigSetup::Init(const char *fileName) printf("%-40s %-d \n", "Info: Fix volume box", 0); } #endif -#ifdef VARIABLE_PARTICLE_NUMBER +#if ENSEMBLE == GEMC || ENSEMBLE == GCMC else if(CheckString(line[0], "SwapFreq")) { -#if ENSEMBLE == NVT || ENSEMBLE == NPT - sys.moves.transfer = 0.000; -#else sys.moves.transfer = stringtod(line[1]); -#endif printf("%-40s %-4.4f \n", "Info: Molecule swap move frequency", sys.moves.transfer); } else if(CheckString(line[0], "MEMC-1Freq")) { @@ -564,6 +581,95 @@ void ConfigSetup::Init(const char *fileName) sys.memcVal.enable = true; sys.memcVal.MEMC3 = true; } + } else if(CheckString(line[0], "CFCMCFreq")) { + sys.moves.cfcmc = stringtod(line[1]); + printf("%-40s %-4.4f \n", "Info: CFCMC move frequency", + sys.moves.cfcmc); + if(sys.moves.cfcmc > 0.0) { + sys.cfcmcVal.enable = true; + } + } else if(CheckString(line[0], "LambdaCoulomb")) { + if(line.size() > 1) { + sys.cfcmcVal.readLambdaCoulomb = true; + printf("%-41s", "Info: Lambda Coulomb"); + for(uint i = 1; i < line.size(); i++) { + double val = stringtod(line[i]); + sys.cfcmcVal.lambdaCoulomb.push_back(val); + printf("%-6.3f", val); + } + std::cout << endl; + } + } else if(CheckString(line[0], "LambdaVDW")) { + if(line.size() > 1) { + sys.cfcmcVal.readLambdaVDW = true; + printf("%-41s", "Info: Lambda VDW"); + for(uint i = 1; i < line.size(); i++) { + double val = stringtod(line[i]); + sys.cfcmcVal.lambdaVDW.push_back(val); + printf("%-6.3f", val); + } + std::cout << endl; + } + } else if(CheckString(line[0], "RelaxingSteps")) { + if(line.size() > 1) { + sys.cfcmcVal.readRelaxSteps = true; + sys.cfcmcVal.relaxSteps = stringtoi(line[1]); + printf("%-40s %-4d \n", "Info: CFCMC Relaxing Steps", + sys.cfcmcVal.relaxSteps); + } + } else if(CheckString(line[0], "HistFlatness")) { + if(line.size() > 1) { + sys.cfcmcVal.readHistFlatness = true; + sys.cfcmcVal.histFlatness = stringtod(line[1]); + printf("%-40s %-4.4f \n", "Info: CFCMC Histogram Flatness", + sys.cfcmcVal.histFlatness); + } + } else if(CheckString(line[0], "MultiParticleRelaxing")) { + if(line.size() > 1) { + sys.cfcmcVal.MPEnable = checkBool(line[1]); + sys.cfcmcVal.readMPEnable = true; + if(sys.cfcmcVal.MPEnable) { + sys.moves.multiParticleEnabled = sys.cfcmcVal.MPEnable; + printf("%-40s %s \n", "Info: CFCMC Relaxing using MultiParticle", + "Active"); + } else { + printf("%-40s %s \n", "Info: CFCMC Relaxing using MultiParticle", + "Inactive"); + } + } + } else if(CheckString(line[0], "ScalePower")) { + if(line.size() > 1) { + sys.cfcmcVal.scalePower = stringtoi(line[1]); + sys.cfcmcVal.scalePowerRead = true; + printf("%-40s %-4d \n", "Info: Soft-core scaling power(p)", + sys.cfcmcVal.scalePower); + } + } else if(CheckString(line[0], "ScaleAlpha")) { + if(line.size() > 1) { + sys.cfcmcVal.scaleAlpha = stringtod(line[1]); + sys.cfcmcVal.scaleAlphaRead = true; + printf("%-40s %-4.4f \n", "Info: Soft-core softness(alpha)", + sys.cfcmcVal.scaleAlpha); + } + } else if(CheckString(line[0], "MinSigma")) { + if(line.size() > 1) { + sys.cfcmcVal.scaleSigma = stringtod(line[1]); + sys.cfcmcVal.scaleSigmaRead = true; + printf("%-40s %-4.4f A \n", "Info: Soft-core minimum sigma", + sys.cfcmcVal.scaleSigma); + } + } else if(CheckString(line[0], "ScaleCoulomb")) { + if(line.size() > 1) { + sys.cfcmcVal.scaleCoulomb = checkBool(line[1]); + sys.cfcmcVal.scaleCoulombRead = true; + if(sys.cfcmcVal.scaleCoulomb) { + printf("%-40s %s \n", "Info: Soft-core for Coulombic interaction", + "Active"); + } else { + printf("%-40s %s \n", "Info: Soft-core for Coulombic interaction", + "Inactive"); + } + } } #endif else if(CheckString(line[0], "CellBasisVector1")) { @@ -639,15 +745,22 @@ void ConfigSetup::Init(const char *fileName) #endif #if ENSEMBLE == GCMC else if(CheckString(line[0], "ChemPot")) { - if(line.size() != 3) { + if (line.size() > 3 && multisim != NULL) { + std::string resName = line[1]; + double val = stringtod(line[2 + multisim->worldRank]); + sys.chemPot.cp[resName] = val; + printf("%-40s %-6s %-6.4f K\n", "Info: Chemical potential", + resName.c_str(), val); + } else if(line.size() != 3) { std::cout << "Error: Chemical potential parameters are not specified!\n"; exit(EXIT_FAILURE); + } else { + std::string resName = line[1]; + double val = stringtod(line[2]); + sys.chemPot.cp[resName] = val; + printf("%-40s %-6s %-6.4f K\n", "Info: Chemical potential", + resName.c_str(), val); } - std::string resName = line[1]; - double val = stringtod(line[2]); - sys.chemPot.cp[resName] = val; - printf("%-40s %-6s %-6.4f K\n", "Info: Chemical potential", - resName.c_str(), val); } else if(CheckString(line[0], "Fugacity")) { if(line.size() != 3) { std::cout << "Error: Fugacity parameters are not specified!\n"; @@ -660,10 +773,108 @@ void ConfigSetup::Init(const char *fileName) printf("%-40s %-6s %-6.4f bar\n", "Info: Fugacity", resName.c_str(), val); } +#endif +#if ENSEMBLE == NVT || ENSEMBLE == NPT + else if(CheckString(line[0], "LambdaCoulomb")) { + if(line.size() > 1) { + sys.freeEn.readLambdaCoulomb = true; + printf("%-41s", "Info: Lambda Coulomb"); + for(uint i = 1; i < line.size(); i++) { + double val = stringtod(line[i]); + sys.freeEn.lambdaCoulomb.push_back(val); + printf("%-6.3f", val); + } + std::cout << endl; + } + } else if(CheckString(line[0], "LambdaVDW")) { + if(line.size() > 1) { + sys.freeEn.readLambdaVDW = true; + printf("%-41s", "Info: Lambda VDW"); + for(uint i = 1; i < line.size(); i++) { + double val = stringtod(line[i]); + sys.freeEn.lambdaVDW.push_back(val); + printf("%-6.3f", val); + } + std::cout << endl; + } + } else if(CheckString(line[0], "FreeEnergyCalc")) { + if(line.size() > 1) { + sys.freeEn.enable = checkBool(line[1]); + if(sys.freeEn.enable) { + printf("%-40s %-s \n", "Info: Free Energy Calculation", "Active"); + if(line.size() > 2) { + sys.freeEn.frequency = stringtoi(line[2]); + sys.freeEn.freqRead = true; + printf("%-40s %-4d \n", "Info: Free Energy Frequency", + sys.freeEn.frequency); + } + } else { + printf("%-40s %-s \n", "Info: Free Energy Calculation", "Inactive"); + } + } + } else if (CheckString(line[0], "MoleculeType")) { + if(line.size() > 1) { + sys.freeEn.molType = line[1]; + sys.freeEn.molTypeRead = true; + if(line.size() > 2) { + sys.freeEn.molIndex = stringtoi(line[2]); + sys.freeEn.molIndexRead = true; + printf("%-40s %-d in %-s \n", "Info: Free Energy Calc for Molecule", + sys.freeEn.molIndex, sys.freeEn.molType.c_str()); + } + } + } else if (CheckString(line[0], "InitialState")) { + if(line.size() > 1) { + sys.freeEn.iState = stringtoi(line[1]); + sys.freeEn.iStateRead = true; + printf("%-40s %-d \n", "Info: Free Energy Calc Lambda state", + sys.freeEn.iState); + } + } else if(CheckString(line[0], "ScalePower")) { + if(line.size() > 1) { + sys.freeEn.scalePower = stringtoi(line[1]); + sys.freeEn.scalePowerRead = true; + printf("%-40s %-4d \n", "Info: Soft-core scaling power(p)", + sys.freeEn.scalePower); + } + } else if(CheckString(line[0], "ScaleAlpha")) { + if(line.size() > 1) { + sys.freeEn.scaleAlpha = stringtod(line[1]); + sys.freeEn.scaleAlphaRead = true; + printf("%-40s %-4.4f \n", "Info: Soft-core softness(alpha)", + sys.freeEn.scaleAlpha); + } + } else if(CheckString(line[0], "MinSigma")) { + if(line.size() > 1) { + sys.freeEn.scaleSigma = stringtod(line[1]); + sys.freeEn.scaleSigmaRead = true; + printf("%-40s %-4.4f \n", "Info: Soft-core minimum sigma", + sys.freeEn.scaleSigma); + } + } else if(CheckString(line[0], "ScaleCoulomb")) { + if(line.size() > 1) { + sys.freeEn.scaleCoulomb = checkBool(line[1]); + sys.freeEn.scaleCoulombRead = true; + if(sys.freeEn.scaleCoulomb) { + printf("%-40s %s \n", "Info: Soft-core for Coulombic interaction", + "Active"); + } else { + printf("%-40s %s \n", "Info: Soft-core for Coulombic interaction", + "Inactive"); + } + } + } #endif else if(CheckString(line[0], "OutputName")) { - out.statistics.settings.uniqueStr.val = line[1]; - printf("%-40s %-s \n", "Info: Output name", line[1].c_str()); + if (multisim != NULL) { + std::stringstream replicaDirectory; + replicaDirectory << multisim->pathToReplicaDirectory << line[1]; + out.statistics.settings.uniqueStr.val = replicaDirectory.str(); + printf("%-40s %-s \n", "Info: Output name", replicaDirectory.str().c_str()); + } else { + out.statistics.settings.uniqueStr.val = line[1]; + printf("%-40s %-s \n", "Info: Output name", line[1].c_str()); + } } else if(CheckString(line[0], "CheckpointFreq")) { out.checkpoint.enable = checkBool(line[1]); if(line.size() == 3) @@ -816,12 +1027,25 @@ void ConfigSetup::fillDefaults(void) sys.elect.enable = true; } + if(sys.moves.rotate == DBL_MAX) { + sys.moves.rotate = 0.000; + printf("%-40s %-4.4f \n", "Default: Rotation move frequency", + sys.moves.rotate); + } + if(sys.moves.intraSwap == DBL_MAX) { sys.moves.intraSwap = 0.000; printf("%-40s %-4.4f \n", "Default: Intra-Swap move frequency", sys.moves.intraSwap); } + if(sys.moves.multiParticle == DBL_MAX) { + sys.moves.multiParticle = 0.000; + printf("%-40s %-4.4f \n", + "Default: Multi-Particle move frequency", + sys.moves.multiParticle); + } + if(sys.moves.intraMemc == DBL_MAX) { sys.moves.intraMemc = 0.0; printf("%-40s %-4.4f \n", "Default: Intra-MEMC move frequency", @@ -840,12 +1064,128 @@ void ConfigSetup::fillDefaults(void) sys.moves.crankShaft); } -#ifdef VARIABLE_PARTICLE_NUMBER +#if ENSEMBLE == GEMC || ENSEMBLE == GCMC if(sys.moves.memc == DBL_MAX) { sys.moves.memc = 0.0; printf("%-40s %-4.4f \n", "Default: MEMC move frequency", sys.moves.memc); } + + if(sys.moves.cfcmc == DBL_MAX) { + sys.moves.cfcmc = 0.0; + printf("%-40s %-4.4f \n", "Default: CFCMC move frequency", + sys.moves.cfcmc); + } + + if(sys.cfcmcVal.enable) { + if(!sys.cfcmcVal.readHistFlatness) { + sys.cfcmcVal.histFlatness = 0.3; + printf("%-40s %-4.4f \n", "Default: CFCMC Histogram Flatness", + sys.cfcmcVal.histFlatness); + } + + if(!sys.elect.enable && !sys.cfcmcVal.readLambdaCoulomb) { + sys.cfcmcVal.lambdaCoulomb.resize(sys.cfcmcVal.lambdaVDW.size(), 0.0); + sys.cfcmcVal.readLambdaCoulomb = true; + printf("%-41s", "Default: Lambda Coulomb"); + for(uint i = 0; i < sys.cfcmcVal.lambdaCoulomb.size(); i++) { + double val = sys.cfcmcVal.lambdaCoulomb[i]; + printf("%-6.3f", val); + } + std::cout << endl; + } + + if(sys.cfcmcVal.readLambdaVDW ) { + if(sys.elect.enable && !sys.cfcmcVal.readLambdaCoulomb) { + sys.cfcmcVal.lambdaCoulomb = sys.cfcmcVal.lambdaVDW; + sys.cfcmcVal.readLambdaCoulomb = true; + printf("%-41s", "Default: Lambda Coulomb"); + for(uint i = 0; i < sys.cfcmcVal.lambdaCoulomb.size(); i++) { + double val = sys.cfcmcVal.lambdaCoulomb[i]; + printf("%-6.3f", val); + } + std::cout << endl; + } + } + + if(!sys.cfcmcVal.readMPEnable) { + sys.cfcmcVal.readMPEnable = true; + sys.cfcmcVal.MPEnable = false; + printf("%-40s %s \n", "Info: CFCMC Relaxing using MultiParticle", + "Inactive"); + } + + if(!sys.cfcmcVal.scalePowerRead) { + sys.cfcmcVal.scalePower = 2; + printf("%-40s %-4d \n", "Default: Soft-core scale power(p)", + sys.cfcmcVal.scalePower); + } + if(!sys.cfcmcVal.scaleAlphaRead) { + sys.cfcmcVal.scaleAlpha = 0.5; + printf("%-40s %-4.4f \n", "Default: Soft-core softness(alpha)", + sys.cfcmcVal.scaleAlpha); + } + if(!sys.cfcmcVal.scaleSigmaRead) { + sys.cfcmcVal.scaleSigma = 3.0; + printf("%-40s %-4.4f A \n", "Default: Soft-core minimum sigma", + sys.cfcmcVal.scaleSigma); + } + if(!sys.cfcmcVal.scaleCoulombRead) { + sys.cfcmcVal.scaleCoulomb = false; + printf("%-40s %s A \n", "Default: Soft-core for Coulombic interaction", + "Inactive"); + } + } + +#endif + +#if ENSEMBLE == NVT || ENSEMBLE == NPT + if(sys.freeEn.enable) { + if(!sys.elect.enable && !sys.freeEn.readLambdaCoulomb) { + sys.freeEn.lambdaCoulomb.resize(sys.freeEn.lambdaVDW.size(), 0.0); + sys.freeEn.readLambdaCoulomb = true; + printf("%-41s", "Default: Lambda Coulomb"); + for(uint i = 0; i < sys.freeEn.lambdaCoulomb.size(); i++) { + double val = sys.freeEn.lambdaCoulomb[i]; + printf("%-6.3f", val); + } + std::cout << endl; + } + + if(sys.freeEn.readLambdaVDW) { + if(sys.elect.enable && !sys.freeEn.readLambdaCoulomb) { + sys.freeEn.lambdaCoulomb = sys.freeEn.lambdaVDW; + sys.freeEn.readLambdaCoulomb = true; + printf("%-41s", "Default: Lambda Coulomb"); + for(uint i = 0; i < sys.freeEn.lambdaCoulomb.size(); i++) { + double val = sys.freeEn.lambdaCoulomb[i]; + printf("%-6.3f", val); + } + std::cout << endl; + } + } + + if(!sys.freeEn.scalePowerRead) { + sys.freeEn.scalePower = 2; + printf("%-40s %-4d \n", "Default: Soft-core scale power(p)", + sys.freeEn.scalePower); + } + if(!sys.freeEn.scaleAlphaRead) { + sys.freeEn.scaleAlpha = 0.5; + printf("%-40s %-4.4f \n", "Default: Soft-core softness(alpha)", + sys.freeEn.scaleAlpha); + } + if(!sys.freeEn.scaleSigmaRead) { + sys.freeEn.scaleSigma = 3.0; + printf("%-40s %-4.4f A \n", "Default: Soft-core minimum sigma", + sys.freeEn.scaleSigma); + } + if(!sys.freeEn.scaleCoulombRead) { + sys.freeEn.scaleCoulomb = false; + printf("%-40s %s A \n", "Default: Soft-core for Coulombic interaction", + "Inactive"); + } + } #endif if(sys.exclude.EXCLUDE_KIND == UINT_MAX) { @@ -853,9 +1193,9 @@ void ConfigSetup::fillDefaults(void) printf("%-40s %-s \n", "Default: Exclude", "ONE-FOUR"); } - if(sys.elect.enable && sys.elect.oneFourScale == DBL_MAX) { - sys.elect.oneFourScale = 0.0f; - if(sys.exclude.EXCLUDE_KIND != sys.exclude.EXC_ONEFOUR_KIND) { + if(sys.elect.oneFourScale == DBL_MAX) { + if(sys.elect.enable) { + sys.elect.oneFourScale = 0.0; printf("%-40s %-lf \n", "Default: Modified 1-4 Electrostatic scaling", sys.elect.oneFourScale); } @@ -932,12 +1272,7 @@ void ConfigSetup::verifyInputs(void) int i; if(!sys.elect.enable && sys.elect.oneFourScale != DBL_MAX) { printf("Warning: 1-4 Electrostatic scaling set, but will be ignored.\n"); - } - - if((sys.elect.oneFourScale != 0.0) && - (sys.exclude.EXCLUDE_KIND == sys.exclude.EXC_ONEFOUR_KIND)) { - printf("Warning: 1-4 Electrostatic scaling set, but will be ignored.\n"); - sys.elect.oneFourScale = 0.0f; + sys.elect.oneFourScale = 0.0; } if(sys.elect.ewald == false && sys.elect.enable == true) { @@ -1017,7 +1352,8 @@ void ConfigSetup::verifyInputs(void) std::cout << "Error: Potential type is not specified!" << std::endl; exit(EXIT_FAILURE); } - if(sys.ff.VDW_KIND == sys.ff.VDW_STD_KIND && sys.ff.doTailCorr == false) { + if(((sys.ff.VDW_KIND == sys.ff.VDW_STD_KIND) || + (sys.ff.VDW_KIND == sys.ff.VDW_EXP6_KIND)) && (sys.ff.doTailCorr == false)) { std::cout << "Warning: Long Range Correction is Inactive for " << "Non-truncated potential." << std::endl; } @@ -1062,14 +1398,6 @@ void ConfigSetup::verifyInputs(void) std::cout << "Error: Displacement move frequency is not specified!\n"; exit(EXIT_FAILURE); } - if(sys.moves.rotate == DBL_MAX) { - std::cout << "Error: Rotation move frequency is not specified!\n"; - exit(EXIT_FAILURE); - } - if(sys.moves.intraSwap == DBL_MAX) { - std::cout << "Error: Intra-Swap move frequency is not specified!\n"; - exit(EXIT_FAILURE); - } #if ENSEMBLE == NPT if(sys.moves.volume == DBL_MAX) { std::cout << "Error: Volume move frequency is not specified!" << std::endl; @@ -1087,7 +1415,8 @@ void ConfigSetup::verifyInputs(void) } if(abs(sys.moves.displace + sys.moves.rotate + sys.moves.transfer + sys.moves.intraSwap + sys.moves.volume + sys.moves.regrowth + - sys.moves.memc + sys.moves.intraMemc + sys.moves.crankShaft - 1.0) > 0.001) { + sys.moves.memc + sys.moves.intraMemc + sys.moves.crankShaft + + sys.moves.multiParticle + sys.moves.cfcmc - 1.0) > 0.001) { std::cout << "Error: Sum of move frequncies are not equal to one!\n"; exit(EXIT_FAILURE); } @@ -1096,9 +1425,10 @@ void ConfigSetup::verifyInputs(void) std::cout << "Error: Volume move frequency is not specified!" << std::endl; exit(EXIT_FAILURE); } + if(abs(sys.moves.displace + sys.moves.rotate + sys.moves.intraSwap + sys.moves.volume + sys.moves.regrowth + sys.moves.intraMemc + - sys.moves.crankShaft - 1.0) > 0.001) { + sys.moves.crankShaft + sys.moves.multiParticle - 1.0) > 0.001) { std::cout << "Error: Sum of move frequncies are not equal to one!\n"; exit(EXIT_FAILURE); } @@ -1110,14 +1440,15 @@ void ConfigSetup::verifyInputs(void) } if(abs(sys.moves.displace + sys.moves.rotate + sys.moves.intraSwap + sys.moves.transfer + sys.moves.regrowth + sys.moves.memc + - sys.moves.intraMemc + sys.moves.crankShaft - 1.0) > 0.001) { + sys.moves.intraMemc + sys.moves.crankShaft + + sys.moves.multiParticle + sys.moves.cfcmc - 1.0) > 0.001) { std::cout << "Error: Sum of move frequncies are not equal to one!!\n"; exit(EXIT_FAILURE); } #else if(abs(sys.moves.displace + sys.moves.rotate + sys.moves.intraSwap + - sys.moves.regrowth + sys.moves.intraMemc + sys.moves.crankShaft - - 1.0) > 0.001) { + sys.moves.regrowth + sys.moves.intraMemc + sys.moves.crankShaft + + sys.moves.multiParticle - 1.0) > 0.001) { std::cout << "Error: Sum of move frequncies are not equal to one!!\n"; exit(EXIT_FAILURE); } @@ -1155,7 +1486,8 @@ void ConfigSetup::verifyInputs(void) exit(EXIT_FAILURE); } if(((sys.ff.VDW_KIND == sys.ff.VDW_STD_KIND) || - (sys.ff.VDW_KIND == sys.ff.VDW_SHIFT_KIND)) && sys.ff.rswitch != DBL_MAX) { + (sys.ff.VDW_KIND == sys.ff.VDW_SHIFT_KIND) || + (sys.ff.VDW_KIND == sys.ff.VDW_EXP6_KIND)) && sys.ff.rswitch != DBL_MAX) { std::cout << "Warning: Switch distance set, but will be ignored." << std::endl; } @@ -1259,6 +1591,180 @@ void ConfigSetup::verifyInputs(void) } } + if(sys.cfcmcVal.enable) { + + if(!sys.cfcmcVal.readLambdaCoulomb) { + std::cout << "Error: Lambda Coulomb states were not defined for " << + "CFCMC move! \n"; + exit(EXIT_FAILURE); + } + + if (!sys.cfcmcVal.readLambdaVDW) { + std::cout << "Error: Lambda VDW states were not defined for " << + "CFCMC move! \n"; + exit(EXIT_FAILURE); + } + + if(sys.cfcmcVal.lambdaCoulomb.size() != sys.cfcmcVal.lambdaVDW.size()) { + std::cout << "Error: Number of Lambda states for VDW and Coulomb " << + "are not same in CFCMC move! \n"; + exit(EXIT_FAILURE); + } + + for(uint i = 0; i < sys.cfcmcVal.lambdaVDW.size(); i++) { + bool decreasing = false; + for(uint j = i; j < sys.cfcmcVal.lambdaVDW.size(); j++) { + if(sys.cfcmcVal.lambdaVDW[i] > sys.cfcmcVal.lambdaVDW[j]) { + decreasing = true; + } + } + if(decreasing) { + std::cout << "Error: Lambda VDW values are not in increasing order " << + "in CFCMC move! \n"; + exit(EXIT_FAILURE); + } + } + + for(uint i = 0; i < sys.cfcmcVal.lambdaCoulomb.size(); i++) { + bool decreasing = false; + for(uint j = i; j < sys.cfcmcVal.lambdaCoulomb.size(); j++) { + if(sys.cfcmcVal.lambdaCoulomb[i] > sys.cfcmcVal.lambdaCoulomb[j]) { + decreasing = true; + } + } + if(decreasing) { + std::cout << "Error: Lambda Coulomb values are not in increasing " << + "order in CFCMC move! \n"; + exit(EXIT_FAILURE); + } + } + + uint last = sys.cfcmcVal.lambdaVDW.size() - 1; + if(sys.cfcmcVal.lambdaVDW[last] < 0.9999) { + std::cout << "Error: Last Lambda value for VDW is not 1.0 " << + "in CFCMC move! \n"; + exit(EXIT_FAILURE); + } + + if(sys.elect.enable) { + last = sys.cfcmcVal.lambdaCoulomb.size() - 1; + if(sys.cfcmcVal.lambdaCoulomb[last] < 0.9999) { + std::cout << "Error: Last Lambda value for Coulomb is not 1.0 " << + "in CFCMC move! \n"; + exit(EXIT_FAILURE); + } + } + + if(!sys.cfcmcVal.readRelaxSteps) { + std::cout << "Error: Relaxing steps was not defined for CFCMC move! \n"; + exit(EXIT_FAILURE); + } else if (sys.cfcmcVal.relaxSteps == 0) { + std::cout << "Warning: No thermal relaxing move will be performed in " << + "CFCMC move! \n"; + } + + if(sys.cfcmcVal.histFlatness > 0.999 || sys.cfcmcVal.histFlatness < 0.001) { + std::cout << "Error: Unacceptable Value for Histogram Flatness in " << + "CFCMC move! \n"; + exit(EXIT_FAILURE); + } + } +#endif +#if ENSEMBLE == NVT || ENSEMBLE == NPT + if(sys.freeEn.enable) { + if(!sys.freeEn.readLambdaCoulomb) { + std::cout << "Error: Lambda Coulomb states were not defined for " << + "Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + + if (!sys.freeEn.readLambdaVDW) { + std::cout << "Error: Lambda VDW states were not defined for " << + "Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + + if(sys.freeEn.lambdaCoulomb.size() != sys.freeEn.lambdaVDW.size()) { + std::cout << "Error: Number of Lambda states for VDW and Coulomb " << + "are not same in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + + for(uint i = 0; i < sys.freeEn.lambdaVDW.size(); i++) { + bool decreasing = false; + for(uint j = i; j < sys.freeEn.lambdaVDW.size(); j++) { + if(sys.freeEn.lambdaVDW[i] > sys.freeEn.lambdaVDW[j]) { + decreasing = true; + } + } + if(decreasing) { + std::cout << "Error: Lambda VDW values are not in increasing order " << + "in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + } + + for(uint i = 0; i < sys.freeEn.lambdaCoulomb.size(); i++) { + bool decreasing = false; + for(uint j = i; j < sys.freeEn.lambdaCoulomb.size(); j++) { + if(sys.freeEn.lambdaCoulomb[i] > sys.freeEn.lambdaCoulomb[j]) { + decreasing = true; + } + } + if(decreasing) { + std::cout << "Error: Lambda Coulomb values are not in increasing " << + "order in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + } + + uint last = sys.freeEn.lambdaVDW.size() - 1; + if(sys.freeEn.lambdaVDW[last] < 0.9999) { + std::cout << "Error: Last Lambda value for VDW is not 1.0 " << + "in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + + if(sys.elect.enable) { + last = sys.freeEn.lambdaCoulomb.size() - 1; + if(sys.freeEn.lambdaCoulomb[last] < 0.9999) { + std::cout << "Error: Last Lambda value for Coulomb is not 1.0 " << + "in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + } + + if(sys.freeEn.lambdaVDW.size() <= sys.freeEn.iState) { + std::cout << "Error: Initial Lambda state is not valid " << + "in Free Energy Calculation! \n"; + exit(EXIT_FAILURE); + } + + if(!sys.freeEn.freqRead) { + std::cout << "Error: Frequency of Free Energy Calculation was " << + "not defined! \n"; + exit(EXIT_FAILURE); + } + if(!sys.freeEn.molTypeRead) { + std::cout << "Error: Molecule Type for Free Energy Calculation was " << + "not defined! \n"; + exit(EXIT_FAILURE); + } + if(!sys.freeEn.molIndexRead) { + std::cout << "Error: Molecule Index for Free Energy Calculation was " << + "not defined! \n"; + exit(EXIT_FAILURE); + } +#if ENSEMBLE == NVT + if(sys.step.pressureCalc) { + if((sys.freeEn.frequency % sys.step.pressureCalcFreq) != 0) { + std::cout << "Error: Free Energy calculation Freq must be common " << + "number of Pressure calculation freq! \n"; + exit(EXIT_FAILURE); + } + } +#endif + } #endif if(sys.T.inKelvin == DBL_MAX) { std::cout << "Error: Temperature is not specified!\n"; @@ -1376,25 +1882,27 @@ void ConfigSetup::verifyInputs(void) #endif } -const std::string config_setup::PRNGKind::KIND_RANDOM = "RANDOM", - config_setup::PRNGKind::KIND_SEED = "INTSEED", - config_setup::PRNGKind::KIND_RESTART = "RESTART", - config_setup::FFKind::FF_CHARMM = "CHARMM", - config_setup::FFKind::FF_EXOTIC = "EXOTIC", - config_setup::FFKind::FF_MARTINI = "MARTINI", - config_setup::FFValues::VDW = "VDW", - config_setup::FFValues::VDW_SHIFT = "VDW_SHIFT", - config_setup::FFValues::VDW_SWITCH = "VDW_SWITCH", - config_setup::Exclude::EXC_ONETWO = "1-2", - config_setup::Exclude::EXC_ONETHREE = "1-3", - config_setup::Exclude::EXC_ONEFOUR = "1-4"; +const std::string config_setup::PRNGKind::KIND_RANDOM = "RANDOM"; +const std::string config_setup::PRNGKind::KIND_SEED = "INTSEED"; +const std::string config_setup::PRNGKind::KIND_RESTART = "RESTART"; +const std::string config_setup::FFKind::FF_CHARMM = "CHARMM"; +const std::string config_setup::FFKind::FF_EXOTIC = "EXOTIC"; +const std::string config_setup::FFKind::FF_MARTINI = "MARTINI"; +const std::string config_setup::FFValues::VDW = "VDW"; +const std::string config_setup::FFValues::VDW_SHIFT = "VDW_SHIFT"; +const std::string config_setup::FFValues::VDW_EXP6 = "VDW_EXP6"; +const std::string config_setup::FFValues::VDW_SWITCH = "VDW_SWITCH"; +const std::string config_setup::Exclude::EXC_ONETWO = "1-2"; +const std::string config_setup::Exclude::EXC_ONETHREE = "1-3"; +const std::string config_setup::Exclude::EXC_ONEFOUR = "1-4"; const char ConfigSetup::defaultConfigFileName[] = "in.dat"; -const char ConfigSetup::configFileAlias[] = "GO-MC Configuration File"; - -const uint config_setup::FFValues::VDW_STD_KIND = 0, - config_setup::FFValues::VDW_SHIFT_KIND = 1, - config_setup::FFValues::VDW_SWITCH_KIND = 2, - config_setup::Exclude::EXC_ONETWO_KIND = 0, - config_setup::Exclude::EXC_ONETHREE_KIND = 1, - config_setup::Exclude::EXC_ONEFOUR_KIND = 2; +const char ConfigSetup::configFileAlias[] = "GOMC Configuration File"; + +const uint config_setup::FFValues::VDW_STD_KIND = 0; +const uint config_setup::FFValues::VDW_SHIFT_KIND = 1; +const uint config_setup::FFValues::VDW_SWITCH_KIND = 2; +const uint config_setup::FFValues::VDW_EXP6_KIND = 3; +const uint config_setup::Exclude::EXC_ONETWO_KIND = 0; +const uint config_setup::Exclude::EXC_ONETHREE_KIND = 1; +const uint config_setup::Exclude::EXC_ONEFOUR_KIND = 2; diff --git a/src/ConfigSetup.h b/src/ConfigSetup.h index 5d93ff525..379431b1c 100644 --- a/src/ConfigSetup.h +++ b/src/ConfigSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -26,6 +26,15 @@ along with this program, also can be found at . #include //for reading in variable # of chem. pot. #endif +#include "GOMC_Config.h" //For PT +#include "ParallelTemperingPreprocessor.h" +#include //for prefixing uniqueVal with the pathToReplicaDirectory +#ifdef WIN32 +#define OS_SEP '\\' +#else +#define OS_SEP '/' +#endif + namespace config_setup { ///////////////////////////////////////////////////////////////////////// @@ -134,8 +143,8 @@ struct FFValues { bool doTailCorr, vdwGeometricSigma; std::string kind; - static const std::string VDW, VDW_SHIFT, VDW_SWITCH; - static const uint VDW_STD_KIND, VDW_SHIFT_KIND, VDW_SWITCH_KIND; + static const std::string VDW, VDW_SHIFT, VDW_SWITCH, VDW_EXP6; + static const uint VDW_STD_KIND, VDW_SHIFT_KIND, VDW_SWITCH_KIND, VDW_EXP6_KIND; }; #if ENSEMBLE == GEMC || ENSEMBLE == NPT @@ -156,12 +165,14 @@ struct Step { //Holds the percentage of each kind of move for this ensemble. struct MovePercents { - double displace, rotate, intraSwap, intraMemc, regrowth, crankShaft; + double displace, rotate, intraSwap, intraMemc, regrowth, crankShaft, + multiParticle; + bool multiParticleEnabled; #ifdef VARIABLE_VOLUME double volume; #endif #ifdef VARIABLE_PARTICLE_NUMBER - double transfer, memc; + double transfer, memc, cfcmc; #endif }; @@ -242,6 +253,45 @@ struct MEMCVal { } }; +struct CFCMCVal { + bool enable, readLambdaCoulomb, readLambdaVDW, readRelaxSteps; + bool readHistFlatness, MPEnable, readMPEnable; + uint relaxSteps; + double histFlatness; + //scaling parameter + uint scalePower; + double scaleAlpha, scaleSigma; + bool scaleCoulomb; + bool scalePowerRead, scaleAlphaRead, scaleSigmaRead, scaleCoulombRead; + std::vector lambdaCoulomb, lambdaVDW; + CFCMCVal(void) + { + readLambdaCoulomb = readRelaxSteps = readHistFlatness = false; + readMPEnable = MPEnable = readLambdaVDW = enable = false; + scalePowerRead = scaleAlphaRead = scaleSigmaRead = scaleCoulombRead = false; + } +}; + +struct FreeEnergy { + bool enable, readLambdaCoulomb, readLambdaVDW, freqRead; + bool molTypeRead, molIndexRead, iStateRead; + uint frequency, molIndex, iState; + //scaling parameter + uint scalePower; + double scaleAlpha, scaleSigma; + bool scaleCoulomb; + bool scalePowerRead, scaleAlphaRead, scaleSigmaRead, scaleCoulombRead; + std::string molType; + std::vector lambdaCoulomb, lambdaVDW; + FreeEnergy(void) + { + readLambdaCoulomb = readLambdaVDW = enable = freqRead = false; + molTypeRead = molIndexRead = iStateRead = false; + scalePowerRead = scaleAlphaRead = scaleSigmaRead = scaleCoulombRead = false; + } +}; + + #if ENSEMBLE == GCMC struct ChemicalPotential { bool isFugacity; @@ -258,6 +308,8 @@ struct SystemVals { Volume volume; //May go unused CBMC cbmcTrials; MEMCVal memcVal, intraMemcVal; + CFCMCVal cfcmcVal; + FreeEnergy freeEn; #if ENSEMBLE == GCMC ChemicalPotential chemPot; #elif ENSEMBLE == GEMC || ENSEMBLE == NPT @@ -337,8 +389,7 @@ class ConfigSetup config_setup::Output out; config_setup::SystemVals sys; ConfigSetup(void); - void Init(const char *fileName); - + void Init(const char *fileName, MultiSim const*const& multisim); private: void fillDefaults(void); bool checkBool(string str); diff --git a/src/ConsoleOutput.cpp b/src/ConsoleOutput.cpp index d30b5096a..5ab70b406 100644 --- a/src/ConsoleOutput.cpp +++ b/src/ConsoleOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -112,6 +112,13 @@ void ConsoleOutput::PrintMove(const uint box, const ulong step) const printElement(var->GetScale(box, sub), elementWidth); } + if(var->Performed(mv::MULTIPARTICLE)) { + sub = mv::MULTIPARTICLE; + printElement(var->GetTries(box, sub), elementWidth); + printElement(var->GetAccepted(box, sub), elementWidth); + printElement(var->GetAcceptPercent(box, sub), elementWidth); + } + if(var->Performed(mv::INTRA_SWAP)) { sub = mv::INTRA_SWAP; printElement(var->GetTries(box, sub), elementWidth); @@ -158,6 +165,13 @@ void ConsoleOutput::PrintMove(const uint box, const ulong step) const printElement(var->GetAccepted(box, sub), elementWidth); printElement(var->GetAcceptPercent(box, sub), elementWidth); } + + if(var->Performed(mv::CFCMC)) { + sub = mv::CFCMC; + printElement(var->GetTries(box, sub), elementWidth); + printElement(var->GetAccepted(box, sub), elementWidth); + printElement(var->GetAcceptPercent(box, sub), elementWidth); + } #endif #if ENSEMBLE == GEMC || ENSEMBLE == NPT @@ -343,6 +357,12 @@ void ConsoleOutput::PrintMoveTitle() printElement("ROTMAX", elementWidth); } + if(var->Performed(mv::MULTIPARTICLE)) { + printElement("MULTIPARTICLE", elementWidth); + printElement("MPACCEPT", elementWidth); + printElement("MPACCEPT%", elementWidth); + } + if(var->Performed(mv::INTRA_SWAP)) { printElement("INTRASWAP", elementWidth); printElement("INTACCEPT", elementWidth); @@ -379,6 +399,12 @@ void ConsoleOutput::PrintMoveTitle() printElement("MOLEXACCEPT", elementWidth); printElement("MOLEXACCEPT%", elementWidth); } + + if(var->Performed(mv::CFCMC)) { + printElement("CFCMCTRANSF", elementWidth); + printElement("CFCMCACCEPT", elementWidth); + printElement("CFCMCACCEPT%", elementWidth); + } #endif #if ENSEMBLE == GEMC || ENSEMBLE == NPT diff --git a/src/ConsoleOutput.h b/src/ConsoleOutput.h index 1a2ad683c..140164cb0 100644 --- a/src/ConsoleOutput.h +++ b/src/ConsoleOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/ConstField.h b/src/ConstField.h index 8e1c96e20..b17a2c5ce 100644 --- a/src/ConstField.h +++ b/src/ConstField.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/CoordinateSetup.h b/src/CoordinateSetup.h index 7ed11ef82..8d86c3b69 100644 --- a/src/CoordinateSetup.h +++ b/src/CoordinateSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Coordinates.cpp b/src/Coordinates.cpp index 2bb53df3f..2884250e1 100644 --- a/src/Coordinates.cpp +++ b/src/Coordinates.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -32,7 +32,8 @@ void Coordinates::CheckCoordinate() for (uint b = 0; b < BOX_TOTAL; b++) { sawZeroCoordinate = false; MoleculeLookup::box_iterator thisMol = molLookRef.BoxBegin(b), - end = molLookRef.BoxEnd(b), endc = molLookRef.BoxEnd(b); + end = molLookRef.BoxEnd(b), + endc = molLookRef.BoxEnd(b); //find the min and max coordinate stRange = molRef.MolStart(*thisMol); --endc; @@ -150,8 +151,6 @@ void Coordinates::VolumeTransferTranslate } } - - //Assumes dest is already initialized void Coordinates::TranslateOneBox (Coordinates & dest, COM & newCOM, COM const& oldCOM, diff --git a/src/Coordinates.h b/src/Coordinates.h index a2670410c..8ec63f980 100644 --- a/src/Coordinates.h +++ b/src/Coordinates.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/EnPartCntSampleOutput.cpp b/src/EnPartCntSampleOutput.cpp index 6848e0747..e7855ab99 100644 --- a/src/EnPartCntSampleOutput.cpp +++ b/src/EnPartCntSampleOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -38,10 +38,17 @@ void EnPartCntSample::Init(pdb_setup::Atoms const& atoms, output.statistics.settings.hist.frequency / stepsPerSample + 1; samplesCollectedInFrame = 0; for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { +#if GOMC_LIB_MPI + name[b] = pathToReplicaDirectory + GetFName(output.state.files.hist.sampleName, + output.state.files.hist.number, + output.state.files.hist.letter, + b); +#else name[b] = GetFName(output.state.files.hist.sampleName, output.state.files.hist.number, output.state.files.hist.letter, b); +#endif samplesE[b] = new double [samplesPerFrame]; samplesN[b] = new uint * [var->numKinds]; for (uint k = 0; k < var->numKinds; ++k) { diff --git a/src/EnPartCntSampleOutput.h b/src/EnPartCntSampleOutput.h index 4e4486d00..9033e014c 100644 --- a/src/EnPartCntSampleOutput.h +++ b/src/EnPartCntSampleOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/EnergyTypes.h b/src/EnergyTypes.h index 6a6eb194c..e5eb620fb 100644 --- a/src/EnergyTypes.h +++ b/src/EnergyTypes.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -135,6 +135,7 @@ class Energy } Energy& operator-=(Energy const& rhs); Energy& operator+=(Energy const& rhs); + Energy& operator*=(double const& rhs); //private: //MEMBERS @@ -174,6 +175,22 @@ inline Energy& Energy::operator+=(Energy const& rhs) return *this; } +inline Energy& Energy::operator*=(double const& rhs) +{ + inter *= rhs; + intraBond *= rhs; + intraNonbond *= rhs; + tc *= rhs; + real *= rhs; + recip *= rhs; + self *= rhs; + correction *= rhs; + totalElect *= rhs; + total *= rhs; + + return *this; +} + class Virial { public: diff --git a/src/EnsemblePreprocessor.h b/src/EnsemblePreprocessor.h index 3931109e9..a50a675e2 100644 --- a/src/EnsemblePreprocessor.h +++ b/src/EnsemblePreprocessor.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Ewald.cpp b/src/Ewald.cpp index f9170b0ad..30bc51c60 100644 --- a/src/Ewald.cpp +++ b/src/Ewald.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -38,7 +38,7 @@ using namespace geom; Ewald::Ewald(StaticVals & stat, System & sys) : ff(stat.forcefield), mols(stat.mol), currentCoords(sys.coordinates), - currentCOM(sys.com), sysPotRef(sys.potential), + currentCOM(sys.com), sysPotRef(sys.potential), lambdaRef(sys.lambdaRef), #ifdef VARIABLE_PARTICLE_NUMBER molLookup(sys.molLookup), #else @@ -49,7 +49,14 @@ Ewald::Ewald(StaticVals & stat, System & sys) : #else currentAxes(*stat.GetBoxDim()) #endif -{} +{ + ewald = false; + electrostatic = false; + alpha = 0.0; + recip_rcut = 0.0; + recip_rcut_Sq = 0.0; + multiParticleEnabled = stat.multiParticleEnabled; +} Ewald::~Ewald() { @@ -106,6 +113,11 @@ void Ewald::Init() particleKind.push_back(molKind.AtomKind(a)); particleMol.push_back(m); particleCharge.push_back(molKind.AtomCharge(a)); + if(abs(molKind.AtomCharge(a)) < 0.000000001) { + particleHasNoCharge.push_back(true); + } else { + particleHasNoCharge.push_back(false); + } } } @@ -203,9 +215,10 @@ void Ewald::BoxReciprocalSetup(uint box, XYZArray const& molCoords) while (thisMol != end) { MoleculeKind const& thisKind = mols.GetKind(*thisMol); + double lambdaCoef = GetLambdaCoef(*thisMol, box); for (j = 0; j < thisKind.NumAtoms(); j++) { thisBoxCoords.Set(i, molCoords[mols.MolStart(*thisMol) + j]); - chargeBox.push_back(thisKind.AtomCharge(j)); + chargeBox.push_back(thisKind.AtomCharge(j) * lambdaCoef); i++; } thisMol++; @@ -226,6 +239,8 @@ void Ewald::BoxReciprocalSetup(uint box, XYZArray const& molCoords) while (thisMol != end) { MoleculeKind const& thisKind = mols.GetKind(*thisMol); + double lambdaCoef = GetLambdaCoef(*thisMol, box); + uint start = mols.MolStart(*thisMol); #ifdef _OPENMP #pragma omp parallel for default(shared) private(i, j, dotProduct, sumReal, sumImaginary) @@ -235,15 +250,18 @@ void Ewald::BoxReciprocalSetup(uint box, XYZArray const& molCoords) sumImaginary = 0.0; for (j = 0; j < thisKind.NumAtoms(); j++) { - dotProduct = Dot(mols.MolStart(*thisMol) + j, - kx[box][i], ky[box][i], + if(particleHasNoCharge[start + j]) { + continue; + } + dotProduct = Dot(start + j, kx[box][i], ky[box][i], kz[box][i], molCoords); sumReal += (thisKind.AtomCharge(j) * cos(dotProduct)); sumImaginary += (thisKind.AtomCharge(j) * sin(dotProduct)); } - sumRnew[box][i] += sumReal; - sumInew[box][i] += sumImaginary; + //we assume all atom charges are scaled with lambda + sumRnew[box][i] += (lambdaCoef * sumReal); + sumInew[box][i] += (lambdaCoef * sumImaginary); } thisMol++; } @@ -292,19 +310,22 @@ double Ewald::MolReciprocal(XYZArray const& molCoords, int i; double sumRealNew, sumImaginaryNew, dotProductNew, dotProductOld, sumRealOld, sumImaginaryOld; + double lambdaCoef = GetLambdaCoef(molIndex, box); #ifdef GOMC_CUDA XYZArray cCoords(length); std::vector MolCharge; for(p = 0; p < length; p++) { cCoords.Set(p, currentCoords[startAtom + p]); - MolCharge.push_back(thisKind.AtomCharge(p)); + MolCharge.push_back(thisKind.AtomCharge(p) * lambdaCoef); } CallMolReciprocalGPU(ff.particles->getCUDAVars(), cCoords, molCoords, MolCharge, imageSizeRef[box], sumRnew[box], sumInew[box], energyRecipNew, box); #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, p, atom, sumRealNew, sumImaginaryNew, sumRealOld, sumImaginaryOld, dotProductNew, dotProductOld) reduction(+:energyRecipNew, energyRecipOld) + #pragma omp parallel for default(shared) private(i, p, atom, sumRealNew, \ + sumImaginaryNew, sumRealOld, sumImaginaryOld, dotProductNew, dotProductOld) \ +reduction(+:energyRecipNew, energyRecipOld) #endif for (i = 0; i < imageSizeRef[box]; i++) { sumRealNew = 0.0; @@ -316,6 +337,9 @@ double Ewald::MolReciprocal(XYZArray const& molCoords, for (p = 0; p < length; ++p) { atom = startAtom + p; + if(particleHasNoCharge[atom]) { + continue; + } dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], molCoords); @@ -331,8 +355,10 @@ double Ewald::MolReciprocal(XYZArray const& molCoords, sumImaginaryOld += (thisKind.AtomCharge(p) * sin(dotProductOld)); } - sumRnew[box][i] = sumRref[box][i] - sumRealOld + sumRealNew; - sumInew[box][i] = sumIref[box][i] - sumImaginaryOld + sumImaginaryNew; + sumRnew[box][i] = sumRref[box][i] + lambdaCoef * + (sumRealNew - sumRealOld); + sumInew[box][i] = sumIref[box][i] + lambdaCoef * + (sumImaginaryNew - sumImaginaryOld); energyRecipNew += (sumRnew[box][i] * sumRnew[box][i] + sumInew[box][i] * sumInew[box][i]) * prefactRef[box][i]; @@ -345,6 +371,8 @@ double Ewald::MolReciprocal(XYZArray const& molCoords, //calculate reciprocate term in destination box for swap move +//No need to scale the charge with lambda, since this function will not be +// called in free energy of CFCMC double Ewald::SwapDestRecip(const cbmc::TrialMol &newMol, const uint box, const int molIndex) @@ -354,12 +382,13 @@ double Ewald::SwapDestRecip(const cbmc::TrialMol &newMol, if (box < BOXES_WITH_U_NB) { - uint p, length; + uint p, length, start; int i; MoleculeKind const& thisKind = newMol.GetKind(); XYZArray molCoords = newMol.GetCoords(); double dotProductNew, sumRealNew, sumImaginaryNew; length = thisKind.NumAtoms(); + start = mols.MolStart(molIndex); #ifdef GOMC_CUDA bool insert = true; std::vector MolCharge; @@ -372,7 +401,8 @@ double Ewald::SwapDestRecip(const cbmc::TrialMol &newMol, insert, energyRecipNew, box); #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, p, dotProductNew, sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) + #pragma omp parallel for default(shared) private(i, p, dotProductNew, \ +sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) #endif for (i = 0; i < imageSizeRef[box]; i++) { sumRealNew = 0.0; @@ -380,6 +410,9 @@ double Ewald::SwapDestRecip(const cbmc::TrialMol &newMol, dotProductNew = 0.0; for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], molCoords); @@ -403,6 +436,116 @@ double Ewald::SwapDestRecip(const cbmc::TrialMol &newMol, return energyRecipNew - energyRecipOld; } +//calculate reciprocate term for lambdaNew and Old with same coordinates +double Ewald::CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box) +{ + double energyRecipNew = 0.0; + double energyRecipOld = 0.0; + + // + //Need to implement the GPU part + // + if (box < BOXES_WITH_U_NB) { + uint p, i, start; + MoleculeKind const& thisKind = mols.GetKind(molIndex); + uint length = thisKind.NumAtoms(); + start = mols.MolStart(molIndex); + double dotProductNew, sumRealNew, sumImaginaryNew; + double lambdaCoef = sqrt(lambdaNew) - sqrt(lambdaOld); + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, p, dotProductNew, \ +sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) +#endif + for (i = 0; i < imageSizeRef[box]; i++) { + sumRealNew = 0.0; + sumImaginaryNew = 0.0; + dotProductNew = 0.0; + + for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } + dotProductNew = Dot(p, kxRef[box][i], + kyRef[box][i], kzRef[box][i], + molCoords); + + sumRealNew += thisKind.AtomCharge(p) * cos(dotProductNew); + sumImaginaryNew += thisKind.AtomCharge(p) * sin(dotProductNew); + } + + //sumRealNew; + sumRnew[box][i] = sumRref[box][i] + lambdaCoef * sumRealNew; + //sumImaginaryNew; + sumInew[box][i] = sumIref[box][i] + lambdaCoef * sumImaginaryNew; + + energyRecipNew += (sumRnew[box][i] * sumRnew[box][i] + sumInew[box][i] + * sumInew[box][i]) * prefactRef[box][i]; + } + energyRecipOld = sysPotRef.boxEnergy[box].recip; + } + + return energyRecipNew - energyRecipOld; +} + +//calculate reciprocate term for lambdaNew and Old with same coordinates +//used int free energy calculation +void Ewald::ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + //Need to implement GPU + uint p, i, s; + uint length = mols.GetKind(molIndex).NumAtoms(); + uint start = mols.MolStart(molIndex); + uint lambdaSize = lambda_Coul.size(); + double *energyRecip = new double [lambdaSize]; + std::fill_n(energyRecip, lambdaSize, 0.0); + + double dotProduct, sumReal, sumImaginary, coefDiff; + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, p, s, dotProduct, \ +sumReal, sumImaginary, coefDiff) reduction(+:energyRecip[:lambdaSize]) +#endif + for (i = 0; i < imageSizeRef[box]; i++) { + sumReal = 0.0; + sumImaginary = 0.0; + dotProduct = 0.0; + + for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } + dotProduct = Dot(p + start, kxRef[box][i], kyRef[box][i], kzRef[box][i], + currentCoords); + sumReal += particleCharge[p + start] * cos(dotProduct); + sumImaginary += particleCharge[p + start] * sin(dotProduct); + } + for(s = 0; s < lambdaSize; s++) { + //Calculate the energy of other state + coefDiff = sqrt(lambda_Coul[s]) - sqrt(lambda_Coul[iState]); + energyRecip[s] += prefactRef[box][i] * + ((sumRref[box][i] + coefDiff * sumReal) * + (sumRref[box][i] + coefDiff * sumReal) + + (sumIref[box][i] + coefDiff * sumImaginary) * + (sumIref[box][i] + coefDiff * sumImaginary)); + } + } + + double energyRecipOld = sysPotRef.boxEnergy[box].recip; + for(s = 0; s < lambdaSize; s++) { + energyDiff[s].recip = energyRecip[s] - energyRecipOld; + } + //Calculate du/dl of Reciprocal for current state with linear scaling + //energy difference E(lambda =1) - E(lambda = 0) + dUdL_Coul.recip += energyDiff[lambdaSize - 1].recip - energyDiff[0].recip; + delete [] energyRecip; +} + void Ewald::RecipInit(uint box, BoxDimensions const& boxAxes) { if(boxAxes.orthogonal[box]) @@ -413,6 +556,8 @@ void Ewald::RecipInit(uint box, BoxDimensions const& boxAxes) //calculate reciprocate term in source box for swap move +//No need to scale the charge with lambda, since this function is not being +// called for free energy and CFCMC double Ewald::SwapSourceRecip(const cbmc::TrialMol &oldMol, const uint box, const int molIndex) { @@ -420,12 +565,13 @@ double Ewald::SwapSourceRecip(const cbmc::TrialMol &oldMol, double energyRecipOld = 0.0; if (box < BOXES_WITH_U_NB) { - uint p; + uint p, start; int i; double sumRealNew, sumImaginaryNew, dotProductNew; MoleculeKind const& thisKind = oldMol.GetKind(); XYZArray molCoords = oldMol.GetCoords(); uint length = thisKind.NumAtoms(); + start = mols.MolStart(molIndex); #ifdef GOMC_CUDA bool insert = false; std::vector MolCharge; @@ -439,7 +585,8 @@ double Ewald::SwapSourceRecip(const cbmc::TrialMol &oldMol, #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, p, dotProductNew, sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) + #pragma omp parallel for default(shared) private(i, p, dotProductNew, \ +sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) #endif for (i = 0; i < imageSizeRef[box]; i++) { sumRealNew = 0.0; @@ -447,6 +594,9 @@ double Ewald::SwapSourceRecip(const cbmc::TrialMol &oldMol, dotProductNew = 0.0; for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], molCoords); @@ -471,22 +621,26 @@ double Ewald::SwapSourceRecip(const cbmc::TrialMol &oldMol, //calculate reciprocate term for inserting some molecules (kindA) in destination // box and removing molecule (kindB) from destination box double Ewald::SwapRecip(const std::vector &newMol, - const std::vector &oldMol) + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexOld) { double energyRecipNew = 0.0; double energyRecipOld = 0.0; + //Change in reciprocal happens in the same box. uint box = newMol[0].GetBox(); if (box < BOXES_WITH_U_NB) { int p, i, m, lengthNew, lengthOld; MoleculeKind const& thisKindNew = newMol[0].GetKind(); MoleculeKind const& thisKindOld = oldMol[0].GetKind(); - double dotProductNew, sumRealNew, sumImaginaryNew; + double dotProductNew, sumRealNew, sumImaginaryNew, lambdaCoef; lengthNew = thisKindNew.NumAtoms(); lengthOld = thisKindOld.NumAtoms(); #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, p, dotProductNew, sumRealNew, sumImaginaryNew) reduction(+:energyRecipNew) + #pragma omp parallel for default(shared) private(i, p, dotProductNew, \ +lambdaCoef) reduction(+:energyRecipNew, sumRealNew, sumImaginaryNew) #endif for (i = 0; i < imageSizeRef[box]; i++) { sumRealNew = 0.0; @@ -494,22 +648,34 @@ double Ewald::SwapRecip(const std::vector &newMol, dotProductNew = 0.0; for (m = 0; m < newMol.size(); m++) { + lambdaCoef = GetLambdaCoef(molIndexNew[m], box); for (p = 0; p < lengthNew; ++p) { - dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], - newMol[m].GetCoords()); - - sumRealNew += (thisKindNew.AtomCharge(p) * cos(dotProductNew)); - sumImaginaryNew += (thisKindNew.AtomCharge(p) * sin(dotProductNew)); + if(particleHasNoCharge[mols.MolStart(molIndexNew[m]) + p]) { + continue; + } + dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], + kzRef[box][i], newMol[m].GetCoords()); + + sumRealNew += (thisKindNew.AtomCharge(p) * lambdaCoef * + cos(dotProductNew)); + sumImaginaryNew += (thisKindNew.AtomCharge(p) * lambdaCoef * + sin(dotProductNew)); } } for (m = 0; m < oldMol.size(); m++) { + lambdaCoef = GetLambdaCoef(molIndexOld[m], box); for (p = 0; p < lengthOld; ++p) { - dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], - oldMol[m].GetCoords()); - - sumRealNew -= (thisKindOld.AtomCharge(p) * cos(dotProductNew)); - sumImaginaryNew -= (thisKindOld.AtomCharge(p) * sin(dotProductNew)); + if(particleHasNoCharge[mols.MolStart(molIndexOld[m]) + p]) { + continue; + } + dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], + kzRef[box][i], oldMol[m].GetCoords()); + + sumRealNew += -(thisKindOld.AtomCharge(p) * lambdaCoef * + cos(dotProductNew)); + sumImaginaryNew += -(thisKindOld.AtomCharge(p) * lambdaCoef * + sin(dotProductNew)); } } @@ -751,7 +917,7 @@ void Ewald::SetRecipRef(uint box) } } -//calculate correction term for a molecule +//calculate correction term for a molecule, with system lambda double Ewald::MolCorrection(uint molIndex, uint box) const { if (box >= BOXES_WITH_U_NB) @@ -764,8 +930,12 @@ double Ewald::MolCorrection(uint molIndex, uint box) const MoleculeKind& thisKind = mols.kinds[mols.kIndex[molIndex]]; uint atomSize = thisKind.NumAtoms(); uint start = mols.MolStart(molIndex); + double lambdaCoef = GetLambdaCoef(molIndex, box); for (uint i = 0; i < atomSize; i++) { + if(particleHasNoCharge[start + i]) { + continue; + } for (uint j = i + 1; j < atomSize; j++) { currentAxes.InRcut(distSq, virComponents, currentCoords, start + i, start + j, box); @@ -775,10 +945,48 @@ double Ewald::MolCorrection(uint molIndex, uint box) const } } - return correction; + return -1.0 * num::qqFact * correction * lambdaCoef * lambdaCoef; } -//calculate self term for a box +//It's called in free energy calculation to calculate the change in +// correction energy in all lambda states +void Ewald::ChangeCorrection(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + uint atomSize = mols.GetKind(molIndex).NumAtoms(); + uint start = mols.MolStart(molIndex); + uint lambdaSize = lambda_Coul.size(); + double coefDiff, distSq, dist, correction = 0.0; + XYZ virComponents; + + //Calculate the correction energy with lambda = 1 + for (uint i = 0; i < atomSize; i++) { + if(particleHasNoCharge[start + i]) { + continue; + } + + for (uint j = i + 1; j < atomSize; j++) { + distSq = 0.0; + currentAxes.InRcut(distSq, virComponents, currentCoords, + start + i, start + j, box); + dist = sqrt(distSq); + correction += (particleCharge[i + start] * particleCharge[j + start] * + erf(ff.alpha[box] * dist) / dist); + } + } + correction *= -1.0 * num::qqFact; + //Calculate the energy difference for each lambda state + for (uint s = 0; s < lambdaSize; s++) { + coefDiff = lambda_Coul[s] - lambda_Coul[iState]; + energyDiff[s].correction += coefDiff * correction; + } + //Calculate du/dl of correction for current state, for linear scaling + dUdL_Coul.correction += correction; +} + +//calculate self term for a box, using system lambda double Ewald::BoxSelf(BoxDimensions const& boxAxes, uint box) const { if (box >= BOXES_WITH_U_NB) @@ -786,16 +994,30 @@ double Ewald::BoxSelf(BoxDimensions const& boxAxes, uint box) const double self = 0.0; double molSelfEnergy; - uint i, j, length; + uint i, j, length, molNum; + double lambdaCoef = 1.0; + for (i = 0; i < mols.GetKindsCount(); i++) { MoleculeKind const& thisKind = mols.kinds[i]; length = thisKind.NumAtoms(); + molNum = molLookup.NumKindInBox(i, box); molSelfEnergy = 0.0; + if(lambdaRef.KindIsFractional(i, box)) { + //If a molecule is fractional, we subtract the fractional molecule and + // add it later + --molNum; + //returns lambda and not sqrt(lambda) + lambdaCoef = lambdaRef.GetLambdaCoulomb(i, box); + } for (j = 0; j < length; j++) { molSelfEnergy += (thisKind.AtomCharge(j) * thisKind.AtomCharge(j)); } - self += (molSelfEnergy * molLookup.NumKindInBox(i, box)); + self += (molSelfEnergy * molNum); + if(lambdaRef.KindIsFractional(i, box)) { + //Add the fractional nolecule part + self += (molSelfEnergy * lambdaCoef); + } } self = -1.0 * self * ff.alpha[box] * num::qqFact / sqrt(M_PI); @@ -806,7 +1028,7 @@ double Ewald::BoxSelf(BoxDimensions const& boxAxes, uint box) const // NOTE: The calculation of W12, W13, W23 is expensive and would not be // requied for pressure and surface tension calculation. So, they have been // commented out. In case you need to calculate them, uncomment them. -Virial Ewald::ForceReciprocal(Virial& virial, uint box) const +Virial Ewald::VirialReciprocal(Virial& virial, uint box) const { Virial tempVir = virial; if (box >= BOXES_WITH_U_NB) @@ -817,7 +1039,7 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const double recipIntra = 0.0; double constVal = 1.0 / (4.0 * ff.alphaSq[box]); - double factor, arg, charge; + double factor, arg, charge, lambdaCoef; uint p, length, start, atom; int i; @@ -842,6 +1064,7 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const length = mols.GetKind(*thisMol).NumAtoms(); start = mols.MolStart(*thisMol); comC = currentCOM.Get(*thisMol); + lambdaCoef = GetLambdaCoef(*thisMol, box); for (p = 0; p < length; p++) { atom = start + p; @@ -853,20 +1076,20 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const thisBoxCoords.Set(atomIndex, atomC); thisBoxCOMDiff.Set(atomIndex, diffC); - double atomCharge = mols.GetKind(*thisMol).AtomCharge(p); - chargeBox.push_back(atomCharge); + // scale the charge with lambda + chargeBox.push_back(particleCharge[atom] * lambdaCoef); atomIndex++; } thisMol++; } - CallForceReciprocalGPU(ff.particles->getCUDAVars(), thisBoxCoords, - thisBoxCOMDiff, chargeBox, wT11, wT12, - wT13, wT22, wT23, wT33, imageSizeRef[box], constVal, - box); + CallVirialReciprocalGPU(ff.particles->getCUDAVars(), thisBoxCoords, + thisBoxCOMDiff, chargeBox, wT11, wT12, + wT13, wT22, wT23, wT33, imageSizeRef[box], constVal, + box); #else #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, factor) reduction(+:wT11, wT12, wT13, wT22, wT23, wT33) + #pragma omp parallel for default(shared) private(i, factor) reduction(+:wT11, wT22, wT33) #endif for (i = 0; i < imageSizeRef[box]; i++) { factor = prefactRef[box][i] * (sumRref[box][i] * sumRref[box][i] + @@ -880,13 +1103,6 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const wT33 += factor * (1.0 - 2.0 * (constVal + 1.0 / hsqrRef[box][i]) * kzRef[box][i] * kzRef[box][i]); - /* - wT12 += factor * (-2.0 * (constVal + 1.0 / hsqrRef[box][i]) * kxRef[box][i] * kyRef[box][i]); - - wT13 += factor * (-2.0 * (constVal + 1.0 / hsqrRef[box][i]) * kxRef[box][i] * kzRef[box][i]); - - wT23 += factor * (-2.0 * (constVal + 1.0 / hsqrRef[box][i]) * kyRef[box][i] * kzRef[box][i]); - */ } //Intramolecular part @@ -894,21 +1110,23 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const length = mols.GetKind(*thisMol).NumAtoms(); start = mols.MolStart(*thisMol); comC = currentCOM.Get(*thisMol); + lambdaCoef = GetLambdaCoef(*thisMol, box); for (p = 0; p < length; p++) { atom = start + p; + if(particleHasNoCharge[atom]) { + continue; + } //compute the vector of the bead to the COM (p) // need to unwrap the atom coordinate atomC = currentCoords.Get(atom); currentAxes.UnwrapPBC(atomC, box, comC); - diffC = atomC - comC; - - // charge = particleCharge[atom]; - charge = mols.GetKind(*thisMol).AtomCharge(p); + //scale the charge with lambda for Free energy calc + charge = particleCharge[atom] * lambdaCoef; #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i, arg, factor) reduction(+:wT11, wT12, wT13, wT22, wT23, wT33) + #pragma omp parallel for default(shared) private(i, arg, factor) reduction(+:wT11, wT22, wT33) #endif for (i = 0; i < imageSizeRef[box]; i++) { //compute the dot product of k and r @@ -916,19 +1134,14 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const kzRef[box][i], currentCoords); factor = prefactRef[box][i] * 2.0 * (sumIref[box][i] * cos(arg) - - sumRref[box][i] * sin(arg)) * charge; + sumRref[box][i] * sin(arg)) * + charge; wT11 += factor * (kxRef[box][i] * diffC.x); wT22 += factor * (kyRef[box][i] * diffC.y); wT33 += factor * (kzRef[box][i] * diffC.z); - - /* - wT12 += factor * 0.5 *(kxRef[box][i] * diffC.y + kyRef[box][i] * diffC.x); - wT13 += factor * 0.5 *(kxRef[box][i] * diffC.z + kzRef[box][i] * diffC.x); - wT23 += factor * 0.5 *(kyRef[box][i] * diffC.z + kzRef[box][i] * diffC.y); - */ } } ++thisMol; @@ -954,7 +1167,10 @@ Virial Ewald::ForceReciprocal(Virial& virial, uint box) const return tempVir; } -//calculate correction term for linear molecule CBMC algorithm +//calculate correction term for a molecule with lambda = 1 +//It's called when the molecule configuration changes, moleculeTransfer, MEMC +//It never been caled in Free Energy calculatio, becaue we are in +// NVT and NPT ensemble double Ewald::SwapCorrection(const cbmc::TrialMol& trialMol) const { uint box = trialMol.GetBox(); @@ -980,6 +1196,42 @@ double Ewald::SwapCorrection(const cbmc::TrialMol& trialMol) const return num::qqFact * correction; } +//calculate correction term for a molecule with system lambda +//It's called when the molecule configuration changes, regrowth, crankshaft, IntraSwap, IntraMEMC ... +double Ewald::SwapCorrection(const cbmc::TrialMol& trialMol, + const uint molIndex) const +{ + uint box = trialMol.GetBox(); + if (box >= BOXES_WITH_U_NB) + return 0.0; + + double dist, distSq; + double correction = 0.0; + XYZ virComponents; + const MoleculeKind& thisKind = trialMol.GetKind(); + uint atomSize = thisKind.NumAtoms(); + uint start = mols.MolStart(molIndex); + double lambdaCoef = GetLambdaCoef(molIndex, box); + + for (uint i = 0; i < atomSize; i++) { + if(particleHasNoCharge[start + i]) { + continue; + } + for (uint j = i + 1; j < atomSize; j++) { + currentAxes.InRcut(distSq, virComponents, trialMol.GetCoords(), + i, j, box); + + dist = sqrt(distSq); + correction -= (thisKind.AtomCharge(i) * thisKind.AtomCharge(j) * + erf(ff.alpha[box] * dist) / dist); + } + } + return num::qqFact * correction * lambdaCoef * lambdaCoef; +} + +//It's called if we transfer one molecule from one box to another +//No need to scale the charge with lambda, since this function is not being +// called from free energy or CFCMC double Ewald::SwapSelf(const cbmc::TrialMol& trialMol) const { uint box = trialMol.GetBox(); @@ -996,6 +1248,32 @@ double Ewald::SwapSelf(const cbmc::TrialMol& trialMol) const return (en_self * ff.alpha[box] * num::qqFact / sqrt(M_PI)); } +//It's called in free energy calculation to calculate the change in +// self energy in all lambda states +void Ewald::ChangeSelf(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + uint atomSize = mols.GetKind(molIndex).NumAtoms(); + uint start = mols.MolStart(molIndex); + uint lambdaSize = lambda_Coul.size(); + double coefDiff, en_self = 0.0; + //Calculate the self energy with lambda = 1 + for (uint i = 0; i < atomSize; i++) { + en_self += (particleCharge[i + start] * particleCharge[i + start]); + } + en_self *= -1.0 * ff.alpha[box] * num::qqFact / sqrt(M_PI); + + //Calculate the energy difference for each lambda state + for (uint s = 0; s < lambdaSize; s++) { + coefDiff = lambda_Coul[s] - lambda_Coul[iState]; + energyDiff[s].self += coefDiff * en_self; + } + //Calculate du/dl of self for current state, for linear scaling + dUdL_Coul.self += en_self; +} + //update reciprocate values void Ewald::UpdateRecip(uint box) { @@ -1039,3 +1317,80 @@ void Ewald::UpdateRecipVec(uint box) imageSizeRef[b] = imageSize[b]; } } + + +//calculate reciprocate force term for a box with molCoords +void Ewald::BoxForceReciprocal(XYZArray const& molCoords, + XYZArray& atomForceRec, + XYZArray& molForceRec, + uint box) +{ + if(multiParticleEnabled && (box < BOXES_WITH_U_NB)) { + // molecule iterator + MoleculeLookup::box_iterator thisMol = molLookup.BoxBegin(box); + MoleculeLookup::box_iterator end = molLookup.BoxEnd(box); + double constValue = 2.0 * ff.alpha[box] / sqrt(M_PI); + + while(thisMol != end) { + uint molIndex = *thisMol; + uint length, start, p, i; + double dot, factor, distSq; + XYZ distVect; + molForceRec.Set(molIndex, 0.0, 0.0, 0.0); + length = mols.GetKind(molIndex).NumAtoms(); + start = mols.MolStart(molIndex); + double lambdaCoef = GetLambdaCoef(molIndex, box); + + for(p = start; p < start + length; p++) { + double X = 0.0, Y = 0.0, Z = 0.0; + + if(!particleHasNoCharge[p]) { + // subtract the intra forces(correction) + for(uint j = start; j < start + length; j++) { + //no self term in force + if(p != j) { + currentAxes.InRcut(distSq, distVect, molCoords, p, j, box); + double dist = sqrt(distSq); + double expConstValue = exp(-1.0 * ff.alphaSq[box] * distSq); + double qiqj = particleCharge[p] * particleCharge[j] * num::qqFact; + double intraForce = qiqj * lambdaCoef * lambdaCoef / distSq; + intraForce *= ((erf(ff.alpha[box] * dist) / dist) - + constValue * expConstValue); + X -= intraForce * distVect.x; + Y -= intraForce * distVect.y; + Z -= intraForce * distVect.z; + } + } +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, dot, factor) \ + reduction(+:X, Y, Z) +#endif + for(i = 0; i < imageSize[box]; i++) { + dot = Dot(p, kx[box][i], ky[box][i], kz[box][i], molCoords); + + factor = 2.0 * particleCharge[p] * prefact[box][i] * lambdaCoef * + (sin(dot) * sumRnew[box][i] - cos(dot) * sumInew[box][i]); + + X += factor * kx[box][i]; + Y += factor * ky[box][i]; + Z += factor * kz[box][i]; + } + } + //printf("Atomforce: %lf, %lf, %lf\n", X, Y, Z); + atomForceRec.Set(p, X, Y, Z); + molForceRec.Add(molIndex, X, Y, Z); + } + thisMol++; + } + } +} + +double Ewald::GetLambdaCoef(uint molA, uint box) const +{ + double lambda = lambdaRef.GetLambdaCoulomb(molA, mols.GetMolKind(molA), box); + //Each charge gets sq root of it. + return sqrt(lambda); +} + + + diff --git a/src/Ewald.h b/src/Ewald.h index 598eed229..b2588684f 100644 --- a/src/Ewald.h +++ b/src/Ewald.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -40,6 +40,7 @@ class COM; class XYZArray; class BoxDimensions; class CalculateEnergy; +class Lambda; class Ewald @@ -76,12 +77,17 @@ class Ewald virtual double BoxSelf(BoxDimensions const& boxAxes, uint box) const; //calculate reciprocate force term for a box - virtual Virial ForceReciprocal(Virial& virial, uint box) const; + virtual Virial VirialReciprocal(Virial& virial, uint box) const; //calculate reciprocate term for displacement and rotation move virtual double MolReciprocal(XYZArray const& molCoords, const uint molIndex, const uint box); + //calculate reciprocate term for lambdaNew and Old with same coordinates + virtual double CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box); + //calculate correction term for a molecule virtual double MolCorrection(uint molIndex, uint box)const; @@ -96,11 +102,17 @@ class Ewald //calculate reciprocate term for inserting some molecules (kindA) in //destination box and removing a molecule (kindB) from destination box virtual double SwapRecip(const std::vector &newMol, - const std::vector &oldMol); + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexOld); - //calculate correction term after swap move + //calculate correction term after swap move, with lambda = 1 virtual double SwapCorrection(const cbmc::TrialMol& trialMol) const; + //calculate correction term after swap move, with system lambda + virtual double SwapCorrection(const cbmc::TrialMol& trialMol, + const uint molIndex) const; + //back up reciptocate value to Ref (will be called during initialization) virtual void SetRecipRef(uint box); @@ -111,7 +123,7 @@ class Ewald virtual void UpdateRecipVec(uint box); //calculate self term after swap move - virtual double SwapSelf(const cbmc::TrialMol& trialMo) const; + virtual double SwapSelf(const cbmc::TrialMol& trialMol) const; //restore cosMol and sinMol virtual void RestoreMol(int molIndex); @@ -127,6 +139,35 @@ class Ewald virtual void UpdateVectorsAndRecipTerms(); + //calculate reciprocate force term for a box with molCoords + virtual void BoxForceReciprocal(XYZArray const& molCoords, + XYZArray& atomForceRec, + XYZArray& molForceRec, + uint box); + + double GetLambdaCoef(uint molA, uint box) const; + + //It's called in free energy calculation to calculate the change in + // self energy in all lambda states + virtual void ChangeSelf(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + + //It's called in free energy calculation to calculate the change in + // correction energy in all lambda states + virtual void ChangeCorrection(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + + //It's called in free energy calculation to calculate the change in + // reciprocal energy in all lambda states + virtual void ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + private: double currentEnergyRecip[BOXES_WITH_U_NB]; @@ -138,7 +179,11 @@ class Ewald const BoxDimensions& currentAxes; const COM& currentCOM; const SystemPotential &sysPotRef; + const Lambda& lambdaRef; + bool electrostatic, ewald, multiParticleEnabled; + double alpha; + double recip_rcut, recip_rcut_Sq; uint *imageSize; uint *imageSizeRef; //const uint imageTotal = GetImageSize(); @@ -155,9 +200,12 @@ class Ewald double **hsqr, **hsqrRef; double **prefact, **prefactRef; + std::vector particleKind; std::vector particleMol; std::vector particleCharge; + std::vector particleHasNoCharge; + }; diff --git a/src/EwaldCached.cpp b/src/EwaldCached.cpp index adeb50da7..9e5bda69d 100644 --- a/src/EwaldCached.cpp +++ b/src/EwaldCached.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -86,6 +86,11 @@ void EwaldCached::Init() particleKind.push_back(molKind.AtomKind(a)); particleMol.push_back(m); particleCharge.push_back(molKind.AtomCharge(a)); + if(abs(molKind.AtomCharge(a)) < 0.000000001) { + particleHasNoCharge.push_back(true); + } else { + particleHasNoCharge.push_back(false); + } } } @@ -180,6 +185,8 @@ void EwaldCached::BoxReciprocalSetup(uint box, XYZArray const& molCoords) while (thisMol != end) { MoleculeKind const& thisKind = mols.GetKind(*thisMol); + double lambdaCoef = GetLambdaCoef(*thisMol, box); + uint start = mols.MolStart(*thisMol); #ifdef _OPENMP #pragma omp parallel for default(shared) private(i, j, dotProduct) @@ -189,17 +196,21 @@ void EwaldCached::BoxReciprocalSetup(uint box, XYZArray const& molCoords) sinMolRef[*thisMol][i] = 0.0; for (j = 0; j < thisKind.NumAtoms(); j++) { + if(particleHasNoCharge[start + j]) { + continue; + } dotProduct = Dot(mols.MolStart(*thisMol) + j, kx[box][i], ky[box][i], kz[box][i], molCoords); - + //cache the Cos and sin term with lambda = 1 cosMolRef[*thisMol][i] += (thisKind.AtomCharge(j) * cos(dotProduct)); sinMolRef[*thisMol][i] += (thisKind.AtomCharge(j) * sin(dotProduct)); } - sumRnew[box][i] += cosMolRef[*thisMol][i]; - sumInew[box][i] += sinMolRef[*thisMol][i]; + //store the summation with system lambda + sumRnew[box][i] += (lambdaCoef * cosMolRef[*thisMol][i]); + sumInew[box][i] += (lambdaCoef * sinMolRef[*thisMol][i]); } thisMol++; } @@ -237,10 +248,12 @@ double EwaldCached::MolReciprocal(XYZArray const& molCoords, if (box < BOXES_WITH_U_NB) { MoleculeKind const& thisKind = mols.GetKind(molIndex); uint length = thisKind.NumAtoms(); + uint start = mols.MolStart(molIndex); uint p; int i; double sumRealNew, sumImaginaryNew, dotProductNew, sumRealOld, sumImaginaryOld; + double lambdaCoef = GetLambdaCoef(molIndex, box); #ifdef _OPENMP #pragma omp parallel for default(shared) private(i, p, sumRealNew, sumImaginaryNew, sumRealOld, sumImaginaryOld, dotProductNew) reduction(+:energyRecipNew) @@ -255,6 +268,9 @@ double EwaldCached::MolReciprocal(XYZArray const& molCoords, sinMolRestore[i] = sinMolRef[molIndex][i]; for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], molCoords); @@ -263,8 +279,10 @@ double EwaldCached::MolReciprocal(XYZArray const& molCoords, sumImaginaryNew += (thisKind.AtomCharge(p) * sin(dotProductNew)); } - sumRnew[box][i] = sumRref[box][i] - sumRealOld + sumRealNew; - sumInew[box][i] = sumIref[box][i] - sumImaginaryOld + sumImaginaryNew; + sumRnew[box][i] = sumRref[box][i] + lambdaCoef * + (sumRealNew - sumRealOld); + sumInew[box][i] = sumIref[box][i] + lambdaCoef * + (sumImaginaryNew - sumImaginaryOld); cosMolRef[molIndex][i] = sumRealNew; sinMolRef[molIndex][i] = sumImaginaryNew; @@ -277,6 +295,8 @@ double EwaldCached::MolReciprocal(XYZArray const& molCoords, } //calculate reciprocate term in destination box for swap move +//No need to scale the charge with lambda, since this function will not be +// called in free energy of CFCMC double EwaldCached::SwapDestRecip(const cbmc::TrialMol &newMol, const uint box, const int molIndex) @@ -284,31 +304,29 @@ double EwaldCached::SwapDestRecip(const cbmc::TrialMol &newMol, double energyRecipNew = 0.0; double energyRecipOld = 0.0; -#ifdef _OPENMP - #pragma omp parallel default(shared) -#endif - { - std::memcpy(cosMolRestore, cosMolRef[molIndex], sizeof(double)*imageTotal); - std::memcpy(sinMolRestore, sinMolRef[molIndex], sizeof(double)*imageTotal); - } - if (box < BOXES_WITH_U_NB) { - uint p, length; + uint p, length, start; int i; MoleculeKind const& thisKind = newMol.GetKind(); XYZArray molCoords = newMol.GetCoords(); double dotProductNew; length = thisKind.NumAtoms(); + start = mols.MolStart(molIndex); #ifdef _OPENMP #pragma omp parallel for default(shared) private(i, p, dotProductNew) reduction(+:energyRecipNew) #endif for (i = 0; i < imageSizeRef[box]; i++) { + cosMolRestore[i] = cosMolRef[molIndex][i]; + sinMolRestore[i] = sinMolRef[molIndex][i]; cosMolRef[molIndex][i] = 0.0; sinMolRef[molIndex][i] = 0.0; dotProductNew = 0.0; for (p = 0; p < length; ++p) { + if(particleHasNoCharge[start + p]) { + continue; + } dotProductNew = Dot(p, kxRef[box][i], kyRef[box][i], kzRef[box][i], molCoords); @@ -335,6 +353,8 @@ double EwaldCached::SwapDestRecip(const cbmc::TrialMol &newMol, //calculate reciprocate term in source box for swap move +//No need to scale the charge with lambda, since this function will not be +// called in free energy of CFCMC double EwaldCached::SwapSourceRecip(const cbmc::TrialMol &oldMol, const uint box, const int molIndex) { @@ -362,7 +382,9 @@ double EwaldCached::SwapSourceRecip(const cbmc::TrialMol &oldMol, //calculate reciprocate term for inserting some molecules (kindA) in destination // box and removing a molecule (kindB) from destination box double EwaldCached::SwapRecip(const std::vector &newMol, - const std::vector &oldMol) + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexOld) { //This function should not be called in IDExchange move std::cout << "Error: Cached Fourier method cannot be used while " << @@ -371,6 +393,56 @@ double EwaldCached::SwapRecip(const std::vector &newMol, return 0.0; } +//calculate reciprocate term for lambdaNew and Old with same coordinates +double EwaldCached::CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box) +{ + //This function should not be called in CFCMC move + std::cout << "Error: Cached Fourier method cannot be used while " << + "performing CFCMC move!" << std::endl; + exit(EXIT_FAILURE); + return 0.0; +} + +//calculate reciprocate term for lambdaNew and Old with same coordinates +void EwaldCached::ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + //Need to implement GPU + uint i, s; + uint lambdaSize = lambda_Coul.size(); + double coefDiff; + double *energyRecip = new double [lambdaSize]; + std::fill_n(energyRecip, lambdaSize, 0.0); + +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(i, s, coefDiff) reduction(+:energyRecip[:lambdaSize]) +#endif + for (i = 0; i < imageSizeRef[box]; i++) { + for(s = 0; s < lambdaSize; s++) { + //Calculate the energy of other state + coefDiff = sqrt(lambda_Coul[s]) - sqrt(lambda_Coul[iState]); + energyRecip[s] += prefactRef[box][i] * + ((sumRref[box][i] + coefDiff * cosMolRef[molIndex][i]) * + (sumRref[box][i] + coefDiff * cosMolRef[molIndex][i]) + + (sumIref[box][i] + coefDiff * sinMolRef[molIndex][i]) * + (sumIref[box][i] + coefDiff * sinMolRef[molIndex][i])); + } + } + + double energyRecipOld = sysPotRef.boxEnergy[box].recip; + for(s = 0; s < lambdaSize; s++) { + energyDiff[s].recip = energyRecip[s] - energyRecipOld; + } + //Calculate du/dl of Reciprocal for current state + //energy difference E(lambda =1) - E(lambda = 0) + dUdL_Coul.recip += energyDiff[lambdaSize - 1].recip - energyDiff[0].recip; + delete [] energyRecip; +} + //restore cosMol and sinMol void EwaldCached::RestoreMol(int molIndex) { @@ -398,31 +470,16 @@ void EwaldCached::exgMolCache() //backup the whole cosMolRef & sinMolRef into cosMolBoxRecip & sinMolBoxRecip void EwaldCached::backupMolCache() { -#if ENSEMBLE == NPT +#if ENSEMBLE == NPT || ENSEMBLE == NVT exgMolCache(); -#elif ENSEMBLE == GEMC - if(GEMC_KIND == mv::GEMC_NVT) { - if(BOX_TOTAL == 2) { - exgMolCache(); - } else { - int m; +#else + uint m; #ifdef _OPENMP - #pragma omp parallel for private(m) + #pragma omp parallel for private(m) #endif - for(m = 0; m < mols.count; m++) { - std::memcpy(cosMolBoxRecip[m], cosMolRef[m], sizeof(double)*imageTotal); - std::memcpy(sinMolBoxRecip[m], sinMolRef[m], sizeof(double)*imageTotal); - } - } - } else { - int m; -#ifdef _OPENMP - #pragma omp parallel for private(m) -#endif - for(m = 0; m < mols.count; m++) { - std::memcpy(cosMolBoxRecip[m], cosMolRef[m], sizeof(double)*imageTotal); - std::memcpy(sinMolBoxRecip[m], sinMolRef[m], sizeof(double)*imageTotal); - } + for(m = 0; m < mols.count; m++) { + std::memcpy(cosMolBoxRecip[m], cosMolRef[m], sizeof(double) * imageTotal); + std::memcpy(sinMolBoxRecip[m], sinMolRef[m], sizeof(double) * imageTotal); } #endif } diff --git a/src/EwaldCached.h b/src/EwaldCached.h index bc38b987f..ec31cddca 100644 --- a/src/EwaldCached.h +++ b/src/EwaldCached.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -30,6 +30,11 @@ class EwaldCached : public Ewald virtual double MolReciprocal(XYZArray const& molCoords, const uint molIndex, const uint box); + //calculate reciprocate term for lambdaNew and Old with same coordinates + virtual double CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box); + //calculate reciprocate term in destination box for swap move virtual double SwapDestRecip(const cbmc::TrialMol &newMol, const uint box, const int molIndex); @@ -41,8 +46,16 @@ class EwaldCached : public Ewald //calculate reciprocate term for inserting some molecules (kindA) in //destination box and removing a molecule (kindB) from destination box virtual double SwapRecip(const std::vector &newMol, - const std::vector &oldMol); - + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexOld); + + //It's called in free energy calculation to calculate the change in + // reciprocal energy in all lambda states + virtual void ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; //restore cosMol and sinMol virtual void RestoreMol(int molIndex); diff --git a/src/FFAngles.h b/src/FFAngles.h index 2799c480b..0441485bd 100644 --- a/src/FFAngles.h +++ b/src/FFAngles.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFBonds.h b/src/FFBonds.h index be0d435fc..a6c1def64 100644 --- a/src/FFBonds.h +++ b/src/FFBonds.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFConst.cpp b/src/FFConst.cpp index 1c84f3f7b..dd9628261 100644 --- a/src/FFConst.cpp +++ b/src/FFConst.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFConst.h b/src/FFConst.h index 5bd3e309c..97c180e68 100644 --- a/src/FFConst.h +++ b/src/FFConst.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFDihedrals.cpp b/src/FFDihedrals.cpp index d52fd04d0..3cdfbdbb7 100644 --- a/src/FFDihedrals.cpp +++ b/src/FFDihedrals.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFDihedrals.h b/src/FFDihedrals.h index b4d0e28c9..7cb290213 100644 --- a/src/FFDihedrals.h +++ b/src/FFDihedrals.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFExp6.h b/src/FFExp6.h new file mode 100644 index 000000000..696547327 --- /dev/null +++ b/src/FFExp6.h @@ -0,0 +1,459 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef FF_EXP6_H +#define FF_EXP6_H + +#include "EnsemblePreprocessor.h" //For "MIE_INT_ONLY" preprocessor. +#include "FFConst.h" //constants related to particles. +#include "BasicTypes.h" //for uint +#include "NumLib.h" //For Cb, Sq +#include "FFParticle.h" +#ifdef GOMC_CUDA +#include "ConstantDefinitionsCUDAKernel.cuh" +#endif + +////////////////////////////////////////////////////////////////////// +//////////////////////////// Exp-6 Style ///////////////////////////// +////////////////////////////////////////////////////////////////////// +// Virial and LJ potential calculation: +// U(rij)= expConst * ( (6/alpha) * exp( alpha * [1-(r/rmin)] )-(rmin/r)^6) ) +// expConst=( eps-ij * alpha )/(alpha-6) +// +// Vir(r)= -du/dr * r +// Vir(r)= 6 * expConst * [(r/rmin) * exp(alpha * [1-(r/rmin)])-(rmin/r)^6]/ r^2 +// + + + +struct FF_EXP6 : public FFParticle { +public: + FF_EXP6(Forcefield &ff) : FFParticle(ff), expConst(NULL), rMin(NULL), + rMaxSq(NULL), expConst_1_4(NULL), rMin_1_4(NULL), rMaxSq_1_4(NULL) {} + virtual ~FF_EXP6() + { + delete[] expConst; + delete[] expConst_1_4; + delete[] rMin; + delete[] rMin_1_4; + delete[] rMaxSq; + delete[] rMaxSq_1_4; + } + + virtual void Init(ff_setup::Particle const& mie, + ff_setup::NBfix const& nbfix); + + virtual double GetRmin(const uint i, const uint j) const; + virtual double GetRmax(const uint i, const uint j) const; + virtual double GetRmin_1_4(const uint i, const uint j) const; + virtual double GetRmax_1_4(const uint i, const uint j) const; + + virtual double CalcEn(const double distSq, + const uint kind1, const uint kind2, + const double lambda) const; + virtual double CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; + virtual void CalcAdd_1_4(double& en, const double distSq, + const uint kind1, const uint kind2) const; + + // coulomb interaction functions + virtual double CalcCoulomb(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, const uint b) const; + virtual void CalcCoulombAdd_1_4(double& en, const double distSq, + const double qi_qj_Fact, + const bool NB) const; + + //!Returns energy correction + virtual double EnergyLRC(const uint kind1, const uint kind2) const; + + //!!Returns virial correction + virtual double VirialLRC(const uint kind1, const uint kind2) const; + + //Calculate the dE/dlambda for vdw energy + virtual double CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const + double lambda) const; + //Calculate the dE/dlambda for Coulomb energy + virtual double CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, uint b) const; + + double *expConst, *expConst_1_4, *rMaxSq, *rMin, *rMaxSq_1_4, *rMin_1_4; + +protected: + virtual double CalcEn(const double distSq, const uint index) const; + virtual double CalcVir(const double distSq, const uint index) const; + virtual double CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const; +}; + +inline void FF_EXP6::Init(ff_setup::Particle const& mie, + ff_setup::NBfix const& nbfix) +{ + //Initializ sigma and epsilon + FFParticle::Init(mie, nbfix); + uint size = num::Sq(count); + //allocate memory + expConst = new double [size]; + rMin = new double [size]; + rMaxSq = new double [size]; + expConst_1_4 = new double [size]; + rMin_1_4 = new double [size]; + rMaxSq_1_4 = new double [size]; + //calculate exp-6 parameter + for(uint i = 0; i < count; ++i) { + for(uint j = 0; j < count; ++j) { + uint idx = FlatIndex(i, j); + //We use n as alpha for exp-6, with geometric combining + expConst[idx] = epsilon[idx] * n[idx] / (n[idx] - 6.0); + expConst_1_4[idx] = epsilon_1_4[idx] * n_1_4[idx] / (n_1_4[idx] - 6.0); + + //Find the Rmin(well depth) + double sigma = sqrt(sigmaSq[idx]); + num::Exp6Fun* func1 = new num::RminFun(n[idx], sigma); + rMin[idx] = num::Zbrent(func1, sigma, 3.0 * sigma, 1.0e-7); + //Find the Rmax(du/dr = 0) + num::Exp6Fun* func2 = new num::RmaxFun(n[idx], sigma, rMin[idx]); + double rmax = Zbrent(func2, 0.0, sigma, 1.0e-7); + rMaxSq[idx] = rmax * rmax; + + //Find the Rmin(well depth) + double sigma_1_4 = sqrt(sigmaSq_1_4[idx]); + num::Exp6Fun* func3 = new num::RminFun(n_1_4[idx], sigma_1_4); + rMin_1_4[idx] = num::Zbrent(func3, sigma_1_4, 3.0 * sigma_1_4, 1.0e-7); + //Find the Rmax(du/dr = 0) + num::Exp6Fun* func4 = new num::RmaxFun(n_1_4[idx], sigma_1_4, rMin_1_4[idx]); + double rmax_1_4 = Zbrent(func4, 0.0, sigma_1_4, 1.0e-7); + rMaxSq_1_4[idx] = rmax_1_4 * rmax_1_4; + + delete func1; + delete func2; + delete func3; + delete func4; + } + } +#ifdef GOMC_CUDA + InitExp6Variables(varCUDA, rMin, expConst, rMaxSq, size); +#endif +} + + +inline void FF_EXP6::CalcAdd_1_4(double& en, const double distSq, + const uint kind1, const uint kind2) const +{ + uint idx = FlatIndex(kind1, kind2); + if(distSq < rMaxSq_1_4[idx]) { + en += num::BIGNUM; + return; + } + + double dist = sqrt(distSq); + double rRat = rMin_1_4[idx] / dist; + double rRat2 = rRat * rRat; + double attract = rRat2 * rRat2 * rRat2; + double alpha_ij = n_1_4[idx]; + double repulse = (6.0 / alpha_ij) * exp(alpha_ij * + (1.0 - dist / rMin_1_4[idx])); + + en += expConst_1_4[idx] * (repulse - attract); +} + +inline void FF_EXP6::CalcCoulombAdd_1_4(double& en, const double distSq, + const double qi_qj_Fact, + const bool NB) const +{ + double dist = sqrt(distSq); + if(NB) + en += qi_qj_Fact / dist; + else + en += qi_qj_Fact * forcefield.scaling_14 / dist; +} + +inline double FF_EXP6::CalcEn(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + if(distSq < rMaxSq[index]) { + return num::BIGNUM; + } + if(lambda >= 0.999999) { + //save computation time + return CalcEn(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + double en = lambda * CalcEn(softRsq, index); + return en; +} + + +inline double FF_EXP6::CalcEn(const double distSq, const uint idx) const +{ + double dist = sqrt(distSq); + double rRat = rMin[idx] / dist; + double rRat2 = rRat * rRat; + double attract = rRat2 * rRat2 * rRat2; + + uint alpha_ij = n[idx]; + double repulse = (6.0 / alpha_ij) * exp(alpha_ij * (1.0 - dist / rMin[idx])); + return expConst[idx] * (repulse - attract); +} + +inline double FF_EXP6::CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + if(distSq < rMaxSq[index]) { + return num::BIGNUM; + } + if(lambda >= 0.999999) { + //save computation time + return CalcVir(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + double vir = lambda * correction * correction * CalcVir(softRsq, index); + return vir; +} + + +inline double FF_EXP6::CalcVir(const double distSq, const uint idx) const +{ + double dist = sqrt(distSq); + double rRat = rMin[idx] / dist; + double rRat2 = rRat * rRat ; + double attract = rRat2 * rRat2 * rRat2; + + uint alpha_ij = n[idx]; + double repulse = (dist / rMin[idx]) * exp(alpha_ij * + (1.0 - dist / rMin[idx])); + //Virial = F.r = -du/dr * 1/r + return 6.0 * expConst[idx] * (repulse - attract) / distSq; +} + +inline double FF_EXP6::CalcCoulomb(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + if(lambda >= 0.999999) { + //save computation time + return CalcCoulomb(distSq, qi_qj_Fact, b); + } + double en = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + en = lambda * CalcCoulomb(softRsq, qi_qj_Fact, b); + } else { + en = lambda * CalcCoulomb(distSq, qi_qj_Fact, b); + } + return en; +} + +inline double FF_EXP6::CalcCoulomb(const double distSq, + const double qi_qj_Fact, + const uint b) const +{ + if(forcefield.ewald) { + double dist = sqrt(distSq); + double val = forcefield.alpha[b] * dist; + return qi_qj_Fact * erfc(val) / dist; + } else { + double dist = sqrt(distSq); + return qi_qj_Fact / dist; + } +} + +inline double FF_EXP6::CalcCoulombVir(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj, + const double lambda, + const uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + if(lambda >= 0.999999) { + //save computation time + return CalcCoulombVir(distSq, qi_qj, b); + } + double vir = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + vir = lambda * correction * correction * CalcCoulombVir(softRsq, qi_qj, b); + } else { + vir = lambda * CalcCoulombVir(distSq, qi_qj, b); + } + return vir; +} + +inline double FF_EXP6::CalcCoulombVir(const double distSq, const double qi_qj, + const uint b) const +{ + if(forcefield.ewald) { + double dist = sqrt(distSq); + double constValue = 2.0 * forcefield.alpha[b] / sqrt(M_PI); + double expConstValue = exp(-1.0 * forcefield.alphaSq[b] * distSq); + double temp = erfc(forcefield.alpha[b] * dist); + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + } else { + double dist = sqrt(distSq); + return qi_qj / (distSq * dist); + } +} + +inline double FF_EXP6::GetRmin(const uint i, const uint j) const +{ + uint idx = FlatIndex(i, j); + return rMin[idx]; +} + +inline double FF_EXP6::GetRmax(const uint i, const uint j) const +{ + uint idx = FlatIndex(i, j); + return sqrt(rMaxSq[idx]); +} + +inline double FF_EXP6::GetRmin_1_4(const uint i, const uint j) const +{ + uint idx = FlatIndex(i, j); + return rMin_1_4[idx]; +} + +inline double FF_EXP6::GetRmax_1_4(const uint i, const uint j) const +{ + uint idx = FlatIndex(i, j); + return sqrt(rMaxSq_1_4[idx]); +} + +//!Returns energy correction +inline double FF_EXP6::EnergyLRC(const uint kind1, const uint kind2) const +{ + uint idx = FlatIndex(kind1, kind2); + double rCut = forcefield.rCut; + //A,B and C for energy equation + double A = 6.0 * epsilon[idx] * exp(n[idx]) / ((double)n[idx] - 6.0); + double B = rMin[idx] / (double)n[idx]; + double C = epsilon[idx] * (double)n[idx] * pow(rMin[idx], 6) / + ((double)n[idx] - 6.0); + + double tc = 2.0 * M_PI * (A * B * exp(-rCut / B) * + (2.0 * pow(B, 2) + (2.0 * B * rCut) + pow(rCut, 2)) - + C / (3.0 * pow(rCut, 3))); + return tc; +} +//!!Returns virial correction +inline double FF_EXP6::VirialLRC(const uint kind1, const uint kind2) const +{ + uint idx = FlatIndex(kind1, kind2); + double rCut = forcefield.rCut; + //A,B and C for virial equation + double A = 6.0 * epsilon[idx] * exp(n[idx]) / ((double)n[idx] - 6.0); + double B = rMin[idx] / (double)n[idx]; + double C = epsilon[idx] * (double)n[idx] * pow(rMin[idx], 6) / + ((double)n[idx] - 6.0); + double tc = 2.0 * M_PI * (A * exp(-rCut / B) * + (6.0 * pow(B, 3) + 6.0 * pow(B, 2) * rCut + + 3.0 * pow(rCut, 2) * B + pow(rCut, 3)) - + 2.0 * C / pow(rCut, 3)); + return tc; +} + +inline double FF_EXP6::CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + double dhdl = CalcEn(softRsq, index) + fCoef * CalcVir(softRsq, index); + return dhdl; +} + +//Calculate the dE/dlambda for Coulomb energy +inline double FF_EXP6::CalcCoulombdEndL(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + double dhdl = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + dhdl = CalcCoulomb(softRsq, qi_qj_Fact, b) + + fCoef * CalcCoulombVir(softRsq, qi_qj_Fact, b); + } else { + dhdl = CalcCoulomb(distSq, qi_qj_Fact, b); + } + return dhdl; +} + +#endif /*FF_EXP6_H*/ diff --git a/src/FFParticle.cpp b/src/FFParticle.cpp index 0c90ace89..caab83eac 100644 --- a/src/FFParticle.cpp +++ b/src/FFParticle.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -13,12 +13,13 @@ along with this program, also can be found at . FFParticle::FFParticle(Forcefield &ff) : forcefield(ff), mass(NULL), nameFirst(NULL), nameSec(NULL), n(NULL), n_1_4(NULL), sigmaSq(NULL), sigmaSq_1_4(NULL), epsilon_cn(NULL), epsilon(NULL), epsilon_1_4(NULL), epsilon_cn_1_4(NULL), epsilon_cn_6(NULL), - epsilon_cn_6_1_4(NULL), nOver6(NULL), nOver6_1_4(NULL), enCorrection(NULL), - virCorrection(NULL) + epsilon_cn_6_1_4(NULL), nOver6(NULL), nOver6_1_4(NULL) #ifdef GOMC_CUDA , varCUDA(NULL) #endif -{} +{ + exp6 = ff.exp6; +} FFParticle::~FFParticle(void) { @@ -41,9 +42,6 @@ FFParticle::~FFParticle(void) delete[] epsilon_cn_6_1_4; delete[] nOver6_1_4; - delete[] enCorrection; - delete[] virCorrection; - #ifdef GOMC_CUDA DestroyCUDAVars(varCUDA); delete varCUDA; @@ -84,13 +82,10 @@ void FFParticle::Init(ff_setup::Particle const& mie, nOver6_1_4 = new double [size]; sigmaSq_1_4 = new double [size]; - enCorrection = new double [size]; - virCorrection = new double [size]; - //Combining VDW parameter - Blend(mie, forcefield.rCut); + Blend(mie); //Adjusting VDW parameter using NBFIX - AdjNBfix(mie, nbfix, forcefield.rCut); + AdjNBfix(mie, nbfix); #ifdef GOMC_CUDA double diElectric_1 = 1.0 / forcefield.dielectric; @@ -104,15 +99,47 @@ void FFParticle::Init(ff_setup::Particle const& mie, double FFParticle::EnergyLRC(const uint kind1, const uint kind2) const { - return enCorrection[FlatIndex(kind1, kind2)]; + uint idx = FlatIndex(kind1, kind2); + double tc = 1.0; + double sigma = sqrt(sigmaSq[idx]); + double rRat = sigma / forcefield.rCut; + double N = (double)(n[idx]); + tc *= sigma * sigmaSq[idx]; + tc *= 2.0 * M_PI * epsilon_cn[idx] / (N - 3.0); + tc *= (pow(rRat, n[idx] - 3) - ((N - 3.0) / 3.0) * pow(rRat, 3)); + /* + if(forcefield.freeEnergy) { + //For free energy calc, we only consider attraction part + tc *= (-((N - 3.0) / 3.0) * pow(rRat, 3)); + } else { + tc *= (pow(rRat, n[idx] - 3) - ((N - 3.0) / 3.0) * pow(rRat, 3)); + } + */ + return tc; } double FFParticle::VirialLRC(const uint kind1, const uint kind2) const { - return virCorrection[FlatIndex(kind1, kind2)]; + uint idx = FlatIndex(kind1, kind2); + double tc = 1.0; + double sigma = sqrt(sigmaSq[idx]); + double rRat = sigma / forcefield.rCut; + double N = (double)(n[idx]); + tc *= sigma * sigmaSq[idx]; + tc *= 2.0 * M_PI * epsilon_cn[idx] / (N - 3.0); + tc *= (N * pow(rRat, n[idx] - 3) - (N - 3.0) * 2.0 * pow(rRat, 3)); + /* + if(forcefield.freeEnergy) { + //For free energy calc, we only consider attraction part + tc *= (- (N - 3.0) * 2.0 * pow(rRat, 3)); + } else { + tc *= (N * pow(rRat, n[idx] - 3) - (N - 3.0) * 2.0 * pow(rRat, 3)); + } + */ + return tc; } -void FFParticle::Blend(ff_setup::Particle const& mie, const double rCut) +void FFParticle::Blend(ff_setup::Particle const& mie) { for(uint i = 0; i < count; ++i) { for(uint j = 0; j < count; ++j) { @@ -123,8 +150,13 @@ void FFParticle::Blend(ff_setup::Particle const& mie, const double rCut) nameSec[idx] = mie.getname(j); nameSec[idx] += mie.getname(i); - n[idx] = num::MeanA(mie.n, mie.n, i, j); - n_1_4[idx] = num::MeanA(mie.n_1_4, mie.n_1_4, i, j); + if(exp6) { + n[idx] = num::MeanG(mie.n, mie.n, i, j); + n_1_4[idx] = num::MeanG(mie.n_1_4, mie.n_1_4, i, j); + } else { + n[idx] = num::MeanA(mie.n, mie.n, i, j); + n_1_4[idx] = num::MeanA(mie.n_1_4, mie.n_1_4, i, j); + } double cn = n[idx] / (n[idx] - 6) * pow(n[idx] / 6, (6 / (n[idx] - 6))); double cn_1_4 = n_1_4[idx] / (n_1_4[idx] - 6) * pow(n_1_4[idx] / 6, (6 / (n_1_4[idx] - 6))); @@ -140,12 +172,8 @@ void FFParticle::Blend(ff_setup::Particle const& mie, const double rCut) sigma_1_4 = num::MeanA(mie.sigma_1_4, mie.sigma_1_4, i, j); } - double tc = 1.0; - double rRat = sigma / rCut; - // calculate sig^2 and tc*sig^3 - num::Cb(sigmaSq[idx], tc, sigma); sigmaSq_1_4[idx] = sigma_1_4 * sigma_1_4; - tc *= 0.5 * 4.0 * M_PI; + sigmaSq[idx] = sigma * sigma; epsilon[idx] = num::MeanG(mie.epsilon, mie.epsilon, i, j); epsilon_cn[idx] = cn * epsilon[idx]; epsilon_1_4[idx] = num::MeanG(mie.epsilon_1_4, mie.epsilon_1_4, i, j); @@ -154,18 +182,12 @@ void FFParticle::Blend(ff_setup::Particle const& mie, const double rCut) epsilon_cn_6_1_4[idx] = epsilon_cn_1_4[idx] * 6; nOver6[idx] = n[idx] / 6; nOver6_1_4[idx] = n_1_4[idx] / 6; - enCorrection[idx] = tc / (n[idx] - 3) * epsilon_cn[idx] * - ( pow(rRat, n[idx] - 3) - - (double)(n[idx] - 3.0) / 3.0 * pow(rRat, 3) ); - virCorrection[idx] = tc / (n[idx] - 3) * epsilon_cn_6[idx] * - ( (double)(n[idx]) / 6.0 * pow(rRat, n[idx] - 3) - - (double)(n[idx] - 3.0) / 3.0 * pow(rRat, 3) ); } } } void FFParticle::AdjNBfix(ff_setup::Particle const& mie, - ff_setup::NBfix const& nbfix, const double rCut) + ff_setup::NBfix const& nbfix) { uint size = num::Sq(count); for(uint i = 0; i < nbfix.epsilon.size(); i++) { @@ -173,14 +195,12 @@ void FFParticle::AdjNBfix(ff_setup::Particle const& mie, if(nbfix.getname(i) == nameFirst[j] || nbfix.getname(i) == nameSec[j]) { n[j] = nbfix.n[i]; n_1_4[j] = nbfix.n_1_4[i]; - double rRat = nbfix.sigma[i] / rCut, tc = 1.0; - //calculating sig^2 and tc*sig^3 - num::Cb(sigmaSq[j], tc, nbfix.sigma[i]); + sigmaSq[j] = nbfix.sigma[i] * nbfix.sigma[i]; sigmaSq_1_4[j] = nbfix.sigma_1_4[i] * nbfix.sigma_1_4[i]; - tc *= 0.5 * 4.0 * M_PI; double cn = n[j] / (n[j] - 6) * pow(n[j] / 6, (6 / (n[j] - 6))); double cn_1_4 = n_1_4[j] / (n_1_4[j] - 6) * pow(n_1_4[j] / 6, (6 / (n_1_4[j] - 6))); + epsilon[j] = nbfix.epsilon[i]; epsilon_cn[j] = cn * nbfix.epsilon[i]; epsilon_1_4[j] = nbfix.epsilon_1_4[i]; @@ -189,12 +209,6 @@ void FFParticle::AdjNBfix(ff_setup::Particle const& mie, epsilon_cn_6_1_4[j] = epsilon_cn_1_4[j] * 6; nOver6[j] = n[j] / 6; nOver6_1_4[j] = n_1_4[j] / 6; - enCorrection[j] = tc / (n[j] - 3) * epsilon_cn[j] * - ( pow(rRat, n[j] - 3) - - (double)(n[j] - 3.0) / 3.0 * pow(rRat, 3) ); - virCorrection[j] = tc / (n[j] - 3) * epsilon_cn_6[j] * - ( (double)(n[j]) / 6.0 * pow(rRat, n[j] - 3) - - (double)(n[j] - 3.0) / 3.0 * pow(rRat, 3) ); } } } @@ -230,6 +244,22 @@ double FFParticle::GetN_1_4(const uint i, const uint j) const uint idx = FlatIndex(i, j); return n_1_4[idx]; } +inline double FFParticle::GetRmin(const uint i, const uint j) const +{ + return 0.0; +} +inline double FFParticle::GetRmax(const uint i, const uint j) const +{ + return 0.0; +} +inline double FFParticle::GetRmin_1_4(const uint i, const uint j) const +{ + return 0.0; +} +inline double FFParticle::GetRmax_1_4(const uint i, const uint j) const +{ + return 0.0; +} // Defining the functions @@ -262,83 +292,213 @@ inline void FFParticle::CalcCoulombAdd_1_4(double& en, const double distSq, en += qi_qj_Fact * forcefield.scaling_14 / dist; } - - //mie potential -inline double FFParticle::CalcEn(const double distSq, - const uint kind1, const uint kind2) const +inline double FFParticle::CalcEn(const double distSq, const uint kind1, + const uint kind2, const double lambda) const { if(forcefield.rCutSq < distSq) return 0.0; uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcEn(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + double en = lambda * CalcEn(softRsq, index); + return en; +} + +inline double FFParticle::CalcEn(const double distSq, const uint index) const +{ double rRat2 = sigmaSq[index] / distSq; double rRat4 = rRat2 * rRat2; double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif + double repulse = pow(rRat2, (n_ij * 0.5)); + + return (epsilon_cn[index] * (repulse - attract)); +} + +inline double FFParticle::CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; - return epsilon_cn[index] * (repulse - attract); + uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcVir(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + double vir = lambda * correction * correction * CalcVir(softRsq, index); + return vir; +} + +inline double FFParticle::CalcVir(const double distSq, const uint index) const +{ + double rNeg2 = 1.0 / distSq; + double rRat2 = rNeg2 * sigmaSq[index]; + double rRat4 = rRat2 * rRat2; + double attract = rRat4 * rRat2; + double n_ij = n[index]; + double repulse = pow(rRat2, (n_ij * 0.5)); + //Virial is F.r = -dE/dr * 1/r + return epsilon_cn_6[index] * (nOver6[index] * repulse - attract) * rNeg2; } inline double FFParticle::CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const { if(forcefield.rCutCoulombSq[b] < distSq) return 0.0; + if(lambda >= 0.999999) { + //save computation time + return CalcCoulomb(distSq, qi_qj_Fact, b); + } + double en = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + en = lambda * CalcCoulomb(softRsq, qi_qj_Fact, b); + } else { + en = lambda * CalcCoulomb(distSq, qi_qj_Fact, b); + } + return en; +} + +inline double FFParticle::CalcCoulomb(const double distSq, + const double qi_qj_Fact, + const uint b) const +{ if(forcefield.ewald) { double dist = sqrt(distSq); double val = forcefield.alpha[b] * dist; - return qi_qj_Fact * erfc(val) / dist; + return qi_qj_Fact * erfc(val) / dist; } else { double dist = sqrt(distSq); - return qi_qj_Fact / dist; + return qi_qj_Fact / dist; } } -inline double FFParticle::CalcVir(const double distSq, - const uint kind1, const uint kind2) const +inline double FFParticle::CalcCoulombVir(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj, + const double lambda, + const uint b) const { - if(forcefield.rCutSq < distSq) + if(forcefield.rCutCoulombSq[b] < distSq) return 0.0; - uint index = FlatIndex(kind1, kind2); - double rNeg2 = 1.0 / distSq; - double rRat2 = rNeg2 * sigmaSq[index]; - double rRat4 = rRat2 * rRat2; - double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else - double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif - - //Virial is the derivative of the pressure... mu - return epsilon_cn_6[index] * (nOver6[index] * repulse - attract) * rNeg2; + if(lambda >= 0.999999) { + //save computation time + return CalcCoulombVir(distSq, qi_qj, b); + } + double vir = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + vir = lambda * correction * correction * CalcCoulombVir(softRsq, qi_qj, b); + } else { + vir = lambda * CalcCoulombVir(distSq, qi_qj, b); + } + return vir; } inline double FFParticle::CalcCoulombVir(const double distSq, const double qi_qj, const uint b) const { - if(forcefield.rCutCoulombSq[b] < distSq) - return 0.0; - if(forcefield.ewald) { double dist = sqrt(distSq); double constValue = 2.0 * forcefield.alpha[b] / sqrt(M_PI); double expConstValue = exp(-1.0 * forcefield.alphaSq[b] * distSq); double temp = 1.0 - erf(forcefield.alpha[b] * dist); - return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; } else { double dist = sqrt(distSq); - return qi_qj / (distSq * dist); + double result = qi_qj / (distSq * dist); + return result; } } + +//Calculate the dE/dlambda for vdw energy +inline double FFParticle::CalcdEndL(const double distSq, const uint kind1, + const uint kind2, + const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + double dhdl = CalcEn(softRsq, index) + fCoef * CalcVir(softRsq, index); + return dhdl; +} + +//Calculate the dE/dlambda for Coulomb energy +inline double FFParticle::CalcCoulombdEndL(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + double dhdl = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + dhdl = CalcCoulomb(softRsq, qi_qj_Fact, b) + + fCoef * CalcCoulombVir(softRsq, qi_qj_Fact, b); + } else { + dhdl = CalcCoulomb(distSq, qi_qj_Fact, b); + } + return dhdl; +} \ No newline at end of file diff --git a/src/FFParticle.h b/src/FFParticle.h index fc7789961..fd4ea4c82 100644 --- a/src/FFParticle.h +++ b/src/FFParticle.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -58,19 +58,27 @@ struct FFParticle { double GetSigma_1_4(const uint i, const uint j) const; double GetN(const uint i, const uint j) const; double GetN_1_4(const uint i, const uint j) const; + virtual double GetRmin(const uint i, const uint j) const; + virtual double GetRmax(const uint i, const uint j) const; + virtual double GetRmin_1_4(const uint i, const uint j) const; + virtual double GetRmax_1_4(const uint i, const uint j) const; + // LJ interaction functions virtual double CalcEn(const double distSq, - const uint kind1, const uint kind2) const; - virtual double CalcVir(const double distSq, - const uint kind1, const uint kind2) const; + const uint kind1, const uint kind2, + const double lambda) const; + virtual double CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; virtual void CalcAdd_1_4(double& en, const double distSq, const uint kind1, const uint kind2) const; // coulomb interaction functions - virtual double CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const; - virtual double CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const; + virtual double CalcCoulomb(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, const uint b) const; + virtual double CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, uint b) const; virtual void CalcCoulombAdd_1_4(double& en, const double distSq, const double qi_qj_Fact, const bool NB) const; @@ -79,6 +87,14 @@ struct FFParticle { //!Returns Energy long-range correction term for a kind pair virtual double VirialLRC(const uint kind1, const uint kind2) const; + //Calculate the dE/dlambda for vdw energy + virtual double CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; + //Calculate the dE/dlambda for Coulomb energy + virtual double CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, uint b) const; + uint NumKinds() const { return count; @@ -96,16 +112,21 @@ struct FFParticle { #endif protected: + virtual double CalcEn(const double distSq, const uint index) const; + virtual double CalcVir(const double distSq, const uint index) const; + virtual double CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const; //Find the index of the pair kind uint FlatIndex(const uint i, const uint j) const { return i + j * count; } //Combining sigma, epsilon, and n value for different kind - void Blend(ff_setup::Particle const& mie, const double rCut); + void Blend(ff_setup::Particle const& mie); //Use NBFIX to adjust sigma, epsilon, and n value for different kind - void AdjNBfix(ff_setup::Particle const& mie, ff_setup::NBfix const& nbfix, - const double rCut); + void AdjNBfix(ff_setup::Particle const& mie, ff_setup::NBfix const& nbfix); //To access rcut and other forcefield data const Forcefield& forcefield; @@ -121,10 +142,11 @@ struct FFParticle { #endif //For LJ eps_cn(en) --> 4eps, eps_cn_6 --> 24eps, eps_cn_n --> 48eps double * sigmaSq, * epsilon, * epsilon_1_4, * epsilon_cn, * epsilon_cn_6, - * nOver6, * sigmaSq_1_4, * epsilon_cn_1_4, * epsilon_cn_6_1_4, * nOver6_1_4, - * enCorrection, * virCorrection; + * nOver6, * sigmaSq_1_4, * epsilon_cn_1_4, * epsilon_cn_6_1_4, + * nOver6_1_4; uint count; + bool exp6; #ifdef GOMC_CUDA VariablesCUDA *varCUDA; #endif diff --git a/src/FFSetup.cpp b/src/FFSetup.cpp index d596777f6..fdd1c5d70 100644 --- a/src/FFSetup.cpp +++ b/src/FFSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -32,10 +32,14 @@ FFSetup::SetReadFunctions(const bool isCHARMM) { std::map funct; //From CHARMM style file. + funct["BOND"] = &bond; funct["BONDS"] = &bond; + funct["ANGLE"] = ∠ funct["ANGLES"] = ∠ + funct["DIHEDRAL"] = &dih; funct["DIHEDRALS"] = &dih; funct["IMPROPER"] = &imp; + funct["IMPROPERS"] = &imp; if (isCHARMM) { funct["NONBONDED"] = &mie; funct["NBFIX"] = &nbfix; @@ -56,7 +60,7 @@ void FFSetup::Init(std::string const& name, const bool isCHARMM) sectKind = SetReadFunctions(isCHARMM); string currSectName = "", varName = ""; string commentChar = "*!"; - string commentStr = "REMARK set AEXP REXP HAEX AAEX NBOND " + string commentStr = "REMARK ATOM ATOMS MASS set AEXP REXP HAEX AAEX NBOND " "CUTNB END CTONN EPS VSWI NBXM INHI"; map::const_iterator sect, currSect; @@ -135,6 +139,11 @@ void Particle::Read(Reader & param, std::string const& firstVar) values >> expN; } + if(values.fail()) { + std::cout << "Error: Incomplete Nonbonded parameters were found in parameter file!\n"; + exit(EXIT_FAILURE); + } + if (isCHARMM()) { //if lj values >> dummy2; } @@ -185,6 +194,11 @@ void NBfix::Read(Reader & param, std::string const& firstVar) values >> expN; } + if(values.fail()) { + std::cout << "Error: Incomplete NBfix parameters were found in parameter file!\n"; + exit(EXIT_FAILURE); + } + values >> e_1_4 >> s_1_4; if (values.fail()) { e_1_4 = e; @@ -231,6 +245,10 @@ void Bond::Read(Reader & param, std::string const& firstVar) double coeff, def; ReadKind(param, firstVar); param.file >> coeff >> def; + if(!param.file.good()) { + std::cout << "Error: Incomplete Bond parameters were found in parameter file!\n"; + exit(EXIT_FAILURE); + } Add(coeff, def); } void Bond::Add(const double coeff, const double def) @@ -246,6 +264,10 @@ void Angle::Read(Reader & param, std::string const& firstVar) bool hsUB; std::stringstream values(LoadLine(param, firstVar)); values >> coeff >> def; + if(values.fail()) { + std::cout << "Error: Incomplete Angle parameters were found in parameter file!\n"; + exit(EXIT_FAILURE); + } values >> coeffUB >> defUB; hsUB = !values.fail(); @@ -273,6 +295,10 @@ void Dihedral::Read(Reader & param, std::string const& firstVar) uint index; std::string merged = ReadKind(param, firstVar); param.file >> coeff >> index >> def; + if(!param.file.good()) { + std::cout << "Error: Incomplete Dihedral parameters were found in parameter file!\n"; + exit(EXIT_FAILURE); + } if(index == 0) { //set phase shif for n=0 to 90 degree // We will have C0 = Kchi (1 + cos(0 * phi + 90)) = Kchi @@ -295,8 +321,13 @@ void Improper::Read(Reader & param, std::string const& firstVar) double coeff, def; std::string merged = ReadKind(param, firstVar); //If new value - if (validname(merged) == false) { + if (validname(merged) == true) { + std::cout << "Warning: GOMC does not support IMPROPER!\n"; param.file >> coeff >> def; + if(!param.file.good()) { + std::cout << "Error: Incomplete Improper parameters was found in parameter file!\n"; + exit(EXIT_FAILURE); + } Add(coeff, def); } } diff --git a/src/FFSetup.h b/src/FFSetup.h index be01d3d5d..b7184b4df 100644 --- a/src/FFSetup.h +++ b/src/FFSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/FFShift.h b/src/FFShift.h index 08b01eef1..e9fd09d70 100644 --- a/src/FFShift.h +++ b/src/FFShift.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -42,17 +42,20 @@ struct FF_SHIFT : public FFParticle { ff_setup::NBfix const& nbfix); virtual double CalcEn(const double distSq, - const uint kind1, const uint kind2) const; - virtual double CalcVir(const double distSq, - const uint kind1, const uint kind2) const; + const uint kind1, const uint kind2, + const double lambda) const; + virtual double CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; virtual void CalcAdd_1_4(double& en, const double distSq, const uint kind1, const uint kind2) const; // coulomb interaction functions - virtual double CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const; - virtual double CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const; + virtual double CalcCoulomb(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, const uint b) const; + virtual double CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, const uint b) const; virtual void CalcCoulombAdd_1_4(double& en, const double distSq, const double qi_qj_Fact, const bool NB) const; @@ -68,7 +71,22 @@ struct FF_SHIFT : public FFParticle { return 0.0; } + //Calculate the dE/dlambda for vdw energy + virtual double CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const + double lambda) const; + //Calculate the dE/dlambda for Coulomb energy + virtual double CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, uint b) const; + protected: + virtual double CalcEn(const double distSq, const uint index) const; + virtual double CalcVir(const double distSq, const uint index) const; + virtual double CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const; double *shiftConst, *shiftConst_1_4; @@ -135,86 +153,207 @@ inline void FF_SHIFT::CalcCoulombAdd_1_4(double& en, const double distSq, en += qi_qj_Fact * forcefield.scaling_14 / dist; } - -//mie potential -inline double FF_SHIFT::CalcEn(const double distSq, - const uint kind1, const uint kind2) const +inline double FF_SHIFT::CalcEn(const double distSq, const uint kind1, + const uint kind2, const double lambda) const { if(forcefield.rCutSq < distSq) return 0.0; uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcEn(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + double en = lambda * CalcEn(softRsq, index); + return en; +} + +inline double FF_SHIFT::CalcEn(const double distSq, const uint index) const +{ double rRat2 = sigmaSq[index] / distSq; double rRat4 = rRat2 * rRat2; double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif + double repulse = pow(rRat2, (n_ij * 0.5)); return (epsilon_cn[index] * (repulse - attract) - shiftConst[index]); } -inline double FF_SHIFT::CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const +inline double FF_SHIFT::CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const { - if(forcefield.rCutCoulombSq[b] < distSq) + if(forcefield.rCutSq < distSq) return 0.0; - if(forcefield.ewald) { - double dist = sqrt(distSq); - double val = forcefield.alpha[b] * dist; - return qi_qj_Fact * erfc(val) / dist; - } else { - double dist = sqrt(distSq); - return qi_qj_Fact * (1.0 / dist - 1.0 / forcefield.rCut); + uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcVir(distSq, index); } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + double vir = lambda * correction * correction * CalcVir(softRsq, index); + return vir; } -//mie potential -inline double FF_SHIFT::CalcVir(const double distSq, - const uint kind1, const uint kind2) const +inline double FF_SHIFT::CalcVir(const double distSq, const uint index) const { - if(forcefield.rCutSq < distSq) - return 0.0; - - uint index = FlatIndex(kind1, kind2); double rNeg2 = 1.0 / distSq; double rRat2 = rNeg2 * sigmaSq[index]; double rRat4 = rRat2 * rRat2; double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif + double repulse = pow(rRat2, (n_ij * 0.5)); //Virial is the derivative of the pressure... mu return epsilon_cn_6[index] * (nOver6[index] * repulse - attract) * rNeg2; } -inline double FF_SHIFT::CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const +inline double FF_SHIFT::CalcCoulomb(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + if(lambda >= 0.999999) { + //save computation time + return CalcCoulomb(distSq, qi_qj_Fact, b); + } + double en = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + en = lambda * CalcCoulomb(softRsq, qi_qj_Fact, b); + } else { + en = lambda * CalcCoulomb(distSq, qi_qj_Fact, b); + } + return en; +} + +inline double FF_SHIFT::CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const +{ + if(forcefield.ewald) { + double dist = sqrt(distSq); + double val = forcefield.alpha[b] * dist; + return qi_qj_Fact * erfc(val) / dist; + } else { + double dist = sqrt(distSq); + return qi_qj_Fact * (1.0 / dist - 1.0 / forcefield.rCut); + } +} + +inline double FF_SHIFT::CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, const uint b) const { if(forcefield.rCutCoulombSq[b] < distSq) return 0.0; + if(lambda >= 0.999999) { + //save computation time + return CalcCoulombVir(distSq, qi_qj, b); + } + double vir = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + vir = lambda * correction * correction * CalcCoulombVir(softRsq, qi_qj, b); + } else { + vir = lambda * CalcCoulombVir(distSq, qi_qj, b); + } + return vir; +} + +inline double FF_SHIFT::CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const +{ if(forcefield.ewald) { double dist = sqrt(distSq); double constValue = 2.0 * forcefield.alpha[b] / sqrt(M_PI); double expConstValue = exp(-1.0 * forcefield.alphaSq[b] * distSq); double temp = erfc(forcefield.alpha[b] * dist); - return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; } else { double dist = sqrt(distSq); return qi_qj / (distSq * dist); } } +inline double FF_SHIFT::CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + double dhdl = CalcEn(softRsq, index) + fCoef * CalcVir(softRsq, index); + return dhdl; +} + +//Calculate the dE/dlambda for Coulomb energy +inline double FF_SHIFT::CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + double dhdl = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + dhdl = CalcCoulomb(softRsq, qi_qj_Fact, b) + + fCoef * CalcCoulombVir(softRsq, qi_qj_Fact, b); + } else { + dhdl = CalcCoulomb(distSq, qi_qj_Fact, b); + } + return dhdl; +} #endif /*FF_SHIFT_H*/ diff --git a/src/FFSwitch.h b/src/FFSwitch.h index 0a8133558..8d3fbc8d7 100644 --- a/src/FFSwitch.h +++ b/src/FFSwitch.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -48,17 +48,23 @@ struct FF_SWITCH : public FFParticle { ff_setup::NBfix const& nbfix); virtual double CalcEn(const double distSq, - const uint kind1, const uint kind2) const; - virtual double CalcVir(const double distSq, - const uint kind1, const uint kind2) const; + const uint kind1, const uint kind2, + const double lambda) const; + virtual double CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; virtual void CalcAdd_1_4(double& en, const double distSq, const uint kind1, const uint kind2) const; // coulomb interaction functions virtual double CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const; - virtual double CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const; + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, const uint b) const; virtual void CalcCoulombAdd_1_4(double& en, const double distSq, const double qi_qj_Fact, const bool NB) const; @@ -74,7 +80,23 @@ struct FF_SWITCH : public FFParticle { return 0.0; } + //Calculate the dE/dlambda for vdw energy + virtual double CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const + double lambda) const; + + //Calculate the dE/dlambda for Coulomb energy + virtual double CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, uint b) const; + protected: + virtual double CalcEn(const double distSq, const uint index) const; + virtual double CalcVir(const double distSq, const uint index) const; + virtual double CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const; double rOn, rOnSq, factor1, factor2; @@ -128,62 +150,69 @@ inline void FF_SWITCH::CalcCoulombAdd_1_4(double& en, const double distSq, en += qi_qj_Fact * forcefield.scaling_14 / dist; } - -//mie potential -inline double FF_SWITCH::CalcEn(const double distSq, - const uint kind1, const uint kind2) const +inline double FF_SWITCH::CalcEn(const double distSq, const uint kind1, + const uint kind2, const double lambda) const { if(forcefield.rCutSq < distSq) return 0.0; uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcEn(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + double en = lambda * CalcEn(softRsq, index); + return en; +} + +inline double FF_SWITCH::CalcEn(const double distSq, const uint index) const +{ double rCutSq_rijSq = forcefield.rCutSq - distSq; double rCutSq_rijSq_Sq = rCutSq_rijSq * rCutSq_rijSq; - double rRat2 = sigmaSq[index] / distSq; double rRat4 = rRat2 * rRat2; double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif + double repulse = pow(rRat2, (n_ij * 0.5)); double fE = rCutSq_rijSq_Sq * factor2 * (factor1 + 2 * distSq); - const double factE = ( distSq > rOnSq ? fE : 1.0); return (epsilon_cn[index] * (repulse - attract)) * factE; } -inline double FF_SWITCH::CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const +inline double FF_SWITCH::CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const { - if(forcefield.rCutCoulombSq[b] < distSq) + if(forcefield.rCutSq < distSq) return 0.0; - if(forcefield.ewald) { - double dist = sqrt(distSq); - double val = forcefield.alpha[b] * dist; - return qi_qj_Fact * erfc(val) / dist; - } else { - double dist = sqrt(distSq); - double switchVal = distSq / forcefield.rCutSq - 1.0; - switchVal *= switchVal; - return qi_qj_Fact * switchVal / dist; + uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcVir(distSq, index); } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + double vir = lambda * correction * correction * CalcVir(softRsq, index); + return vir; } -//mie potential -inline double FF_SWITCH::CalcVir(const double distSq, - const uint kind1, const uint kind2) const +inline double FF_SWITCH::CalcVir(const double distSq, const uint index) const { - if(forcefield.rCutSq < distSq) - return 0.0; - - uint index = FlatIndex(kind1, kind2); double rCutSq_rijSq = forcefield.rCutSq - distSq; double rCutSq_rijSq_Sq = rCutSq_rijSq * rCutSq_rijSq; @@ -191,47 +220,163 @@ inline double FF_SWITCH::CalcVir(const double distSq, double rRat2 = rNeg2 * sigmaSq[index]; double rRat4 = rRat2 * rRat2; double attract = rRat4 * rRat2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double repulse = num::POW(rRat2, rRat4, attract, n_ij); -#else double n_ij = n[index]; - double repulse = pow(sqrt(rRat2), n_ij); -#endif + double repulse = pow(rRat2, (n_ij * 0.5)); double fE = rCutSq_rijSq_Sq * factor2 * (factor1 + 2 * distSq); double fW = 12.0 * factor2 * rCutSq_rijSq * (rOnSq - distSq); const double factE = ( distSq > rOnSq ? fE : 1.0); const double factW = ( distSq > rOnSq ? fW : 0.0); - double Wij = epsilon_cn_6[index] * (nOver6[index] * repulse - attract) * rNeg2; double Eij = epsilon_cn[index] * (repulse - attract); return (Wij * factE - Eij * factW); } +inline double FF_SWITCH::CalcCoulomb(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + if(lambda >= 0.999999) { + //save computation time + return CalcCoulomb(distSq, qi_qj_Fact, b); + } + double en = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + en = lambda * CalcCoulomb(softRsq, qi_qj_Fact, b); + } else { + en = lambda * CalcCoulomb(distSq, qi_qj_Fact, b); + } + return en; +} + +inline double FF_SWITCH::CalcCoulomb(const double distSq, + const double qi_qj_Fact, + const uint b) const +{ + if(forcefield.ewald) { + double dist = sqrt(distSq); + double val = forcefield.alpha[b] * dist; + return qi_qj_Fact * erfc(val) / dist; + } else { + double dist = sqrt(distSq); + double switchVal = distSq / forcefield.rCutSq - 1.0; + switchVal *= switchVal; + return qi_qj_Fact * switchVal / dist; + } +} + inline double FF_SWITCH::CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const + const uint kind1, + const uint kind2, + const double qi_qj, + const double lambda, + const uint b) const { if(forcefield.rCutCoulombSq[b] < distSq) return 0.0; + if(lambda >= 0.999999) { + //save computation time + return CalcCoulombVir(distSq, qi_qj, b); + } + double vir = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + vir = lambda * correction * correction * CalcCoulombVir(softRsq, qi_qj, b); + } else { + vir = lambda * CalcCoulombVir(distSq, qi_qj, b); + } + return vir; +} + +inline double FF_SWITCH::CalcCoulombVir(const double distSq, const double qi_qj, + const uint b) const +{ if(forcefield.ewald) { double dist = sqrt(distSq); double constValue = 2.0 * forcefield.alpha[b] / sqrt(M_PI); double expConstValue = exp(-1.0 * forcefield.alphaSq[b] * distSq); double temp = erfc(forcefield.alpha[b] * dist); - return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; } else { double dist = sqrt(distSq); double switchVal = distSq / forcefield.rCutSq - 1.0; switchVal *= switchVal; double dSwitchVal = 2.0 * (distSq / forcefield.rCutSq - 1.0) * 2.0 * dist / forcefield.rCutSq; - return -1.0 * qi_qj * (dSwitchVal / distSq - switchVal / (distSq * dist)); + return -qi_qj * (dSwitchVal / distSq - switchVal / (distSq * dist)); } } +inline double FF_SWITCH::CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + double dhdl = CalcEn(softRsq, index) + fCoef * CalcVir(softRsq, index); + return dhdl; +} + +//Calculate the dE/dlambda for Coulomb energy +inline double FF_SWITCH::CalcCoulombdEndL(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + double dhdl = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + dhdl = CalcCoulomb(softRsq, qi_qj_Fact, b) + + fCoef * CalcCoulombVir(softRsq, qi_qj_Fact, b); + } else { + dhdl = CalcCoulomb(distSq, qi_qj_Fact, b); + } + return dhdl; +} #endif /*FF_SWITCH_H*/ diff --git a/src/FFSwitchMartini.h b/src/FFSwitchMartini.h index 3b4d4877c..1f062d7ed 100644 --- a/src/FFSwitchMartini.h +++ b/src/FFSwitchMartini.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -72,17 +72,23 @@ struct FF_SWITCH_MARTINI : public FFParticle { ff_setup::NBfix const& nbfix); virtual double CalcEn(const double distSq, - const uint kind1, const uint kind2) const; - virtual double CalcVir(const double distSq, - const uint kind1, const uint kind2) const; + const uint kind1, const uint kind2, + const double lambda) const; + virtual double CalcVir(const double distSq, const uint kind1, + const uint kind2, const double lambda) const; virtual void CalcAdd_1_4(double& en, const double distSq, const uint kind1, const uint kind2) const; // coulomb interaction functions virtual double CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const; - virtual double CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const; + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const uint kind1, + const uint kind2, const double qi_qj, + const double lambda, const uint b) const; virtual void CalcCoulombAdd_1_4(double& en, const double distSq, const double qi_qj_Fact, const bool NB) const; @@ -99,7 +105,22 @@ struct FF_SWITCH_MARTINI : public FFParticle { return 0.0; } + //Calculate the dE/dlambda for vdw energy + virtual double CalcdEndL(const double distSq, const uint kind1, + const uint kind2, const + double lambda) const; + //Calculate the dE/dlambda for Coulomb energy + virtual double CalcCoulombdEndL(const double distSq, const uint kind1, + const uint kind2, const double qi_qj_Fact, + const double lambda, uint b) const; + protected: + virtual double CalcEn(const double distSq, const uint index) const; + virtual double CalcVir(const double distSq, const uint index) const; + virtual double CalcCoulomb(const double distSq, const double qi_qj_Fact, + const uint b) const; + virtual double CalcCoulombVir(const double distSq, const double qi_qj, + uint b) const; double *An, *Bn, *Cn, *An_1_4, *Bn_1_4, *Cn_1_4; double *sig6, *sig6_1_4, *sign, *sign_1_4; @@ -221,25 +242,38 @@ inline void FF_SWITCH_MARTINI::CalcCoulombAdd_1_4(double& en, } -//mie potential inline double FF_SWITCH_MARTINI::CalcEn(const double distSq, const uint kind1, - const uint kind2) const + const uint kind2, + const double lambda) const { if(forcefield.rCutSq < distSq) return 0.0; uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcEn(distSq, index); + } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + double en = lambda * CalcEn(softRsq, index); + return en; +} + +inline double FF_SWITCH_MARTINI::CalcEn(const double distSq, + const uint index) const +{ double r_2 = 1.0 / distSq; double r_4 = r_2 * r_2; double r_6 = r_4 * r_2; -#ifdef MIE_INT_ONLY - uint n_ij = n[index]; - double r_n = num::POW(r_2, r_4, attract, n_ij); -#else double n_ij = n[index]; - double r_n = pow(sqrt(r_2), n_ij); -#endif + double r_n = pow(r_2, (n_ij * 0.5)); double rij_ron = sqrt(distSq) - rOn; double rij_ron_2 = rij_ron * rij_ron; @@ -258,37 +292,34 @@ inline double FF_SWITCH_MARTINI::CalcEn(const double distSq, return Eij; } -inline double FF_SWITCH_MARTINI::CalcCoulomb(const double distSq, - const double qi_qj_Fact, const uint b) const +inline double FF_SWITCH_MARTINI::CalcVir(const double distSq, + const uint kind1, + const uint kind2, + const double lambda) const { - if(forcefield.rCutCoulombSq[b] < distSq) + if(forcefield.rCutSq < distSq) return 0.0; - if(forcefield.ewald) { - double dist = sqrt(distSq); - double val = forcefield.alpha[b] * dist; - return qi_qj_Fact * erfc(val) / dist; - } else { - // in Martini, the Coulomb switching distance is zero, so we will have - // sqrt(distSq) - rOnCoul = sqrt(distSq) - double dist = sqrt(distSq); - double rij_ronCoul_3 = dist * distSq; - double rij_ronCoul_4 = distSq * distSq; - - double coul = -(A1 / 3.0) * rij_ronCoul_3 - (B1 / 4.0) * rij_ronCoul_4 - C1; - return qi_qj_Fact * diElectric_1 * (1.0 / dist + coul); + uint index = FlatIndex(kind1, kind2); + if(lambda >= 0.999999) { + //save computation time + return CalcVir(distSq, index); } + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + double vir = lambda * correction * correction * CalcVir(softRsq, index); + return vir; } -//mie potential inline double FF_SWITCH_MARTINI::CalcVir(const double distSq, - const uint kind1, - const uint kind2) const + const uint index) const { - if(forcefield.rCutSq < distSq) - return 0.0; - - uint index = FlatIndex(kind1, kind2); double n_ij = n[index]; double r_1 = 1.0 / sqrt(distSq); double r_8 = pow(r_1, 8); @@ -298,7 +329,6 @@ inline double FF_SWITCH_MARTINI::CalcVir(const double distSq, double rij_ron_2 = rij_ron * rij_ron; double rij_ron_3 = rij_ron_2 * rij_ron; - double dshifttempRep = An[index] * rij_ron_2 + Bn[index] * rij_ron_3; double dshifttempAtt = A6 * rij_ron_2 + B6 * rij_ron_3; @@ -312,30 +342,159 @@ inline double FF_SWITCH_MARTINI::CalcVir(const double distSq, } +inline double FF_SWITCH_MARTINI::CalcCoulomb(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + const uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + if(lambda >= 0.999999) { + //save computation time + return CalcCoulomb(distSq, qi_qj_Fact, b); + } + double en = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + en = lambda * CalcCoulomb(softRsq, qi_qj_Fact, b); + } else { + en = lambda * CalcCoulomb(distSq, qi_qj_Fact, b); + } + return en; +} + +inline double FF_SWITCH_MARTINI::CalcCoulomb(const double distSq, + const double qi_qj_Fact, + const uint b) const +{ + if(forcefield.ewald) { + double dist = sqrt(distSq); + double val = forcefield.alpha[b] * dist; + return qi_qj_Fact * erfc(val) / dist; + } else { + // in Martini, the Coulomb switching distance is zero, so we will have + // sqrt(distSq) - rOnCoul = sqrt(distSq) + double dist = sqrt(distSq); + double rij_ronCoul_3 = dist * distSq; + double rij_ronCoul_4 = distSq * distSq; + + double coul = -(A1 / 3.0) * rij_ronCoul_3 - (B1 / 4.0) * rij_ronCoul_4 - C1; + return qi_qj_Fact * diElectric_1 * (1.0 / dist + coul); + } +} + inline double FF_SWITCH_MARTINI::CalcCoulombVir(const double distSq, - const double qi_qj, const uint b) const + const uint kind1, + const uint kind2, + const double qi_qj, + const double lambda, + const uint b) const { if(forcefield.rCutCoulombSq[b] < distSq) return 0.0; + if(lambda >= 0.999999) { + //save computation time + return CalcCoulombVir(distSq, qi_qj, b); + } + double vir = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + //We need to fix the return value from calcVir + vir = lambda * correction * correction * CalcCoulombVir(softRsq, qi_qj, b); + } else { + vir = lambda * CalcCoulombVir(distSq, qi_qj, b); + } + return vir; +} + +inline double FF_SWITCH_MARTINI::CalcCoulombVir(const double distSq, + const double qi_qj, + const uint b) const +{ if(forcefield.ewald) { double dist = sqrt(distSq); double constValue = 2.0 * forcefield.alpha[b] / sqrt(M_PI); double expConstValue = exp(-1.0 * forcefield.alphaSq[b] * distSq); double temp = erfc(forcefield.alpha[b] * dist); - return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; } else { // in Martini, the Coulomb switching distance is zero, so we will have // sqrt(distSq) - rOnCoul = sqrt(distSq) double dist = sqrt(distSq); double rij_ronCoul_2 = distSq; double rij_ronCoul_3 = dist * distSq; - double rij_ronCoul_4 = distSq * distSq; double virCoul = A1 / rij_ronCoul_2 + B1 / rij_ronCoul_3; return qi_qj * diElectric_1 * ( 1.0 / (dist * distSq) + virCoul / dist); } } +inline double FF_SWITCH_MARTINI::CalcdEndL(const double distSq, + const uint kind1, + const uint kind2, + const double lambda) const +{ + if(forcefield.rCutSq < distSq) + return 0.0; + + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + double dhdl = CalcEn(softRsq, index) + fCoef * CalcVir(softRsq, index); + return dhdl; +} + +//Calculate the dE/dlambda for Coulomb energy +inline double FF_SWITCH_MARTINI::CalcCoulombdEndL(const double distSq, + const uint kind1, + const uint kind2, + const double qi_qj_Fact, + const double lambda, + uint b) const +{ + if(forcefield.rCutCoulombSq[b] < distSq) + return 0.0; + + double dhdl = 0.0; + if(forcefield.sc_coul) { + uint index = FlatIndex(kind1, kind2); + double sigma6 = sigmaSq[index] * sigmaSq[index] * sigmaSq[index]; + sigma6 = std::max(sigma6, forcefield.sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = forcefield.sc_alpha * pow((1.0 - lambda), forcefield.sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double fCoef = lambda * forcefield.sc_alpha * forcefield.sc_power / 6.0; + fCoef *= pow(1.0 - lambda, forcefield.sc_power - 1) * sigma6 / (softRsq * softRsq); + dhdl = CalcCoulomb(softRsq, qi_qj_Fact, b) + + fCoef * CalcCoulombVir(softRsq, qi_qj_Fact, b); + } else { + dhdl = CalcCoulomb(distSq, qi_qj_Fact, b); + } + return dhdl; +} #endif /*FF_SWITCH_MARTINI_H*/ diff --git a/src/FixedWidthReader.h b/src/FixedWidthReader.h index 7d8a8b635..13af36dda 100644 --- a/src/FixedWidthReader.h +++ b/src/FixedWidthReader.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -21,6 +21,11 @@ class FixedWidthReader : public Reader const bool crit = true, const bool note = true): Reader(name, alias, false, NULL, false, NULL, crit, note), line("") {} + // this default constructor was defined for PDBSetup class to be able to + // create objects without initializing right away. + FixedWidthReader(): + Reader("", "", false, NULL, false, NULL, true, true), line("") {} + //Functions to get values from file, using fields. FixedWidthReader & Get(double & d, ConstField const& field) { @@ -64,14 +69,17 @@ class FixedWidthReader : public Reader if (GoodFileWData()) { std::getline(file, line); str = Str(field); -#ifndef NDEBUG - //big ol' waste of lines - //std::cout << line << std::endl; -#endif } return GoodFileWData(); } + void SetData(std::string const& name, std::string const& alias) + { + fileName = name; + fileAlias = alias; + nameWAlias = fileAlias + ": \t" + fileName; + } + protected: std::string Str(ConstField const& field) { diff --git a/src/Forcefield.cpp b/src/Forcefield.cpp index 55a7c8030..0b47a7c2a 100644 --- a/src/Forcefield.cpp +++ b/src/Forcefield.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -10,6 +10,7 @@ along with this program, also can be found at . #include "FFShift.h" #include "FFSwitch.h" #include "FFSwitchMartini.h" +#include "FFExp6.h" #ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES #endif @@ -56,6 +57,7 @@ void Forcefield::InitBasicVals(config_setup::SystemVals const& val, vdwKind = val.ff.VDW_KIND; exckind = val.exclude.EXCLUDE_KIND; + freeEnergy = val.freeEn.enable; electrostatic = val.elect.enable; ewald = val.elect.ewald; @@ -63,6 +65,24 @@ void Forcefield::InitBasicVals(config_setup::SystemVals const& val, rswitch = val.ff.rswitch; dielectric = val.elect.dielectric; + if(val.freeEn.enable) { + sc_alpha = val.freeEn.scaleAlpha; + sc_sigma = val.freeEn.scaleSigma; + sc_power = val.freeEn.scalePower; + sc_coul = val.freeEn.scaleCoulomb; + } else if (val.cfcmcVal.enable) { + sc_alpha = val.cfcmcVal.scaleAlpha; + sc_sigma = val.cfcmcVal.scaleSigma; + sc_power = val.cfcmcVal.scalePower; + sc_coul = val.cfcmcVal.scaleCoulomb; + } else { + sc_alpha = 0.0; + sc_sigma = 0.0; + sc_power = 0; + sc_coul = false; + } + sc_sigma_6 = pow(sc_sigma, 6); + for(uint b = 0 ; b < BOX_TOTAL; b++) { rCutCoulomb[b] = val.elect.cutoffCoulomb[b]; rCutCoulombSq[b] = rCutCoulomb[b] * rCutCoulomb[b]; @@ -74,6 +94,7 @@ void Forcefield::InitBasicVals(config_setup::SystemVals const& val, vdwGeometricSigma = val.ff.vdwGeometricSigma; isMartini = ffKind.isMARTINI; + exp6 = (vdwKind == val.ff.VDW_EXP6_KIND); #if ENSEMBLE == GCMC isFugacity = val.chemPot.isFugacity; @@ -81,12 +102,19 @@ void Forcefield::InitBasicVals(config_setup::SystemVals const& val, if(vdwKind == val.ff.VDW_STD_KIND) particles = new FFParticle(*this); + else if(vdwKind == val.ff.VDW_EXP6_KIND) + particles = new FF_EXP6(*this); else if(vdwKind == val.ff.VDW_SHIFT_KIND) particles = new FF_SHIFT(*this); else if (vdwKind == val.ff.VDW_SWITCH_KIND && ffKind.isMARTINI) particles = new FF_SWITCH_MARTINI(*this); - else + else if (vdwKind == val.ff.VDW_SWITCH_KIND && !ffKind.isMARTINI) particles = new FF_SWITCH(*this); + else { + std::cout << "Undefined Potential Type detected!\n" << "Exiting!\n"; + exit(EXIT_FAILURE); + } + if(ffKind.isMARTINI) angles = new FFAngleMartini(); diff --git a/src/Forcefield.h b/src/Forcefield.h index 3564a093a..918aadc6b 100644 --- a/src/Forcefield.h +++ b/src/Forcefield.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -23,7 +23,7 @@ struct SystemVals; class FFSetup; class Setup; class FFPrintout; -class FFParticle; +struct FFParticle; class Forcefield { @@ -56,13 +56,18 @@ class Forcefield double rswitch; //Switch distance double dielectric; //dielectric for martini double scaling_14; //!. +********************************************************************************/ +#include "FreeEnergyOutput.h" +#include "PDBConst.h" +#include "OutConst.h" +#include "ConfigSetup.h" +#include + +#include + +FreeEnergyOutput::FreeEnergyOutput(OutputVars & v, System & sys) : + calcEn(sys.calcEnergy), freeEnVal(sys.statV.freeEnVal), + lambdaRef(sys.lambdaRef) +{ + this->var = &v; + for (uint b = 0; b < BOXES_WITH_U_NB; b++) { + energyDiff[b] = NULL; + } +#if ENSEMBLE == NPT + //unit is K * molecule / A3 + imposedP = sys.statV.pressure; +#endif +} + +void FreeEnergyOutput::Init(pdb_setup::Atoms const& atoms, + config_setup::Output const& output) +{ + stepsPerSample = freeEnVal.frequency; + stepsPerOut = freeEnVal.frequency; + enableOut = freeEnVal.enable; + lambdaSize = freeEnVal.lambdaVDW.size(); + iState = freeEnVal.iState; + + if(enableOut) { + for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { + std::stringstream sstrm; + std::string strKind, fileName; + sstrm << (b); + sstrm >> strKind; + fileName = "Free_Energy_BOX_"; + fileName += strKind; + fileName += "_"; + fileName += uniqueName; + fileName += ".dat"; +#if GOMC_LIB_MPI + name[b] = pathToReplicaDirectory + fileName; +#else + name[b] = fileName; +#endif + outF[b].open(name[b].c_str(), std::ofstream::out); + energyDiff[b] = new Energy[lambdaSize]; + } + WriteHeader(); + } +} + +std::string FreeEnergyOutput::GetString(double a, uint p) +{ + std::stringstream sstrm; + std::string tempStr; + sstrm << std::fixed << std::setprecision(p) << (a); + //sstrm.precision(p); + //sstrm >> tempStr; + tempStr = sstrm.str(); + return tempStr; +} + +FreeEnergyOutput::~FreeEnergyOutput() +{ + for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { + if(outF[b].is_open()) { + outF[b].close(); + } + + if (energyDiff[b] != NULL) { + delete[] energyDiff[b]; + } + } +} + +void FreeEnergyOutput::Sample(const ulong step) +{ + if ((step + 1) % stepsPerSample == 0) { + for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { + CalculateFreeEnergy(b); + } + } +} + +void FreeEnergyOutput::DoOutput(const ulong step) +{ + //Write to histogram file, We dont check the equilibrium. + if ((step + 1) % stepsPerOut == 0) { + for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { + if (outF[b].is_open()) { + PrintData(b, step + 1); + } else { + std::cerr << "Unable to write to file \"" << name[b] << "\" " + << "(Free Energy file)" << std::endl; + outF[b].close(); + } + } + } +} + +void FreeEnergyOutput::PrintData(const uint b, const uint step) +{ + outF[b] << std::setw(11) << std::left << step << " "; + outF[b] << std::setw(25) << std::right << std::fixed << Etotal << " "; + outF[b] << std::setw(25) << dUdL_Coulomb[b].Total() << " "; + outF[b] << std::setw(25) << dUdL_VDW[b].Total() << " "; + + for(uint i = 0; i < lambdaSize; i++) { + outF[b] << std::setw(25) << energyDiff[b][i].Total() << " "; + } +#if ENSEMBLE == NVT + if(var->pressureCalc) { + outF[b] << std::setw(25) << PV; + } +#elif ENSEMBLE == NPT + outF[b] << std::setw(25) << PV; +#endif + outF[b] << std::endl; +} + +void FreeEnergyOutput::WriteHeader(void) +{ + for (uint b = 0; b < BOXES_WITH_U_NB; ++b) { + if (outF[b].is_open()) { + std::string toPrint = ""; + toPrint += "#T = "; + toPrint += GetString(var->T_in_K, 4); + toPrint += "(K), Lambda State "; + toPrint += GetString(iState, 0); + toPrint += ": (lambda Coulomb, lambda VDW) = ("; + toPrint += GetString(freeEnVal.lambdaCoulomb[iState], 4); + toPrint += ","; + toPrint += GetString(freeEnVal.lambdaVDW[iState], 4); + toPrint += ")\n"; + outF[b] << toPrint; + + //We care about format + outF[b] << std::setw(11) << std::left << "#Steps" << " "; + outF[b] << std::setw(25) << std::right << "Total_En(kJ/mol)" << " "; + toPrint = "dU/dL(Coulomb="; + toPrint += GetString(freeEnVal.lambdaCoulomb[iState], 4); + toPrint += ")"; + outF[b] << std::setw(25) << std::right << toPrint << " "; + toPrint = "dU/dL(VDW="; + toPrint += GetString(freeEnVal.lambdaVDW[iState], 4); + toPrint += ")"; + outF[b] << std::setw(25) << std::right << toPrint << " "; + + std::string fixStr = "DelE(L->("; + for(uint i = 0; i < lambdaSize; i++) { + toPrint = fixStr; + toPrint += GetString(freeEnVal.lambdaCoulomb[i], 4); + toPrint += ","; + toPrint += GetString(freeEnVal.lambdaVDW[i], 4); + toPrint += "))"; + outF[b] << std::setw(25) << std::right << toPrint << " "; + } +#if ENSEMBLE == NVT + if(var->pressureCalc) { + outF[b] << std::setw(25) << std::right << "PV(kJ/mol)"; + } +#elif ENSEMBLE == NPT + outF[b] << std::setw(25) << std::right << "PV(kJ/mol)"; +#endif + outF[b] << std::endl; + outF[b] << std::setprecision(10); + outF[b].setf(std::ios_base::right, std::ios_base::adjustfield); + } else + std::cerr << "Unable to write to file \"" << name[b] << "\" " + << "(Free Energy file)" << std::endl; + } +} + +void FreeEnergyOutput::CalculateFreeEnergy(const uint b) +{ +#if ENSEMBLE == NVT + if(var->pressureCalc) { + PV = var->pressure[b] * var->volumeRef[b] * unit::BAR_TO_K_MOLECULE_PER_A3; + PV *= unit::K_TO_KJ_PER_MOL; + } +#elif ENSEMBLE == NPT + // no need to convert pressure (bar) to K * molecule /A3 + PV = imposedP * var->volumeRef[b] * unit::K_TO_KJ_PER_MOL; +#endif + Etotal = var->energyRef[b].Total() * unit::K_TO_KJ_PER_MOL; + uint molIndex = lambdaRef.GetMolIndex(b); + //Reset the energy value + dUdL_VDW[b].Zero(); + dUdL_Coulomb[b].Zero(); + for(uint i = 0; i < lambdaSize; i++) { + energyDiff[b][i].Zero(); + } + //Calculate delta E and dE/dLambda for all lambda states + calcEn.EnergyChange(energyDiff[b], dUdL_VDW[b], dUdL_Coulomb[b], + freeEnVal.lambdaVDW, freeEnVal.lambdaCoulomb, iState, + molIndex, b); + //Convert to kJ/mol + dUdL_VDW[b] *= unit::K_TO_KJ_PER_MOL; + dUdL_Coulomb[b] *= unit::K_TO_KJ_PER_MOL; + for(uint i = 0; i < lambdaSize; i++) { + energyDiff[b][i] *= unit::K_TO_KJ_PER_MOL; + } +} diff --git a/src/FreeEnergyOutput.h b/src/FreeEnergyOutput.h new file mode 100644 index 000000000..19f87b751 --- /dev/null +++ b/src/FreeEnergyOutput.h @@ -0,0 +1,61 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef FREEENERGY_OUTPUT_H +#define FREEENERGY_OUTPUT_H + +#include +#include + +#include "OutputAbstracts.h" +#include "OutputVars.h" +#include "../lib/BasicTypes.h" //For ulong, uint +#include "../lib/StrLib.h" +#include "../lib/Lambda.h" +#include "System.h" +#include "PDBSetup.h" //For atoms class. +#include "EnergyTypes.h" +#include "CalculateEnergy.h" +#include "UnitConst.h" //For unit conversion factors + +struct FreeEnergyOutput : OutputableBase { + + FreeEnergyOutput(OutputVars & v, System & sys); + + ~FreeEnergyOutput(); + + virtual void Sample(const ulong step); + + //No additional init. + virtual void Init(pdb_setup::Atoms const& atoms, + config_setup::Output const& output); + + virtual void DoOutput(const ulong step); + +private: + void PrintData(const uint b, const uint step); + void CalculateFreeEnergy(const uint b); + void WriteHeader(void); + std::string GetString(double a, uint p); + + uint stepsPerSample; + const CalculateEnergy& calcEn; + const config_setup::FreeEnergy& freeEnVal; + const Lambda& lambdaRef; + Energy dUdL_VDW[BOXES_WITH_U_NB], dUdL_Coulomb[BOXES_WITH_U_NB]; + Energy *energyDiff[BOXES_WITH_U_NB]; + double PV; // Pressure * Volume + double Etotal; //Total Energy + uint lambdaSize, iState; + + std::ofstream outF[BOXES_WITH_U_NB]; + std::string name[BOXES_WITH_U_NB]; +#if ENSEMBLE == NPT + double imposedP; //imposed pressure in NPT +#endif +}; + +#endif /*HIST_OUTPUT_H*/ diff --git a/src/FxdWidthWrtr.h b/src/FxdWidthWrtr.h index 05492b08f..04780834e 100644 --- a/src/FxdWidthWrtr.h +++ b/src/FxdWidthWrtr.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/GPU/CalculateEnergyCUDAKernel.cu b/src/GPU/CalculateEnergyCUDAKernel.cu index 7e84226be..581619a86 100644 --- a/src/GPU/CalculateEnergyCUDAKernel.cu +++ b/src/GPU/CalculateEnergyCUDAKernel.cu @@ -1,16 +1,17 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . ********************************************************************************/ #ifdef GOMC_CUDA -#include "CalculateEnergyCUDAKernel.cuh" #include -#include "ConstantDefinitionsCUDAKernel.cuh" -#include "CalculateMinImageCUDAKernel.cuh" #include "cub/cub.cuh" #include +#include "ConstantDefinitionsCUDAKernel.cuh" +#include "CalculateMinImageCUDAKernel.cuh" +#include "CalculateForceCUDAKernel.cuh" +#include "CalculateEnergyCUDAKernel.cuh" using namespace cub; @@ -22,47 +23,63 @@ void CallBoxInterGPU(VariablesCUDA *vars, bool electrostatic, vector particleCharge, vector particleKind, + vector particleMol, double &REn, double &LJEn, + double *lambdaVDW, + double *lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, uint const box) { int atomNumber = coords.Count(); - int *gpu_pair1, *gpu_pair2, *gpu_particleKind; + int *gpu_pair1, *gpu_pair2, *gpu_particleKind, *gpu_particleMol; int blocksPerGrid, threadsPerBlock; double *gpu_particleCharge; double *gpu_REn, *gpu_LJEn; double *gpu_final_REn, *gpu_final_LJEn; double cpu_final_REn, cpu_final_LJEn; - - cudaMalloc((void**) &gpu_pair1, pair1.size() * sizeof(int)); - cudaMalloc((void**) &gpu_pair2, pair2.size() * sizeof(int)); - cudaMalloc((void**) &gpu_particleCharge, - particleCharge.size() * sizeof(double)); - cudaMalloc((void**) &gpu_particleKind, - particleKind.size() * sizeof(int)); - cudaMalloc((void**) &gpu_REn, pair1.size() * sizeof(double)); - cudaMalloc((void**) &gpu_LJEn, pair1.size() * sizeof(double)); - cudaMalloc((void**) &gpu_final_REn, sizeof(double)); - cudaMalloc((void**) &gpu_final_LJEn, sizeof(double)); - + double *gpu_lambdaVDW, *gpu_lambdaCoulomb; + + gpuErrchk(cudaMalloc((void**) &gpu_pair1, pair1.size() * sizeof(int))); + gpuErrchk(cudaMalloc((void**) &gpu_pair2, pair2.size() * sizeof(int))); + gpuErrchk(cudaMalloc((void**) &gpu_particleCharge, + particleCharge.size() * sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_particleKind, particleKind.size() * sizeof(int))); + gpuErrchk(cudaMalloc((void**) &gpu_particleMol, particleMol.size() * sizeof(int))); + gpuErrchk(cudaMalloc((void**) &gpu_REn, pair1.size() * sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_LJEn, pair1.size() * sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_final_REn, sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_final_LJEn, sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_lambdaVDW, pair1.size() * sizeof(double))); + gpuErrchk(cudaMalloc((void**) &gpu_lambdaCoulomb, pair1.size() * sizeof(double))); // Copy necessary data to GPU - cudaMemcpy(gpu_pair1, &pair1[0], pair1.size() * sizeof(int), - cudaMemcpyHostToDevice); - cudaMemcpy(gpu_pair2, &pair2[0], pair2.size() * sizeof(int), - cudaMemcpyHostToDevice); - cudaMemcpy(gpu_particleCharge, &particleCharge[0], - particleCharge.size() * sizeof(double), - cudaMemcpyHostToDevice); - cudaMemcpy(gpu_particleKind, &particleKind[0], - particleKind.size() * sizeof(int), - cudaMemcpyHostToDevice); - cudaMemcpy(vars->gpu_x, coords.x, atomNumber * sizeof(double), - cudaMemcpyHostToDevice); - cudaMemcpy(vars->gpu_y, coords.y, atomNumber * sizeof(double), - cudaMemcpyHostToDevice); - cudaMemcpy(vars->gpu_z, coords.z, atomNumber * sizeof(double), - cudaMemcpyHostToDevice); + gpuErrchk(cudaMemcpy(gpu_pair1, &pair1[0], pair1.size() * sizeof(int), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_pair2, &pair2[0], pair2.size() * sizeof(int), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_particleCharge, &particleCharge[0], + particleCharge.size() * sizeof(double), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_particleKind, &particleKind[0], + particleKind.size() * sizeof(int), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_particleMol, &particleMol[0], particleMol.size() * sizeof(int), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(vars->gpu_x, coords.x, atomNumber * sizeof(double), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(vars->gpu_y, coords.y, atomNumber * sizeof(double), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(vars->gpu_z, coords.z, atomNumber * sizeof(double), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_lambdaVDW, lambdaVDW, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice)); + gpuErrchk(cudaMemcpy(gpu_lambdaCoulomb, lambdaCoulomb, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice)); + checkLastErrorCUDA(__FILE__, __LINE__); // Run the kernel... threadsPerBlock = 256; @@ -78,6 +95,7 @@ void CallBoxInterGPU(VariablesCUDA *vars, electrostatic, gpu_particleCharge, gpu_particleKind, + gpu_particleMol, gpu_REn, gpu_LJEn, pair1.size(), @@ -101,7 +119,17 @@ void CallBoxInterGPU(VariablesCUDA *vars, vars->gpu_Invcell_x[box], vars->gpu_Invcell_y[box], vars->gpu_Invcell_z[box], + gpu_lambdaVDW, + gpu_lambdaCoulomb, + sc_coul, + sc_sigma_6, + sc_alpha, + sc_power, + vars->gpu_rMin, + vars->gpu_rMaxSq, + vars->gpu_expConst, box); + checkLastErrorCUDA(__FILE__, __LINE__); // ReduceSum void * d_temp_storage = NULL; @@ -130,14 +158,19 @@ void CallBoxInterGPU(VariablesCUDA *vars, REn = cpu_final_REn; LJEn = cpu_final_LJEn; + cudaDeviceSynchronize(); + cudaFree(gpu_pair1); cudaFree(gpu_pair2); cudaFree(gpu_particleCharge); cudaFree(gpu_particleKind); + cudaFree(gpu_particleMol); cudaFree(gpu_REn); cudaFree(gpu_LJEn); cudaFree(gpu_final_REn); cudaFree(gpu_final_LJEn); + cudaFree(gpu_lambdaVDW); + cudaFree(gpu_lambdaCoulomb); } __global__ void BoxInterGPU(int *gpu_pair1, @@ -151,6 +184,7 @@ __global__ void BoxInterGPU(int *gpu_pair1, bool electrostatic, double *gpu_particleCharge, int *gpu_particleKind, + int *gpu_particleMol, double *gpu_REn, double *gpu_LJEn, int pairSize, @@ -174,6 +208,15 @@ __global__ void BoxInterGPU(int *gpu_pair1, double *gpu_Invcell_x, double *gpu_Invcell_y, double *gpu_Invcell_z, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, int box) { int threadID = blockIdx.x * blockDim.x + threadIdx.x; @@ -182,63 +225,95 @@ __global__ void BoxInterGPU(int *gpu_pair1, double distSq; double qi_qj_fact; double qqFact = 167000.0; + double virX = 0.0, virY = 0.0, virZ = 0.0; gpu_REn[threadID] = 0.0; gpu_LJEn[threadID] = 0.0; double cutoff = fmax(gpu_rCut[0], gpu_rCutCoulomb[box]); - if(InRcutGPU(distSq, gpu_x[gpu_pair1[threadID]], gpu_y[gpu_pair1[threadID]], - gpu_z[gpu_pair1[threadID]], gpu_x[gpu_pair2[threadID]], - gpu_y[gpu_pair2[threadID]], gpu_z[gpu_pair2[threadID]], - xAxes, yAxes, zAxes, xAxes / 2.0, yAxes / 2.0, zAxes / 2.0, - cutoff, gpu_nonOrth[0], gpu_cell_x, gpu_cell_y, - gpu_cell_z, gpu_Invcell_x, gpu_Invcell_y, gpu_Invcell_z)) { + if(InRcutGPU(distSq, virX, virY, virZ, gpu_x[gpu_pair1[threadID]], + gpu_y[gpu_pair1[threadID]], gpu_z[gpu_pair1[threadID]], + gpu_x[gpu_pair2[threadID]], gpu_y[gpu_pair2[threadID]], + gpu_z[gpu_pair2[threadID]], xAxes, yAxes, zAxes, xAxes / 2.0, + yAxes / 2.0, zAxes / 2.0, cutoff, gpu_nonOrth[0], gpu_cell_x, + gpu_cell_y, gpu_cell_z, gpu_Invcell_x, gpu_Invcell_y, + gpu_Invcell_z)) { if(electrostatic) { qi_qj_fact = gpu_particleCharge[gpu_pair1[threadID]] * gpu_particleCharge[gpu_pair2[threadID]] * qqFact; - gpu_REn[threadID] = CalcCoulombGPU(distSq, qi_qj_fact, gpu_rCutLow[0], + gpu_REn[threadID] = CalcCoulombGPU(distSq, + gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]], + qi_qj_fact, gpu_rCutLow[0], gpu_ewald[0], gpu_VDW_Kind[0], gpu_alpha[box], gpu_rCutCoulomb[box], gpu_isMartini[0], - gpu_diElectric_1[0]); + gpu_diElectric_1[0], + gpu_lambdaCoulomb[threadID], + sc_coul, + sc_sigma_6, + sc_alpha, + sc_power, + gpu_sigmaSq[threadID], + gpu_count[0]); } gpu_LJEn[threadID] = CalcEnGPU(distSq, gpu_particleKind[gpu_pair1[threadID]], gpu_particleKind[gpu_pair2[threadID]], gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, gpu_VDW_Kind[0], gpu_isMartini[0], - gpu_rCut[0], gpu_rOn[0], gpu_count[0]); + gpu_rCut[0], gpu_rOn[0], gpu_count[0], + gpu_lambdaVDW[threadID], + sc_sigma_6, sc_alpha, sc_power, gpu_rMin, + gpu_rMaxSq, gpu_expConst); } } -__device__ double CalcCoulombGPU(double distSq, double qi_qj_fact, - double gpu_rCutLow, int gpu_ewald, - int gpu_VDW_Kind, double gpu_alpha, - double gpu_rCutCoulomb, int gpu_isMartini, - double gpu_diElectric_1) +__device__ double CalcCoulombGPU(double distSq, int kind1, int kind2, + double qi_qj_fact, double gpu_rCutLow, + int gpu_ewald, int gpu_VDW_Kind, + double gpu_alpha, double gpu_rCutCoulomb, + int gpu_isMartini, double gpu_diElectric_1, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq, + int gpu_count) { if((gpu_rCutCoulomb * gpu_rCutCoulomb) < distSq) { return 0.0; } + int index = FlatIndexGPU(kind1, kind2, gpu_count); if(gpu_VDW_Kind == GPU_VDW_STD_KIND) { - return CalcCoulombParticleGPU(distSq, qi_qj_fact, gpu_alpha); + return CalcCoulombParticleGPU(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, + gpu_lambdaCoulomb, sc_coul, sc_sigma_6, + sc_alpha, sc_power, gpu_sigmaSq); } else if(gpu_VDW_Kind == GPU_VDW_SHIFT_KIND) { return CalcCoulombShiftGPU(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, - gpu_rCutCoulomb); + gpu_rCutCoulomb, gpu_lambdaCoulomb, sc_coul, + sc_sigma_6, sc_alpha, sc_power, gpu_sigmaSq); + } else if(gpu_VDW_Kind == GPU_VDW_EXP6_KIND) { + return CalcCoulombExp6GPU(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, + gpu_lambdaCoulomb, sc_coul, sc_sigma_6, sc_alpha, + sc_power, gpu_sigmaSq); } else if(gpu_VDW_Kind == GPU_VDW_SWITCH_KIND && gpu_isMartini) { - return CalcCoulombSwitchMartiniGPU(distSq, qi_qj_fact, gpu_ewald, - gpu_alpha, gpu_rCutCoulomb, - gpu_diElectric_1); + return CalcCoulombSwitchMartiniGPU(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, + gpu_rCutCoulomb, gpu_diElectric_1, + gpu_lambdaCoulomb, sc_coul, sc_sigma_6, + sc_alpha, sc_power, gpu_sigmaSq); } else return CalcCoulombSwitchGPU(distSq, qi_qj_fact, gpu_alpha, gpu_ewald, - gpu_rCutCoulomb); + gpu_rCutCoulomb, gpu_lambdaCoulomb, sc_coul, + sc_sigma_6, sc_alpha, sc_power, gpu_sigmaSq); } __device__ double CalcEnGPU(double distSq, int kind1, int kind2, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, int gpu_VDW_Kind, int gpu_isMartini, double gpu_rCut, double gpu_rOn, - int gpu_count) + int gpu_count, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, uint sc_power, + double *gpu_rMin, double *gpu_rMaxSq, + double *gpu_expConst) { if((gpu_rCut * gpu_rCut) < distSq) { return 0.0; @@ -246,31 +321,100 @@ __device__ double CalcEnGPU(double distSq, int kind1, int kind2, int index = FlatIndexGPU(kind1, kind2, gpu_count); if(gpu_VDW_Kind == GPU_VDW_STD_KIND) { - return CalcEnParticleGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); + return CalcEnParticleGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, + gpu_lambdaVDW, sc_sigma_6, sc_alpha, sc_power); } else if(gpu_VDW_Kind == GPU_VDW_SHIFT_KIND) { return CalcEnShiftGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, - gpu_rCut); + gpu_rCut, gpu_lambdaVDW, sc_sigma_6, sc_alpha, + sc_power); + } else if(gpu_VDW_Kind == GPU_VDW_EXP6_KIND) { + return CalcEnExp6GPU(distSq, index, gpu_sigmaSq[index], gpu_n[index], + gpu_lambdaVDW, sc_sigma_6, + sc_alpha, sc_power, gpu_rMin[index], + gpu_rMaxSq[index], gpu_expConst[index]); } else if(gpu_VDW_Kind == GPU_VDW_SWITCH_KIND && gpu_isMartini) { return CalcEnSwitchMartiniGPU(distSq, index, gpu_sigmaSq, gpu_n, - gpu_epsilon_Cn, gpu_rCut, gpu_rOn); + gpu_epsilon_Cn, gpu_rCut, gpu_rOn, + gpu_lambdaVDW, sc_sigma_6, sc_alpha, + sc_power); } else return CalcEnSwitchGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, - gpu_rCut, gpu_rOn); + gpu_rCut, gpu_rOn, gpu_lambdaVDW, sc_sigma_6, + sc_alpha, sc_power); } //ElectroStatic Calculation //**************************************************************// __device__ double CalcCoulombParticleGPU(double distSq, double qi_qj_fact, + double gpu_ewald, double gpu_alpha, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombParticleGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha); + } + if(sc_coul) { + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 * dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + return gpu_lambdaCoulomb * CalcCoulombParticleGPUNoLambda(softRsq, qi_qj_fact, gpu_ewald, gpu_alpha); + } else { + return gpu_lambdaCoulomb * CalcCoulombParticleGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha); + } +} + +__device__ double CalcCoulombParticleGPUNoLambda(double distSq, + double qi_qj_fact, + double gpu_ewald, double gpu_alpha) { - double dist = sqrt(distSq); - double value = gpu_alpha * dist; - return qi_qj_fact * (1 - erf(value)) / dist; + if(gpu_ewald) { + double dist = sqrt(distSq); + double value = gpu_alpha * dist; + return qi_qj_fact * erfc(value) / dist; + } else { + double dist = sqrt(distSq); + return qi_qj_fact / dist; + } } __device__ double CalcCoulombShiftGPU(double distSq, double qi_qj_fact, int gpu_ewald, double gpu_alpha, - double gpu_rCut) + double gpu_rCut, double gpu_lambdaCoulomb, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_sigmaSq) +{ + + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombShiftGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, + gpu_rCut); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 * dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + return gpu_lambdaCoulomb * CalcCoulombShiftGPUNoLambda(softRsq, qi_qj_fact, + gpu_ewald, gpu_alpha, + gpu_rCut); + } else { + return gpu_lambdaCoulomb * CalcCoulombShiftGPUNoLambda(distSq, qi_qj_fact, + gpu_ewald, gpu_alpha, + gpu_rCut); + } +} + +__device__ double CalcCoulombShiftGPUNoLambda(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha, + double gpu_rCut) { if(gpu_ewald) { double dist = sqrt(distSq); @@ -282,9 +426,76 @@ __device__ double CalcCoulombShiftGPU(double distSq, double qi_qj_fact, } } +__device__ double CalcCoulombExp6GPU(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombExp6GPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), + (double)sc_power); + double softDist6 = lambdaCoef * sigma6 * dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + return gpu_lambdaCoulomb * CalcCoulombExp6GPUNoLambda(softRsq, qi_qj_fact, + gpu_ewald, gpu_alpha); + } else { + return gpu_lambdaCoulomb * CalcCoulombExp6GPUNoLambda(distSq, qi_qj_fact, + gpu_ewald, gpu_alpha); + } +} + +__device__ double CalcCoulombExp6GPUNoLambda(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha) +{ + if(gpu_ewald) { + double dist = sqrt(distSq); + double val = gpu_alpha * dist; + return qi_qj_fact * erfc(val) / dist; + } else { + double dist = sqrt(distSq); + return qi_qj_fact / dist; + } +} + __device__ double CalcCoulombSwitchMartiniGPU(double distSq, double qi_qj_fact, int gpu_ewald, double gpu_alpha, double gpu_rCut, + double gpu_diElectric_1, + double gpu_lambdaCoulomb, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_sigmaSq) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombSwitchMartiniGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut, gpu_diElectric_1); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 * dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + return gpu_lambdaCoulomb * CalcCoulombSwitchMartiniGPUNoLambda(softRsq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut, gpu_diElectric_1); + } else { + return gpu_lambdaCoulomb * CalcCoulombSwitchMartiniGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut, gpu_diElectric_1); + } +} + +__device__ double CalcCoulombSwitchMartiniGPUNoLambda(double distSq, + double qi_qj_fact, + int gpu_ewald, + double gpu_alpha, + double gpu_rCut, double gpu_diElectric_1) { if(gpu_ewald) { @@ -310,10 +521,33 @@ __device__ double CalcCoulombSwitchMartiniGPU(double distSq, double qi_qj_fact, } } - __device__ double CalcCoulombSwitchGPU(double distSq, double qi_qj_fact, double gpu_alpha, int gpu_ewald, - double gpu_rCut) + double gpu_rCut, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombSwitchGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 * dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + return gpu_lambdaCoulomb * CalcCoulombSwitchGPUNoLambda(softRsq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut); + } else { + return gpu_lambdaCoulomb * CalcCoulombSwitchGPUNoLambda(distSq, qi_qj_fact, gpu_ewald, gpu_alpha, gpu_rCut); + } +} + +__device__ double CalcCoulombSwitchGPUNoLambda(double distSq, double qi_qj_fact, + double gpu_alpha, int gpu_ewald, + double gpu_rCut) { if(gpu_ewald) { double dist = sqrt(distSq); @@ -332,7 +566,30 @@ __device__ double CalcCoulombSwitchGPU(double distSq, double qi_qj_fact, //**************************************************************// __device__ double CalcEnParticleGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, - double *gpu_epsilon_Cn) + double *gpu_epsilon_Cn, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcEnParticleGPUNoLambda(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + + return gpu_lambdaVDW * CalcEnParticleGPUNoLambda(softRsq, index, gpu_sigmaSq, + gpu_n, gpu_epsilon_Cn); +} + +__device__ double CalcEnParticleGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn) { double rRat2 = gpu_sigmaSq[index] / distSq; double rRat4 = rRat2 * rRat2; @@ -343,7 +600,33 @@ __device__ double CalcEnParticleGPU(double distSq, int index, __device__ double CalcEnShiftGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut) + double gpu_rCut, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcEnShiftGPUNoLambda(distSq, index, gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn, gpu_rCut); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + + return gpu_lambdaVDW * CalcEnShiftGPUNoLambda(softRsq, index, gpu_sigmaSq, + gpu_n, gpu_epsilon_Cn, + gpu_rCut); +} + +__device__ double CalcEnShiftGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, + double *gpu_n, double *gpu_epsilon_Cn, + double gpu_rCut) { double rRat2 = gpu_sigmaSq[index] / distSq; double rRat4 = rRat2 * rRat2; @@ -359,10 +642,75 @@ __device__ double CalcEnShiftGPU(double distSq, int index, double *gpu_sigmaSq, return (gpu_epsilon_Cn[index] * (repulse - attract) - shiftConst); } +__device__ double CalcEnExp6GPU(double distSq, int index, double gpu_sigmaSq, + double gpu_n, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_rMin, + double gpu_rMaxSq, double gpu_expConst) +{ + if(distSq < gpu_rMaxSq) { + return num::BIGNUM; + } + if(gpu_lambdaVDW >= 0.999999) { + return CalcEnExp6GPUNoLambda(distSq, gpu_n, gpu_rMin, gpu_expConst); + } + double sigma6 = gpu_sigmaSq * gpu_sigmaSq * gpu_sigmaSq; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + + return gpu_lambdaVDW * CalcEnExp6GPUNoLambda(softRsq, gpu_n, gpu_rMin, + gpu_expConst); +} + +__device__ double CalcEnExp6GPUNoLambda(double distSq, double gpu_n, + double gpu_rMin, double gpu_expConst) +{ + double dist = sqrt(distSq); + double rRat = gpu_rMin / dist; + double rRat2 = rRat * rRat; + double attract = rRat2 * rRat2 * rRat2; + + uint alph_ij = gpu_n; + double repulse = (6.0 / alph_ij) * exp(alph_ij * (1.0 - dist / gpu_rMin)); + return gpu_expConst * (repulse - attract); +} + __device__ double CalcEnSwitchMartiniGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut, double gpu_rOn) + double gpu_rCut, double gpu_rOn, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcEnSwitchMartiniGPUNoLambda(distSq, index, gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn, gpu_rCut, gpu_rOn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + + return gpu_lambdaVDW * CalcEnSwitchMartiniGPUNoLambda(softRsq, index, + gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn, + gpu_rCut, gpu_rOn); +} + +__device__ double CalcEnSwitchMartiniGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, + double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, + double gpu_rOn) { double r_2 = 1.0 / distSq; double r_4 = r_2 * r_2; @@ -403,10 +751,32 @@ __device__ double CalcEnSwitchMartiniGPU(double distSq, int index, return Eij; } - __device__ double CalcEnSwitchGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut, double gpu_rOn) + double gpu_rCut, double gpu_rOn, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcEnSwitchGPUNoLambda(distSq, index, gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn, gpu_rCut, gpu_rOn); + } + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + + return gpu_lambdaVDW * CalcEnSwitchGPUNoLambda(softRsq, index, gpu_sigmaSq, + gpu_n, gpu_epsilon_Cn, + gpu_rCut, gpu_rOn); +} + +__device__ double CalcEnSwitchGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, double gpu_rOn) { double rCutSq = gpu_rCut * gpu_rCut; double rOnSq = gpu_rOn * gpu_rOn; diff --git a/src/GPU/CalculateEnergyCUDAKernel.cuh b/src/GPU/CalculateEnergyCUDAKernel.cuh index ef4e8249d..e417ba432 100644 --- a/src/GPU/CalculateEnergyCUDAKernel.cuh +++ b/src/GPU/CalculateEnergyCUDAKernel.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -23,8 +23,15 @@ void CallBoxInterGPU(VariablesCUDA *vars, bool electrostatic, vector particleCharge, vector particleKind, + vector particleMol, double &REn, - double &RLJEn, + double &LJEn, + double *lambdaVDW, + double *lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, uint const box); __global__ void BoxInterGPU(int *gpu_pair1, @@ -38,6 +45,7 @@ __global__ void BoxInterGPU(int *gpu_pair1, bool electrostatic, double *gpu_particleCharge, int *gpu_particleKind, + int *gpu_particleMol, double *gpu_REn, double *gpu_LJEn, int pairSize, @@ -61,49 +69,152 @@ __global__ void BoxInterGPU(int *gpu_pair1, double *gpu_Invcell_x, double *gpu_Invcell_y, double *gpu_Invcell_z, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, int box); -__device__ double CalcCoulombGPU(double distSq, double qi_qj_fact, - double gpu_rCutLow, int gpu_ewald, - int gpu_VDW_Kind, double gpu_alpha, - double gpu_rCutCoulomb, int gpu_isMartini, - double gpu_diElectric_1); +__device__ double CalcCoulombGPU(double distSq, int kind1, int kind2, + double qi_qj_fact, double gpu_rCutLow, + int gpu_ewald, int gpu_VDW_Kind, + double gpu_alpha, double gpu_rCutCoulomb, + int gpu_isMartini, double gpu_diElectric_1, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq, + int gpu_count); +__device__ double CalcCoulombVirGPU(double distSq, double qi_qj, + double gpu_rCutCoulomb, double gpu_alpha, + int gpu_VDW_Kind, int gpu_ewald, + double gpu_diElectric_1, int gpu_isMartini); __device__ double CalcEnGPU(double distSq, int kind1, int kind2, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, int gpu_VDW_Kind, int gpu_isMartini, double gpu_rCut, double gpu_rOn, - int gpu_count); + int gpu_count, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, uint sc_power, + double *gpu_rMin, double *gpu_rMaxSq, + double *gpu_expConst); //ElectroStatic Calculation //**************************************************************// __device__ double CalcCoulombParticleGPU(double distSq, double qi_qj_fact, + double gpu_ewald, double gpu_alpha, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq); +__device__ double CalcCoulombParticleGPUNoLambda(double distSq, + double qi_qj_fact, + double gpu_ewald, double gpu_alpha); __device__ double CalcCoulombShiftGPU(double distSq, double qi_qj_fact, int gpu_ewald, double gpu_alpha, - double gpu_rCut); + double gpu_rCut, double gpu_lambdaCoulomb, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_sigmaSq); +__device__ double CalcCoulombShiftGPUNoLambda(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha, + double gpu_rCut); +__device__ double CalcCoulombExp6GPU(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq); +__device__ double CalcCoulombExp6GPUNoLambda(double distSq, double qi_qj_fact, + int gpu_ewald, double gpu_alpha); __device__ double CalcCoulombSwitchMartiniGPU(double distSq, double qi_qj_fact, int gpu_ewald, double gpu_alpha, double gpu_rCut, + double gpu_diElectric_1, + double gpu_lambdaCoulomb, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_sigmaSq); +__device__ double CalcCoulombSwitchMartiniGPUNoLambda(double distSq, + double qi_qj_fact, + int gpu_ewald, + double gpu_alpha, + double gpu_rCut, double gpu_diElectric_1); __device__ double CalcCoulombSwitchGPU(double distSq, double qi_qj_fact, double gpu_alpha, int gpu_ewald, - double gpu_rCut); + double gpu_rCut, + double gpu_lambdaCoulomb, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_sigmaSq); +__device__ double CalcCoulombSwitchGPUNoLambda(double distSq, double qi_qj_fact, + double gpu_alpha, int gpu_ewald, + double gpu_rCut); + +__device__ double CalcCoulombVirParticleGPU(double distSq, double qi_qj, + double gpu_ewald, double gpu_alpha); +__device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, double qi_qj, + double gpu_ewald, + double gpu_alpha, + double gpu_rCut, + double gpu_diElectric_1); +__device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, + double gpu_ewald, double gpu_alpha, + double gpu_rCut); //VDW Calculation //*****************************************************************// __device__ double CalcEnParticleGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, - double *gpu_epsilon_Cn); + double *gpu_epsilon_Cn, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power); +__device__ double CalcEnParticleGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn); __device__ double CalcEnShiftGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut); + double gpu_rCut, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power); +__device__ double CalcEnShiftGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, + double *gpu_n, double *gpu_epsilon_Cn, + double gpu_rCut); +__device__ double CalcEnExp6GPU(double distSq, int index, double gpu_sigmaSq, + double gpu_n, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power, double gpu_rMin, + double gpu_rMaxSq, double gpu_expConst); +__device__ double CalcEnExp6GPUNoLambda(double distSq, double gpu_n, + double gpu_rMin, double gpu_expConst); __device__ double CalcEnSwitchMartiniGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut, double gpu_rOn); + double gpu_rCut, double gpu_rOn, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power); +__device__ double CalcEnSwitchMartiniGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, + double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, + double gpu_rOn); __device__ double CalcEnSwitchGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, - double gpu_rCut, double gpu_rOn); + double gpu_rCut, double gpu_rOn, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power); +__device__ double CalcEnSwitchGPUNoLambda(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, double gpu_rOn); #endif /*GOMC_CUDA*/ diff --git a/src/GPU/CalculateEwaldCUDAKernel.cu b/src/GPU/CalculateEwaldCUDAKernel.cu index 436e44e68..1daf9cd0f 100644 --- a/src/GPU/CalculateEwaldCUDAKernel.cu +++ b/src/GPU/CalculateEwaldCUDAKernel.cu @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -77,12 +77,14 @@ void CallBoxReciprocalSetupGPU(VariablesCUDA *vars, vars->gpu_sumRnew[box], vars->gpu_sumInew[box], imageSize); + checkLastErrorCUDA(__FILE__, __LINE__); BoxReciprocalGPU <<< blocksPerGrid, threadsPerBlock>>>(vars->gpu_prefact[box], vars->gpu_sumRnew[box], vars->gpu_sumInew[box], gpu_energyRecip, imageSize); + checkLastErrorCUDA(__FILE__, __LINE__); #ifndef NDEBUG // In the future maybe we could remove this for Nondebug? @@ -167,6 +169,7 @@ void CallMolReciprocalGPU(VariablesCUDA *vars, vars->gpu_prefactRef[box], gpu_energyRecipNew, imageSize); + checkLastErrorCUDA(__FILE__, __LINE__); #ifndef NDEBUG cudaMemcpy(sumRnew, vars->gpu_sumRnew[box], imageSize * sizeof(double), cudaMemcpyDeviceToHost); @@ -241,7 +244,7 @@ void CallSwapReciprocalGPU(VariablesCUDA *vars, insert, gpu_energyRecipNew, imageSize); - + checkLastErrorCUDA(__FILE__, __LINE__); #ifndef NDEBUG // In the future maybe we could remove this for Nondebug? cudaMemcpy(sumRnew, vars->gpu_sumRnew[box], imageSize * sizeof(double), diff --git a/src/GPU/CalculateEwaldCUDAKernel.cuh b/src/GPU/CalculateEwaldCUDAKernel.cuh index 96b48a6fa..3fb9f3e92 100644 --- a/src/GPU/CalculateEwaldCUDAKernel.cuh +++ b/src/GPU/CalculateEwaldCUDAKernel.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/GPU/CalculateForceCUDAKernel.cu b/src/GPU/CalculateForceCUDAKernel.cu index bd1d01063..717ad6aa7 100644 --- a/src/GPU/CalculateForceCUDAKernel.cu +++ b/src/GPU/CalculateForceCUDAKernel.cu @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -8,6 +8,7 @@ along with this program, also can be found at . #include #include "CalculateForceCUDAKernel.cuh" +#include "CalculateEnergyCUDAKernel.cuh" #include "ConstantDefinitionsCUDAKernel.cuh" #include "CalculateMinImageCUDAKernel.cuh" #include "cub/cub.cuh" @@ -37,6 +38,12 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, double &vT22, double &vT23, double &vT33, + double *arr_lambdaVDW, + double *arr_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, uint const box) { int atomNumber = currentCoords.Count(); @@ -47,6 +54,7 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, int blocksPerGrid, threadsPerBlock; double *gpu_particleCharge; double *gpu_final_value; + double *gpu_lambdaVDW, *gpu_lambdaCoulomb; cudaMalloc((void**) &gpu_pair1, pair1.size() * sizeof(int)); cudaMalloc((void**) &gpu_pair2, pair2.size() * sizeof(int)); @@ -55,6 +63,8 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, cudaMalloc((void**) &gpu_particleKind, particleKind.size() * sizeof(int)); cudaMalloc((void**) &gpu_particleMol, particleMol.size() * sizeof(int)); cudaMalloc((void**) &gpu_final_value, sizeof(double)); + cudaMalloc((void**) &gpu_lambdaVDW, pair1.size() * sizeof(double)); + cudaMalloc((void**) &gpu_lambdaCoulomb, pair1.size() * sizeof(double)); cudaMemcpy(gpu_pair1, &pair1[0], pair1.size() * sizeof(int), cudaMemcpyHostToDevice); @@ -81,6 +91,10 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, cudaMemcpy(gpu_particleMol, &particleMol[0], particleMol.size() * sizeof(int), cudaMemcpyHostToDevice); + cudaMemcpy(gpu_lambdaVDW, arr_lambdaVDW, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_lambdaCoulomb, arr_lambdaCoulomb, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice); // Run the kernel... threadsPerBlock = 256; @@ -133,7 +147,17 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, vars->gpu_Invcell_y[box], vars->gpu_Invcell_z[box], vars->gpu_nonOrth, + gpu_lambdaVDW, + gpu_lambdaCoulomb, + sc_coul, + sc_sigma_6, + sc_alpha, + sc_power, + vars->gpu_rMin, + vars->gpu_rMaxSq, + vars->gpu_expConst, box); + checkLastErrorCUDA(__FILE__, __LINE__); cudaDeviceSynchronize(); // ReduceSum // Virial of LJ void *d_temp_storage = NULL; @@ -201,21 +225,225 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, cudaFree(gpu_particleMol); cudaFree(gpu_particleCharge); cudaFree(gpu_final_value); + cudaFree(gpu_lambdaVDW); + cudaFree(gpu_lambdaCoulomb); } -void CallForceReciprocalGPU(VariablesCUDA *vars, - XYZArray const ¤tCoords, - XYZArray const ¤tCOMDiff, - vector &particleCharge, - double &rT11, - double &rT12, - double &rT13, - double &rT22, - double &rT23, - double &rT33, - uint imageSize, - double constVal, - uint box) +void CallBoxForceGPU(VariablesCUDA *vars, + vector pair1, + vector pair2, + XYZArray const &coords, + BoxDimensions const &boxAxes, + bool electrostatic, + vector particleCharge, + vector particleKind, + vector particleMol, + double &REn, + double &LJEn, + double *aForcex, + double *aForcey, + double *aForcez, + double *mForcex, + double *mForcey, + double *mForcez, + int atomCount, + int molCount, + bool reset_force, + bool copy_back, + double *lambdaVDW, + double *lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + uint const box) +{ + int atomNumber = coords.Count(); + int *gpu_pair1, *gpu_pair2, *gpu_particleKind, *gpu_particleMol; + int blocksPerGrid, threadsPerBlock; + double *gpu_particleCharge; + double *gpu_REn, *gpu_LJEn; + double *gpu_final_REn, *gpu_final_LJEn; + double cpu_final_REn, cpu_final_LJEn; + double *gpu_lambdaVDW, *gpu_lambdaCoulomb; + + if(reset_force) { + cudaMemset(vars->gpu_aForcex, 0, atomCount * sizeof(double)); + cudaMemset(vars->gpu_aForcey, 0, atomCount * sizeof(double)); + cudaMemset(vars->gpu_aForcez, 0, atomCount * sizeof(double)); + cudaMemset(vars->gpu_mForcex, 0, molCount * sizeof(double)); + cudaMemset(vars->gpu_mForcey, 0, molCount * sizeof(double)); + cudaMemset(vars->gpu_mForcez, 0, molCount * sizeof(double)); + } + + cudaMalloc((void**) &gpu_pair1, pair1.size() * sizeof(int)); + cudaMalloc((void**) &gpu_pair2, pair2.size() * sizeof(int)); + cudaMalloc((void**) &gpu_particleCharge, + particleCharge.size() * sizeof(double)); + cudaMalloc((void**) &gpu_particleKind, particleKind.size() * sizeof(int)); + cudaMalloc((void**) &gpu_particleMol, particleMol.size() * sizeof(int)); + cudaMalloc((void**) &gpu_REn, pair1.size() * sizeof(double)); + cudaMalloc((void**) &gpu_LJEn, pair1.size() * sizeof(double)); + cudaMalloc((void**) &gpu_final_REn, sizeof(double)); + cudaMalloc((void**) &gpu_final_LJEn, sizeof(double)); + cudaMalloc((void**) &gpu_lambdaVDW, pair1.size() * sizeof(double)); + cudaMalloc((void**) &gpu_lambdaCoulomb, pair1.size() * sizeof(double)); + + // Copy necessary data to GPU + cudaMemcpy(gpu_pair1, &pair1[0], pair1.size() * sizeof(int), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_pair2, &pair2[0], pair2.size() * sizeof(int), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_particleCharge, &particleCharge[0], + particleCharge.size() * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_particleKind, &particleKind[0], + particleKind.size() * sizeof(int), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_particleMol, &particleMol[0], particleMol.size() * sizeof(int), + cudaMemcpyHostToDevice); + cudaMemcpy(vars->gpu_x, coords.x, atomNumber * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(vars->gpu_y, coords.y, atomNumber * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(vars->gpu_z, coords.z, atomNumber * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_lambdaVDW, lambdaVDW, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(gpu_lambdaCoulomb, lambdaCoulomb, pair1.size() * sizeof(double), + cudaMemcpyHostToDevice); + + // Run the kernel... + threadsPerBlock = 256; + blocksPerGrid = (int)(pair1.size() / threadsPerBlock) + 1; + BoxForceGPU <<< blocksPerGrid, threadsPerBlock>>>(gpu_pair1, + gpu_pair2, + vars->gpu_x, + vars->gpu_y, + vars->gpu_z, + boxAxes.GetAxis(box).x, + boxAxes.GetAxis(box).y, + boxAxes.GetAxis(box).z, + electrostatic, + gpu_particleCharge, + gpu_particleKind, + gpu_particleMol, + gpu_REn, + gpu_LJEn, + pair1.size(), + vars->gpu_sigmaSq, + vars->gpu_epsilon_Cn, + vars->gpu_n, + vars->gpu_VDW_Kind, + vars->gpu_isMartini, + vars->gpu_count, + vars->gpu_rCut, + vars->gpu_rCutCoulomb, + vars->gpu_rCutLow, + vars->gpu_rOn, + vars->gpu_alpha, + vars->gpu_ewald, + vars->gpu_diElectric_1, + vars->gpu_nonOrth, + vars->gpu_cell_x[box], + vars->gpu_cell_y[box], + vars->gpu_cell_z[box], + vars->gpu_Invcell_x[box], + vars->gpu_Invcell_y[box], + vars->gpu_Invcell_z[box], + vars->gpu_aForcex, + vars->gpu_aForcey, + vars->gpu_aForcez, + vars->gpu_mForcex, + vars->gpu_mForcey, + vars->gpu_mForcez, + gpu_lambdaVDW, + gpu_lambdaCoulomb, + sc_coul, + sc_sigma_6, + sc_alpha, + sc_power, + vars->gpu_rMin, + vars->gpu_rMaxSq, + vars->gpu_expConst, + box); + + checkLastErrorCUDA(__FILE__, __LINE__); + // ReduceSum + void * d_temp_storage = NULL; + size_t temp_storage_bytes = 0; + DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, gpu_REn, + gpu_final_REn, pair1.size()); + CubDebugExit(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, gpu_REn, + gpu_final_REn, pair1.size()); + cudaFree(d_temp_storage); + + // LJ ReduceSum + d_temp_storage = NULL; + temp_storage_bytes = 0; + DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, gpu_LJEn, + gpu_final_LJEn, pair1.size()); + CubDebugExit(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, gpu_LJEn, + gpu_final_LJEn, pair1.size()); + cudaFree(d_temp_storage); + // Copy back the result to CPU ! :) + CubDebugExit(cudaMemcpy(&cpu_final_REn, gpu_final_REn, sizeof(double), + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(&cpu_final_LJEn, gpu_final_LJEn, sizeof(double), + cudaMemcpyDeviceToHost)); + REn = cpu_final_REn; + LJEn = cpu_final_LJEn; + + if(copy_back) { + CubDebugExit(cudaMemcpy(aForcex, vars->gpu_aForcex, + sizeof(double) * atomCount, + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(aForcey, vars->gpu_aForcey, + sizeof(double) * atomCount, + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(aForcez, vars->gpu_aForcez, + sizeof(double) * atomCount, + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(mForcex, vars->gpu_mForcex, + sizeof(double) * molCount, + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(mForcey, vars->gpu_mForcey, + sizeof(double) * molCount, + cudaMemcpyDeviceToHost)); + CubDebugExit(cudaMemcpy(mForcez, vars->gpu_mForcez, + sizeof(double) * molCount, + cudaMemcpyDeviceToHost)); + } + cudaDeviceSynchronize(); + + cudaFree(gpu_pair1); + cudaFree(gpu_pair2); + cudaFree(gpu_particleCharge); + cudaFree(gpu_particleKind); + cudaFree(gpu_particleMol); + cudaFree(gpu_REn); + cudaFree(gpu_LJEn); + cudaFree(gpu_final_REn); + cudaFree(gpu_final_LJEn); + cudaFree(gpu_lambdaVDW); + cudaFree(gpu_lambdaCoulomb); +} + +void CallVirialReciprocalGPU(VariablesCUDA *vars, + XYZArray const ¤tCoords, + XYZArray const ¤tCOMDiff, + vector &particleCharge, + double &rT11, + double &rT12, + double &rT13, + double &rT22, + double &rT23, + double &rT33, + uint imageSize, + double constVal, + uint box) { int atomNumber = currentCoords.Count(); int blocksPerGrid, threadsPerBlock; @@ -245,31 +473,31 @@ void CallForceReciprocalGPU(VariablesCUDA *vars, // Run the kernel... threadsPerBlock = 256; blocksPerGrid = (int)(imageSize / threadsPerBlock) + 1; - ForceReciprocalGPU <<< blocksPerGrid, - threadsPerBlock>>>(vars->gpu_x, - vars->gpu_y, - vars->gpu_z, - vars->gpu_dx, - vars->gpu_dy, - vars->gpu_dz, - vars->gpu_kxRef[box], - vars->gpu_kyRef[box], - vars->gpu_kzRef[box], - vars->gpu_prefactRef[box], - vars->gpu_hsqrRef[box], - vars->gpu_sumRref[box], - vars->gpu_sumIref[box], - gpu_particleCharge, - vars->gpu_rT11, - vars->gpu_rT12, - vars->gpu_rT13, - vars->gpu_rT22, - vars->gpu_rT23, - vars->gpu_rT33, - constVal, - imageSize, - atomNumber); - + VirialReciprocalGPU <<< blocksPerGrid, + threadsPerBlock>>>(vars->gpu_x, + vars->gpu_y, + vars->gpu_z, + vars->gpu_dx, + vars->gpu_dy, + vars->gpu_dz, + vars->gpu_kxRef[box], + vars->gpu_kyRef[box], + vars->gpu_kzRef[box], + vars->gpu_prefactRef[box], + vars->gpu_hsqrRef[box], + vars->gpu_sumRref[box], + vars->gpu_sumIref[box], + gpu_particleCharge, + vars->gpu_rT11, + vars->gpu_rT12, + vars->gpu_rT13, + vars->gpu_rT22, + vars->gpu_rT23, + vars->gpu_rT33, + constVal, + imageSize, + atomNumber); + checkLastErrorCUDA(__FILE__, __LINE__); // ReduceSum // Virial of Reciprocal void *d_temp_storage = NULL; size_t temp_storage_bytes = 0; @@ -354,6 +582,15 @@ __global__ void BoxInterForceGPU(int *gpu_pair1, double *gpu_Invcell_y, double *gpu_Invcell_z, int *gpu_nonOrth, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, int box) { int threadID = blockIdx.x * blockDim.x + threadIdx.x; @@ -394,8 +631,13 @@ __global__ void BoxInterForceGPU(int *gpu_pair1, qi_qj = gpu_particleCharge[gpu_pair1[threadID]] * gpu_particleCharge[gpu_pair2[threadID]]; pRF = CalcCoulombForceGPU(distSq, qi_qj, gpu_VDW_Kind[0], gpu_ewald[0], - gpu_isMartini[0], gpu_alpha[box], gpu_rCutCoulomb[box], - gpu_diElectric_1[0]); + gpu_isMartini[0], gpu_alpha[box], + gpu_rCutCoulomb[box], gpu_diElectric_1[0], + gpu_sigmaSq, sc_coul, sc_sigma_6, sc_alpha, + sc_power, gpu_lambdaCoulomb[threadID], + gpu_count[0], + gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]]); gpu_rT11[threadID] = pRF * (virX * diff_comx); gpu_rT22[threadID] = pRF * (virY * diff_comy); @@ -411,7 +653,9 @@ __global__ void BoxInterForceGPU(int *gpu_pair1, gpu_particleKind[gpu_pair2[threadID]], gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, gpu_rCut[0], gpu_rOn[0], gpu_isMartini[0], gpu_VDW_Kind[0], - gpu_count[0]); + gpu_count[0], gpu_lambdaVDW[threadID], sc_sigma_6, + sc_alpha, sc_power, gpu_rMin, gpu_rMaxSq, + gpu_expConst); gpu_vT11[threadID] = pVF * (virX * diff_comx); gpu_vT22[threadID] = pVF * (virY * diff_comy); @@ -424,30 +668,179 @@ __global__ void BoxInterForceGPU(int *gpu_pair1, } } +__global__ void BoxForceGPU(int *gpu_pair1, + int *gpu_pair2, + double *gpu_x, + double *gpu_y, + double *gpu_z, + double xAxes, + double yAxes, + double zAxes, + bool electrostatic, + double *gpu_particleCharge, + int *gpu_particleKind, + int *gpu_particleMol, + double *gpu_REn, + double *gpu_LJEn, + int pairSize, + double *gpu_sigmaSq, + double *gpu_epsilon_Cn, + double *gpu_n, + int *gpu_VDW_Kind, + int *gpu_isMartini, + int *gpu_count, + double *gpu_rCut, + double *gpu_rCutCoulomb, + double *gpu_rCutLow, + double *gpu_rOn, + double *gpu_alpha, + int *gpu_ewald, + double *gpu_diElectric_1, + int *gpu_nonOrth, + double *gpu_cell_x, + double *gpu_cell_y, + double *gpu_cell_z, + double *gpu_Invcell_x, + double *gpu_Invcell_y, + double *gpu_Invcell_z, + double *gpu_aForcex, + double *gpu_aForcey, + double *gpu_aForcez, + double *gpu_mForcex, + double *gpu_mForcey, + double *gpu_mForcez, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, + int box) +{ + int threadID = blockIdx.x * blockDim.x + threadIdx.x; + if(threadID >= pairSize) + return; + double distSq; + double qi_qj_fact; + double qqFact = 167000.0; + double virX = 0.0, virY = 0.0, virZ = 0.0; + double forceRealx = 0.0, forceRealy = 0.0, forceRealz = 0.0; + double forceLJx = 0.0, forceLJy = 0.0, forceLJz = 0.0; + gpu_REn[threadID] = 0.0; + gpu_LJEn[threadID] = 0.0; + double cutoff = fmax(gpu_rCut[0], gpu_rCutCoulomb[box]); + if(InRcutGPU(distSq, virX, virY, virZ, gpu_x[gpu_pair1[threadID]], + gpu_y[gpu_pair1[threadID]], gpu_z[gpu_pair1[threadID]], + gpu_x[gpu_pair2[threadID]], gpu_y[gpu_pair2[threadID]], + gpu_z[gpu_pair2[threadID]], xAxes, yAxes, zAxes, xAxes / 2.0, + yAxes / 2.0, zAxes / 2.0, cutoff, gpu_nonOrth[0], gpu_cell_x, + gpu_cell_y, gpu_cell_z, gpu_Invcell_x, gpu_Invcell_y, + gpu_Invcell_z)) { + if(electrostatic) { + qi_qj_fact = gpu_particleCharge[gpu_pair1[threadID]] * + gpu_particleCharge[gpu_pair2[threadID]] * qqFact; + gpu_REn[threadID] = CalcCoulombGPU(distSq, + gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]], + qi_qj_fact, gpu_rCutLow[0], + gpu_ewald[0], gpu_VDW_Kind[0], + gpu_alpha[box], + gpu_rCutCoulomb[box], + gpu_isMartini[0], + gpu_diElectric_1[0], + gpu_lambdaCoulomb[threadID], + sc_coul, + sc_sigma_6, + sc_alpha, + sc_power, + gpu_sigmaSq[threadID], + gpu_count[0]); + } + gpu_LJEn[threadID] = CalcEnGPU(distSq, + gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]], + gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, + gpu_VDW_Kind[0], gpu_isMartini[0], + gpu_rCut[0], gpu_rOn[0], gpu_count[0], + gpu_lambdaVDW[threadID], + sc_sigma_6, sc_alpha, sc_power, gpu_rMin, + gpu_rMaxSq, gpu_expConst); + if(electrostatic) { + double coulombVir = CalcCoulombForceGPU(distSq, qi_qj_fact, + gpu_VDW_Kind[0], gpu_ewald[0], + gpu_isMartini[0], + gpu_alpha[box], + gpu_rCutCoulomb[box], + gpu_diElectric_1[0], + gpu_sigmaSq, sc_coul, sc_sigma_6, + sc_alpha, sc_power, + gpu_lambdaCoulomb[threadID], + gpu_count[0], + gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]]); + forceRealx = virX * coulombVir; + forceRealy = virY * coulombVir; + forceRealz = virZ * coulombVir; + } + double pVF = CalcEnForceGPU(distSq, gpu_particleKind[gpu_pair1[threadID]], + gpu_particleKind[gpu_pair2[threadID]], + gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, + gpu_rCut[0], gpu_rOn[0], gpu_isMartini[0], + gpu_VDW_Kind[0], gpu_count[0], + gpu_lambdaVDW[threadID], sc_sigma_6, sc_alpha, + sc_power, gpu_rMin, gpu_rMaxSq, gpu_expConst); + forceLJx = virX * pVF; + forceLJy = virY * pVF; + forceLJz = virZ * pVF; -__global__ void ForceReciprocalGPU(double *gpu_x, - double *gpu_y, - double *gpu_z, - double *gpu_comDx, - double *gpu_comDy, - double *gpu_comDz, - double *gpu_kxRef, - double *gpu_kyRef, - double *gpu_kzRef, - double *gpu_prefactRef, - double *gpu_hsqrRef, - double *gpu_sumRref, - double *gpu_sumIref, - double *gpu_particleCharge, - double *gpu_rT11, - double *gpu_rT12, - double *gpu_rT13, - double *gpu_rT22, - double *gpu_rT23, - double *gpu_rT33, - double constVal, - uint imageSize, - uint atomNumber) + atomicAdd(&gpu_aForcex[gpu_pair1[threadID]], forceRealx + forceLJx); + atomicAdd(&gpu_aForcey[gpu_pair1[threadID]], forceRealy + forceLJy); + atomicAdd(&gpu_aForcez[gpu_pair1[threadID]], forceRealz + forceLJz); + atomicAdd(&gpu_aForcex[gpu_pair2[threadID]], -1.0 * (forceRealx + forceLJx)); + atomicAdd(&gpu_aForcey[gpu_pair2[threadID]], -1.0 * (forceRealy + forceLJy)); + atomicAdd(&gpu_aForcez[gpu_pair2[threadID]], -1.0 * (forceRealz + forceLJz)); + + atomicAdd(&gpu_mForcex[gpu_particleMol[gpu_pair1[threadID]]], + forceRealx + forceLJx); + atomicAdd(&gpu_mForcey[gpu_particleMol[gpu_pair1[threadID]]], + forceRealy + forceLJy); + atomicAdd(&gpu_mForcez[gpu_particleMol[gpu_pair1[threadID]]], + forceRealz + forceLJz); + atomicAdd(&gpu_mForcex[gpu_particleMol[gpu_pair2[threadID]]], + -1.0 * (forceRealx + forceLJx)); + atomicAdd(&gpu_mForcey[gpu_particleMol[gpu_pair2[threadID]]], + -1.0 * (forceRealy + forceLJy)); + atomicAdd(&gpu_mForcez[gpu_particleMol[gpu_pair2[threadID]]], + -1.0 * (forceRealz + forceLJz)); + } +} + +__global__ void VirialReciprocalGPU(double *gpu_x, + double *gpu_y, + double *gpu_z, + double *gpu_comDx, + double *gpu_comDy, + double *gpu_comDz, + double *gpu_kxRef, + double *gpu_kyRef, + double *gpu_kzRef, + double *gpu_prefactRef, + double *gpu_hsqrRef, + double *gpu_sumRref, + double *gpu_sumIref, + double *gpu_particleCharge, + double *gpu_rT11, + double *gpu_rT12, + double *gpu_rT13, + double *gpu_rT22, + double *gpu_rT23, + double *gpu_rT33, + double constVal, + uint imageSize, + uint atomNumber) { int threadID = blockIdx.x * blockDim.x + threadIdx.x; if(threadID >= imageSize) @@ -499,32 +892,15 @@ __global__ void ForceReciprocalGPU(double *gpu_x, } } -__device__ double CalcCoulombForceGPU(double distSq, double qi_qj, - int gpu_VDW_Kind, int gpu_ewald, - int gpu_isMartini, double gpu_alpha, - double gpu_rCutCoulomb, double gpu_diElectric_1) -{ - if((gpu_rCutCoulomb * gpu_rCutCoulomb) < distSq) { - return 0.0; - } - - if(gpu_VDW_Kind == GPU_VDW_STD_KIND) { - return CalcCoulombVirParticleGPU(distSq, qi_qj, gpu_alpha); - } else if(gpu_VDW_Kind == GPU_VDW_SHIFT_KIND) { - return CalcCoulombVirShiftGPU(distSq, qi_qj, gpu_ewald, gpu_alpha); - } else if(gpu_VDW_Kind == GPU_VDW_SWITCH_KIND && gpu_isMartini) { - return CalcCoulombVirSwitchMartiniGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, - gpu_rCutCoulomb, gpu_diElectric_1); - } else - return CalcCoulombVirSwitchGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, - gpu_rCutCoulomb); -} - __device__ double CalcEnForceGPU(double distSq, int kind1, int kind2, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, double gpu_rCut, double gpu_rOn, int gpu_isMartini, - int gpu_VDW_Kind, int gpu_count) + int gpu_VDW_Kind, int gpu_count, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power, + double *gpu_rMin, double *gpu_rMaxSq, + double *gpu_expConst) { if((gpu_rCut * gpu_rCut) < distSq) { return 0.0; @@ -533,12 +909,20 @@ __device__ double CalcEnForceGPU(double distSq, int kind1, int kind2, int index = FlatIndexGPU(kind1, kind2, gpu_count); if(gpu_VDW_Kind == GPU_VDW_STD_KIND) { return CalcVirParticleGPU(distSq, index, gpu_sigmaSq, gpu_n, - gpu_epsilon_Cn); + gpu_epsilon_Cn, gpu_lambdaVDW, sc_sigma_6, + sc_alpha, sc_power); } else if(gpu_VDW_Kind == GPU_VDW_SHIFT_KIND) { - return CalcVirShiftGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); + return CalcVirShiftGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, + gpu_lambdaVDW, sc_sigma_6, sc_alpha, sc_power); + } else if(gpu_VDW_Kind == GPU_VDW_EXP6_KIND) { + return CalcVirExp6GPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_rMin, + gpu_rMaxSq, gpu_expConst, gpu_lambdaVDW, sc_sigma_6, + sc_alpha, sc_power); } else if(gpu_VDW_Kind == GPU_VDW_SWITCH_KIND && gpu_isMartini) { return CalcVirSwitchMartiniGPU(distSq, index, gpu_sigmaSq, gpu_n, - gpu_epsilon_Cn, gpu_rCut, gpu_rOn); + gpu_epsilon_Cn, gpu_rCut, gpu_rOn, + gpu_lambdaVDW, sc_sigma_6, sc_alpha, + sc_power); } else return CalcVirSwitchGPU(distSq, index, gpu_sigmaSq, gpu_epsilon_Cn, gpu_n, gpu_rCut, gpu_rOn); @@ -547,13 +931,73 @@ __device__ double CalcEnForceGPU(double distSq, int kind1, int kind2, //ElectroStatic Calculation //**************************************************************// __device__ double CalcCoulombVirParticleGPU(double distSq, double qi_qj, - double gpu_alpha) + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb) { - double dist = sqrt(distSq); - double constValue = 2.0 * gpu_alpha / sqrt(M_PI); - double expConstValue = exp(-1.0 * gpu_alpha * gpu_alpha * distSq); - double temp = 1.0 - erf(gpu_alpha * dist); - return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombVirParticleGPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaCoulomb * correction * correction * + CalcCoulombVirParticleGPU(softRsq, qi_qj, gpu_ewald, gpu_alpha); + } else { + return gpu_lambdaCoulomb * + CalcCoulombVirParticleGPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } +} + +__device__ double CalcCoulombVirParticleGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha) +{ + if(gpu_ewald) { + double dist = sqrt(distSq); + double constValue = 2.0 * gpu_alpha / sqrt(M_PI); + double expConstValue = exp(-1.0 * gpu_alpha * gpu_alpha * distSq); + double temp = 1.0 - erf(gpu_alpha * dist); + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + } else { + double dist = sqrt(distSq); + double result = qi_qj / (distSq * dist); + return result; + } +} + +__device__ double CalcCoulombVirShiftGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombVirShiftGPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaCoulomb * correction * correction * + CalcCoulombVirShiftGPU(softRsq, qi_qj, gpu_ewald, gpu_alpha); + } else { + return gpu_lambdaCoulomb * + CalcCoulombVirShiftGPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } } __device__ double CalcCoulombVirShiftGPU(double distSq, double qi_qj, @@ -570,6 +1014,85 @@ __device__ double CalcCoulombVirShiftGPU(double distSq, double qi_qj, return qi_qj / (distSq * dist); } } + +__device__ double CalcCoulombVirExp6GPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombVirExp6GPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } + if(sc_coul) { + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaCoulomb * correction * correction * + CalcCoulombVirExp6GPU(softRsq, qi_qj, gpu_ewald, gpu_alpha); + } else { + return gpu_lambdaCoulomb * + CalcCoulombVirExp6GPU(distSq, qi_qj, gpu_ewald, gpu_alpha); + } +} + +__device__ double CalcCoulombVirExp6GPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha) +{ + if(gpu_ewald) { + double dist = sqrt(distSq); + double constValue = 2.0 * gpu_alpha / sqrt(M_PI); + double expConstValue = exp(-1.0 * gpu_alpha * gpu_alpha * distSq); + double temp = erfc(gpu_alpha * dist); + return qi_qj * (temp / dist + constValue * expConstValue) / distSq; + } else { + double dist = sqrt(distSq); + return qi_qj / (distSq * dist); + } +} + +__device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, + double qi_qj, + int gpu_ewald, + double gpu_alpha, + double gpu_rCut, + double gpu_diElectric_1, + int index, + double *gpu_sigmaSq, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double gpu_lambdaCoulomb) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombVirSwitchMartiniGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCut, gpu_diElectric_1); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaCoulomb * correction * correction * + CalcCoulombVirSwitchMartiniGPU(softRsq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCut, gpu_diElectric_1); + } else { + return gpu_lambdaCoulomb * + CalcCoulombVirSwitchMartiniGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCut, gpu_diElectric_1); + } +} + __device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, double qi_qj, int gpu_ewald, double gpu_alpha, @@ -599,6 +1122,35 @@ __device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, double qi_qj, } } +__device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + double gpu_rCut, int index, + double *gpu_sigmaSq, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, + double gpu_lambdaCoulomb) +{ + if(gpu_lambdaCoulomb >= 0.999999) { + return CalcCoulombVirSwitchGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCut); + } + + if(sc_coul) { + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaCoulomb), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, 1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaCoulomb * correction * correction * + CalcCoulombVirSwitchGPU(softRsq, qi_qj, gpu_ewald, gpu_alpha, gpu_rCut); + } else { + return gpu_lambdaCoulomb * + CalcCoulombVirSwitchGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, gpu_rCut); + } +} + __device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, int gpu_ewald, double gpu_alpha, double gpu_rCut) @@ -622,6 +1174,28 @@ __device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, //VDW Calculation //*****************************************************************// +__device__ double CalcVirParticleGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcVirParticleGPU(distSq, index, gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaVDW * correction * correction * + CalcVirParticleGPU(softRsq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); +} + __device__ double CalcVirParticleGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn) @@ -635,6 +1209,26 @@ __device__ double CalcVirParticleGPU(double distSq, int index, ((gpu_n[index] / 6.0) * repulse - attract) * rNeg2; } +__device__ double CalcVirShiftGPU(double distSq, int index, double *gpu_sigmaSq, + double *gpu_n, double *gpu_epsilon_Cn, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcVirShiftGPU(distSq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaVDW * correction * correction * + CalcVirShiftGPU(softRsq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn); +} + __device__ double CalcVirShiftGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn) { @@ -647,6 +1241,69 @@ __device__ double CalcVirShiftGPU(double distSq, int index, double *gpu_sigmaSq, ((gpu_n[index] / 6.0) * repulse - attract) * rNeg2; } +__device__ double CalcVirExp6GPU(double distSq, int index, double *gpu_sigmaSq, + double *gpu_n, double *gpu_rMin, + double *gpu_rMaxSq, double *gpu_expConst, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power) +{ + if(distSq < gpu_rMaxSq[index]) { + return num::BIGNUM; + } + if(gpu_lambdaVDW >= 0.999999) { + return CalcVirExp6GPU(distSq, index, gpu_n, gpu_rMin, gpu_expConst); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaVDW * correction * correction * + CalcVirExp6GPU(softRsq, index, gpu_n, gpu_rMin, gpu_expConst); +} + +__device__ double CalcVirExp6GPU(double distSq, int index, double *gpu_n, + double *gpu_rMin, double *gpu_expConst) +{ + double dist = sqrt(distSq); + double rRat = gpu_rMin[index] / dist; + double rRat2 = rRat * rRat; + double attract = rRat2 * rRat2 * rRat2; + + uint alpha_ij = gpu_n[index]; + double repulse = (dist / gpu_rMin[index]) * exp(alpha_ij * + (1.0 - dist / gpu_rMin[index])); + return 6.0 * gpu_expConst[index] * (repulse - attract) / distSq; +} + +__device__ double CalcVirSwitchMartiniGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, double gpu_rOn, + double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcVirSwitchMartiniGPU(distSq, index, gpu_sigmaSq, gpu_n, + gpu_epsilon_Cn, gpu_rCut, gpu_rOn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaVDW * correction * correction * + CalcVirSwitchMartiniGPU(softRsq, index, gpu_sigmaSq, gpu_n, gpu_epsilon_Cn, + gpu_rCut, gpu_rOn); +} + __device__ double CalcVirSwitchMartiniGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, @@ -686,6 +1343,30 @@ __device__ double CalcVirSwitchMartiniGPU(double distSq, int index, return Wij; } +__device__ double CalcVirSwitchGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_epsilon_Cn, + double *gpu_n, double gpu_rCut, + double gpu_rOn, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power) +{ + if(gpu_lambdaVDW >= 0.999999) { + return CalcVirSwitchGPU(distSq, index, gpu_sigmaSq, gpu_epsilon_Cn, gpu_n, + gpu_rCut, gpu_rOn); + } + + double sigma6 = gpu_sigmaSq[index] * gpu_sigmaSq[index] * gpu_sigmaSq[index]; + sigma6 = max(sigma6, sc_sigma_6); + double dist6 = distSq * distSq * distSq; + double lambdaCoef = sc_alpha * pow((1.0 - gpu_lambdaVDW), (double)sc_power); + double softDist6 = lambdaCoef * sigma6 + dist6; + double softRsq = pow(softDist6, (double)1.0 / 3.0); + double correction = distSq / softRsq; + return gpu_lambdaVDW * correction * correction * + CalcVirSwitchGPU(softRsq, index, gpu_sigmaSq, gpu_epsilon_Cn, gpu_n, + gpu_rCut, gpu_rOn); +} + __device__ double CalcVirSwitchGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_epsilon_Cn, double *gpu_n, double gpu_rCut, diff --git a/src/GPU/CalculateForceCUDAKernel.cuh b/src/GPU/CalculateForceCUDAKernel.cuh index 3ab96d429..f010361f9 100644 --- a/src/GPU/CalculateForceCUDAKernel.cuh +++ b/src/GPU/CalculateForceCUDAKernel.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -12,9 +12,40 @@ along with this program, also can be found at . #include "XYZArray.h" #include "BoxDimensions.h" #include "VariablesCUDA.cuh" +#include "ConstantDefinitionsCUDAKernel.cuh" +#include "CalculateMinImageCUDAKernel.cuh" using namespace std; +void CallBoxForceGPU(VariablesCUDA *vars, + vector pair1, + vector pair2, + XYZArray const &coords, + BoxDimensions const &boxAxes, + bool electrostatic, + vector particleCharge, + vector particleKind, + vector particleMol, + double &REn, + double &LJEn, + double *aForcex, + double *aForcey, + double *aForcez, + double *mForcex, + double *mForcey, + double *mForcez, + int atomCount, + int molCount, + bool reset_force, + bool copy_back, + double *lambdaVDW, + double *lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + uint const box); + void CallBoxInterForceGPU(VariablesCUDA *vars, vector &pair1, vector &pair2, @@ -37,21 +68,79 @@ void CallBoxInterForceGPU(VariablesCUDA *vars, double &vT22, double &vT23, double &vT33, + double *arr_lambdaVDW, + double *arr_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, uint const box); -void CallForceReciprocalGPU(VariablesCUDA *vars, - XYZArray const ¤tCoords, - XYZArray const ¤tCOMDiff, - vector &particleCharge, - double &rT11, - double &rT12, - double &rT13, - double &rT22, - double &rT23, - double &rT33, - uint imageSize, - double constVal, - uint box); +void CallVirialReciprocalGPU(VariablesCUDA *vars, + XYZArray const ¤tCoords, + XYZArray const ¤tCOMDiff, + vector &particleCharge, + double &rT11, + double &rT12, + double &rT13, + double &rT22, + double &rT23, + double &rT33, + uint imageSize, + double constVal, + uint box); + +__global__ void BoxForceGPU(int *gpu_pair1, + int *gpu_pair2, + double *gpu_x, + double *gpu_y, + double *gpu_z, + double xAxes, + double yAxes, + double zAxes, + bool electrostatic, + double *gpu_particleCharge, + int *gpu_particleKind, + int *gpu_particleMol, + double *gpu_REn, + double *gpu_LJEn, + int pairSize, + double *gpu_sigmaSq, + double *gpu_epsilon_Cn, + double *gpu_n, + int *gpu_VDW_Kind, + int *gpu_isMartini, + int *gpu_count, + double *gpu_rCut, + double *gpu_rCutCoulomb, + double *gpu_rCutLow, + double *gpu_rOn, + double *gpu_alpha, + int *gpu_ewald, + double *gpu_diElectric_1, + int *gpu_nonOrth, + double *gpu_cell_x, + double *gpu_cell_y, + double *gpu_cell_z, + double *gpu_Invcell_x, + double *gpu_Invcell_y, + double *gpu_Invcell_z, + double *gpu_aForcex, + double *gpu_aForcey, + double *gpu_aForcez, + double *gpu_mForcex, + double *gpu_mForcey, + double *gpu_mForcez, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, + int box); __global__ void BoxInterForceGPU(int *gpu_pair1, int *gpu_pair2, @@ -101,39 +190,41 @@ __global__ void BoxInterForceGPU(int *gpu_pair1, double *gpu_Invcell_y, double *gpu_Invcell_z, int *gpu_nonOrth, + double *gpu_lambdaVDW, + double *gpu_lambdaCoulomb, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst, int box); -__global__ void ForceReciprocalGPU(double *gpu_x, - double *gpu_y, - double *gpu_z, - double *gpu_comDx, - double *gpu_comDy, - double *gpu_comDz, - double *gpu_kxRef, - double *gpu_kyRef, - double *gpu_kzRef, - double *gpu_prefactRef, - double *gpu_hsqrRef, - double *gpu_sumRref, - double *gpu_sumIref, - double *gpu_particleCharge, - double *gpu_rT11, - double *gpu_rT12, - double *gpu_rT13, - double *gpu_rT22, - double *gpu_rT23, - double *gpu_rT33, - double constVal, - uint imageSize, - uint atomNumber); - -__device__ double CalcCoulombForceGPU(double distSq, double qi_qj, - int gpu_VDW_Kind, - int gpu_ewald, - int gpu_isMartini, - double gpu_alpha, - double gpu_rCutCoulomb, - double gpu_diElectric_1); +__global__ void VirialReciprocalGPU(double *gpu_x, + double *gpu_y, + double *gpu_z, + double *gpu_comDx, + double *gpu_comDy, + double *gpu_comDz, + double *gpu_kxRef, + double *gpu_kyRef, + double *gpu_kzRef, + double *gpu_prefactRef, + double *gpu_hsqrRef, + double *gpu_sumRref, + double *gpu_sumIref, + double *gpu_particleCharge, + double *gpu_rT11, + double *gpu_rT12, + double *gpu_rT13, + double *gpu_rT22, + double *gpu_rT23, + double *gpu_rT33, + double constVal, + uint imageSize, + uint atomNumber); + __device__ double CalcEnForceGPU(double distSq, int kind1, int kind2, double *gpu_sigmaSq, double *gpu_n, @@ -142,39 +233,167 @@ __device__ double CalcEnForceGPU(double distSq, int kind1, int kind2, double gpu_rOn, int gpu_isMartini, int gpu_VDW_Kind, - int gpu_count); + int gpu_count, + double gpu_lambdaVDW, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double *gpu_rMin, + double *gpu_rMaxSq, + double *gpu_expConst); //ElectroStatic Calculation //**************************************************************// __device__ double CalcCoulombVirParticleGPU(double distSq, double qi_qj, - double gpu_alpha); + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb); +__device__ double CalcCoulombVirParticleGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha); +__device__ double CalcCoulombVirShiftGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb); __device__ double CalcCoulombVirShiftGPU(double distSq, double qi_qj, int gpu_ewald, double gpu_alpha); +__device__ double CalcCoulombVirExp6GPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + int index, double *gpu_sigmaSq, + bool sc_coul, double sc_sigma_6, + double sc_alpha, uint sc_power, + double gpu_lambdaCoulomb); +__device__ double CalcCoulombVirExp6GPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha); +__device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, double qi_qj, + int gpu_ewald, + double gpu_alpha, + double gpu_rCut, + double gpu_diElectric_1, + int index, + double *gpu_sigmaSq, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double gpu_lambdaCoulomb); __device__ double CalcCoulombVirSwitchMartiniGPU(double distSq, double qi_qj, int gpu_ewald, double gpu_alpha, double gpu_rCut, double gpu_diElectric_1); +__device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, + int gpu_ewald, double gpu_alpha, + double gpu_rCut, int index, + double *gpu_sigmaSq, bool sc_coul, + double sc_sigma_6, double sc_alpha, + uint sc_power, + double gpu_lambdaCoulomb); __device__ double CalcCoulombVirSwitchGPU(double distSq, double qi_qj, int gpu_ewald, double gpu_alpha, double gpu_rCut); //VDW Calculation //*****************************************************************// +__device__ double CalcVirParticleGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_lambdaVDW, double sc_sigma_6, + double sc_alpha, uint sc_power); __device__ double CalcVirParticleGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn); +__device__ double CalcVirShiftGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power); __device__ double CalcVirShiftGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn); +__device__ double CalcVirExp6GPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_rMin, double *gpu_rMaxSq, + double *gpu_expConst, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power); +__device__ double CalcVirExp6GPU(double distSq, int index, double *gpu_n, + double *gpu_rMin, double *gpu_expConst); +__device__ double CalcVirSwitchMartiniGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_n, + double *gpu_epsilon_Cn, + double gpu_rCut, double rOn, + double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power); __device__ double CalcVirSwitchMartiniGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_n, double *gpu_epsilon_Cn, double gpu_rCut, double rOn); +__device__ double CalcVirSwitchGPU(double distSq, int index, + double *gpu_sigmaSq, double *gpu_epsilon_Cn, + double *gpu_n, double gpu_rCut, + double gpu_rOn, double gpu_lambdaVDW, + double sc_sigma_6, double sc_alpha, + uint sc_power); __device__ double CalcVirSwitchGPU(double distSq, int index, double *gpu_sigmaSq, double *gpu_epsilon_Cn, double *gpu_n, double gpu_rCut, double gpu_rOn); + +// Have to move the implementation for some functions here +// since CUDA doesn't allow __global__ to call __device__ +// from different files +// Wanted to call CalcCoulombForceGPU() from CalculateEnergyCUDAKernel.cu file +__device__ inline double CalcCoulombForceGPU(double distSq, double qi_qj, + int gpu_VDW_Kind, int gpu_ewald, + int gpu_isMartini, + double gpu_alpha, + double gpu_rCutCoulomb, + double gpu_diElectric_1, + double *gpu_sigmaSq, + bool sc_coul, + double sc_sigma_6, + double sc_alpha, + uint sc_power, + double gpu_lambdaCoulomb, + int gpu_count, int kind1, + int kind2) +{ + if((gpu_rCutCoulomb * gpu_rCutCoulomb) < distSq) { + return 0.0; + } + + int index = FlatIndexGPU(kind1, kind2, gpu_count); + if(gpu_VDW_Kind == GPU_VDW_STD_KIND) { + return CalcCoulombVirParticleGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, index, + gpu_sigmaSq, sc_coul, sc_sigma_6, sc_alpha, + sc_power, gpu_lambdaCoulomb); + } else if(gpu_VDW_Kind == GPU_VDW_SHIFT_KIND) { + return CalcCoulombVirShiftGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, index, + gpu_sigmaSq, sc_coul, sc_sigma_6, sc_alpha, + sc_power, gpu_lambdaCoulomb); + } else if(gpu_VDW_Kind == GPU_VDW_EXP6_KIND) { + return CalcCoulombVirExp6GPU(distSq, qi_qj, gpu_ewald, gpu_alpha, index, + gpu_sigmaSq, sc_coul, sc_sigma_6, sc_alpha, + sc_power, gpu_lambdaCoulomb); + } else if(gpu_VDW_Kind == GPU_VDW_SWITCH_KIND && gpu_isMartini) { + return CalcCoulombVirSwitchMartiniGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCutCoulomb, gpu_diElectric_1, + index, gpu_sigmaSq, sc_coul, + sc_sigma_6, sc_alpha, sc_power, + gpu_lambdaCoulomb); + } else + return CalcCoulombVirSwitchGPU(distSq, qi_qj, gpu_ewald, gpu_alpha, + gpu_rCutCoulomb, index, gpu_sigmaSq, sc_coul, + sc_sigma_6, sc_alpha, sc_power, + gpu_lambdaCoulomb); +} + + #endif /*GOMC_CUDA*/ #endif /*CALCULATE_FORCE_CUDA_KERNEL*/ diff --git a/src/GPU/CalculateMinImageCUDAKernel.cuh b/src/GPU/CalculateMinImageCUDAKernel.cuh index e4e3cb2fa..370e6077d 100644 --- a/src/GPU/CalculateMinImageCUDAKernel.cuh +++ b/src/GPU/CalculateMinImageCUDAKernel.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -123,4 +123,23 @@ __device__ inline double DotProductGPU(double kx, double ky, double kz, return (kx * x + ky * y + kz * z); } +// Add atomic operations for GPUs that do not support it +// atomicAdd and atomicSub only support double for Compute Capability >= 6.0 +#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600 +#else +static __inline__ __device__ double atomicAdd(double *address, double val) +{ + unsigned long long int* address_as_ull = (unsigned long long int*)address; + unsigned long long int old = *address_as_ull, assumed; + if (val == 0.0) + return __longlong_as_double(old); + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + __longlong_as_double(assumed))); + } while (assumed != old); + return __longlong_as_double(old); +} +#endif + #endif /*GOMC_CUDA*/ diff --git a/src/GPU/ConstantDefinitionsCUDAKernel.cu b/src/GPU/ConstantDefinitionsCUDAKernel.cu index d180876e6..58301f695 100644 --- a/src/GPU/ConstantDefinitionsCUDAKernel.cu +++ b/src/GPU/ConstantDefinitionsCUDAKernel.cu @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -55,6 +55,7 @@ void InitGPUForceField(VariablesCUDA &vars, double const *sigmaSq, cudaMemcpy(vars.gpu_ewald, &ewald, sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy(vars.gpu_diElectric_1, &diElectric_1, sizeof(double), cudaMemcpyHostToDevice); + checkLastErrorCUDA(__FILE__, __LINE__); } void InitCoordinatesCUDA(VariablesCUDA *vars, uint atomNumber, @@ -104,6 +105,30 @@ void InitCoordinatesCUDA(VariablesCUDA *vars, uint atomNumber, cudaMalloc(&vars->gpu_Invcell_y[b], 3 * sizeof(double)); cudaMalloc(&vars->gpu_Invcell_z[b], 3 * sizeof(double)); } + + cudaMalloc(&vars->gpu_aForcex, atomNumber * sizeof(double)); + cudaMalloc(&vars->gpu_aForcey, atomNumber * sizeof(double)); + cudaMalloc(&vars->gpu_aForcez, atomNumber * sizeof(double)); + cudaMalloc(&vars->gpu_mForcex, maxMolNumber * sizeof(double)); + cudaMalloc(&vars->gpu_mForcey, maxMolNumber * sizeof(double)); + cudaMalloc(&vars->gpu_mForcez, maxMolNumber * sizeof(double)); + checkLastErrorCUDA(__FILE__, __LINE__); +} + +void InitExp6Variables(VariablesCUDA *vars, double *rMin, double *expConst, + double *rMaxSq, uint size) +{ + cudaMalloc(&vars->gpu_rMin, size * sizeof(double)); + cudaMalloc(&vars->gpu_rMaxSq, size * sizeof(double)); + cudaMalloc(&vars->gpu_expConst, size * sizeof(double)); + + cudaMemcpy(vars->gpu_rMin, rMin, size * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(vars->gpu_rMaxSq, rMaxSq, size * sizeof(double), + cudaMemcpyHostToDevice); + cudaMemcpy(vars->gpu_expConst, expConst, size * sizeof(double), + cudaMemcpyHostToDevice); + checkLastErrorCUDA(__FILE__, __LINE__); } void InitEwaldVariablesCUDA(VariablesCUDA *vars, uint imageTotal) @@ -140,6 +165,7 @@ void InitEwaldVariablesCUDA(VariablesCUDA *vars, uint imageTotal) cudaMalloc(&vars->gpu_hsqr[b], imageTotal * sizeof(double)); cudaMalloc(&vars->gpu_hsqrRef[b], imageTotal * sizeof(double)); } + checkLastErrorCUDA(__FILE__, __LINE__); } void CopyCurrentToRefCUDA(VariablesCUDA *vars, uint box, uint imageTotal) @@ -158,6 +184,7 @@ void CopyCurrentToRefCUDA(VariablesCUDA *vars, uint box, uint imageTotal) imageTotal * sizeof(double), cudaMemcpyDeviceToDevice); cudaMemcpy(vars->gpu_kzRef[box], vars->gpu_kz[box], imageTotal * sizeof(double), cudaMemcpyDeviceToDevice); + checkLastErrorCUDA(__FILE__, __LINE__); } void UpdateRecipVecCUDA(VariablesCUDA *vars, uint box) @@ -204,6 +231,7 @@ void UpdateCellBasisCUDA(VariablesCUDA *vars, uint box, double *cellBasis_x, cudaMemcpy(vars->gpu_cell_z[box], cellBasis_z, 3 * sizeof(double), cudaMemcpyHostToDevice); cudaMemcpy(vars->gpu_nonOrth, &nonOrth, sizeof(int), cudaMemcpyHostToDevice); + checkLastErrorCUDA(__FILE__, __LINE__); } void UpdateInvCellBasisCUDA(VariablesCUDA *vars, uint box, @@ -218,6 +246,7 @@ void UpdateInvCellBasisCUDA(VariablesCUDA *vars, uint box, cudaMemcpy(vars->gpu_Invcell_z[box], invCellBasis_z, 3 * sizeof(double), cudaMemcpyHostToDevice); cudaMemcpy(vars->gpu_nonOrth, &nonOrth, sizeof(int), cudaMemcpyHostToDevice); + checkLastErrorCUDA(__FILE__, __LINE__); } void DestroyEwaldCUDAVars(VariablesCUDA *vars) @@ -281,6 +310,12 @@ void DestroyCUDAVars(VariablesCUDA *vars) cudaFree(vars->gpu_comx); cudaFree(vars->gpu_comy); cudaFree(vars->gpu_comz); + cudaFree(vars->gpu_aForcex); + cudaFree(vars->gpu_aForcey); + cudaFree(vars->gpu_aForcez); + cudaFree(vars->gpu_mForcex); + cudaFree(vars->gpu_mForcey); + cudaFree(vars->gpu_mForcez); cudaFree(vars->gpu_rT11); cudaFree(vars->gpu_rT12); cudaFree(vars->gpu_rT13); diff --git a/src/GPU/ConstantDefinitionsCUDAKernel.cuh b/src/GPU/ConstantDefinitionsCUDAKernel.cuh index 5d3312fc9..e6972d474 100644 --- a/src/GPU/ConstantDefinitionsCUDAKernel.cuh +++ b/src/GPU/ConstantDefinitionsCUDAKernel.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -17,6 +17,7 @@ along with this program, also can be found at . #define GPU_VDW_STD_KIND 0 #define GPU_VDW_SHIFT_KIND 1 #define GPU_VDW_SWITCH_KIND 2 +#define GPU_VDW_EXP6_KIND 3 #define MAX_PAIR_SIZE 10000000 void InitGPUForceField(VariablesCUDA &vars, double const *sigmaSq, @@ -38,6 +39,8 @@ void UpdateInvCellBasisCUDA(VariablesCUDA *vars, uint box, double *invCellBasis_z); void DestroyEwaldCUDAVars(VariablesCUDA *vars); void DestroyCUDAVars(VariablesCUDA *vars); +void InitExp6Variables(VariablesCUDA *vars, double *rMin, double *expConst, + double *rMaxSq, uint size); #endif /*GOMC_CUDA*/ #endif /*CONSTANT_DEFINITIONS_CUDA_KERNEL*/ diff --git a/src/GPU/VariablesCUDA.cuh b/src/GPU/VariablesCUDA.cuh index 9f10117ee..76eef8ef9 100644 --- a/src/GPU/VariablesCUDA.cuh +++ b/src/GPU/VariablesCUDA.cuh @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -21,6 +21,15 @@ inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort = } } +inline void checkLastErrorCUDA(const char *file, int line) +{ + cudaError_t code = cudaGetLastError(); + if (code != cudaSuccess) { + fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); + exit(code); + } +} + inline void printFreeMemory() { size_t free_byte ; @@ -57,6 +66,12 @@ public: gpu_rCutCoulomb = NULL; gpu_ewald = NULL; gpu_diElectric_1 = NULL; + gpu_aForcex = NULL; + gpu_aForcey = NULL; + gpu_aForcez = NULL; + gpu_mForcex = NULL; + gpu_mForcey = NULL; + gpu_mForcez = NULL; } double *gpu_sigmaSq; double *gpu_epsilon_Cn; @@ -87,5 +102,8 @@ public: double **gpu_cell_x, **gpu_cell_y, **gpu_cell_z; double **gpu_Invcell_x, **gpu_Invcell_y, **gpu_Invcell_z; int *gpu_nonOrth; + double *gpu_aForcex, *gpu_aForcey, *gpu_aForcez; + double *gpu_mForcex, *gpu_mForcey, *gpu_mForcez; + double *gpu_rMin, *gpu_expConst, *gpu_rMaxSq; }; #endif diff --git a/src/Geometry.cpp b/src/Geometry.cpp index 8c43e1cf1..39496f4f9 100644 --- a/src/Geometry.cpp +++ b/src/Geometry.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Geometry.h b/src/Geometry.h index ea4de915e..3130fcbb7 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -9,6 +9,7 @@ along with this program, also can be found at . #include "BasicTypes.h" #include "SubdividedArray.h" +#include #include @@ -97,6 +98,7 @@ class GeomFeature //!/param b index of bond within feature; in [0, bondsPer) uint GetBond(uint feature, uint b) const { + assert((feature * bondsPer + b) < (count * bondsPer)); return bondIndices[feature * bondsPer + b]; } diff --git a/src/HistOutput.cpp b/src/HistOutput.cpp index 4ebefd2b8..b6671751a 100644 --- a/src/HistOutput.cpp +++ b/src/HistOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -40,10 +40,17 @@ void Histogram::Init(pdb_setup::Atoms const& atoms, molCount[b] = new uint *[var->numKinds]; outF[b] = new std::ofstream[var->numKinds]; for (uint k = 0; k < var->numKinds; ++k) { +#if GOMC_LIB_MPI + name[b][k] = pathToReplicaDirectory + GetFName( output.state.files.hist.histName, + output.state.files.hist.number, + output.state.files.hist.letter, + b, k); +#else name[b][k] = GetFName(output.state.files.hist.histName, output.state.files.hist.number, output.state.files.hist.letter, b, k); +#endif } } //Figure out total of each kind of molecule in ALL boxes, including diff --git a/src/HistOutput.h b/src/HistOutput.h index f525808af..b97548fd3 100644 --- a/src/HistOutput.h +++ b/src/HistOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/InputAbstracts.h b/src/InputAbstracts.h index 0a5d81021..c967ead32 100644 --- a/src/InputAbstracts.h +++ b/src/InputAbstracts.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/InputFileReader.cpp b/src/InputFileReader.cpp index 0af83b4e8..d2a38ef55 100644 --- a/src/InputFileReader.cpp +++ b/src/InputFileReader.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/InputFileReader.h b/src/InputFileReader.h index 8000b4720..e9ac1f4ca 100644 --- a/src/InputFileReader.h +++ b/src/InputFileReader.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Main.cpp b/src/Main.cpp index e1a23c84b..e38adfd87 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,11 +1,15 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . ********************************************************************************/ #include "Simulation.h" #include "GOMC_Config.h" //For version number +#if GOMC_LIB_MPI +#include +#include "ParallelTemperingPreprocessor.h" +#endif #ifdef GOMC_CUDA #include "cuda.h" #include @@ -43,6 +47,10 @@ void PrintGPUHardwareInfo(); int main(int argc, char *argv[]) { +#if GOMC_LIB_MPI + ParallelTemperingPreprocessor pt(argc, argv); + MultiSim * multisim = pt.checkIfValidRank() ? new MultiSim(pt) : NULL; +#endif #ifndef NDEBUG PrintDebugMode(); #endif @@ -105,9 +113,21 @@ int main(int argc, char *argv[]) //ONCE FILE FOUND PASS STRING TO SIMULATION CLASS TO READ AND //HANDLE PDB|PSF FILE +#if GOMC_LIB_MPI + if(multisim != NULL) { + Simulation sim(inputFileString.c_str(), multisim); + sim.RunSimulation(); + PrintSimulationFooter(); + delete multisim; + MPI_Finalize(); + } else { + MPI_Finalize(); + } +#else Simulation sim(inputFileString.c_str()); sim.RunSimulation(); PrintSimulationFooter(); +#endif } return 0; } @@ -214,6 +234,7 @@ uint ReadNum(char *argv) return thread; } + } void PrintHardwareInfo() @@ -251,6 +272,12 @@ void PrintGPUHardwareInfo() int fastIndex = 0; cudaGetDeviceCount(&nDevices); + + if(nDevices == 0) { + printf("There are no available device(s) that support CUDA\n"); + exit(EXIT_FAILURE); + } + if(nDevices <= 4) { printf("GPU information:\n"); for (int i = 0; i < nDevices; i++) { diff --git a/src/MolPick.h b/src/MolPick.h index db4c9975d..dcd5e70ad 100644 --- a/src/MolPick.h +++ b/src/MolPick.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/MolSetup.cpp b/src/MolSetup.cpp index 5148cb0e2..48921db58 100644 --- a/src/MolSetup.cpp +++ b/src/MolSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -70,17 +70,20 @@ int ReadPSFAtoms(FILE* psf, //pre: stream is before !BONDS post: stream is in bond section just after //the first appearance of the last molecule int ReadPSFBonds(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom); + std::vector >& firstAtom, + const uint nbonds); //adds angles in psf to kindMap //pre: stream is before !NTHETA post: stream is in angle section just //after the first appearance of the last molecule int ReadPSFAngles(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom); + std::vector >& firstAtom, + const uint nangles); //adds dihedrals in psf to kindMap //pre: stream is before !NPHI post: stream is in dihedral section just //after the first appearance of the last molecule int ReadPSFDihedrals(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom); + std::vector >& firstAtom, + const uint ndihedrals); } @@ -567,8 +570,7 @@ int ReadPSF(const char* psfFilename, MolMap& kindMap) //build list of start particles for each type, so we can find it and skip //everything else std::vector > firstAtomLookup; - for (MolMap::iterator it = kindMap.begin(); - it != kindMap.end(); ++it) { + for (MolMap::iterator it = kindMap.begin(); it != kindMap.end(); ++it) { firstAtomLookup.push_back(std::make_pair(it->second.firstAtomID, it->first)); } std::sort(firstAtomLookup.begin(), firstAtomLookup.end()); @@ -576,54 +578,51 @@ int ReadPSF(const char* psfFilename, MolMap& kindMap) while (strstr(input, "!NBOND") == NULL) { check = fgets(input, 511, psf); if (check == NULL) { - fprintf(stderr, "ERROR: Unable to read bonds from PSF file %s", psfFilename); + fprintf(stderr, "ERROR: Unable to read bonds from PSF file %s", + psfFilename); fclose(psf); return READERROR; } } //make sure molecule has bonds, appears before !NBOND count = atoi(input); - if (count != 0) { - if (ReadPSFBonds(psf, kindMap, firstAtomLookup) == READERROR) { - fclose(psf); - return READERROR; - } + if (ReadPSFBonds(psf, kindMap, firstAtomLookup, count) == READERROR) { + fclose(psf); + return READERROR; } //find angle header+count fseek(psf, 0, SEEK_SET); while (strstr(input, "!NTHETA") == NULL) { check = fgets(input, 511, psf); if (check == NULL) { - fprintf(stderr, "ERROR: Unable to read angles from PSF file %s", psfFilename); + fprintf(stderr, "ERROR: Unable to read angles from PSF file %s", + psfFilename); fclose(psf); return READERROR; } } //make sure molecule has angles, count appears before !NTHETA count = atoi(input); - if (count != 0) { - if (ReadPSFAngles(psf, kindMap, firstAtomLookup) == READERROR) { - fclose(psf); - return READERROR; - } + if (ReadPSFAngles(psf, kindMap, firstAtomLookup, count) == READERROR) { + fclose(psf); + return READERROR; } //find dihedrals header+count fseek(psf, 0, SEEK_SET); while (strstr(input, "!NPHI") == NULL) { check = fgets(input, 511, psf); if (check == NULL) { - fprintf(stderr, "ERROR: Unable to read dihedrals from PSF file %s", psfFilename); + fprintf(stderr, "ERROR: Unable to read dihedrals from PSF file %s", + psfFilename); fclose(psf); return READERROR; } } //make sure molecule has dihs, count appears before !NPHI count = atoi(input); - if (count != 0) { - if (ReadPSFDihedrals(psf, kindMap, firstAtomLookup) == READERROR) { - fclose(psf); - return READERROR; - } + if (ReadPSFDihedrals(psf, kindMap, firstAtomLookup, count) == READERROR) { + fclose(psf); + return READERROR; } fclose(psf); @@ -682,33 +681,44 @@ int ReadPSFAtoms(FILE* psf, MolMap& kindMap, unsigned int nAtoms) //pre: stream is before !BONDS post: stream is in bond section just after //the first appearance of the last molecule int ReadPSFBonds(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom) + std::vector >& firstAtom, + const uint nbonds) { unsigned int atom0, atom1; - int dummy = fscanf(psf, "%u %u", &atom0, &atom1); - UNUSED(dummy); - for (unsigned int i = 0; i < firstAtom.size(); ++i) { - MolKind& currentMol = kindMap[firstAtom[i].second]; - //continue if atom has no bonds - if (currentMol.atoms.size() < 2) - continue; - //index of first atom in moleule - unsigned int molBegin = firstAtom[i].first; - //index AFTER last atom in molecule - unsigned int molEnd = molBegin + currentMol.atoms.size(); - while (atom0 < molBegin || atom0 >= molEnd) { - dummy = fscanf(psf, "%u %u", &atom0, &atom1); - if (feof(psf) || ferror(psf)) { - fprintf(stderr, "ERROR: Could not find all bonds in PSF file "); - return READERROR; - } + int dummy; + std::vector defined(firstAtom.size(), false); + for (uint n = 0; n < nbonds; n++) { + dummy = fscanf(psf, "%u %u", &atom0, &atom1); + if(dummy != 2) { + fprintf(stderr, "ERROR: Incorrect Number of bonds in PSF file "); + return READERROR; + } else if (feof(psf) || ferror(psf)) { + fprintf(stderr, "ERROR: Could not find all bonds in PSF file "); + return READERROR; } - //read in bonds - while (atom0 >= molBegin && atom0 < molEnd) { - currentMol.bonds.push_back(Bond(atom0 - molBegin, atom1 - molBegin)); - dummy = fscanf(psf, "%u %u", &atom0, &atom1); - if(dummy != 2) + + //loop to find the molecule kind with this bond + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + //index of first atom in molecule + unsigned int molBegin = firstAtom[i].first; + //index AFTER last atom in molecule + unsigned int molEnd = molBegin + currentMol.atoms.size(); + //assign the bond + if (atom0 >= molBegin && atom0 < molEnd) { + currentMol.bonds.push_back(Bond(atom0 - molBegin, atom1 - molBegin)); + //once we found the molecule kind, break from the loop + defined[i] = true; break; + } + } + } + //Check if we defined all bonds + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + if(currentMol.atoms.size() > 1 && !defined[i]) { + std::cout << "Warning: Bond is missing for " << firstAtom[i].second + << " !\n"; } } return 0; @@ -718,34 +728,45 @@ int ReadPSFBonds(FILE* psf, MolMap& kindMap, //pre: stream is before !NTHETA post: stream is in angle section just after //the first appearance of the last molecule int ReadPSFAngles(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom) + std::vector >& firstAtom, + const uint nangles) { unsigned int atom0, atom1, atom2; - int dummy = fscanf(psf, "%u %u %u", &atom0, &atom1, &atom2); - UNUSED(dummy); - for (unsigned int i = 0; i < firstAtom.size(); ++i) { - MolKind& currentMol = kindMap[firstAtom[i].second]; - //continue if atom has no angles - if (currentMol.atoms.size() < 3) - continue; - //index of first atom in moleule - unsigned int molBegin = firstAtom[i].first; - //index AFTER last atom in molecule - unsigned int molEnd = molBegin + currentMol.atoms.size(); - while (atom0 < molBegin || atom0 >= molEnd) { - dummy = fscanf(psf, "%u %u %u", &atom0, &atom1, &atom2); - if (feof(psf) || ferror(psf)) { - fprintf(stderr, "ERROR: Could not find all angles in PSF file "); - return READERROR; - } + int dummy; + std::vector defined(firstAtom.size(), false); + for (uint n = 0; n < nangles; n++) { + dummy = fscanf(psf, "%u %u %u", &atom0, &atom1, &atom2); + if(dummy != 3) { + fprintf(stderr, "ERROR: Incorrect Number of angles in PSF file "); + return READERROR; + } else if (feof(psf) || ferror(psf)) { + fprintf(stderr, "ERROR: Could not find all angles in PSF file "); + return READERROR; } - //read in angles - while (atom0 >= molBegin && atom0 < molEnd) { - currentMol.angles.push_back(Angle(atom0 - molBegin, atom1 - molBegin, - atom2 - molBegin)); - dummy = fscanf(psf, "%u %u %u", &atom0, &atom1, &atom2); - if(dummy != 3) + + //loop to find the molecule kind with this angles + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + //index of first atom in moleule + unsigned int molBegin = firstAtom[i].first; + //index AFTER last atom in molecule + unsigned int molEnd = molBegin + currentMol.atoms.size(); + //assign the angle + if (atom0 >= molBegin && atom0 < molEnd) { + currentMol.angles.push_back(Angle(atom0 - molBegin, atom1 - molBegin, + atom2 - molBegin)); + //once we found the molecule kind, break from the loop + defined[i] = true; break; + } + } + } + //Check if we defined all angles + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + if(currentMol.atoms.size() > 2 && !defined[i]) { + std::cout << "Warning: Angle is missing for " << firstAtom[i].second + << " !\n"; } } return 0; @@ -772,60 +793,54 @@ bool ContainsDihedral(const std::vector& vec, const int dih[]) //the first appearance of the last molecule // int ReadPSFDihedrals(FILE* psf, MolMap& kindMap, - std::vector >& firstAtom) + std::vector >& firstAtom, const uint ndihedrals) { Dihedral dih(0, 0, 0, 0); - - int dummy = fscanf(psf, "%u %u %u %u", &dih.a0, &dih.a1, &dih.a2, &dih.a3); - UNUSED(dummy); - //for all atoms - for (unsigned int i = 0; i < firstAtom.size(); ++i) { - MolKind& currentMol = kindMap[firstAtom[i].second]; - //continue if molecule has no dihedrals - if (currentMol.atoms.size() < 4) - continue; - //index of first atom in moleule - unsigned int molBegin = firstAtom[i].first; - //index AFTER last atom in molecule - unsigned int molEnd = molBegin + currentMol.atoms.size(); - //continue if molecule has more that 3 atoms but has no dihedrals - if(i == 0) { - // if it is the first molecule and index of dihedral is greater than - // molBegin, it means it does not have any dihedral. It works when - // we have only two molecule kinds. - if(dih.a0 > molBegin && dih.a0 > molEnd) { - continue; - } + int dummy; + std::vector defined(firstAtom.size(), false); + for (uint n = 0; n < ndihedrals; n++) { + dummy = fscanf(psf, "%u %u %u %u", &dih.a0, &dih.a1, &dih.a2, &dih.a3); + if(dummy != 4) { + fprintf(stderr, "ERROR: Incorrect Number of dihedrals in PSF file "); + return READERROR; + } else if (feof(psf) || ferror(psf)) { + fprintf(stderr, "ERROR: Could not find all dihedrals in PSF file "); + return READERROR; } - //scan to to first appearance of molecule - while (dih.a0 < molBegin || dih.a0 >= molEnd) { - dummy = fscanf(psf, "%u %u %u %u", &dih.a0, &dih.a1, &dih.a2, &dih.a3); - if (feof(psf) || ferror(psf)) { - fprintf(stderr, "ERROR: Could not find all dihedrals in PSF file "); - return READERROR; - } - //for case that molecule has more thatn 3 atoms and represend as - //second molecule but it has no dihedral. It works when - // we have only two molecule kinds and no improper - if (dih.a0 == 0) + + //loop to find the molecule kind with this dihedral + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + //index of first atom in moleule + unsigned int molBegin = firstAtom[i].first; + //index AFTER last atom in molecule + unsigned int molEnd = molBegin + currentMol.atoms.size(); + //assign dihedral + if (dih.a0 >= molBegin && dih.a0 < molEnd) { + dih.a0 -= molBegin; + dih.a1 -= molBegin; + dih.a2 -= molBegin; + dih.a3 -= molBegin; + //some xplor PSF files have duplicate dihedrals, we need to ignore these + if (std::find(currentMol.dihedrals.begin(), currentMol.dihedrals.end(), + dih) == currentMol.dihedrals.end()) { + currentMol.dihedrals.push_back(dih); + } + //once we found the molecule kind, break from the loop + defined[i] = true; break; - } - //read in dihedrals - while (dih.a0 >= molBegin && dih.a0 < molEnd) { - dih.a0 -= molBegin; - dih.a1 -= molBegin; - dih.a2 -= molBegin; - dih.a3 -= molBegin; - //some xplor PSF files have duplicate dihedrals, we need to ignore these - if (std::find(currentMol.dihedrals.begin(), currentMol.dihedrals.end(), - dih) == currentMol.dihedrals.end()) { - currentMol.dihedrals.push_back(dih); } - dummy = fscanf(psf, "%u %u %u %u", &dih.a0, &dih.a1, &dih.a2, &dih.a3); - if(dummy != 4) - break; + } + } + //Check if we defined all dihedrals + for (unsigned int i = 0; i < firstAtom.size(); ++i) { + MolKind& currentMol = kindMap[firstAtom[i].second]; + if(currentMol.atoms.size() > 3 && !defined[i]) { + std::cout << "Warning: Dihedral is missing for " << firstAtom[i].second + << " !\n"; } } return 0; } + } diff --git a/src/MolSetup.h b/src/MolSetup.h index a676f8c9a..998f85cae 100644 --- a/src/MolSetup.h +++ b/src/MolSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/MoleculeKind.cpp b/src/MoleculeKind.cpp index a0f514084..d3c6dc7db 100644 --- a/src/MoleculeKind.cpp +++ b/src/MoleculeKind.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/MoleculeKind.h b/src/MoleculeKind.h index e5ab0eb22..6138d79fe 100644 --- a/src/MoleculeKind.h +++ b/src/MoleculeKind.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/MoleculeLookup.cpp b/src/MoleculeLookup.cpp index 4d14fc1f8..9f2a489c5 100644 --- a/src/MoleculeLookup.cpp +++ b/src/MoleculeLookup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/MoleculeLookup.h b/src/MoleculeLookup.h index b25f555a8..40595dcce 100644 --- a/src/MoleculeLookup.h +++ b/src/MoleculeLookup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Molecules.cpp b/src/Molecules.cpp index c22723aba..27a78d0f4 100644 --- a/src/Molecules.cpp +++ b/src/Molecules.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -13,6 +13,7 @@ along with this program, also can be found at . #include "VectorLib.h" //for transfer. #include //For count. #include +#include "System.h" class System; @@ -43,6 +44,11 @@ void Molecules::Init(Setup & setup, Forcefield & forcefield, //Molecule instance arrays/data count = atoms.startIdxRes.size(); + if (count == 0) { + std::cerr << "Error: No Molecule was found in the PDB file(s)!" << std::endl; + exit(EXIT_FAILURE); + } + start = new uint [count + 1]; start = vect::TransferInto(start, atoms.startIdxRes); start[count] = atoms.x.size(); @@ -145,7 +151,6 @@ void Molecules::Init(Setup & setup, Forcefield & forcefield, pairVirCorrections[i * kindsCount + j]; } } - } void Molecules::PrintLJInfo(std::vector &totAtomKind, @@ -155,32 +160,72 @@ void Molecules::PrintLJInfo(std::vector &totAtomKind, if(printFlag) { uint size = totAtomKind.size(); printf("NonBonded 1-4 parameters:\n"); - printf("%-6s %-10s %17s %11s %7s \n", "Type1", "Type2", "Epsilon(K)", - "Sigma(A)", "N"); + if(forcefield.exp6) { + printf("%-6s %-10s %17s %11s %11s %11s %7s \n", "Type1", "Type2", + "Epsilon(K)", "Sigma(A)", "Rmax(A)", "Rmin(A)", "alpha"); + } else { + printf("%-6s %-10s %17s %11s %7s \n", "Type1", "Type2", "Epsilon(K)", + "Sigma(A)", "N"); + } for(uint i = 0; i < size; i++) { for(uint j = i; j < size; j++) { - printf("%-6s %-10s %17.4f %11.4f %7.2f \n", names[i].c_str(), - names[j].c_str(), - forcefield.particles->GetEpsilon_1_4(totAtomKind[i], - totAtomKind[j]), - forcefield.particles->GetSigma_1_4(totAtomKind[i], - totAtomKind[j]), - forcefield.particles->GetN_1_4(totAtomKind[i], - totAtomKind[j])); + if(forcefield.exp6) { + printf("%-6s %-10s %17.4f %11.4f %11.4f %11.4f %7.2f \n", + names[i].c_str(), names[j].c_str(), + forcefield.particles->GetEpsilon_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetSigma_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetRmax_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetRmin_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetN_1_4(totAtomKind[i], + totAtomKind[j])); + } else { + printf("%-6s %-10s %17.4f %11.4f %7.2f \n", names[i].c_str(), + names[j].c_str(), + forcefield.particles->GetEpsilon_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetSigma_1_4(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetN_1_4(totAtomKind[i], + totAtomKind[j])); + } } } std::cout << std::endl; printf("NonBonded parameters:\n"); - printf("%-6s %-10s %17s %11s %7s \n", "Type1", "Type2", "Epsilon(K)", - "Sigma(A)", "N"); + if(forcefield.exp6) { + printf("%-6s %-10s %17s %11s %11s %11s %7s \n", "Type1", "Type2", + "Epsilon(K)", "Sigma(A)", "Rmax(A)", "Rmin(A)", "alpha"); + } else { + printf("%-6s %-10s %17s %11s %7s \n", "Type1", "Type2", "Epsilon(K)", + "Sigma(A)", "N"); + } for(uint i = 0; i < size; i++) { for(uint j = i; j < size; j++) { - printf("%-6s %-10s %17.4f %11.4f %7.2f \n", names[i].c_str(), - names[j].c_str(), - forcefield.particles->GetEpsilon(totAtomKind[i], totAtomKind[j]), - forcefield.particles->GetSigma(totAtomKind[i], totAtomKind[j]), - forcefield.particles->GetN(totAtomKind[i], totAtomKind[j])); + if(forcefield.exp6) { + printf("%-6s %-10s %17.4f %11.4f %11.4f %11.4f %7.2f \n", + names[i].c_str(), names[j].c_str(), + forcefield.particles->GetEpsilon(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetSigma(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetRmax(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetRmin(totAtomKind[i], + totAtomKind[j]), + forcefield.particles->GetN(totAtomKind[i], + totAtomKind[j])); + } else { + printf("%-6s %-10s %17.4f %11.4f %7.2f \n", names[i].c_str(), + names[j].c_str(), + forcefield.particles->GetEpsilon(totAtomKind[i], totAtomKind[j]), + forcefield.particles->GetSigma(totAtomKind[i], totAtomKind[j]), + forcefield.particles->GetN(totAtomKind[i], totAtomKind[j])); + } } } std::cout << std::endl; diff --git a/src/Molecules.h b/src/Molecules.h index f972efdcc..fde0b7efa 100644 --- a/src/Molecules.h +++ b/src/Molecules.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -104,6 +104,7 @@ class Molecules MoleculeKind * kinds; uint kindsCount; + uint fractionKind, lambdaSize; double* pairEnCorrections; double* pairVirCorrections; diff --git a/src/MoveConst.h b/src/MoveConst.h index 34e2a258b..c981964bc 100644 --- a/src/MoveConst.h +++ b/src/MoveConst.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -28,160 +28,80 @@ const uint GEMC_NPT = 1; ////////////////////////////////////////////////////////// const uint DISPLACE = 0; const uint ROTATE = 1; +const uint MULTIPARTICLE = 2; #if ENSEMBLE == NVT -const uint INTRA_SWAP = 2; -const uint REGROWTH = 3; -const uint INTRA_MEMC = 4; -const uint CRANKSHAFT = 5; -const uint MOVE_KINDS_TOTAL = 6; -#elif ENSEMBLE == GCMC -const uint INTRA_SWAP = 2; -const uint REGROWTH = 3; -const uint INTRA_MEMC = 4; -const uint CRANKSHAFT = 5; -const uint MEMC = 6; -const uint MOL_TRANSFER = 7; -const uint MOVE_KINDS_TOTAL = 8; -#elif ENSEMBLE == GEMC -const uint VOL_TRANSFER = 2; const uint INTRA_SWAP = 3; const uint REGROWTH = 4; const uint INTRA_MEMC = 5; const uint CRANKSHAFT = 6; -const uint MEMC = 7; -const uint MOL_TRANSFER = 8; -const uint MOVE_KINDS_TOTAL = 9; -#elif ENSEMBLE == NPT -const uint VOL_TRANSFER = 2; +const uint MOVE_KINDS_TOTAL = 7; +#elif ENSEMBLE == GCMC const uint INTRA_SWAP = 3; const uint REGROWTH = 4; const uint INTRA_MEMC = 5; const uint CRANKSHAFT = 6; -const uint MOVE_KINDS_TOTAL = 7; +const uint MEMC = 7; +const uint MOL_TRANSFER = 8; +const uint CFCMC = 9; +const uint MOVE_KINDS_TOTAL = 10; +#elif ENSEMBLE == GEMC +const uint VOL_TRANSFER = 3; +const uint INTRA_SWAP = 4; +const uint REGROWTH = 5; +const uint INTRA_MEMC = 6; +const uint CRANKSHAFT = 7; +const uint MEMC = 8; +const uint MOL_TRANSFER = 9; +const uint CFCMC = 10; +const uint MOVE_KINDS_TOTAL = 11; +#elif ENSEMBLE == NPT +const uint VOL_TRANSFER = 3; +const uint INTRA_SWAP = 4; +const uint REGROWTH = 5; +const uint INTRA_MEMC = 6; +const uint CRANKSHAFT = 7; +const uint MOVE_KINDS_TOTAL = 8; #endif const uint BOX0 = 0; const uint BOX1 = 1; -const uint PICK_ONE_MOLECULE = 0; -const uint PICK_ALL_MOLECULE = 1; - -const uint TRANS_DISPLACE = 0; -const uint TRANS_ROTATE = 1; -const uint TRANS_SCALE_COM = 2; -//Vlugt CBMC scheme, simple gen. -// -//Weight comes from the intramolecular potential (LJ(inter, intra)+TC) -//While intra only is used by stochastic generation and pick of final -//trial pos to use. -// -//This is only valid, generally for linear molecules. -const uint TRANS_GEN_TRIAL_LINEAR = 3; -//Pick angles that match Boltzmann acc. for n_ch_bend -//Pass them to dihedrals, pick one via Boltzman. -// -//Disadvantage: -// n_ch_lj is limited to a single generated conformation. -// LJ particle is then locked into place as we must always accept -// pick it when generating trial conf. in the old box. -// hence if we had more trial conformations over a free new box particle -// it'd introduce a bias. -// -// i.e. We must pick ONE and only one of each "thing" per conformation -// phase, hence in the last phase we always have to pick the old conf. -// in the old box, hence you can't do biased sel. of locations based -// on the LJ pot. -// -//IN OLD BOX: -// if first angle, usual actual angle, dihedral, lj in old.(i=j=k=1) -// if first dihedral, dihedral, lj is old (j=k=1) -// if first lj ... just LJ is old., i.e. in old box -// Now each phase incorporates the weight of the last, so the pick -const uint GEN_TRIAL_DECOUPLED = 4; -//Pick angles that match boltz. acc., then pick dihedrals based on -// wi * Wb / Wt -//We now have if i = j = k = 1 is the only case where the old conf. -//is used. Otherwise since each step depends on the last -const uint GEN_TRIAL_COUPLED = 5; -const uint GEN_TRIAL_COUPLED_DECOUPLED = 6; -const uint TRANS_KINDS_TOTAL = 7; - -const uint IT_SINGLE_MOL = 0; -const uint IT_ALL_MOL = 1; -const uint IT_KINDS_TOTAL = 2; - ////////////////////////////////////////////////////////// -//NVT : 1. Disp (box 0) 2. Rotate (box 0) 3. IntraSwap (box 0) -// 4. Regrowth (box 0) 5. IntraMEMC (box 0) 6. CrankShaft (box 0) +//NVT : 1. Disp (box 0) 2. Rotate (box 0) 3. MultiParticle (box 0) +// 4. IntraSwap (box 0) 5. Regrowth (box 0) 6. IntraMEMC (box 0) +// 7. CrankShaft (box 0) // -//GCMC: 1. Disp (box 0) 2. Rotate (box 0) 3. IntraSwap (box 0) -// 4. Regrowth (box 0) 5. IntraMEMC (box 0) 6. CrankShaft (box 0) -// 7. MEMC (box 0) 8. Deletion (box 0) 9. Insertion (box 0) +//GCMC: 1. Disp (box 0) 2. Rotate (box 0) 3. MultiParticle (box 0) +// 4. IntraSwap (box 0) 5. Regrowth (box 0) 6. IntraMEMC (box 0) +// 7. CrankShaft (box 0) 8. MEMC (box 0) 9. Deletion (box 0) +// 10. Insertion (box 0) 11. CFCMC (box 0) 12. CFCMC (box 1) // -//GEMC: 1. Disp (box 0) 2. Disp (box 1) -// 3. Rotate (box 0) 4. Rotate (box 1) -// 5. Vol. (b0->b1) 6. Vol. (b1->b0) -// 7. IntraSwap (box 0) 8. IntraSwap (box 1) -// 9. Regrowth (box 0) 10. Regrowth (box 1) -// 11. IntraMEMC (box 0) 12. IntraMEMC (box 1) -// 13. CrankShaft (box 0) 14. CrankShaft (box 1) -// 15. MEMC (box 0) 16. MEMC (box 1) -// 17. Mol Trans (b0->b1), 18. Mol Trans (b1->b0) +//GEMC: 1. Disp (box 0) 2. Disp (box 1) +// 3. MultiParticle (box 0) 4. MultiParticle (box 1) +// 5. Rotate (box 0) 6. Rotate (box 1) +// 7. Vol. (b0->b1) 8. Vol. (b1->b0) +// 9. IntraSwap (box 0) 10. IntraSwap (box 1) +// 11. Regrowth (box 0) 12. Regrowth (box 1) +// 13. IntraMEMC (box 0) 14. IntraMEMC (box 1) +// 15. CrankShaft (box 0) 16. CrankShaft (box 1) +// 17. MEMC (box 0) 18. MEMC (box 1) +// 19. Mol Trans (b0->b1) 20. Mol Trans (b1->b0) +// 21. CFCMC (b0->b1) 22. CFCMC (b1->b0) // -//NPT : 1. Disp (box 0) 2. Rotate (box 0) 3. Vol. (box 0) -// 4. IntraSwap (box 0) 5. Regrowth (box 0) 6. IntraMEMC (box 0) -// 7. CrankShaft (box 0) - -/* -#if ENSEMBLE == NVT -const uint COUNT = 6; -const uint SCALEABLE = 2; -#elif ENSEMBLE == GCMC -const uint COUNT = 9; -const uint SCALEABLE = 2; -#elif ENSEMBLE == GEMC -const uint COUNT = 18; -const uint SCALEABLE = 6; -#elif ENSEMBLE == NPT -const uint COUNT = 7; -const uint SCALEABLE = 3; -#endif - -*/ - +//NPT : 1. Disp (box 0) 2. Rotate (box 0) 3. MultiParticle (box 0) +// 4. Vol. (box 0) 5. IntraSwap (box 0) 6. Regrowth (box 0) +// 7. IntraMEMC (box 0) 8. CrankShaft (box 0) //AUTO REJECTION OR ACCEPTANCE FLAGS -//early exit flags. -namespace auto_accept -{ -const uint ONLY_IN_BOX_ROT_OR_DISP = 0; -} namespace fail_state { const uint NO_FAIL = 1; const uint ROTATE_ON_SINGLE_ATOM = 2; const uint NO_MOL_OF_KIND_IN_BOX = 3; -const uint INNER_CUTOFF_NEW_TRIAL_POS = 4; -const uint VOL_TRANS_WOULD_SHRINK_BOX_BELOW_CUTOFF = 5; } -/* -inline uint GetMoveSubIndex(const uint maj, const uint b = 0) -{ -#if ENSEMBLE == GCMC - if(maj == mv::MOL_TRANSFER) - return maj + b; - else - return maj; -#else - return maj * BOX_TOTAL + b; -#endif -} -*/ - - } #endif /*MOVES_CONST_H*/ diff --git a/src/MoveSettings.cpp b/src/MoveSettings.cpp index a89ed86c7..cd0963251 100644 --- a/src/MoveSettings.cpp +++ b/src/MoveSettings.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -19,7 +19,7 @@ void MoveSettings::Init(StaticVals const& statV, pdb_setup::Remarks const& remarks, const uint tkind) { - + isSingleMoveAccepted = true; totKind = tkind; perAdjust = statV.simEventFreq.perAdjust; for(uint b = 0; b < BOX_TOTAL; b++) { @@ -62,6 +62,16 @@ void MoveSettings::Init(StaticVals const& statV, } } } + + // Initialize MultiParticle settings + for(int b = 0; b < BOX_TOTAL; b++) { + mp_r_max[b] = 0.02 * M_PI; + mp_t_max[b] = 0.05; + for(int m = 0; m < mp::MPMVCOUNT; m++) { + mp_tries[b][m] = 0; + mp_accepted[b][m] = 0; + } + } } //Process results of move we just did in terms of acceptance counters @@ -73,14 +83,20 @@ void MoveSettings::Update(const uint move, const bool isAccepted, if(isAccepted) { tempAccepted[box][move][kind]++; accepted[box][move][kind]++; + + if(move != mv::MULTIPARTICLE) + isSingleMoveAccepted = true; } + if(move == mv::MULTIPARTICLE) + isSingleMoveAccepted = false; + acceptPercent[box][move][kind] = (double)(accepted[box][move][kind]) / (double)(tries[box][move][kind]); //for any move that we dont care about kind of molecule, it should be included //in the if condition - if (move == mv::INTRA_MEMC + if (move == mv::INTRA_MEMC || move == mv::MULTIPARTICLE #if ENSEMBLE == GEMC || ENSEMBLE == GCMC || move == mv::MEMC #endif @@ -115,6 +131,37 @@ void MoveSettings::AdjustMoves(const uint step) } } +void MoveSettings::AdjustMultiParticle(const uint box, const uint typePick) +{ + uint totalTries = mp_tries[box][mp::MPDISPLACE] + + mp_tries[box][mp::MPROTATE]; + if((totalTries + 1) % perAdjust == 0 ) { + double currentAccept = (double)mp_accepted[box][mp::MPDISPLACE] / + (double)mp_tries[box][mp::MPDISPLACE]; + double fractOfTargetAccept = currentAccept / mp::TARGET_ACCEPT_FRACT; + mp_t_max[box] *= fractOfTargetAccept; + num::Bound(mp_t_max[box], 0.001, + (boxDimRef.axis.Min(box) / 2) - 0.001); + + currentAccept = (double)mp_accepted[box][mp::MPROTATE] / + (double)mp_tries[box][mp::MPROTATE]; + fractOfTargetAccept = currentAccept / mp::TARGET_ACCEPT_FRACT; + mp_r_max[box] *= fractOfTargetAccept; + num::Bound(mp_r_max[box], 0.001, M_PI - 0.001); + } +} + +void MoveSettings::UpdateMoveSettingMultiParticle(const uint box, bool isAccept, + const uint typePick) +{ + if(typePick != mp::MPALLRANDOM) { + mp_tries[box][typePick]++; + if(isAccept) { + mp_accepted[box][typePick]++; + } + } +} + //Adjust responsibly void MoveSettings::Adjust(const uint box, const uint move, const uint kind) { @@ -172,7 +219,7 @@ uint MoveSettings::GetAcceptTot(const uint box, const uint move) const sum += accepted[box][move][k]; } - if(move == mv::INTRA_MEMC + if(move == mv::INTRA_MEMC || move == mv::MULTIPARTICLE #if ENSEMBLE == GEMC || ENSEMBLE == GCMC || move == mv::MEMC #endif @@ -182,7 +229,6 @@ uint MoveSettings::GetAcceptTot(const uint box, const uint move) const ) { sum /= totKind; } - return sum; } @@ -197,12 +243,12 @@ double MoveSettings::GetScaleTot(const uint box, const uint move) const uint MoveSettings::GetTrialTot(const uint box, const uint move) const { - uint sum = 0.0; + uint sum = 0; for(uint k = 0; k < totKind; k++) { sum += tries[box][move][k]; } - if(move == mv::INTRA_MEMC + if(move == mv::INTRA_MEMC || move == mv::MULTIPARTICLE #if ENSEMBLE == GEMC || ENSEMBLE == GCMC || move == mv::MEMC #endif diff --git a/src/MoveSettings.h b/src/MoveSettings.h index a024e0c20..24fe4c24a 100644 --- a/src/MoveSettings.h +++ b/src/MoveSettings.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -14,6 +14,21 @@ along with this program, also can be found at . #include "MoveConst.h" //For sizes of arrays. #include +namespace mp +{ +const int MPDISPLACE = 0; +const int MPROTATE = 1; +const int MPMVCOUNT = 2; +const int MPALLDISPLACE = 0; +const int MPALLROTATE = 1; +const int MPALLRANDOM = 2; +// MPTOTALTYPES = 2, we perform either: All displacement, All rotation move. +// MPTOTALTYPES = 3, we perform either: All displacement, All rotation move, +// or combined displacement+rotation +const int MPTOTALTYPES = 2; +const double TARGET_ACCEPT_FRACT = 0.3; +} + class StaticVals; //For various initialization constants. class BoxDimensions; //For axis sizes @@ -31,6 +46,10 @@ class MoveSettings tries.resize(BOX_TOTAL); tempAccepted.resize(BOX_TOTAL); tempTries.resize(BOX_TOTAL); + mp_r_max.resize(BOX_TOTAL); + mp_t_max.resize(BOX_TOTAL); + mp_accepted.resize(BOX_TOTAL); + mp_tries.resize(BOX_TOTAL); for(uint b = 0; b < BOX_TOTAL; b++) { acceptPercent[b].resize(mv::MOVE_KINDS_TOTAL); scale[b].resize(mv::MOVE_KINDS_TOTAL); @@ -38,6 +57,8 @@ class MoveSettings tries[b].resize(mv::MOVE_KINDS_TOTAL); tempAccepted[b].resize(mv::MOVE_KINDS_TOTAL); tempTries[b].resize(mv::MOVE_KINDS_TOTAL); + mp_accepted[b].resize(mp::MPMVCOUNT); + mp_tries[b].resize(mp::MPMVCOUNT); } } @@ -56,6 +77,10 @@ class MoveSettings void Adjust(const uint box, const uint move, const uint kind); + void AdjustMultiParticle(const uint box, const uint typePick); + + void UpdateMoveSettingMultiParticle(uint box, bool isAccept, uint typePick); + double Scale(const uint box, const uint move, const uint kind = 0) const { return scale[box][move][kind]; @@ -71,17 +96,39 @@ class MoveSettings return tries[box][move][kind]; } + double GetRMAX(const uint box) const + { + return mp_r_max[box]; + } + + double GetTMAX(const uint box) const + { + return mp_t_max[box]; + } + uint GetAcceptTot(const uint box, const uint move) const; uint GetTrialTot(const uint box, const uint move) const; double GetScaleTot(const uint box, const uint move) const; + bool GetSingleMoveAccepted() + { + return isSingleMoveAccepted; + } + void SetSingleMoveAccepted() + { + isSingleMoveAccepted = true; + } private: vector< vector< vector > > scale, acceptPercent; vector< vector< vector > > accepted, tries, tempAccepted, tempTries; + vector< vector< uint > > mp_accepted, mp_tries; + vector< double > mp_r_max; + vector< double > mp_t_max; uint perAdjust; uint totKind; + bool isSingleMoveAccepted; #if ENSEMBLE == GEMC uint GEMC_KIND; diff --git a/src/NoEwald.cpp b/src/NoEwald.cpp index 429e5ae7b..2acb0b96b 100644 --- a/src/NoEwald.cpp +++ b/src/NoEwald.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -35,15 +35,16 @@ double NoEwald::BoxReciprocal(uint box) const return 0.0; } - -//calculate reciprocate force term for a box -Virial NoEwald::ForceReciprocal(Virial& virial, uint box) const +//calculate reciprocate force term for a box with molCoords +void NoEwald::BoxForceReciprocal(XYZArray const& molCoords, + XYZArray& atomForceRec, XYZArray& molForceRec, + uint box) { - return virial; + return; } -//calculate correction force term for a box -Virial NoEwald::ForceCorrection(Virial& virial, uint box) const +//calculate reciprocate force term for a box +Virial NoEwald::VirialReciprocal(Virial& virial, uint box) const { return virial; } @@ -57,6 +58,15 @@ double NoEwald::MolReciprocal(XYZArray const& molCoords, } +//calculate reciprocate term for lambdaNew and Old with same coordinates +double NoEwald::CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box) +{ + return 0.0; +} + + //calculate self term for a box double NoEwald::BoxSelf(BoxDimensions const& boxAxes, uint box) const { @@ -91,7 +101,9 @@ double NoEwald::SwapSourceRecip(const cbmc::TrialMol &oldMol, //calculate reciprocate term for inserting some molecules (kindA) in destination // box and removing a molecule (kindB) from destination box double NoEwald::SwapRecip(const std::vector &newMol, - const std::vector &oldMol) + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexold) { return 0.0; } @@ -108,6 +120,43 @@ double NoEwald::SwapCorrection(const cbmc::TrialMol& trialMol) const { return 0.0; } +//calculate correction term after swap move +double NoEwald::SwapCorrection(const cbmc::TrialMol& trialMol, + const uint molIndex) const +{ + return 0.0; +} + +//It's called in free energy calculation to calculate the change in +// self energy in all lambda states +void NoEwald::ChangeSelf(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + return; +} + +//It's called in free energy calculation to calculate the change in +// correction energy in all lambda states +void NoEwald::ChangeCorrection(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + return; +} + +//It's called in free energy calculation to calculate the change in +// reciprocal energy in all lambda states +void NoEwald::ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const +{ + return; +} + //back up reciptocate value to Ref (will be called during initialization) void NoEwald::SetRecipRef(uint box) diff --git a/src/NoEwald.h b/src/NoEwald.h index 87758b204..d9a62e1ec 100644 --- a/src/NoEwald.h +++ b/src/NoEwald.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -33,11 +33,13 @@ class NoEwald : public Ewald //calculate reciprocate energy term for a box virtual double BoxReciprocal(uint box) const; - //calculate reciprocate force term for a box - virtual Virial ForceReciprocal(Virial& virial, uint box) const; + //calculate reciprocate force term for a box with molCoords + virtual void BoxForceReciprocal(XYZArray const& molCoords, + XYZArray& atomForceRec, XYZArray& molForceRec, + uint box); - //calculate correction force term for a box - virtual Virial ForceCorrection(Virial& virial, uint box) const; + //calculate reciprocate force term for a box + virtual Virial VirialReciprocal(Virial& virial, uint box) const; //calculate correction term for a molecule virtual double MolCorrection(uint molIndex, uint box)const; @@ -46,12 +48,21 @@ class NoEwald : public Ewald virtual double MolReciprocal(XYZArray const& molCoords, const uint molIndex, const uint box); + //calculate reciprocate term for lambdaNew and Old with same coordinates + virtual double CFCMCRecip(XYZArray const& molCoords, const double lambdaOld, + const double lambdaNew, const uint molIndex, + const uint box); + //calculate self term after swap move virtual double SwapSelf(const cbmc::TrialMol& trialMo) const; - //calculate correction term after swap move + //calculate correction term after swap move with lambda = 1 virtual double SwapCorrection(const cbmc::TrialMol& trialMol) const; + //calculate correction term after swap move, with system lambda + virtual double SwapCorrection(const cbmc::TrialMol& trialMol, + const uint molIndex) const; + //calculate reciprocate term in destination box for swap move virtual double SwapDestRecip(const cbmc::TrialMol &newMol, const uint box, const int molIndex); @@ -63,11 +74,34 @@ class NoEwald : public Ewald //calculate reciprocate term for inserting some molecules (kindA) in //destination box and removing a molecule (kindB) from destination box virtual double SwapRecip(const std::vector &newMol, - const std::vector &oldMol); + const std::vector &oldMol, + const std::vector molIndexNew, + const std::vector molIndexold); //back up reciptocate value to Ref (will be called during initialization) virtual void SetRecipRef(uint box); + //It's called in free energy calculation to calculate the change in + // self energy in all lambda states + virtual void ChangeSelf(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + + //It's called in free energy calculation to calculate the change in + // correction energy in all lambda states + virtual void ChangeCorrection(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + + //It's called in free energy calculation to calculate the change in + // reciprocal energy in all lambda states + virtual void ChangeRecip(Energy *energyDiff, Energy &dUdL_Coul, + const std::vector &lambda_Coul, + const uint iState, const uint molIndex, + const uint box) const; + //update reciprocate values virtual void UpdateRecip(uint box); diff --git a/src/OutConst.cpp b/src/OutConst.cpp index e4ca2f8a9..00d300776 100644 --- a/src/OutConst.cpp +++ b/src/OutConst.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/OutConst.h b/src/OutConst.h index ded187891..38f51e9ac 100644 --- a/src/OutConst.h +++ b/src/OutConst.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/OutputAbstracts.h b/src/OutputAbstracts.h index 59fd8aab6..94ba31325 100644 --- a/src/OutputAbstracts.h +++ b/src/OutputAbstracts.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -16,6 +16,13 @@ along with this program, also can be found at . #include "ConfigSetup.h" //For enables, etc. #include "PDBSetup.h" //For atoms class +#include "GOMC_Config.h" //For MPI +#ifdef WIN32 +#define OS_SEP '\\' +#else +#define OS_SEP '/' +#endif + class OutputVars; class System; @@ -61,7 +68,28 @@ class OutputableBase void Init(const ulong tillEquil, const ulong totSteps, std::string const& uniqueForFileIO) { +#if GOMC_LIB_MPI + // For some reason, whether or not split passes by reference or value changes per OS. + // Hence the need for a disposable copy of the string + std::string copyOfUniqueForFileIO = uniqueForFileIO.c_str(); +#ifdef WIN32 + std::vector tokens = OutputableBase::split(copyOfUniqueForFileIO, std::string(2, OS_SEP)); +#else + std::vector tokens = OutputableBase::split(copyOfUniqueForFileIO, std::string(1, OS_SEP)); +#endif + std::stringstream replicaDirectory; + // Loop terminates before last entry to remove the value that + // The user gets passes as "OutputName", which we have prefixed w a path + // In the ParallelTemperingPreproccessing in Main.cpp. + // This leaves only the path, which we store as "pathToReplicaDirectory" + for(int i = 0; i < tokens.size() - 1; ++i) { + replicaDirectory << tokens[i] << OS_SEP; + } + pathToReplicaDirectory = replicaDirectory.str(); + uniqueName = tokens[tokens.size() - 1]; +#else uniqueName = uniqueForFileIO; +#endif stepsTillEquil = tillEquil; totSimSteps = totSteps; firstPrint = true; @@ -77,8 +105,26 @@ class OutputableBase forceOutput = false; } +#if GOMC_LIB_MPI + std::vector split(std::string str, std::string sep) + { + char* cstr = const_cast(str.c_str()); + char* current; + std::vector arr; + current = strtok(cstr, sep.c_str()); + while(current != NULL) { + arr.push_back(current); + current = strtok(NULL, sep.c_str()); + } + return arr; + } +#endif + //private: std::string uniqueName; +#if GOMC_LIB_MPI + std::string pathToReplicaDirectory; +#endif ulong stepsPerOut, stepsTillEquil, totSimSteps; bool enableOut, firstPrint; bool forceOutput; diff --git a/src/OutputVars.cpp b/src/OutputVars.cpp index c2dfea077..92bfa25a9 100644 --- a/src/OutputVars.cpp +++ b/src/OutputVars.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -15,14 +15,13 @@ along with this program, also can be found at . #endif OutputVars::OutputVars(System & sys, StaticVals const& statV) : - calc(sys.calcEnergy) + calc(sys.calcEnergy), T_in_K(statV.forcefield.T_in_K) { InitRef(sys, statV); } void OutputVars::InitRef(System & sys, StaticVals const& statV) { - T_in_K = statV.forcefield.T_in_K; volumeRef = sys.boxDimRef.volume; axisRef = &sys.boxDimRef.axis; volInvRef = sys.boxDimRef.volInv; @@ -122,56 +121,65 @@ void OutputVars::CalcAndConvert(ulong step) //in) if (pressureCalc) { - if((step + 1) % pCalcFreq == 0) { - virialRef[b] = calc.ForceCalc(b); - *virialTotRef += virialRef[b]; - } - } - - //calculate surface tension in mN/M - surfaceTens[b] = (virialRef[b].totalTens[2][2] - 0.5 * - (virialRef[b].totalTens[0][0] + - virialRef[b].totalTens[1][1])); - surfaceTens[b] *= unit::K_TO_MN_PER_M / - (2.0 * axisRef->Get(b).x * axisRef->Get(b).y); - - //save the pressure tensor for print - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - //convert K to bar - pressureTens[b][i][j] = virialRef[b].totalTens[i][j] * - unit::K_MOLECULE_PER_A3_TO_BAR / volumeRef[b]; - } - } + if((step + 1) % pCalcFreq == 0 || step == 0) { + if(step != 0) { + virialRef[b] = calc.VirialCalc(b); + *virialTotRef += virialRef[b]; + } + //calculate surface tension in mN/M + surfaceTens[b] = (virialRef[b].totalTens[2][2] - 0.5 * + (virialRef[b].totalTens[0][0] + + virialRef[b].totalTens[1][1])); + surfaceTens[b] *= unit::K_TO_MN_PER_M / + (2.0 * axisRef->Get(b).x * axisRef->Get(b).y); + + //save the pressure tensor for print + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + //convert K to bar + pressureTens[b][i][j] = virialRef[b].totalTens[i][j] * + unit::K_MOLECULE_PER_A3_TO_BAR / volumeRef[b]; + } + } + + virial[b] = virialRef[b]; + virial[b] /= unit::DIMENSIONALITY; + virial[b] /= volumeRef[b]; + virial[b].Total(); + //Calculate the pressure + rawPressure[b] = 0.0; + for (uint k = 0; k < numKinds; k++) { + // Instead of converting to mass first + // (an alternate route to calculate the ideal gas pressure) + // the form of the Boltzmann constant that kcal/mol/K is used + // such that a single conversion factor can be applied to both + // the ideal and virial components of the pressure. + rawPressure[b] += densityByKindBox[k + numKinds * b]; + } + // Finish ideal component + rawPressure[b] *= T_in_K; + // Add the virial component + rawPressure[b] += virial[b].total; + + // Convert to desired units + // ( starting: K * molecule / Angstrom^3 ) + pressure[b] = rawPressure[b]; + pressure[b] *= unit::K_MOLECULE_PER_A3_TO_BAR; - virial[b] = virialRef[b]; - virial[b] /= unit::DIMENSIONALITY; - virial[b] /= volumeRef[b]; - virial[b].Total(); - - rawPressure[b] = 0.0; - densityTot[b] = 0.0; - for (uint k = 0; k < numKinds; k++) { - double density = densityByKindBox[k + numKinds * b]; - - // Instead of converting to mass first - // (an alternate route to calculate the ideal gas pressure) - // the form of the Boltzmann constant that kcal/mol/K is used - // such that a single conversion factor can be applied to both - // the ideal and virial components of the pressure. - rawPressure[b] += density; +#if ENSEMBLE == GEMC + // delta Hv = (Uv-Ul) + P(Vv-Vl) + heatOfVap = energyRef[vapBox].total / numByBox[vapBox] - + energyRef[liqBox].total / numByBox[liqBox]; + heatOfVap += rawPressure[vapBox] * + (volumeRef[vapBox] / numByBox[vapBox] - + volumeRef[liqBox] / numByBox[liqBox]); + heatOfVap *= unit::K_TO_KJ_PER_MOL; +#endif + } } - // Finish ideal component - rawPressure[b] *= T_in_K; - // Add the virial component - rawPressure[b] += virial[b].total; - - // Convert to desired units - // ( starting: K * molecule / Angstrom^3 ) - pressure[b] = rawPressure[b]; - pressure[b] *= unit::K_MOLECULE_PER_A3_TO_BAR; } + for (uint b = 0; b < BOX_TOTAL; b++) { densityTot[b] = 0.0; for (uint k = 0; k < numKinds; k++) { @@ -185,13 +193,4 @@ void OutputVars::CalcAndConvert(ulong step) } densityTot[b] *= 1000; } -#if ENSEMBLE == GEMC - // delta Hv = (Uv-Ul) + P(Vv-Vl) - heatOfVap = energyRef[vapBox].total / numByBox[vapBox] - - energyRef[liqBox].total / numByBox[liqBox] + - rawPressure[vapBox] * (volumeRef[vapBox] / numByBox[vapBox] - - volumeRef[liqBox] / numByBox[liqBox]); - heatOfVap *= unit::K_TO_KJ_PER_MOL; -#endif - } diff --git a/src/OutputVars.h b/src/OutputVars.h index 54617c2fe..09ea249f2 100644 --- a/src/OutputVars.h +++ b/src/OutputVars.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -48,7 +48,7 @@ class OutputVars uint numKinds; //Constants - double T_in_K; + const double& T_in_K; //References double * volumeRef; diff --git a/src/PDBConst.h b/src/PDBConst.h index caac9d8e1..3312dd3a1 100644 --- a/src/PDBConst.h +++ b/src/PDBConst.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -25,6 +25,7 @@ namespace label static const std::string REMARK = "REMARK"; static const std::string CRYST1 = "CRYST1"; static const std::string ATOM = "ATOM "; +static const std::string HETATM = "HETATM"; static const ConstField POS(0, 6); } namespace remark diff --git a/src/PDBOutput.cpp b/src/PDBOutput.cpp index 734bf1cc0..b7f187ee6 100644 --- a/src/PDBOutput.cpp +++ b/src/PDBOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/PDBOutput.h b/src/PDBOutput.h index 5103c33f3..0c566416d 100644 --- a/src/PDBOutput.h +++ b/src/PDBOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/PDBSetup.cpp b/src/PDBSetup.cpp index 986fb2253..316443e67 100644 --- a/src/PDBSetup.cpp +++ b/src/PDBSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -210,9 +210,15 @@ void PDBSetup::Init(config_setup::RestartSettings const& restart, } else { alias = pdbAlias[b]; } - FixedWidthReader pdb(name[b], alias); - pdb.open(); - while (pdb.Read(varName, pdb_entry::label::POS)) { + pdb[b].SetData(name[b], alias); + + // Open PDB only once and stay there + // instead of re-opening it for every frame + // refer to issue #131 + if(frameNum == 1) + pdb[b].open(); + + while (pdb[b].Read(varName, pdb_entry::label::POS)) { //If end of frame, and this is the frame we wanted, //end read on this file if (remarks.reached[b] && str::compare(varName, pdb_entry::end::STR)) { @@ -225,7 +231,7 @@ void PDBSetup::Init(config_setup::RestartSettings const& restart, if (dataKind != dataKinds.end() && (remarks.reached[b] || str::compare(dataKind->first, pdb_entry::label::REMARK))) { - dataKind->second->Read(pdb); + dataKind->second->Read(pdb[b]); } } // If the recalcTrajectory is true and reached was still false @@ -235,7 +241,6 @@ void PDBSetup::Init(config_setup::RestartSettings const& restart, << ".. and couldn't find remark in PDB file!" << std::endl; exit(EXIT_FAILURE); } - pdb.close(); } } diff --git a/src/PDBSetup.h b/src/PDBSetup.h index 9229c3fec..7ecb820e7 100644 --- a/src/PDBSetup.h +++ b/src/PDBSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -111,6 +111,7 @@ struct PDBSetup { pdb_setup::Atoms atoms; pdb_setup::Cryst1 cryst; pdb_setup::Remarks remarks; + FixedWidthReader pdb[BOX_TOTAL]; PDBSetup(void) : dataKinds(SetReadFunctions()) {} void Init(config_setup::RestartSettings const& restart, std::string const*const name, uint frameNumber = 1); @@ -123,6 +124,7 @@ struct PDBSetup { funct[pdb_entry::label::REMARK] = &remarks; funct[pdb_entry::label::CRYST1] = &cryst; funct[pdb_entry::label::ATOM] = &atoms; + funct[pdb_entry::label::HETATM] = &atoms; return funct; } const std::map dataKinds; diff --git a/src/PRNG.h b/src/PRNG.h index daee684be..d700ebce8 100644 --- a/src/PRNG.h +++ b/src/PRNG.h @@ -1,6 +1,6 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -235,7 +235,6 @@ class PRNG subDraw -= b * boxDiv; } - void PickBool(bool &b, const double subDraw, const double movPerc) const { //Calculate "chunk" of move for each box. @@ -308,6 +307,37 @@ class PRNG return rejectState; } + //Returns false if none of that kind of molecule in selected box or molecule + //is fixed using tag (beta == 1). + uint PickMol(uint & m, uint & mk, const uint b) + { + uint rejectState = mv::fail_state::NO_FAIL; + uint mkTot = molLookRef.GetNumCanMoveKind(); + //Which molecule kind chunk are we in? + uint k = randIntExc(mkTot); + + //clamp if some rand err. + if (k == mkTot) + k = mkTot - 1; + + mk = molLookRef.GetCanMoveKind(k); + + //Pick molecule with the help of molecule lookup table. + if ((molLookRef.NumKindInBox(mk, b) == 0)) { + rejectState = mv::fail_state::NO_MOL_OF_KIND_IN_BOX; + } else { + //Among the ones of that kind in that box, pick one @ random. + //Molecule with a tag (beta == 1) cannot be selected. + do { + uint mOff = randIntExc(molLookRef.NumKindInBox(mk, b)); + //Lookup true index in table. + m = molLookRef.GetMolNum(mOff, mk, b); + } while(molLookRef.IsFix(m)); + } + + return rejectState; + } + //Returns false if none of that kind of molecule in selected box or molecule //is fixed using tag (beta >= 1). uint PickMol2(uint & m, uint & mk, const uint b, diff --git a/src/PRNGSetup.cpp b/src/PRNGSetup.cpp index daf144ca1..cd4b188c1 100644 --- a/src/PRNGSetup.cpp +++ b/src/PRNGSetup.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/PRNGSetup.h b/src/PRNGSetup.h index 64fe65466..ddf779d9a 100644 --- a/src/PRNGSetup.h +++ b/src/PRNGSetup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/PSFOutput.cpp b/src/PSFOutput.cpp index 656ec0d38..5c51bd52c 100644 --- a/src/PSFOutput.cpp +++ b/src/PSFOutput.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/PSFOutput.h b/src/PSFOutput.h index 16d15d380..cf48a538a 100644 --- a/src/PSFOutput.h +++ b/src/PSFOutput.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/ParallelTemperingPreprocessor.cpp b/src/ParallelTemperingPreprocessor.cpp new file mode 100644 index 000000000..6e0837ea0 --- /dev/null +++ b/src/ParallelTemperingPreprocessor.cpp @@ -0,0 +1,357 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ + +#include "ParallelTemperingPreprocessor.h" + +#if GOMC_LIB_MPI +ParallelTemperingPreprocessor::ParallelTemperingPreprocessor( int argc, + char *argv[]) +{ + // Initialize the MPI environment + MPI_Init(NULL, NULL); + + // Get the number of processes + MPI_Comm_size(MPI_COMM_WORLD, &worldSize); + + // Get the rank of the process + MPI_Comm_rank(MPI_COMM_WORLD, &worldRank); + + //CHECK IF ARGS/FILE PROVIDED IN CMD LINE + if (argc < 2) { + std::cout << "Error: Input parameter file (*.dat or *.conf) not specified on command line!\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } else { + if(argc == 2) { + //FIRST PARAMETER WILL BE FILE NAME + inputFileStringMPI = argv[1]; + } else { + //SECOND PARAMETER WILL BE FILE NAME + inputFileStringMPI = argv[2]; + + if(argv[1][0] == '+' && argv[1][1] == 'p') { + // placeholder + } else { + std::cout << "Error: Undefined command to set number of threads!\n"; + std::cout << "Use +p# command to set number of threads.\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } + } + //OPEN FILE + inputFileReaderMPI.open(inputFileStringMPI.c_str(), ios::in | ios::out); + + //CHECK IF FILE IS OPENED...IF NOT OPENED EXCEPTION REASON FIRED + if (!inputFileReaderMPI.is_open()) { + std::cout << "Error: Cannot open/find " << inputFileStringMPI << + " in the directory provided!\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } + + //CLOSE FILE TO NOW PASS TO SIMULATION + inputFileReaderMPI.close(); + + if(checkIfParallelTempering(inputFileStringMPI.c_str())) { + checkIfValid(inputFileStringMPI.c_str()); + if(worldRank >= getNumberOfReplicas(inputFileStringMPI.c_str())) { + std::cout << "You may not request more processes (" << worldSize + << ") than there are replicas(" << getNumberOfReplicas(inputFileStringMPI.c_str()) + << ")! Exiting this process[" << worldRank << "]!\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } else { +#if ENSEMBLE == GCMC + pathToReplicaDirectory = setupReplicaDirectoriesAndRedirectSTDOUTToFile ( getMultiSimFolderName(inputFileStringMPI.c_str()), + getTemperature(inputFileStringMPI.c_str(), worldRank), + getChemicalPotential(inputFileStringMPI.c_str(), worldRank)); +#else + pathToReplicaDirectory = setupReplicaDirectoriesAndRedirectSTDOUTToFile ( getMultiSimFolderName(inputFileStringMPI.c_str()), + getTemperature(inputFileStringMPI.c_str(), worldRank)); +#endif + } + } + } +} + +bool ParallelTemperingPreprocessor::checkIfValidRank() +{ + if (worldRank >= getNumberOfReplicas(inputFileStringMPI.c_str())) { + return false; + } else { + return true; + } +} + + +bool ParallelTemperingPreprocessor::checkIfParallelTempering(const char *fileName) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + bool isParallelTemperingInTemperature = false; + bool isParallelTemperingInChemicalPotential = false; + bool isParallelTemperingInFreeEnergyCoulomb = false; + bool isParallelTemperingInFreeEnergyVDW = false; + + while(reader.readNextLine(line)) { + if(line.size() == 0) + continue; + if(CheckString(line[0], "Temperature")) { + if (line.size() > 2) + isParallelTemperingInTemperature = true; + } else if (CheckString(line[0], "ChemPot")) { + if (line.size() > 3) + isParallelTemperingInChemicalPotential = true; + } else if (CheckString(line[0], "LambdaCoulomb")) { + if (line.size() > 2) + isParallelTemperingInFreeEnergyCoulomb = true; + } else if (CheckString(line[0], "LambdaVDW")) { + if (line.size() > 2) + isParallelTemperingInFreeEnergyVDW = true; + } + // Clear and get ready for the next line + line.clear(); + } + return isParallelTemperingInTemperature || isParallelTemperingInChemicalPotential || + isParallelTemperingInFreeEnergyCoulomb || isParallelTemperingInFreeEnergyVDW; +} + +void ParallelTemperingPreprocessor::checkIfValid(const char *fileName) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + int numberOfTemperatures = 0; + vector < int > numberOfChemPots; + int numberOfLambdaCoulombs = 0; + int numberOfLambdaVDWs = 0; + + while(reader.readNextLine(line)) { + if(line.size() == 0) + continue; + if(CheckString(line[0], "Temperature")) { + numberOfTemperatures = line.size() - 1; + } else if (CheckString(line[0], "ChemPot")) { + numberOfChemPots.push_back(line.size() - 2); + } else if (CheckString(line[0], "LambdaCoulomb")) { + numberOfLambdaCoulombs = line.size() - 1; + } else if (CheckString(line[0], "LambdaVDW")) { + numberOfLambdaVDWs = line.size() - 1; + } + // Clear and get ready for the next line + line.clear(); + } + + for( vector < int >::iterator it = numberOfChemPots.begin(); it != numberOfChemPots.end(); ++it ) { + if (*it > 1 && numberOfTemperatures > 1 && *it != numberOfTemperatures) { + std::cout << "Error: Unequal number of temperatures and chemical potentials in Multicanonical!\n"; + std::cout << "If you only want to only sample mu-space or temperature-space\n"; + std::cout << "provide only one temperature or only one chemical potential.\n"; + std::cout << "Number of temperatures provided: " << numberOfTemperatures << "\n"; + std::cout << "Number of chemical potentials provided: " << *it << "\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } + } + + if ( numberOfLambdaCoulombs != numberOfLambdaVDWs) { + std::cout << "Error: Unequal number of LambdaCoulombs and LambdaVDWs in Free Energy calculation!\n"; + std::cout << "Number of temperatures provided: " << numberOfLambdaCoulombs << "\n"; + std::cout << "Number of temperatures provided: " << numberOfLambdaVDWs << "\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } +} + +int ParallelTemperingPreprocessor::getNumberOfReplicas(const char *fileName) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + int numberOfTemperatures = 0; + vector < int > numberOfChemPots; + int numberOfLambdaCoulombs = 0; + int numberOfLambdaVDWs = 0; + + int numberOfReplicas = 0; + + while(reader.readNextLine(line)) { + if(line.size() == 0) + continue; + if(CheckString(line[0], "Temperature")) { + numberOfTemperatures = line.size() - 1; + } else if (CheckString(line[0], "ChemPot")) { + numberOfChemPots.push_back(line.size() - 2); + } else if (CheckString(line[0], "LambdaCoulomb")) { + numberOfLambdaCoulombs = line.size() - 1; + } else if (CheckString(line[0], "LambdaVDW")) { + numberOfLambdaVDWs = line.size() - 1; + } + // Clear and get ready for the next line + line.clear(); + } + + for( vector < int >::iterator it = numberOfChemPots.begin(); it != numberOfChemPots.end(); ++it ) { + numberOfReplicas = std::max(numberOfReplicas, *it); + } + numberOfReplicas = std::max(numberOfReplicas, numberOfTemperatures); + numberOfReplicas = std::max(numberOfReplicas, numberOfLambdaCoulombs); + numberOfReplicas = std::max(numberOfReplicas, numberOfLambdaVDWs); + + return numberOfReplicas; +} + +std::string ParallelTemperingPreprocessor::getMultiSimFolderName(const char *fileName) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + string folderName; + + while(reader.readNextLine(line)) { + if(line.size() == 0) { + continue; + } else if(line[0] == "MultiSimFolderName") { + std::stringstream ss; + for (int i = 1; i < line.size(); i++) { + if (line[i] == " ") { + ss << '_'; + } else { + ss << line[i]; + if (i + 1 != line.size()) + ss << "_"; + } + } + folderName = ss.str(); + } + // Clear and get ready for the next line + line.clear(); + } + if (folderName.empty()) { + folderName = "MultiSimFolderName"; + } + return folderName; +} + +std::string ParallelTemperingPreprocessor::getTemperature(const char *fileName, int worldRank) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + string temperature; + + + while(reader.readNextLine(line)) { + if(line.size() == 0) { + continue; + } else if(line[0] == "Temperature") { + if (line.size() > 2) { + temperature = line[worldRank + 1]; + } else { + temperature = line[1]; + } + } + // Clear and get ready for the next line + line.clear(); + } + return temperature; +} + + + +std::string ParallelTemperingPreprocessor::getChemicalPotential(const char *fileName, int worldRank) +{ + InputFileReader reader; + std::vector line; + reader.Open(fileName); + std::stringstream chemPotStream; + std::string resName; + std::string val; + + while(reader.readNextLine(line)) { + if(line.size() == 0) { + continue; + } else if(CheckString(line[0], "ChemPot")) { + if (line.size() > 3) { + resName = line[1]; + val = line[2 + worldRank]; + chemPotStream << "_" << resName << "_" << val; + } else if(line.size() != 3) { + std::cout << "Error: Chemical potential parameters are not specified!\n"; + MPI_Finalize(); + exit(EXIT_FAILURE); + } else { + resName = line[1]; + val = line[2]; + chemPotStream << "_" << resName << "_" << val; + } + } + // Clear and get ready for the next line + line.clear(); + } + return chemPotStream.str(); +} + +std::string ParallelTemperingPreprocessor::setupReplicaDirectoriesAndRedirectSTDOUTToFile(std::string multiSimTitle, std::string temperature) +{ + std::stringstream replicaTemp; + replicaTemp << "temp_" << temperature; + std::string replicaDirectory = replicaTemp.str(); + return mkdirWrapper(multiSimTitle, replicaDirectory); +} + +std::string ParallelTemperingPreprocessor::setupReplicaDirectoriesAndRedirectSTDOUTToFile(std::string multiSimTitle, std::string temperature, std::string chemPot) +{ + std::stringstream replicaTemp; + replicaTemp << "temp_" << temperature << chemPot; + std::string replicaDirectory = replicaTemp.str(); + return mkdirWrapper(multiSimTitle, replicaDirectory); +} + +std::string ParallelTemperingPreprocessor::mkdirWrapper(std::string multisimDirectoryName, string replicaDirectoryName) +{ + std::stringstream replicaStream; + + replicaStream << "." << OS_SEP << multisimDirectoryName << OS_SEP + << replicaDirectoryName << OS_SEP; + std::string replicaDirectoryPath = replicaStream.str(); + + //printf("Creating directory : %s\n", multisimDirectoryName.c_str()); + mkdir(multisimDirectoryName.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + mkdir(replicaDirectoryPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + std::string pathToReplicaDirectory = replicaStream.str(); + replicaStream << "ConsoleOut.dat"; + std::string pathToReplicaLogFile = replicaStream.str(); + if(worldRank == 0) { + std::cout << "Monitor progress of your simulation by navigating to a replica output directory and issuing:\n" + << "\t$ tail -f \"YourUniqueFileName\".console" << std::endl; + } + freopen(pathToReplicaLogFile.c_str(), "w", stdout); + return pathToReplicaDirectory; + +} + +bool ParallelTemperingPreprocessor::CheckString(string str1, string str2) +{ + for(int k = 0; k < str1.length(); k++) { + str1[k] = toupper(str1[k]); + } + + for(int j = 0; j < str2.length(); j++) { + str2[j] = toupper(str2[j]); + } + + return (str1 == str2); +} + +MultiSim::MultiSim(ParallelTemperingPreprocessor & pt) : + worldSize(pt.worldSize), worldRank(pt.worldRank), pathToReplicaDirectory(pt.pathToReplicaDirectory) +{} + +#endif \ No newline at end of file diff --git a/src/ParallelTemperingPreprocessor.h b/src/ParallelTemperingPreprocessor.h new file mode 100644 index 000000000..f8e242772 --- /dev/null +++ b/src/ParallelTemperingPreprocessor.h @@ -0,0 +1,66 @@ + +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef ParallelTemperingPreprocessor_H +#define ParallelTemperingPreprocessor_H +#include "InputFileReader.h" +#include +#include +#include +#include +#include + +#include "GOMC_Config.h" //For version number +#if GOMC_LIB_MPI +#include +#endif + +#ifdef WIN32 +#define OS_SEP '\\' +#else +#define OS_SEP '/' +#endif + +class ParallelTemperingPreprocessor +{ +public: + +#if GOMC_LIB_MPI + + explicit ParallelTemperingPreprocessor( int argc, + char *argv[]); + bool checkIfValidRank(); + bool checkIfParallelTempering(const char *fileName); + void checkIfValid(const char *fileName); + int getNumberOfReplicas(const char *fileName); + std::string getMultiSimFolderName(const char *fileName); + std::string getTemperature(const char *fileName, int worldRank); + std::string getChemicalPotential(const char *fileName, int worldRank); + std::string setupReplicaDirectoriesAndRedirectSTDOUTToFile(std::string multiSimTitle, std::string temperature); + std::string setupReplicaDirectoriesAndRedirectSTDOUTToFile(std::string multiSimTitle, std::string temperature, std::string chemPot); + std::string mkdirWrapper(std::string multisimDirectoryName, std::string replicaDirectoryName); + bool CheckString(string str1, string str2); +private: + friend class MultiSim; + std::string inputFileStringMPI; + fstream inputFileReaderMPI; + std::string pathToReplicaDirectory; + int worldSize, worldRank; + +#endif + +}; + +class MultiSim +{ +public: + explicit MultiSim(ParallelTemperingPreprocessor & pt); + const int worldSize, worldRank; + const std::string pathToReplicaDirectory; +private: +}; +#endif /*ParallelTemperingPreprocessor_H*/ diff --git a/src/Reader.cpp b/src/Reader.cpp index 926c0cf0d..f193a4897 100644 --- a/src/Reader.cpp +++ b/src/Reader.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Reader.h b/src/Reader.h index 02c85b987..6393bd580 100644 --- a/src/Reader.h +++ b/src/Reader.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/SeedReader.h b/src/SeedReader.h index 190fddd84..aaceec3ce 100644 --- a/src/SeedReader.h +++ b/src/SeedReader.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Setup.h b/src/Setup.h index b723100c0..b5d13a2f4 100644 --- a/src/Setup.h +++ b/src/Setup.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -15,7 +15,8 @@ along with this program, also can be found at . #include "PDBSetup.h" #include "PRNGSetup.h" #include "MolSetup.h" - +#include "GOMC_Config.h" //For PT +#include "ParallelTemperingPreprocessor.h" class Setup { public: @@ -26,10 +27,10 @@ class Setup PRNGSetup prng; //4 MolSetup mol; //5 - void Init(char const*const configFileName) + void Init(char const*const configFileName, MultiSim const*const& multisim) { //Read in all config data - config.Init(configFileName); + config.Init(configFileName, multisim); //Read in FF data. ff.Init(config.in.files.param.name, config.in.ffKind.isCHARMM); //Read PDB data diff --git a/src/SimEventFrequency.h b/src/SimEventFrequency.h index 17ac68ea7..d978aa9e5 100644 --- a/src/SimEventFrequency.h +++ b/src/SimEventFrequency.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Simulation.cpp b/src/Simulation.cpp index a20141f1d..6105be1cc 100644 --- a/src/Simulation.cpp +++ b/src/Simulation.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -12,13 +12,13 @@ along with this program, also can be found at . #include #include -Simulation::Simulation(char const*const configFileName) +Simulation::Simulation(char const*const configFileName, MultiSim const*const& multisim) { startStep = 0; //NOTE: //IMPORTANT! Keep this order... //as system depends on staticValues, and cpu sometimes depends on both. - set.Init(configFileName); + set.Init(configFileName, multisim); totalSteps = set.config.sys.step.total; staticValues = new StaticVals(set); system = new System(*staticValues); @@ -123,4 +123,5 @@ void Simulation::RunningCheck(const uint step) << std::endl << std::endl; } + #endif diff --git a/src/Simulation.h b/src/Simulation.h index 738f4856f..1bae0b664 100644 --- a/src/Simulation.h +++ b/src/Simulation.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -12,11 +12,13 @@ along with this program, also can be found at . #include "System.h" #include "StaticVals.h" #include "BasicTypes.h" +#include "GOMC_Config.h" //For PT +#include "ParallelTemperingPreprocessor.h" class Simulation { public: - explicit Simulation(char const*const configFileName); + explicit Simulation(char const*const configFileName, MultiSim const*const& multisim = NULL); ~Simulation(); void RunSimulation(void); diff --git a/src/StaticVals.cpp b/src/StaticVals.cpp index 77140bffe..d41faceae 100644 --- a/src/StaticVals.cpp +++ b/src/StaticVals.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -46,6 +46,9 @@ void StaticVals::InitMovePercents(config_setup::MovePercents const& perc) case mv::DISPLACE: movePerc[m] = perc.displace; break; + case mv::MULTIPARTICLE: + movePerc[m] = perc.multiParticle; + break; case mv::ROTATE: movePerc[m] = perc.rotate; break; @@ -74,6 +77,9 @@ void StaticVals::InitMovePercents(config_setup::MovePercents const& perc) case mv::MEMC : movePerc[m] = perc.memc; break; + case mv::CFCMC : + movePerc[m] = perc.cfcmc; + break; #endif #endif default: @@ -90,7 +96,7 @@ void StaticVals::InitMovePercents(config_setup::MovePercents const& perc) void StaticVals::IsBoxOrthogonal(config_setup::Volume const& vol) { double cosAngle[BOX_TOTAL][3]; - double orthogonal[BOX_TOTAL]; + bool orthogonal[BOX_TOTAL]; for (uint b = 0; b < BOX_TOTAL; b++) { double cellLengthX = vol.axis[b].Length(0); @@ -107,16 +113,33 @@ void StaticVals::IsBoxOrthogonal(config_setup::Volume const& vol) orthogonal[b] = ((cosAngle[b][0] == 0.0) && (cosAngle[b][1] == 0.0) && (cosAngle[b][2] == 0.0)); - isOrthogonal = (isOrthogonal && orthogonal[b]); + isOrthogonal &= orthogonal[b]; + } +} + + +void StaticVals::IsBoxOrthogonal(const double cellAngle[][3]) +{ + for (uint b = 0; b < BOX_TOTAL; b++) { + bool orthogonal = ((cellAngle[b][0] == 90.0) && (cellAngle[b][1] == 90.0) && + (cellAngle[b][2] == 90.0)); + isOrthogonal &= orthogonal; } } StaticVals::StaticVals(Setup & set) : memcVal(set.config.sys.memcVal), - intraMemcVal(set.config.sys.intraMemcVal) + intraMemcVal(set.config.sys.intraMemcVal), + cfcmcVal(set.config.sys.cfcmcVal), + freeEnVal(set.config.sys.freeEn) { + multiParticleEnabled = set.config.sys.moves.multiParticleEnabled; isOrthogonal = true; - IsBoxOrthogonal(set.config.sys.volume); + if(set.config.in.restart.enable) { + IsBoxOrthogonal(set.pdb.cryst.cellAngle); + } else { + IsBoxOrthogonal(set.config.sys.volume); + } #ifndef VARIABLE_VOLUME boxDimensions = NULL; if(isOrthogonal) { diff --git a/src/StaticVals.h b/src/StaticVals.h index f4c004d21..ecd0b8394 100644 --- a/src/StaticVals.h +++ b/src/StaticVals.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -33,6 +33,7 @@ class StaticVals void Init(Setup & set, System& sys); void InitOver(Setup & set, System& sys); void IsBoxOrthogonal(config_setup::Volume const& vol); + void IsBoxOrthogonal(const double cellAngle[][3]); #ifndef VARIABLE_VOLUME BoxDimensions * GetBoxDim() { @@ -46,6 +47,7 @@ class StaticVals bool fixVolBox0; #endif bool isOrthogonal; + bool multiParticleEnabled; Forcefield forcefield; SimEventFrequency simEventFreq; @@ -55,6 +57,7 @@ class StaticVals double movePerc[mv::MOVE_KINDS_TOTAL]; double totalPerc; config_setup::MEMCVal intraMemcVal; + config_setup::FreeEnergy freeEnVal; //Only include these variables if they're static for this ensemble... #ifndef VARIABLE_VOLUME @@ -64,7 +67,8 @@ class StaticVals MoleculeLookup molLookup; #endif #ifdef VARIABLE_PARTICLE_NUMBER - config_setup::MEMCVal memcVal; + config_setup::MEMCVal memcVal; + config_setup::CFCMCVal cfcmcVal; #endif bool IsEquil(const uint step) diff --git a/src/SubdividedArray.h b/src/SubdividedArray.h index 21e0d6fa9..9c541b89b 100644 --- a/src/SubdividedArray.h +++ b/src/SubdividedArray.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/System.cpp b/src/System.cpp index 151d77447..14c71716b 100644 --- a/src/System.cpp +++ b/src/System.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -22,6 +22,7 @@ along with this program, also can be found at . #include "VolumeTransfer.h" #include "MoleculeTransfer.h" #include "IntraSwap.h" +#include "MultiParticle.h" #include "Regrowth.h" #include "MoleculeExchange1.h" #include "MoleculeExchange2.h" @@ -30,6 +31,7 @@ along with this program, also can be found at . #include "IntraMoleculeExchange2.h" #include "IntraMoleculeExchange3.h" #include "CrankShaft.h" +#include "CFCMC.h" System::System(StaticVals& statics) : statV(statics), @@ -72,6 +74,7 @@ System::~System() #if ENSEMBLE == GEMC || ENSEMBLE == GCMC delete moves[mv::MOL_TRANSFER]; delete moves[mv::MEMC]; + delete moves[mv::CFCMC]; #endif } @@ -105,6 +108,12 @@ void System::Init(Setup const& set, ulong & startStep) } com.CalcCOM(); + // Allocate space for atom forces + atomForceRef.Init(set.pdb.atoms.beta.size()); + molForceRef.Init(com.Count()); + // Allocate space for reciprocate force + atomForceRecRef.Init(set.pdb.atoms.beta.size()); + molForceRecRef.Init(com.Count()); cellList.SetCutoff(); cellList.GridAll(boxDimRef, coordinates, molLookupRef); @@ -126,6 +135,8 @@ void System::Init(Setup const& set, ulong & startStep) calcEwald = new NoEwald(statV, *this); #endif + //Initial the lambda before calling SystemTotal + InitLambda(); calcEnergy.Init(*this); calcEwald->Init(); potential = calcEnergy.SystemTotal(); @@ -137,6 +148,7 @@ void System::Init(Setup const& set, ulong & startStep) void System::InitMoves(Setup const& set) { moves[mv::DISPLACE] = new Translate(*this, statV); + moves[mv::MULTIPARTICLE] = new MultiParticle(*this, statV); moves[mv::ROTATE] = new Rotate(*this, statV); moves[mv::INTRA_SWAP] = new IntraSwap(*this, statV); moves[mv::REGROWTH] = new Regrowth(*this, statV); @@ -161,9 +173,46 @@ void System::InitMoves(Setup const& set) } else { moves[mv::MEMC] = new MoleculeExchange3(*this, statV); } + moves[mv::CFCMC] = new CFCMC(*this, statV); #endif } +void System::InitLambda() +{ + if(statV.freeEnVal.enable) { + bool found = false; + for(uint k = 0; k < statV.mol.GetKindsCount(); k++) { + std::string kindName = statV.mol.kinds[k].name; + if(statV.freeEnVal.molType == kindName) { + found = true; + uint totalMol = molLookupRef.NumKindInBox(k, mv::BOX0); + //In PDB file, molIndex start from 1. + uint FEmolIndex = statV.freeEnVal.molIndex - 1; + if(totalMol == 0) { + found = false; + } else if(totalMol <= FEmolIndex) { + std::cout << "Error: Molecule index " << statV.freeEnVal.molIndex << + " of kind " << kindName << " does not exist in the simulation box!\n"; + exit(EXIT_FAILURE); + } else { + uint m = molLookupRef.GetMolNum(FEmolIndex, k, mv::BOX0); + uint state = statV.freeEnVal.iState; + double lambdaCoulomb = statV.freeEnVal.lambdaCoulomb[state]; + double lambdaVDW = statV.freeEnVal.lambdaVDW[state]; + lambdaRef.Set(lambdaVDW, lambdaCoulomb, m, k, mv::BOX0); + } + break; + } + } + + if(!found) { + std::cout << "Error: No molecule of kind " << statV.freeEnVal.molType << + " in the simulation box! \n"; + exit(EXIT_FAILURE); + } + } +} + void System::RecalculateTrajectory(Setup &set, uint frameNum) { set.pdb.Init(set.config.in.restart, set.config.in.files.pdb.name, frameNum); @@ -259,6 +308,7 @@ void System::PrintTime() //std::cout << "MC moves Execution time:\n"; printf("%-36s %10.4f sec.\n", "Displacement:", moveTime[mv::DISPLACE]); printf("%-36s %10.4f sec.\n", "Rotation:", moveTime[mv::ROTATE]); + printf("%-36s %10.4f sec.\n", "MultiParticle:", moveTime[mv::MULTIPARTICLE]); printf("%-36s %10.4f sec.\n", "Intra-Swap:", moveTime[mv::INTRA_SWAP]); printf("%-36s %10.4f sec.\n", "Regrowth:", moveTime[mv::REGROWTH]); printf("%-36s %10.4f sec.\n", "Intra-MEMC:", moveTime[mv::INTRA_MEMC]); @@ -268,6 +318,7 @@ void System::PrintTime() printf("%-36s %10.4f sec.\n", "Mol-Transfer:", moveTime[mv::MOL_TRANSFER]); printf("%-36s %10.4f sec.\n", "MEMC:", moveTime[mv::MEMC]); + printf("%-36s %10.4f sec.\n", "CFCMC:", moveTime[mv::CFCMC]); #endif #if ENSEMBLE == GEMC || ENSEMBLE == NPT printf("%-36s %10.4f sec.\n", "Vol-Transfer:", moveTime[mv::VOL_TRANSFER]); diff --git a/src/System.h b/src/System.h index 3e26972e5..e009e37c2 100644 --- a/src/System.h +++ b/src/System.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -24,11 +24,14 @@ along with this program, also can be found at . #include "CellList.h" #include "Clock.h" #include "CheckpointSetup.h" +#include "../lib/Lambda.h" //Initialization variables class Setup; class StaticVals; class MoveBase; +class Lambda; + class System { @@ -93,6 +96,11 @@ class System MoveSettings moveSettings; SystemPotential potential; Coordinates coordinates; + XYZArray atomForceRef; + XYZArray molForceRef; + XYZArray atomForceRecRef; + XYZArray molForceRecRef; + Lambda lambdaRef; COM com; CalculateEnergy calcEnergy; @@ -108,7 +116,9 @@ class System ~System(); + private: + void InitLambda(); void InitMoves(Setup const& set); void PickMove(uint & kind, double & draw); uint SetParams(const uint kind, const double draw); @@ -116,8 +126,8 @@ class System void CalcEn(const uint kind); void Accept(const uint kind, const uint rejectState, const uint step); - MoveBase * moves[mv::MOVE_KINDS_TOTAL]; double moveTime[mv::MOVE_KINDS_TOTAL]; + MoveBase * moves[mv::MOVE_KINDS_TOTAL]; Clock time; }; diff --git a/src/TransformMatrix.h b/src/TransformMatrix.h index 5563d9d79..b1bea6cf8 100644 --- a/src/TransformMatrix.h +++ b/src/TransformMatrix.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/UnitConst.h b/src/UnitConst.h index 7c289b381..7ece4af03 100644 --- a/src/UnitConst.h +++ b/src/UnitConst.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/Writer.h b/src/Writer.h index a52ac8a87..d8cbbe51d 100644 --- a/src/Writer.h +++ b/src/Writer.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/XYZArray.h b/src/XYZArray.h index 3d17cdf8e..919db0335 100644 --- a/src/XYZArray.h +++ b/src/XYZArray.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -115,6 +115,10 @@ class XYZArray void SetRange(const uint start, const uint stop, XYZ const& val); + void ResetRange(const uint val, const uint stop); + + void Reset(); + //////////////////////////////////////////////////////////////////// //Add values in two diferent arrays. @@ -229,6 +233,14 @@ class XYZArray z[i] += val.z; } + // Sub XYZ value val to row i + void Sub(const uint i, XYZ const& val) + { + x[i] -= val.x; + y[i] -= val.y; + z[i] -= val.z; + } + //Sub values from a single element of the array's values. void Sub(const uint i, const double a, const double b, const double c) { @@ -391,6 +403,23 @@ inline void XYZArray::SetRange(const uint start, const uint stop, } } +inline void XYZArray::ResetRange(const uint val, const uint stop) +{ +#ifdef _OPENMP + #pragma omp parallel default(shared) +#endif + { + memset(this->x, val, stop * sizeof(double)); + memset(this->y, val, stop * sizeof(double)); + memset(this->z, val, stop * sizeof(double)); + } +} + +inline void XYZArray::Reset() +{ + ResetRange(0, count); +} + inline void XYZArray::Init(const uint n) { count = n; diff --git a/src/cbmc/DCComponent.h b/src/cbmc/DCComponent.h index fe9f9ae80..e7b21ab83 100644 --- a/src/cbmc/DCComponent.h +++ b/src/cbmc/DCComponent.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCCrankShaftAng.cpp b/src/cbmc/DCCrankShaftAng.cpp index e4c259e68..ffc45a70f 100644 --- a/src/cbmc/DCCrankShaftAng.cpp +++ b/src/cbmc/DCCrankShaftAng.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -400,7 +400,7 @@ void DCCrankShaftAng::ParticleNonbonded1_N(cbmc::TrialMol const& mol, *partner, box)) { nonbonded[t] += data->ff.particles->CalcEn(distSq, kind.AtomKind(partIndex), - kind.AtomKind(*partner)); + kind.AtomKind(*partner), 1.0); if(data->ff.electrostatic) { double qi_qj_Fact = kind.AtomCharge(partIndex) * kind.AtomCharge(*partner) * num::qqFact; diff --git a/src/cbmc/DCCrankShaftAng.h b/src/cbmc/DCCrankShaftAng.h index effc90f28..5845c7525 100644 --- a/src/cbmc/DCCrankShaftAng.h +++ b/src/cbmc/DCCrankShaftAng.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCCrankShaftDih.cpp b/src/cbmc/DCCrankShaftDih.cpp index 4b793af2c..637698c34 100644 --- a/src/cbmc/DCCrankShaftDih.cpp +++ b/src/cbmc/DCCrankShaftDih.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -411,7 +411,7 @@ void DCCrankShaftDih::ParticleNonbonded1_N(cbmc::TrialMol const& mol, *partner, box)) { nonbonded[t] += data->ff.particles->CalcEn(distSq, kind.AtomKind(partIndex), - kind.AtomKind(*partner)); + kind.AtomKind(*partner), 1.0); if(data->ff.electrostatic) { double qi_qj_Fact = kind.AtomCharge(partIndex) * kind.AtomCharge(*partner) * num::qqFact; diff --git a/src/cbmc/DCCrankShaftDih.h b/src/cbmc/DCCrankShaftDih.h index 9f3ed910d..0c5621015 100644 --- a/src/cbmc/DCCrankShaftDih.h +++ b/src/cbmc/DCCrankShaftDih.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCCyclic.cpp b/src/cbmc/DCCyclic.cpp index e7b95db24..b15607129 100644 --- a/src/cbmc/DCCyclic.cpp +++ b/src/cbmc/DCCyclic.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -399,19 +399,18 @@ void DCCyclic::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) newMol.AddAtom(partner, oldMol.AtomPosition(partner)); oldMol.ConfirmOldAtom(partner); } - - if(nodes[current].edges.size() == 1) { - //If current is the terminal node, continue building all edges - BuildEdges(oldMol, newMol, molIndex, current); - } else { - //First we pick a edge and continue copying the coordinate - //Then continue to build the rest of the molecule from current - //Copy the edges of the node to fringe - fringe = nodes[current].edges; + //First we pick a edge that will be fix and continue copy the coordinate + //We continue the same until only one edge left from this node + //If current is the terminal node, we dont enter to while loop + //Then continue to build the rest of the molecule from current + + //Copy the edges of the node to currFringe + currFringe = nodes[current].edges; + while (currFringe.size() > 1) { //randomely pick one one of the edges connected to fixNode - uint pickFixEdg = data.prng.randIntExc(fringe.size()); + uint pickFixEdg = data.prng.randIntExc(currFringe.size()); //Travel to picked edges and make it as new fixNode - uint fixNode = fringe[pickFixEdg].destination; + uint fixNode = currFringe[pickFixEdg].destination; visited[fixNode] = true; destVisited[nodes[fixNode].atomIndex] = true; //Copy the all atoms bonded to fixNode's focus @@ -440,7 +439,7 @@ void DCCyclic::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) newMol.AddAtom(partner, oldMol.AtomPosition(partner)); oldMol.ConfirmOldAtom(partner); } - //Travel to new fixNode, remove traversed edge + //Travel to new fixNode, remove traverled edge fringe[0] = fringe.back(); fringe.pop_back(); visited[fixNode] = true; @@ -453,35 +452,35 @@ void DCCyclic::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) } } } - //Now Start building the rest of the molecule from current - //Copy the edges of the current node to fringe - fringe = nodes[current].edges; - //Remove the fixed edge from fringe - fringe.erase(fringe.begin() + pickFixEdg); - for(uint i = 0; i < fringe.size(); i++) { - destVisited[fringe[i].atomIndex] = true; - } - //Advance along edges, building as we go - while(!fringe.empty()) { - //Randomely pick one of the edges connected to node - uint pick = data.prng.randIntExc(fringe.size()); - DCComponent* comp = fringe[pick].connect; - //Call DCLinkedHedron and build all Atoms connected to selected edge - comp->PrepareNew(newMol, molIndex); - comp->BuildNew(newMol, molIndex); - comp->PrepareOld(oldMol, molIndex); - comp->BuildOld(oldMol, molIndex); - current = fringe[pick].destination; - //Remove the edge that we visited - fringe[pick] = fringe.back(); - fringe.pop_back(); - visited[current] = true; - for(uint i = 0; i < nodes[current].edges.size(); ++i) { - Edge& e = nodes[current].edges[i]; - if(!visited[e.destination] && !destVisited[e.atomIndex]) { - fringe.push_back(e); - destVisited[e.atomIndex] = true; - } + //Remove the fixed edge from currFring + currFringe.erase(currFringe.begin() + pickFixEdg); + } + + for(uint i = 0; i < currFringe.size(); i++) { + destVisited[currFringe[i].atomIndex] = true; + } + //Now Start building the rest of the molecule from current + //Start with only one left edge + //Advance along edges, building as we go + while(!currFringe.empty()) { + //Randomely pick one of the edges connected to node + uint pick = data.prng.randIntExc(currFringe.size()); + DCComponent* comp = currFringe[pick].connect; + //Call DCLinkedCycle and build all Atoms connected to selected edge + comp->PrepareNew(newMol, molIndex); + comp->BuildNew(newMol, molIndex); + comp->PrepareOld(oldMol, molIndex); + comp->BuildOld(oldMol, molIndex); + current = currFringe[pick].destination; + //Remove the edge that we visited + currFringe[pick] = currFringe.back(); + currFringe.pop_back(); + visited[current] = true; + for(uint i = 0; i < nodes[current].edges.size(); ++i) { + Edge& e = nodes[current].edges[i]; + if(!visited[e.destination] && !destVisited[e.atomIndex]) { + currFringe.push_back(e); + destVisited[e.atomIndex] = true; } } } diff --git a/src/cbmc/DCCyclic.h b/src/cbmc/DCCyclic.h index 5d61e162c..c0d1a6dbf 100644 --- a/src/cbmc/DCCyclic.h +++ b/src/cbmc/DCCyclic.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -79,7 +79,7 @@ class DCCyclic : public CBMC std::vector isRing; //To check if atom is belong to a ring std::vector ringIdx; //index to the row of cyclicAtoms std::vector nodes; - std::vector fringe; + std::vector fringe, currFringe; std::vector visited, destVisited; std::vector crankshaft; std::vector< std::vector > cyclicAtoms; diff --git a/src/cbmc/DCData.h b/src/cbmc/DCData.h index 18d9e467b..74a3e4456 100644 --- a/src/cbmc/DCData.h +++ b/src/cbmc/DCData.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFactory.h b/src/cbmc/DCFactory.h index 7bf7e8321..d1cb516fd 100644 --- a/src/cbmc/DCFactory.h +++ b/src/cbmc/DCFactory.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeCycle.cpp b/src/cbmc/DCFreeCycle.cpp index eeeb8c6e1..5fa0730b1 100644 --- a/src/cbmc/DCFreeCycle.cpp +++ b/src/cbmc/DCFreeCycle.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeCycle.h b/src/cbmc/DCFreeCycle.h index 0912eff5a..95ed88e49 100644 --- a/src/cbmc/DCFreeCycle.h +++ b/src/cbmc/DCFreeCycle.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeCycleSeed.cpp b/src/cbmc/DCFreeCycleSeed.cpp index b2574c975..d0e392e97 100644 --- a/src/cbmc/DCFreeCycleSeed.cpp +++ b/src/cbmc/DCFreeCycleSeed.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeCycleSeed.h b/src/cbmc/DCFreeCycleSeed.h index 646b4538f..632e973b2 100644 --- a/src/cbmc/DCFreeCycleSeed.h +++ b/src/cbmc/DCFreeCycleSeed.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeHedron.cpp b/src/cbmc/DCFreeHedron.cpp index 8cc8d4219..692389e45 100644 --- a/src/cbmc/DCFreeHedron.cpp +++ b/src/cbmc/DCFreeHedron.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeHedron.h b/src/cbmc/DCFreeHedron.h index ceff80401..b890c43db 100644 --- a/src/cbmc/DCFreeHedron.h +++ b/src/cbmc/DCFreeHedron.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeHedronSeed.cpp b/src/cbmc/DCFreeHedronSeed.cpp index 791ef0fc5..a2dea5b72 100644 --- a/src/cbmc/DCFreeHedronSeed.cpp +++ b/src/cbmc/DCFreeHedronSeed.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCFreeHedronSeed.h b/src/cbmc/DCFreeHedronSeed.h index acb27dbd5..97c001dde 100644 --- a/src/cbmc/DCFreeHedronSeed.h +++ b/src/cbmc/DCFreeHedronSeed.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCGraph.cpp b/src/cbmc/DCGraph.cpp index c8e6f9bbd..07363f007 100644 --- a/src/cbmc/DCGraph.cpp +++ b/src/cbmc/DCGraph.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -272,19 +272,18 @@ void DCGraph::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) newMol.AddAtom(partner, oldMol.AtomPosition(partner)); oldMol.ConfirmOldAtom(partner); } - - if(nodes[current].edges.size() == 1) { - //If current is the terminal node, continue building all edges - BuildEdges(oldMol, newMol, molIndex, current); - } else { - //First we pick a edge and continue copying the coordinate - //Then continue to build the rest of the molecule from current - //Copy the edges of the node to fringe - fringe = nodes[current].edges; - //randomely pick one one of the edges connected to fixNode - uint pickFixEdg = data.prng.randIntExc(fringe.size()); + //First we pick a edge that will be fix and continue copy the coordinate + //We continue the same until only one edge left from this node + //If current is the terminal node, we dont enter to while loop + //Then continue to build the rest of the molecule from current + + //Copy the edges of the node to currFringe + currFringe = nodes[current].edges; + while (currFringe.size() > 1) { + //randomely pick one of the edges connected to current (fixNode) + uint pickFixEdg = data.prng.randIntExc(currFringe.size()); //Travel to picked edges and make it as new fixNode - uint fixNode = fringe[pickFixEdg].destination; + uint fixNode = currFringe[pickFixEdg].destination; visited[fixNode] = true; //Copy the all atoms bonded to fixNode's focus for(uint b = 0; b < nodes[fixNode].partnerIndex.size(); b++) { @@ -294,7 +293,7 @@ void DCGraph::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) } //Copy the edges of the new node to fringe fringe = nodes[fixNode].edges; - //remove the edge that we travelled from + //remove the edge that we traveled from for( uint f = 0; f < fringe.size(); f++) { if(fringe[f].destination == current) fringe.erase(fringe.begin() + f); @@ -320,31 +319,30 @@ void DCGraph::Regrowth(TrialMol& oldMol, TrialMol& newMol, uint molIndex) } } } - //Now Start building the rest of the molecule from current - //Copy the edges of the current node to fringe - fringe = nodes[current].edges; - //Remove the fixed edge from fringe - fringe.erase(fringe.begin() + pickFixEdg); - //Advance along edges, building as we go - while(!fringe.empty()) { - //Randomely pick one of the edges connected to node - uint pick = data.prng.randIntExc(fringe.size()); - DCComponent* comp = fringe[pick].component; - //Call DCLinkedHedron and build all Atoms connected to selected edge - comp->PrepareNew(newMol, molIndex); - comp->BuildNew(newMol, molIndex); - comp->PrepareOld(oldMol, molIndex); - comp->BuildOld(oldMol, molIndex); - current = fringe[pick].destination; - //Remove the edge that we visited - fringe[pick] = fringe.back(); - fringe.pop_back(); - visited[current] = true; - for(uint i = 0; i < nodes[current].edges.size(); ++i) { - Edge& e = nodes[current].edges[i]; - if(!visited[e.destination]) { - fringe.push_back(e); - } + //Remove the fixed edge from currFring + currFringe.erase(currFringe.begin() + pickFixEdg); + } + //Now Start building the rest of the molecule from current + //Start with only one left edge + //Advance along edges, building as we go + while(!currFringe.empty()) { + //Randomely pick one of the edges connected to node + uint pick = data.prng.randIntExc(currFringe.size()); + DCComponent* comp = currFringe[pick].component; + //Call DCLinkedHedron and build all Atoms connected to selected edge + comp->PrepareNew(newMol, molIndex); + comp->BuildNew(newMol, molIndex); + comp->PrepareOld(oldMol, molIndex); + comp->BuildOld(oldMol, molIndex); + current = currFringe[pick].destination; + //Remove the edge that we visited + currFringe[pick] = currFringe.back(); + currFringe.pop_back(); + visited[current] = true; + for(uint i = 0; i < nodes[current].edges.size(); ++i) { + Edge& e = nodes[current].edges[i]; + if(!visited[e.destination]) { + currFringe.push_back(e); } } } diff --git a/src/cbmc/DCGraph.h b/src/cbmc/DCGraph.h index 12747395b..3e17dc42d 100644 --- a/src/cbmc/DCGraph.h +++ b/src/cbmc/DCGraph.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -74,7 +74,7 @@ class DCGraph : public CBMC DCData data; bool hasCrankShaft; std::vector nodes; - std::vector fringe; + std::vector fringe, currFringe; std::vector visited; std::vector shaftNodes; XYZArray coords; diff --git a/src/cbmc/DCHedron.cpp b/src/cbmc/DCHedron.cpp index ec0d329ec..83dc7f480 100644 --- a/src/cbmc/DCHedron.cpp +++ b/src/cbmc/DCHedron.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCHedron.h b/src/cbmc/DCHedron.h index 2282f4c2d..af023f576 100644 --- a/src/cbmc/DCHedron.h +++ b/src/cbmc/DCHedron.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCHedronCycle.cpp b/src/cbmc/DCHedronCycle.cpp index 49a48ad6c..50e0859e4 100644 --- a/src/cbmc/DCHedronCycle.cpp +++ b/src/cbmc/DCHedronCycle.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCHedronCycle.h b/src/cbmc/DCHedronCycle.h index c717e69d6..4ffba0d4f 100644 --- a/src/cbmc/DCHedronCycle.h +++ b/src/cbmc/DCHedronCycle.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinear.cpp b/src/cbmc/DCLinear.cpp index 3b0260959..2682275ec 100644 --- a/src/cbmc/DCLinear.cpp +++ b/src/cbmc/DCLinear.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinear.h b/src/cbmc/DCLinear.h index 008e2f514..fdaa6dc9c 100644 --- a/src/cbmc/DCLinear.h +++ b/src/cbmc/DCLinear.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinkedCycle.cpp b/src/cbmc/DCLinkedCycle.cpp index 02c5ec3d8..48a895264 100644 --- a/src/cbmc/DCLinkedCycle.cpp +++ b/src/cbmc/DCLinkedCycle.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinkedCycle.h b/src/cbmc/DCLinkedCycle.h index 496fd920b..59557f330 100644 --- a/src/cbmc/DCLinkedCycle.h +++ b/src/cbmc/DCLinkedCycle.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinkedHedron.cpp b/src/cbmc/DCLinkedHedron.cpp index 59febc30d..99b690412 100644 --- a/src/cbmc/DCLinkedHedron.cpp +++ b/src/cbmc/DCLinkedHedron.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCLinkedHedron.h b/src/cbmc/DCLinkedHedron.h index 42443d038..6c4931c5d 100644 --- a/src/cbmc/DCLinkedHedron.h +++ b/src/cbmc/DCLinkedHedron.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCOnSphere.cpp b/src/cbmc/DCOnSphere.cpp index f1a2bf9cc..2501d7fb9 100644 --- a/src/cbmc/DCOnSphere.cpp +++ b/src/cbmc/DCOnSphere.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCOnSphere.h b/src/cbmc/DCOnSphere.h index 7b0aa801e..ebbc8b397 100644 --- a/src/cbmc/DCOnSphere.h +++ b/src/cbmc/DCOnSphere.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCRotateCOM.cpp b/src/cbmc/DCRotateCOM.cpp index 781fe700e..83042007f 100644 --- a/src/cbmc/DCRotateCOM.cpp +++ b/src/cbmc/DCRotateCOM.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCRotateCOM.h b/src/cbmc/DCRotateCOM.h index 75b1f4c1d..c0a80f095 100644 --- a/src/cbmc/DCRotateCOM.h +++ b/src/cbmc/DCRotateCOM.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCRotateOnAtom.cpp b/src/cbmc/DCRotateOnAtom.cpp index a32a8ac52..90198b09c 100644 --- a/src/cbmc/DCRotateOnAtom.cpp +++ b/src/cbmc/DCRotateOnAtom.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -428,7 +428,7 @@ void DCRotateOnAtom::ParticleNonbonded1_N(cbmc::TrialMol const& mol, *partner, box)) { nonbonded[t] += data->ff.particles->CalcEn(distSq, kind.AtomKind(partIndex), - kind.AtomKind(*partner)); + kind.AtomKind(*partner), 1.0); if(data->ff.electrostatic) { double qi_qj_Fact = kind.AtomCharge(partIndex) * kind.AtomCharge(*partner) * num::qqFact; diff --git a/src/cbmc/DCRotateOnAtom.h b/src/cbmc/DCRotateOnAtom.h index f0cad9de0..163732085 100644 --- a/src/cbmc/DCRotateOnAtom.h +++ b/src/cbmc/DCRotateOnAtom.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCSingle.cpp b/src/cbmc/DCSingle.cpp index db1c653e4..f273e9625 100644 --- a/src/cbmc/DCSingle.cpp +++ b/src/cbmc/DCSingle.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/DCSingle.h b/src/cbmc/DCSingle.h index 3124569d5..83e712144 100644 --- a/src/cbmc/DCSingle.h +++ b/src/cbmc/DCSingle.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/cbmc/TrialMol.cpp b/src/cbmc/TrialMol.cpp index fc83d5801..4d6082f41 100644 --- a/src/cbmc/TrialMol.cpp +++ b/src/cbmc/TrialMol.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -327,14 +327,17 @@ void TrialMol::SetBackBone(const uint bb[2]) XYZ TrialMol::GetCOM() { XYZ tcom; + uint atomNumber = tCoords.Count(); XYZArray temp(tCoords); axes->UnwrapPBC(temp, box, tCoords.Get(0)); tCoords = temp; - for(uint p = 0; p < tCoords.Count(); p++) { - tcom += tCoords.Get(p); + for(uint p = 0; p < atomNumber; p++) { + tcom += temp.Get(p); } - tcom *= (1.0 / tCoords.Count()); + tcom *= (1.0 / (double)(atomNumber)); + //Unwrap with respect to COM + axes->UnwrapPBC(tCoords, box, tcom); return tcom; } diff --git a/src/cbmc/TrialMol.h b/src/cbmc/TrialMol.h index 4345d4f74..c87184595 100644 --- a/src/cbmc/TrialMol.h +++ b/src/cbmc/TrialMol.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -110,6 +110,17 @@ class TrialMol en += energy; } + //Keep the tcoords and reset everythings + void Reset() + { + totalWeight = 1.0; + std::fill_n(atomBuilt, kind->NumAtoms(), false); + growthToWorld.LoadIdentity(); + en.Zero(); + bonds.Unset(); + overlap = false; + } + //!Confirms that atom at index i has been built (used for oldMols) void ConfirmOldAtom(uint i); diff --git a/src/moves/CFCMC.h b/src/moves/CFCMC.h new file mode 100644 index 000000000..47c80653a --- /dev/null +++ b/src/moves/CFCMC.h @@ -0,0 +1,717 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef CFCMC_H +#define CFCMC_H + +#if ENSEMBLE==GCMC || ENSEMBLE==GEMC + +#include "MoveBase.h" +#include "TrialMol.h" +#include + +using std::vector; + +class CFCMC : public MoveBase +{ +public: + + CFCMC(System &sys, StaticVals const& statV) : + ffRef(statV.forcefield), molLookRef(sys.molLookupRef), + lambdaRef(sys.lambdaRef), MoveBase(sys, statV), MP(sys, statV) + { + if(statV.cfcmcVal.enable) { + MPEnable = statV.cfcmcVal.MPEnable; + steps = 0; + histFreq = statV.GetPerAdjust(); + relaxSteps = statV.cfcmcVal.relaxSteps; + lambdaWindow = statV.cfcmcVal.lambdaVDW.size() - 1; + lambdaCoulomb = statV.cfcmcVal.lambdaCoulomb; + lambdaVDW = statV.cfcmcVal.lambdaVDW; + flatness = statV.cfcmcVal.histFlatness; + nuTolerance = 1e-6; + uint totKind = molRef.GetKindsCount(); + nu.resize(BOX_TOTAL); + hist.resize(BOX_TOTAL); + bias.resize(BOX_TOTAL); + kCount.resize(BOX_TOTAL); + firstPrint.resize(BOX_TOTAL); + for(uint b = 0; b < BOX_TOTAL; b++) { + nu[b].resize(totKind, 0.01); + hist[b].resize(totKind); + bias[b].resize(totKind); + kCount[b].resize(totKind); + firstPrint[b].resize(totKind, true); + } + for(uint b = 0; b < BOX_TOTAL; b++) { + for(uint k = 0; k < totKind; k++) { + hist[b][k].resize(lambdaWindow + 1, 0); + bias[b][k].resize(lambdaWindow + 1, 0.0); + } + } + } + } + + virtual uint Prep(const double subDraw, const double movPerc); + virtual uint Transform(); + virtual void CalcEn(); + virtual void Accept(const uint earlyReject, const uint step); + virtual void PrintAcceptKind(); + +private: + + double GetCoeff() const; + uint GetBoxPairAndMol(const double subDraw, const double movPerc); + void ShiftMolToSourceBox(); + void ShiftMolToDestBox(); + void UpdateBias(); + bool AcceptInflating(); + void AcceptRelaxing(uint box); + void CalcEnCFCMC(uint lambdaIdxOldS, uint lambdaIdxNewS); + void CalcEnRelaxing(uint box); + uint TransformRelaxing(uint box); + void RelaxingMolecules(); + + Lambda & lambdaRef; + uint sourceBox, destBox; + uint pStartCFCMC, pLenCFCMC; + uint molIndex, kindIndex; + uint lambdaIdxOld, lambdaIdxNew; + uint box[2]; + uint relaxSteps, lambdaWindow, histFreq; + bool overlapCFCMC; + vector< vector < bool > > firstPrint; + vector< vector< vector > > hist; + vector< vector< uint > > kCount; + + double W_tc, W_recip; + double correctDiffSource, correctDiffDest, selfDiffSource, selfDiffDest; + double recipDiffSource, recipDiffDest, tcDiffSource, tcDiffDest; + double nuTolerance, flatness, molInSourceBox, molInDestBox; + vector< vector< vector > > bias; + vector< double > lambdaCoulomb, lambdaVDW; + vector< vector > nu; + + //variable needs for relaxing + MultiParticle MP; + bool MPEnable; + uint b, m, mk, pStart, pLen; + long steps; + XYZ newCOM; + XYZArray newMolPos; + Intermolecular inter_LJ, inter_Real, recip; + + + cbmc::TrialMol oldMolCFCMC, newMolCFCMC; + Energy oldEnergy[BOX_TOTAL], newEnergy[BOX_TOTAL]; + MoleculeLookup & molLookRef; + Forcefield const& ffRef; + SystemPotential sysPotNew; +}; + +void CFCMC::PrintAcceptKind() +{ + for(uint k = 0; k < molRef.GetKindsCount(); k++) { + printf("%-30s %-5s ", "% Accepted CFCMC-Transfer ", + molRef.kinds[k].name.c_str()); + for(uint b = 0; b < BOX_TOTAL; b++) { + if(moveSetRef.GetTrial(b, mv::CFCMC, k) > 0) + printf("%10.5f ", (100.0 * moveSetRef.GetAccept(b, mv::CFCMC, k))); + else + printf("%10.5f ", 0.0); + } + std::cout << std::endl; + } + for(uint k = 0; k < molRef.GetKindsCount(); k++) { + std::cout << "hist " << molRef.kinds[k].name.c_str() << ": "; + for(uint i = 0; i <= lambdaWindow; i++) { + std::cout << hist[0][k][i] << " "; + } + std::cout << std::endl; + } + + for(uint k = 0; k < molRef.GetKindsCount(); k++) { + std::cout << "Bias " << molRef.kinds[k].name.c_str() << ": "; + for(uint i = 0; i <= lambdaWindow; i++) { + std::cout << bias[0][k][i] << " "; + } + std::cout << std::endl; + } +} + +inline uint CFCMC::GetBoxPairAndMol(const double subDraw, const double movPerc) +{ + // Need to call a function to pick a molecule that is not fixed but cannot be + // swap between boxes. (beta != 1, beta !=2) + uint state = prng.PickMolAndBoxPair2(molIndex, kindIndex, sourceBox, destBox, + subDraw, movPerc); +#if ENSEMBLE == GCMC + if(state == mv::fail_state::NO_MOL_OF_KIND_IN_BOX && sourceBox == mv::BOX1) { + std::cout << "Error: There are no molecules of kind " << + molRef.kinds[kindIndex].name << " left in reservoir.\n"; + exit(EXIT_FAILURE); + } +#endif + + if(state == mv::fail_state::NO_FAIL) { + pStartCFCMC = pLenCFCMC = 0; + molRef.GetRangeStartLength(pStartCFCMC, pLenCFCMC, molIndex); + box[0] = sourceBox; + box[1] = destBox; + } + return state; +} + +inline uint CFCMC::Prep(const double subDraw, const double movPerc) +{ + overlapCFCMC = false; + uint state = GetBoxPairAndMol(subDraw, movPerc); + if(state == mv::fail_state::NO_FAIL) { + newMolCFCMC = cbmc::TrialMol(molRef.kinds[kindIndex], boxDimRef, destBox); + oldMolCFCMC = cbmc::TrialMol(molRef.kinds[kindIndex], boxDimRef, sourceBox); + oldMolCFCMC.SetCoords(coordCurrRef, pStartCFCMC); + //Unwrap the old coordinate for using in new coordinate after wraping + XYZArray mol(pLenCFCMC); + coordCurrRef.CopyRange(mol, pStartCFCMC, 0, pLenCFCMC); + boxDimRef.UnwrapPBC(mol, sourceBox, comCurrRef.Get(molIndex)); + boxDimRef.WrapPBC(mol, destBox); + //Later it will shift to random COM + newMolCFCMC.SetCoords(mol, 0); + // store number of molecule in box before shifting molecule + molInSourceBox = (double)molLookRef.NumKindInBox(kindIndex, sourceBox); + molInDestBox = (double)molLookRef.NumKindInBox(kindIndex, destBox); + for(uint b = 0; b < BOX_TOTAL; b++) { + for (uint k = 0; k < molRef.GetKindsCount(); ++k) { + kCount[b][k] = molLookRef.NumKindInBox(k, b); + if(b == sourceBox && k == kindIndex) { + //consider the fraction molecule as different molecule kind + --kCount[b][k]; + } + } + } + } + return state; +} + + +inline uint CFCMC::Transform() +{ + //Start with full interaction in sourceBox, zero interaction in destBox + //SInce we have the lambda for growing molecule, in sourceBox, lambdaWindow + // correspond to full interaction and (lambdaWindow - X) is for destBox + lambdaIdxOld = lambdaWindow; + lambdaIdxNew = lambdaIdxOld - 1; + //Update the interaction in destBox + lambdaRef.Set(lambdaVDW[lambdaWindow - lambdaIdxNew], + lambdaCoulomb[lambdaWindow - lambdaIdxNew], molIndex, + kindIndex, destBox); + //Start growing the fractional molecule in destBox + molRef.kinds[kindIndex].BuildIDNew(newMolCFCMC, molIndex); + overlapCFCMC = newMolCFCMC.HasOverlap(); + //Add bonded energy because we dont considered in DCRotate.cpp + newMolCFCMC.AddEnergy(calcEnRef.MoleculeIntra(newMolCFCMC, molIndex)); + ShiftMolToDestBox(); + + do { + //Set the interaction in source and destBox + lambdaRef.Set(lambdaVDW[lambdaIdxOld], lambdaCoulomb[lambdaIdxOld], + molIndex, kindIndex, sourceBox); + lambdaRef.Set(lambdaVDW[lambdaWindow - lambdaIdxOld], + lambdaCoulomb[lambdaWindow - lambdaIdxOld], molIndex, + kindIndex, destBox); + if(lambdaIdxNew == 0) { + //removing the fractional molecule in last steps using CBMC in sourceBox + molRef.kinds[kindIndex].BuildIDOld(oldMolCFCMC, molIndex); + //Add bonded energy because we dont considered in DCRotate.cpp + oldMolCFCMC.AddEnergy(calcEnRef.MoleculeIntra(oldMolCFCMC, molIndex)); + } else if(lambdaIdxNew == lambdaWindow) { + //removing the inserted fractional molecule using CBMC in destBox + cellList.RemoveMol(molIndex, destBox, coordCurrRef); + molRef.kinds[kindIndex].BuildIDOld(newMolCFCMC, molIndex); + //Add bonded energy because we dont considered in DCRotate.cpp + newMolCFCMC.AddEnergy(calcEnRef.MoleculeIntra(newMolCFCMC, molIndex)); + cellList.AddMol(molIndex, destBox, coordCurrRef); + } + //Calculate the old and new energy in source and destBox(if we dont do CBMC) + CalcEnCFCMC(lambdaIdxOld, lambdaIdxNew); + //Accept or reject the inflation + bool acceptedInflate = AcceptInflating(); + if(acceptedInflate) { + lambdaIdxOld = lambdaIdxNew; + //Update the interaction in sourceBox and destBox + lambdaRef.Set(lambdaVDW[lambdaIdxNew], lambdaCoulomb[lambdaIdxNew], + molIndex, kindIndex, sourceBox); + lambdaRef.Set(lambdaVDW[lambdaWindow - lambdaIdxNew], + lambdaCoulomb[lambdaWindow - lambdaIdxNew], molIndex, + kindIndex, destBox); + } + RelaxingMolecules(); + //Dont update Bias if move resulted in overLap + if(!overlapCFCMC) { + UpdateBias(); + } + //pick new lambda in the neighborhood + lambdaIdxNew = lambdaIdxOld + (prng.randInt(1) ? 1 : -1); + } while(lambdaIdxOld > 0 && lambdaIdxOld < lambdaWindow); + + return mv::fail_state::NO_FAIL; +} + +inline void CFCMC::CalcEn() +{ + return; +} + +inline void CFCMC::CalcEnCFCMC(uint lambdaIdxOldS, uint lambdaIdxNewS) +{ + W_tc = 1.0; + W_recip = 1.0; + correctDiffDest = correctDiffSource = 0.0; + selfDiffDest = selfDiffSource = 0.0; + recipDiffDest = recipDiffSource = 0.0; + tcDiffDest = tcDiffSource = 0.0; + oldEnergy[sourceBox].Zero(); + newEnergy[sourceBox].Zero(); + oldEnergy[destBox].Zero(); + newEnergy[destBox].Zero(); + + if(overlapCFCMC) { + //Do not calculate the energy difference if we have overlap + return; + } + double lambdaOld_VDW_S = lambdaVDW[lambdaIdxOldS]; + double lambdaNew_VDW_S = lambdaVDW[lambdaIdxNewS]; + double lambdaOld_VDW_D = lambdaVDW[lambdaWindow - lambdaIdxOldS]; + double lambdaNew_VDW_D = lambdaVDW[lambdaWindow - lambdaIdxNewS]; + double lambdaOld_Coulomb_S = lambdaCoulomb[lambdaIdxOldS]; + double lambdaNew_Coulomb_S = lambdaCoulomb[lambdaIdxNewS]; + double lambdaOld_Coulomb_D = lambdaCoulomb[lambdaWindow - lambdaIdxOldS]; + double lambdaNew_Coulomb_D = lambdaCoulomb[lambdaWindow - lambdaIdxNewS]; + + //Calculating long range correction + if(ffRef.useLRC) { + //Calculate LRC difference for lambdaNew and lambdaOld + tcDiffSource = calcEnRef.MoleculeTailChange(sourceBox, kindIndex, + kCount[sourceBox], lambdaOld_VDW_S, + lambdaNew_VDW_S); + tcDiffDest = calcEnRef.MoleculeTailChange(destBox, kindIndex, + kCount[destBox], lambdaOld_VDW_D, + lambdaNew_VDW_D); + W_tc = exp(-1.0 * ffRef.beta * (tcDiffSource + tcDiffDest)); + } + + //No need to calculate energy when performing CBMC + ShiftMolToSourceBox(); + if(lambdaIdxNew != 0) { + //calculate inter energy for lambda new and old in source Box + calcEnRef.SingleMoleculeInter(oldEnergy[sourceBox], newEnergy[sourceBox], + lambdaOld_VDW_S, lambdaNew_VDW_S, + lambdaOld_Coulomb_S, lambdaNew_Coulomb_S, + molIndex, sourceBox); + } + + + ShiftMolToDestBox(); + if(lambdaIdxOld != lambdaWindow && lambdaIdxNew != lambdaWindow) { + //calculate inter energy for lambda new and old in dest Box + calcEnRef.SingleMoleculeInter(oldEnergy[destBox], newEnergy[destBox], + lambdaOld_VDW_D, lambdaNew_VDW_D, + lambdaOld_Coulomb_D, lambdaNew_Coulomb_D, + molIndex, destBox); + } + + + //Calculate self and correction difference for lambdaNew and lambdaOld + //For electrostatic we use linear scaling + double coefDiffS = lambdaNew_Coulomb_S - lambdaOld_Coulomb_S; + double coefDiffD = lambdaNew_Coulomb_D - lambdaOld_Coulomb_D; + correctDiffSource = coefDiffS * calcEwald->SwapCorrection(oldMolCFCMC); + correctDiffDest = coefDiffD * calcEwald->SwapCorrection(newMolCFCMC); + selfDiffSource = coefDiffS * calcEwald->SwapSelf(oldMolCFCMC); + selfDiffDest = coefDiffD * calcEwald->SwapSelf(newMolCFCMC); + //calculate Recprocal Difference in source and dest box + recipDiffSource = calcEwald->CFCMCRecip(oldMolCFCMC.GetCoords(), + lambdaOld_Coulomb_S, + lambdaNew_Coulomb_S, + molIndex, sourceBox); + recipDiffDest = calcEwald->CFCMCRecip(newMolCFCMC.GetCoords(), + lambdaOld_Coulomb_D, + lambdaNew_Coulomb_D, + molIndex, destBox); + + //need to contribute the self and correction energy + W_recip = exp(-1.0 * ffRef.beta * (recipDiffSource + recipDiffDest + + correctDiffSource + correctDiffDest + + selfDiffSource + selfDiffDest)); +} + +inline double CFCMC::GetCoeff() const +{ + double coef = 1.0; + uint idxSNew = lambdaIdxNew; + uint idxSOld = lambdaIdxOld; + uint idxDNew = lambdaWindow - lambdaIdxNew; + uint idxDOld = lambdaWindow - lambdaIdxOld;; + double biasCoef = 1.0; + biasCoef *= exp(bias[sourceBox][kindIndex][idxSNew] - + bias[sourceBox][kindIndex][idxSOld]); + biasCoef *= exp(bias[destBox][kindIndex][idxDNew] - + bias[destBox][kindIndex][idxDOld]); + + //transition from lambda = 1 to any value means deletion + //transition to lambda = 1 from any value means insertion +#if ENSEMBLE == GEMC + if(idxSOld == lambdaWindow) { + coef *= molInSourceBox * boxDimRef.volInv[sourceBox]; + coef *= 0.5; + } + if(idxSNew == lambdaWindow) { + coef *= boxDimRef.volume[sourceBox] / molInSourceBox; + coef *= 2.0; + } else if(idxSNew == 0) { + //same as (idxDNew == lambdaWindow) + coef *= boxDimRef.volume[destBox] / (molInDestBox + 1.0); + coef *= 2.0; + } + + return coef * biasCoef; + +#elif ENSEMBLE == GCMC + if(sourceBox == mv::BOX0) { + //deletion + if(idxSOld == lambdaWindow) { + coef *= molInSourceBox * boxDimRef.volInv[sourceBox]; + coef *= exp(-BETA * molRef.kinds[kindIndex].chemPot); + coef *= 0.5; + } + if(idxSNew == lambdaWindow) { + coef *= boxDimRef.volume[sourceBox] / molInSourceBox; + coef *= exp(BETA * molRef.kinds[kindIndex].chemPot); + coef *= 2.0; + } else if(idxSNew == 0) { + coef *= 2.0; + } + return coef * biasCoef; + } else { + //insertion + if(idxDOld == 0) { + coef *= 0.5; + } + if(idxDNew == 0) { + coef *= 2.0; + } else if(idxDNew == lambdaWindow) { + coef *= boxDimRef.volume[destBox] / (molInDestBox + 1.0); + coef *= exp(BETA * molRef.kinds[kindIndex].chemPot); + coef *= 2.0; + } + return coef * biasCoef; + } +#endif + +} + +inline void CFCMC::Accept(const uint rejectState, const uint step) +{ + bool result = false; + steps = step; + //If we didn't skip the move calculation + if(rejectState == mv::fail_state::NO_FAIL) { + //If lambdaIdxOld is zero, it means molecule transfered to destBox. + result = (lambdaIdxOld == 0); + if(result) { + //Set full interaction in destBox, zero interaction in sourceBox + lambdaRef.UnSet(destBox, sourceBox); + } else { + //Set full interaction in sourceBox, zero interaction in destBox + lambdaRef.UnSet(sourceBox, destBox); + //Shift the molecule back + ShiftMolToSourceBox(); + } + } + moveSetRef.Update(mv::CFCMC, result, step, destBox, kindIndex); +} + + +inline void CFCMC::ShiftMolToSourceBox() +{ + cellList.RemoveMol(molIndex, destBox, coordCurrRef); + //Set coordinates, new COM; shift index to new box's list + oldMolCFCMC.GetCoords().CopyRange(coordCurrRef, 0, pStartCFCMC, pLenCFCMC); + comCurrRef.SetNew(molIndex, sourceBox); + molLookRef.ShiftMolBox(molIndex, destBox, sourceBox, kindIndex); + cellList.AddMol(molIndex, sourceBox, coordCurrRef); +} + +inline void CFCMC::ShiftMolToDestBox() +{ + cellList.RemoveMol(molIndex, sourceBox, coordCurrRef); + //Set coordinates, new COM; shift index to new box's list + newMolCFCMC.GetCoords().CopyRange(coordCurrRef, 0, pStartCFCMC, pLenCFCMC); + comCurrRef.SetNew(molIndex, destBox); + molLookRef.ShiftMolBox(molIndex, sourceBox, destBox, kindIndex); + cellList.AddMol(molIndex, destBox, coordCurrRef); +} + +inline void CFCMC::UpdateBias() +{ + //Find the index for source and dest box + uint idxS = lambdaIdxOld; + uint idxD = lambdaWindow - lambdaIdxOld; + vector < uint > idx; + idx.push_back(idxS); + idx.push_back(idxD); + + for(uint b = 0; b < 2; b++) { + hist[box[b]][kindIndex][idx[b]] += 1; + + //In Bias, if lambda is 1.0, it is also 0.0 for continueity + if((idxS == lambdaWindow) || (idxS == 0)) { + hist[box[b]][kindIndex][idx[1 - b]] += 1; + } + + //Stop the modifying bias if we converged + if(nu[box[b]][kindIndex] <= nuTolerance) { + if(firstPrint[box[b]][kindIndex]) { + printf("STOPED MODIFYING BIAS FOR %s. \n", + molRef.kinds[kindIndex].name.c_str()); + firstPrint[box[b]][kindIndex] = false; + } + //Check after equilibration if all the bin visited atleast X% of + // the most visited bin + long trial = std::accumulate(hist[box[b]][kindIndex].begin(), hist[box[b]][kindIndex].end(), 0); + if((trial + 1) % (histFreq * 10) == 0) { + long int maxVisited = *max_element(hist[box[b]][kindIndex].begin(), + hist[box[b]][kindIndex].end()); + long int minVisited = *min_element(hist[box[b]][kindIndex].begin(), + hist[box[b]][kindIndex].end()); + //check to see if all the bin visited atleast X% of the most visited bin + if(minVisited < flatness * maxVisited) { + //nu[box[b]][kindIndex] = 0.01; + //std::fill_n(hist[box[b]][kindIndex].begin(), lambdaWindow + 1, 0); + printf("Warning [%d][%4s]: Minimum visited lambda state (%ld) is not %4.2f of Maximum visited lambda state (%ld)! \n", + box[b], molRef.kinds[kindIndex].name.c_str(), minVisited, + flatness, maxVisited); + } + } + continue; + } + + //Update the bias for both box + bias[box[b]][kindIndex][idx[b]] -= nu[box[b]][kindIndex]; + //In Bias, if lambda is 1.0, it is also 0.0 for continueity + if((idxS == lambdaWindow) || (idxS == 0)) { + bias[box[b]][kindIndex][idx[1 - b]] -= nu[box[b]][kindIndex]; + } + +#if ENSEMBLE == GCMC + //We dont consider biasing in reservoir + bias[1][kindIndex][idxD] = bias[1][kindIndex][idxS] = 0.0; + hist[1][kindIndex][idxD] = hist[1][kindIndex][idxS] = 0; +#endif + + //long trial = moveSetRef.GetTrial(box[b], mv::CFCMC, kindIndex); + long trial = std::accumulate(hist[box[b]][kindIndex].begin(), hist[box[b]][kindIndex].end(), 0); + //Check flatness + if((trial + 1) % histFreq == 0) { + uint maxVisited = *max_element(hist[box[b]][kindIndex].begin(), + hist[box[b]][kindIndex].end()); + uint minVisited = *min_element(hist[box[b]][kindIndex].begin(), + hist[box[b]][kindIndex].end()); + //check to see if all the bin visited atleast X% of the most visited bin + if(minVisited > flatness * maxVisited) { + nu[box[b]][kindIndex] *= 0.5; + std::fill_n(hist[box[b]][kindIndex].begin(), lambdaWindow + 1, 0); + printf("Bias-Adj [%d][%4s]: %4.10f \n", + box[b], molRef.kinds[kindIndex].name.c_str(), + nu[box[b]][kindIndex]); + } else { + printf("Hist [%d][%4s]: [", box[b], + molRef.kinds[kindIndex].name.c_str()); + for(uint i = 0; i <= lambdaWindow; i++) { + printf("%8ld", hist[box[b]][kindIndex][i]); + } + std::cout << "] \n"; + //Reset the histogram to reevaluate it + //std::fill_n(hist[box[b]][kindIndex].begin(), lambdaWindow + 1, 0); + } + } + } +} + + +inline bool CFCMC::AcceptInflating() +{ + double molTransCoeff = GetCoeff(); + double W1 = exp(-BETA * (newEnergy[sourceBox].Total() - + oldEnergy[sourceBox].Total())); + double W2 = exp(-BETA * (newEnergy[destBox].Total() - + oldEnergy[destBox].Total())); + //override the weight and energy if we used CBMC. + if(lambdaIdxNew == 0) { + W1 = 1.0 / oldMolCFCMC.GetWeight(); + oldEnergy[sourceBox] = oldMolCFCMC.GetEnergy(); + oldMolCFCMC.Reset(); + } else if(lambdaIdxNew == lambdaWindow) { + W2 = 1.0 / newMolCFCMC.GetWeight(); + oldEnergy[destBox] = newMolCFCMC.GetEnergy(); + newMolCFCMC.Reset(); + } + if(lambdaIdxOld == lambdaWindow) { + W2 = newMolCFCMC.GetWeight(); + newEnergy[destBox] = newMolCFCMC.GetEnergy(); + newMolCFCMC.Reset(); + } + double Wrat = W1 * W2 * W_tc * W_recip; + + bool result = prng() < molTransCoeff * Wrat; + //Reject the move if we had overlaped + result = result && !overlapCFCMC; + /* + if(!overlapCFCMC) { + printf("lambda[%5s]: %-2d -> %-2d : WS: %5.1e : WD: %5.1e : Wtot: %5.1e : Coef: %5.1e : SourceBox: %d \n", molRef.kinds[kindIndex].name.c_str(), lambdaIdxOld, lambdaIdxNew, W1, W2, Wrat, molTransCoeff, sourceBox); + } + */ + if(result) { + //Add tail corrections + sysPotRef.boxEnergy[sourceBox].tc += tcDiffSource; + sysPotRef.boxEnergy[destBox].tc += tcDiffDest; + //Add rest of energy. + sysPotRef.boxEnergy[sourceBox] -= oldEnergy[sourceBox]; + sysPotRef.boxEnergy[sourceBox] += newEnergy[sourceBox]; + sysPotRef.boxEnergy[destBox] -= oldEnergy[destBox]; + sysPotRef.boxEnergy[destBox] += newEnergy[destBox]; + //Add correction energy + sysPotRef.boxEnergy[sourceBox].correction += correctDiffSource; + sysPotRef.boxEnergy[destBox].correction += correctDiffDest; + //Add self energy + sysPotRef.boxEnergy[sourceBox].self += selfDiffSource; + sysPotRef.boxEnergy[destBox].self += selfDiffDest; + //Add Reciprocal energy + sysPotRef.boxEnergy[sourceBox].recip += recipDiffSource; + sysPotRef.boxEnergy[destBox].recip += recipDiffDest; + + calcEwald->UpdateRecip(sourceBox); + calcEwald->UpdateRecip(destBox); + + //Retotal + sysPotRef.Total(); + //set single move accept to true for multiparticle + moveSetRef.SetSingleMoveAccepted(); + } + overlapCFCMC = false; + + return result; +} + + +inline void CFCMC::RelaxingMolecules() +{ + ShiftMolToSourceBox(); + if(sourceBox < BOXES_WITH_U_NB) { + for(uint s = 0; s < relaxSteps; s++) { + uint state = TransformRelaxing(sourceBox); + if(state == mv::fail_state::NO_FAIL) { + CalcEnRelaxing(sourceBox); + AcceptRelaxing(sourceBox); + } + } + oldMolCFCMC.SetCoords(coordCurrRef, pStartCFCMC); + } + + ShiftMolToDestBox(); + if(destBox < BOXES_WITH_U_NB) { + for(uint s = 0; s < relaxSteps; s++) { + uint state = TransformRelaxing(destBox); + if(state == mv::fail_state::NO_FAIL) { + CalcEnRelaxing(destBox); + AcceptRelaxing(destBox); + } + } + newMolCFCMC.SetCoords(coordCurrRef, pStartCFCMC); + } +} + +inline uint CFCMC::TransformRelaxing(uint b) +{ + uint state = mv::fail_state::NO_FAIL; + if(MPEnable) { + MP.PrepCFCMC(b); + MP.Transform(); + } else { + //Randomely pick a molecule in Box + uint state = prng.PickMol(m, mk, b); + if(state == mv::fail_state::NO_FAIL) { + pStart = 0, pLen = 0; + molRef.GetRangeStartLength(pStart, pLen, m); + newMolPos.Uninit(); + newMolPos.Init(pLen); + newCOM = comCurrRef.Get(m); + bool trans = prng.randInt(1); + trans |= (pLen == 1); // single molecule, translation only + + if(trans) { + coordCurrRef.TranslateRand(newMolPos, newCOM, pStart, pLen, + m, b, moveSetRef.Scale(b, mv::DISPLACE, mk)); + } else { + coordCurrRef.RotateRand(newMolPos, pStart, pLen, m, b, + moveSetRef.Scale(b, mv::ROTATE, mk)); + } + } + } + return state; +} + +inline void CFCMC::CalcEnRelaxing(uint b) +{ + if(MPEnable) { + MP.CalcEn(); + } else { + cellList.RemoveMol(m, b, coordCurrRef); + overlap = false; + //calculate LJ interaction and real term of electrostatic interaction + overlap = calcEnRef.MoleculeInter(inter_LJ, inter_Real, newMolPos, m, b); + if(!overlap) { + //calculate reciprocate term of electrostatic interaction + recip.energy = calcEwald->MolReciprocal(newMolPos, m, b); + } + } +} + +inline void CFCMC::AcceptRelaxing(uint b) +{ + if(MPEnable) { + MP.Accept(mv::fail_state::NO_FAIL, steps); + } else { + bool res = false; + double pr = prng(); + res = pr < exp(-BETA * (inter_LJ.energy + inter_Real.energy + + recip.energy)); + bool result = res && !overlap; + + if (result) { + //Set new energy. + // setting energy and virial of LJ interaction + sysPotRef.boxEnergy[b].inter += inter_LJ.energy; + // setting energy and virial of coulomb interaction + sysPotRef.boxEnergy[b].real += inter_Real.energy; + // setting energy and virial of recip term + sysPotRef.boxEnergy[b].recip += recip.energy;; + + //Copy coords + newMolPos.CopyRange(coordCurrRef, 0, pStart, pLen); + comCurrRef.Set(m, newCOM); + calcEwald->UpdateRecip(b); + + sysPotRef.Total(); + } + cellList.AddMol(m, b, coordCurrRef); + } +} + + +#endif + +#endif diff --git a/src/moves/CrankShaft.h b/src/moves/CrankShaft.h index 85b9b1740..d9dd2485a 100644 --- a/src/moves/CrankShaft.h +++ b/src/moves/CrankShaft.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -105,8 +105,8 @@ inline void CrankShaft::CalcEn() correct_new = 0.0; if (newMol.GetWeight() != 0.0 && !overlap) { - correct_new = calcEwald->SwapCorrection(newMol); - correct_old = calcEwald->SwapCorrection(oldMol); + correct_new = calcEwald->SwapCorrection(newMol, molIndex); + correct_old = calcEwald->SwapCorrection(oldMol, molIndex); recipDiff.energy = calcEwald->MolReciprocal(newMol.GetCoords(), molIndex, sourceBox); //self energy is same @@ -126,7 +126,7 @@ inline void CrankShaft::Accept(const uint rejectState, const uint step) double Wrat = Wn / Wo * W_recip; //safety to make sure move will be rejected in overlap case - if(newMol.GetWeight() != 0.0 && !overlap) { + if(!overlap) { result = prng() < Wrat; } else result = false; @@ -158,6 +158,7 @@ inline void CrankShaft::Accept(const uint rejectState, const uint step) } calcEwald->UpdateRecip(sourceBox); + //Retotal sysPotRef.Total(); } else { diff --git a/src/moves/IntraMoleculeExchange1.h b/src/moves/IntraMoleculeExchange1.h index fac799d79..7ec77888e 100644 --- a/src/moves/IntraMoleculeExchange1.h +++ b/src/moves/IntraMoleculeExchange1.h @@ -144,13 +144,13 @@ inline void IntraMoleculeExchange1::SetMEMC(StaticVals const& statV) } if(kindS == -1) { - printf("Error: Residue name %s was not found in PDB file as small molecule kind to be exchanged or it is fixed in its position.\n", + printf("Error: In Intra-MEMC move, residue name %s was not found in PDB file as small molecule kind to be exchanged or it is fixed in its position.\n", statV.intraMemcVal.smallKind[t].c_str()); exit(EXIT_FAILURE); } if(kindL == -1) { - printf("Error: Residue name %s was not found in PDB file as large molecule kind to be exchanged or it is fixed in its position.\n", + printf("Error: In Intra-MEMC move, residue name %s was not found in PDB file as large molecule kind to be exchanged or it is fixed in its position.\n", statV.intraMemcVal.largeKind[t].c_str()); exit(EXIT_FAILURE); } @@ -166,7 +166,7 @@ inline void IntraMoleculeExchange1::SetMEMC(StaticVals const& statV) for(uint i = 0; i < 2; i++) { if(largeBB[i] == -1) { - printf("Error: Atom name %s or %s was not found in %s residue.\n", + printf("Error: In Intra-MEMC move, atom name %s or %s was not found in %s residue.\n", statV.intraMemcVal.largeBBAtom1[t].c_str(), statV.intraMemcVal.largeBBAtom2[t].c_str(), statV.intraMemcVal.largeKind[t].c_str()); @@ -177,7 +177,7 @@ inline void IntraMoleculeExchange1::SetMEMC(StaticVals const& statV) if(statV.intraMemcVal.MEMC1 || statV.intraMemcVal.MEMC2) { if(molRef.kinds[kindL].NumAtoms() > 1) { if(largeBB[0] == largeBB[1]) { - printf("Error: Atom names in large molecule backbone cannot be same!\n"); + printf("Error: In Intra-MEMC move, atom names in large molecule backbone cannot be same!\n"); exit(EXIT_FAILURE); } } @@ -366,29 +366,23 @@ inline uint IntraMoleculeExchange1::Prep(const double subDraw, sourceBox)); } - //set the old coordinate after unwrap them + //set the old coordinate and new coordinate for(uint n = 0; n < numInCavA; n++) { - XYZArray molA(pLenA[n]); - coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); - boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavA matrix to slant the old trial of molA oldMolA[n].SetCavMatrix(cavA); - //set coordinate of moleA to newMolA, later it will shift to centerB - newMolA[n].SetCoords(molA, 0); + //Later it will shift to centerB + newMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavB matrix to slant the new trial of molA newMolA[n].SetCavMatrix(cavB); } for(uint n = 0; n < numInCavB; n++) { - XYZArray molB(pLenB[n]); - coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); - boxDimRef.UnwrapPBC(molB, sourceBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavB matrix to slant the old trial of molB oldMolB[n].SetCavMatrix(cavB); - //set coordinate of moleB to newMolB, later it will shift to centerA - newMolB[n].SetCoords(molB, 0); + //Later it will shift to centerA + newMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavA matrix to slant the new trial of molB newMolB[n].SetCavMatrix(cavA); } @@ -466,8 +460,8 @@ inline void IntraMoleculeExchange1::CalcEn() correctDiff = 0.0; if(!overlap) { - recipDiffA = calcEwald->SwapRecip(newMolA, oldMolA); - recipDiffB = calcEwald->SwapRecip(newMolB, oldMolB); + recipDiffA = calcEwald->SwapRecip(newMolA, oldMolA, molIndexA, molIndexA); + recipDiffB = calcEwald->SwapRecip(newMolB, oldMolB, molIndexB, molIndexB); //No need to contribute the self and correction energy since insertion //and deletion are rigid body diff --git a/src/moves/IntraMoleculeExchange2.h b/src/moves/IntraMoleculeExchange2.h index 654d4163d..108828a7b 100644 --- a/src/moves/IntraMoleculeExchange2.h +++ b/src/moves/IntraMoleculeExchange2.h @@ -61,7 +61,7 @@ inline void IntraMoleculeExchange2::SetMEMC(StaticVals const& statV) for(uint i = 0; i < 2; i++) { if(smallBB[i] == -1) { - printf("Error: Atom name %s or %s was not found in %s residue.\n", + printf("Error: In Intra-ME-2 move, atom name %s or %s was not found in %s residue.\n", statV.intraMemcVal.smallBBAtom1[t].c_str(), statV.intraMemcVal.smallBBAtom2[t].c_str(), statV.intraMemcVal.smallKind[t].c_str()); @@ -71,7 +71,7 @@ inline void IntraMoleculeExchange2::SetMEMC(StaticVals const& statV) if(molRef.kinds[kindSVec[t]].NumAtoms() > 1) { if(smallBB[0] == smallBB[1]) { - printf("Error: Atom names in small molecule backbone cannot be same!\n"); + printf("Error: In Intra-ME-2 move, atom names in small molecule backbone cannot be same!\n"); exit(EXIT_FAILURE); } } @@ -230,27 +230,21 @@ inline uint IntraMoleculeExchange2::Prep(const double subDraw, //set the old coordinate after unwrap them for(uint n = 0; n < numInCavA; n++) { - XYZArray molA(pLenA[n]); - coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); - boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavA matrix to slant the old trial of molA oldMolA[n].SetCavMatrix(cavA); //set coordinate of moleA to newMolA, later it will shift to centerB - newMolA[n].SetCoords(molA, 0); + newMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavB matrix to slant the new trial of molA newMolA[n].SetCavMatrix(cavB); } for(uint n = 0; n < numInCavB; n++) { - XYZArray molB(pLenB[n]); - coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); - boxDimRef.UnwrapPBC(molB, sourceBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavB matrix to slant the old trial of molB oldMolB[n].SetCavMatrix(cavB); //set coordinate of moleB to newMolB, later it will shift to centerA - newMolB[n].SetCoords(molB, 0); + newMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavA matrix to slant the new trial of molB newMolB[n].SetCavMatrix(cavA); } diff --git a/src/moves/IntraMoleculeExchange3.h b/src/moves/IntraMoleculeExchange3.h index bed4fe960..af1fcb961 100644 --- a/src/moves/IntraMoleculeExchange3.h +++ b/src/moves/IntraMoleculeExchange3.h @@ -47,7 +47,7 @@ inline void IntraMoleculeExchange3::SetMEMC(StaticVals const& statV) { for(uint t = 0; t < exchangeRatioVec.size(); t++) { if(largeBBVec[t][0] != largeBBVec[t][1]) { - printf("Error: In ME-3 move, atom name of backbone should be same.\n"); + printf("Error: In Intra-ME-3 move, two atoms with same name should be used as backbone.\n"); printf("Atom names in backbone was set to %s or %s in %s residue.\n", statV.intraMemcVal.largeBBAtom1[t].c_str(), statV.intraMemcVal.largeBBAtom2[t].c_str(), @@ -189,27 +189,21 @@ inline uint IntraMoleculeExchange3::Prep(const double subDraw, //set the old coordinate after unwrap them for(uint n = 0; n < numInCavA; n++) { - XYZArray molA(pLenA[n]); - coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); - boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavA matrix to slant the old trial of molA oldMolA[n].SetCavMatrix(cavA); //set coordinate of moleA to newMolA, later it will shift to centerB - newMolA[n].SetCoords(molA, 0); + newMolA[n].SetCoords(coordCurrRef, pStartA[n]); //copy cavB matrix to slant the new trial of molA newMolA[n].SetCavMatrix(cavB); } for(uint n = 0; n < numInCavB; n++) { - XYZArray molB(pLenB[n]); - coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); - boxDimRef.UnwrapPBC(molB, sourceBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavB matrix to slant the old trial of molB oldMolB[n].SetCavMatrix(cavB); //set coordinate of moleB to newMolB, later it will shift to centerA - newMolB[n].SetCoords(molB, 0); + newMolB[n].SetCoords(coordCurrRef, pStartB[n]); //copy cavA matrix to slant the new trial of molB newMolB[n].SetCavMatrix(cavA); } @@ -293,11 +287,11 @@ inline void IntraMoleculeExchange3::CalcEn() // inserted rigid body. We just need it for kindL if(!overlap) { for(uint n = 0; n < numInCavB; n++) { - correctDiff += calcEwald->SwapCorrection(newMolB[n]); - correctDiff -= calcEwald->SwapCorrection(oldMolB[n]); + correctDiff += calcEwald->SwapCorrection(newMolB[n], molIndexB[n]); + correctDiff -= calcEwald->SwapCorrection(oldMolB[n], molIndexB[n]); } - recipDiffA = calcEwald->SwapRecip(newMolA, oldMolA); - recipDiffB = calcEwald->SwapRecip(newMolB, oldMolB); + recipDiffA = calcEwald->SwapRecip(newMolA, oldMolA, molIndexA, molIndexA); + recipDiffB = calcEwald->SwapRecip(newMolB, oldMolB, molIndexB, molIndexB); W_recip = exp(-1.0 * ffRef.beta * (recipDiffA + recipDiffB + correctDiff)); diff --git a/src/moves/IntraSwap.h b/src/moves/IntraSwap.h index 923c800cd..acc8a1703 100644 --- a/src/moves/IntraSwap.h +++ b/src/moves/IntraSwap.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -108,8 +108,8 @@ inline void IntraSwap::CalcEn() correct_new = 0.0; if (newMol.GetWeight() != 0.0 && !overlap) { - correct_new = calcEwald->SwapCorrection(newMol); - correct_old = calcEwald->SwapCorrection(oldMol); + correct_new = calcEwald->SwapCorrection(newMol, molIndex); + correct_old = calcEwald->SwapCorrection(oldMol, molIndex); recipDiff.energy = calcEwald->MolReciprocal(newMol.GetCoords(), molIndex, sourceBox); //self energy is same @@ -150,7 +150,6 @@ inline void IntraSwap::Accept(const uint rejectState, const uint step) comCurrRef.SetNew(molIndex, destBox); cellList.AddMol(molIndex, destBox, coordCurrRef); - //Zero out box energies to prevent small number //errors in double. if (molLookRef.NumInBox(sourceBox) == 1) { @@ -160,7 +159,8 @@ inline void IntraSwap::Accept(const uint rejectState, const uint step) sysPotRef.boxVirial[sourceBox].real = 0; } - calcEwald->UpdateRecip(sourceBox); + calcEwald->UpdateRecip(destBox); + //Retotal sysPotRef.Total(); } else { diff --git a/src/moves/MoleculeExchange1.h b/src/moves/MoleculeExchange1.h index 2bbe1e00e..29021695a 100644 --- a/src/moves/MoleculeExchange1.h +++ b/src/moves/MoleculeExchange1.h @@ -160,13 +160,13 @@ inline void MoleculeExchange1::SetMEMC(StaticVals const& statV) } if(kindS == -1) { - printf("Error: Residue name %s was not found in PDB file as small molecule kind to be exchanged or not allowed to be transferred.\n", + printf("Error: In MEMC move, residue name %s was not found in PDB file as small molecule kind to be exchanged or not allowed to be transferred.\n", statV.memcVal.smallKind[t].c_str()); exit(EXIT_FAILURE); } if(kindL == -1) { - printf("Error: Residue name %s was not found in PDB file as large molecule kind to be exchanged or not allowed to be transferred.\n", + printf("Error: In MEMC move, residue name %s was not found in PDB file as large molecule kind to be exchanged or not allowed to be transferred.\n", statV.memcVal.largeKind[t].c_str()); exit(EXIT_FAILURE); } @@ -182,7 +182,7 @@ inline void MoleculeExchange1::SetMEMC(StaticVals const& statV) for(uint i = 0; i < 2; i++) { if(largeBB[i] == -1) { - printf("Error: Atom name %s or %s was not found in %s residue.\n", + printf("Error: In MEMC move, atom name %s or %s was not found in %s residue.\n", statV.memcVal.largeBBAtom1[t].c_str(), statV.memcVal.largeBBAtom2[t].c_str(), statV.memcVal.largeKind[t].c_str()); @@ -193,7 +193,7 @@ inline void MoleculeExchange1::SetMEMC(StaticVals const& statV) if(statV.memcVal.MEMC1 || statV.memcVal.MEMC2) { if(molRef.kinds[kindL].NumAtoms() > 1) { if(largeBB[0] == largeBB[1]) { - printf("Error: Atom names in large molecule backbone cannot be same!\n"); + printf("Error: In MEMC move, atom names in large molecule backbone cannot be same!\n"); exit(EXIT_FAILURE); } } @@ -429,12 +429,13 @@ inline uint MoleculeExchange1::Prep(const double subDraw, const double movPerc) destBox)); } - //set the old coordinate after unwrap them + //set the old coordinate and new after proper wrap & unwrap for(uint n = 0; n < numInCavA; n++) { XYZArray molA(pLenA[n]); coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + boxDimRef.WrapPBC(molA, destBox); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //set coordinate of moleA to newMolA, later it will shift to center newMolA[n].SetCoords(molA, 0); //copy cavA matrix to slant the old trial of molA @@ -445,7 +446,8 @@ inline uint MoleculeExchange1::Prep(const double subDraw, const double movPerc) XYZArray molB(pLenB[n]); coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); boxDimRef.UnwrapPBC(molB, destBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + boxDimRef.WrapPBC(molB, sourceBox); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //set coordinate of moleB to newMolB, later it will shift newMolB[n].SetCoords(molB, 0); //copy cavA matrix to slant the new trial of molB @@ -576,7 +578,7 @@ inline void MoleculeExchange1::CalcEn() self_newA += calcEwald->SwapSelf(newMolA[n]); self_oldA += calcEwald->SwapSelf(oldMolA[n]); } - recipDest = calcEwald->SwapRecip(newMolA, oldMolB); + recipDest = calcEwald->SwapRecip(newMolA, oldMolB, molIndexA, molIndexB); for(uint n = 0; n < numInCavB; n++) { correct_newB += calcEwald->SwapCorrection(newMolB[n]); @@ -584,7 +586,7 @@ inline void MoleculeExchange1::CalcEn() self_newB += calcEwald->SwapSelf(newMolB[n]); self_oldB += calcEwald->SwapSelf(oldMolB[n]); } - recipSource = calcEwald->SwapRecip(newMolB, oldMolA); + recipSource = calcEwald->SwapRecip(newMolB, oldMolA, molIndexB, molIndexA); //need to contribute the self and correction energy W_recip = exp(-1.0 * ffRef.beta * (recipSource + recipDest + @@ -753,9 +755,8 @@ inline void MoleculeExchange1::Accept(const uint rejectState, const uint step) sysPotRef.boxEnergy[destBox].self += self_newA; sysPotRef.boxEnergy[destBox].self -= self_oldB; - for (uint b = 0; b < BOX_TOTAL; b++) { - calcEwald->UpdateRecip(b); - } + calcEwald->UpdateRecip(sourceBox); + calcEwald->UpdateRecip(destBox); //molA and molB already transfered to destBox and added to cellist //Retotal sysPotRef.Total(); diff --git a/src/moves/MoleculeExchange2.h b/src/moves/MoleculeExchange2.h index bed7cda83..27782b4ed 100644 --- a/src/moves/MoleculeExchange2.h +++ b/src/moves/MoleculeExchange2.h @@ -64,7 +64,7 @@ inline void MoleculeExchange2::SetMEMC(StaticVals const& statV) for(uint i = 0; i < 2; i++) { if(smallBB[i] == -1) { - printf("Error: Atom name %s or %s was not found in %s residue.\n", + printf("Error: In ME-2 move, atom name %s or %s was not found in %s residue.\n", statV.memcVal.smallBBAtom1[t].c_str(), statV.memcVal.smallBBAtom2[t].c_str(), statV.memcVal.smallKind[t].c_str()); @@ -74,7 +74,7 @@ inline void MoleculeExchange2::SetMEMC(StaticVals const& statV) if(molRef.kinds[kindSVec[t]].NumAtoms() > 1) { if(smallBB[0] == smallBB[1]) { - printf("Error: Atom names in small molecule backbone cannot be same!\n"); + printf("Error: In ME-2 move, atom names in small molecule backbone cannot be same!\n"); exit(EXIT_FAILURE); } } @@ -243,12 +243,13 @@ inline uint MoleculeExchange2::Prep(const double subDraw, const double movPerc) destBox)); } - //set the old coordinate after unwrap them + //set the old coordinate and new after proper wrap & unwrap for(uint n = 0; n < numInCavA; n++) { XYZArray molA(pLenA[n]); coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + boxDimRef.WrapPBC(molA, destBox); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //set coordinate of moleA to newMolA, later it will shift to center newMolA[n].SetCoords(molA, 0); //copy cavA matrix to slant the old trial of molA @@ -259,7 +260,8 @@ inline uint MoleculeExchange2::Prep(const double subDraw, const double movPerc) XYZArray molB(pLenB[n]); coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); boxDimRef.UnwrapPBC(molB, destBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + boxDimRef.WrapPBC(molB, sourceBox); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //set coordinate of moleB to newMolB, later it will shift newMolB[n].SetCoords(molB, 0); //copy cavA matrix to slant the new trial of molB diff --git a/src/moves/MoleculeExchange3.h b/src/moves/MoleculeExchange3.h index c65ae89e7..6a83b7f27 100644 --- a/src/moves/MoleculeExchange3.h +++ b/src/moves/MoleculeExchange3.h @@ -50,7 +50,7 @@ inline void MoleculeExchange3::SetMEMC(StaticVals const& statV) { for(uint t = 0; t < exchangeRatioVec.size(); t++) { if(largeBBVec[t][0] != largeBBVec[t][1]) { - printf("Error: In ME-3 move, atom name of backbone should be same.\n"); + printf("Error: In ME-3 move, two atoms with same name should be used as backbone.\n"); printf("Atom names in backbone was set to %s or %s in %s residue.\n", statV.memcVal.largeBBAtom1[t].c_str(), statV.memcVal.largeBBAtom2[t].c_str(), @@ -200,12 +200,13 @@ inline uint MoleculeExchange3::Prep(const double subDraw, const double movPerc) destBox)); } - //set the old coordinate after unwrap them + //set the old coordinate and new after proper wrap & unwrap for(uint n = 0; n < numInCavA; n++) { XYZArray molA(pLenA[n]); coordCurrRef.CopyRange(molA, pStartA[n], 0, pLenA[n]); boxDimRef.UnwrapPBC(molA, sourceBox, comCurrRef.Get(molIndexA[n])); - oldMolA[n].SetCoords(molA, 0); + boxDimRef.WrapPBC(molA, destBox); + oldMolA[n].SetCoords(coordCurrRef, pStartA[n]); //set coordinate of moleA to newMolA, later it will shift to center newMolA[n].SetCoords(molA, 0); //copy cavA matrix to slant the old trial of molA @@ -216,7 +217,8 @@ inline uint MoleculeExchange3::Prep(const double subDraw, const double movPerc) XYZArray molB(pLenB[n]); coordCurrRef.CopyRange(molB, pStartB[n], 0, pLenB[n]); boxDimRef.UnwrapPBC(molB, destBox, comCurrRef.Get(molIndexB[n])); - oldMolB[n].SetCoords(molB, 0); + boxDimRef.WrapPBC(molB, sourceBox); + oldMolB[n].SetCoords(coordCurrRef, pStartB[n]); //set coordinate of moleB to newMolB, later it will shift newMolB[n].SetCoords(molB, 0); //copy cavA matrix to slant the new trial of molB diff --git a/src/moves/MoleculeTransfer.h b/src/moves/MoleculeTransfer.h index e31cdd081..a3ed2b4c7 100644 --- a/src/moves/MoleculeTransfer.h +++ b/src/moves/MoleculeTransfer.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -121,6 +121,7 @@ inline void MoleculeTransfer::CalcEn() correct_old = calcEwald->SwapCorrection(oldMol); self_new = calcEwald->SwapSelf(newMol); self_old = calcEwald->SwapSelf(oldMol); + //SwapDestRecip must be called first to backup the cosMol and sinMol recipGain.energy = calcEwald->SwapDestRecip(newMol, destBox, molIndex); recipLose.energy = @@ -175,7 +176,7 @@ inline void MoleculeTransfer::Accept(const uint rejectState, const uint step) double Wrat = Wn / Wo * W_tc * W_recip; //safety to make sure move will be rejected in overlap case - if(newMol.GetWeight() != 0.0 && !overlap) { + if(!overlap) { result = prng() < molTransCoeff * Wrat; } else result = false; @@ -217,9 +218,8 @@ inline void MoleculeTransfer::Accept(const uint rejectState, const uint step) sysPotRef.boxVirial[sourceBox].real = 0; } - for (uint b = 0; b < BOXES_WITH_U_NB; b++) { - calcEwald->UpdateRecip(b); - } + calcEwald->UpdateRecip(sourceBox); + calcEwald->UpdateRecip(destBox); //Retotal sysPotRef.Total(); diff --git a/src/moves/MoveBase.h b/src/moves/MoveBase.h index b1800335d..eb1fd0933 100644 --- a/src/moves/MoveBase.h +++ b/src/moves/MoveBase.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -36,11 +36,18 @@ class MoveBase calcEnRef(sys.calcEnergy), comCurrRef(sys.com), coordCurrRef(sys.coordinates), prng(sys.prng), molRef(statV.mol), BETA(statV.forcefield.beta), ewald(statV.forcefield.ewald), - cellList(sys.cellList) + cellList(sys.cellList), molRemoved(false), + atomForceRef(sys.atomForceRef), + molForceRef(sys.molForceRef), + atomForceRecRef(sys.atomForceRecRef), + molForceRecRef(sys.molForceRecRef) { + atomForceNew.Init(sys.atomForceRef.Count()); + molForceNew.Init(sys.molForceRef.Count()); calcEwald = sys.GetEwald(); molRemoved = false; overlap = false; + multiParticleEnabled = sys.statV.multiParticleEnabled; } //Based on the random draw, determine the move kind, box, and @@ -75,6 +82,12 @@ class MoveBase COM & comCurrRef; CalculateEnergy & calcEnRef; Ewald * calcEwald; + XYZArray& atomForceRef; + XYZArray atomForceNew; + XYZArray& molForceRef; + XYZArray molForceNew; + XYZArray& atomForceRecRef; + XYZArray& molForceRecRef; PRNG & prng; BoxDimensions & boxDimRef; @@ -83,6 +96,7 @@ class MoveBase const bool ewald; CellList& cellList; bool molRemoved, fixBox0, overlap; + bool multiParticleEnabled; }; //Data needed for transforming a molecule's position via inter or intrabox diff --git a/src/moves/MultiParticle.h b/src/moves/MultiParticle.h new file mode 100644 index 000000000..9c79d8ff4 --- /dev/null +++ b/src/moves/MultiParticle.h @@ -0,0 +1,494 @@ +/******************************************************************************* +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 +Copyright (C) 2018 GOMC Group +A copy of the GNU General Public License can be found in the COPYRIGHT.txt +along with this program, also can be found at . +********************************************************************************/ +#ifndef MULTIPARTICLE_H +#define MULTIPARTICLE_H + +#include "MoveBase.h" +#include "System.h" +#include "StaticVals.h" +#include + +#define MIN_FORCE 1E-12 +#define MAX_FORCE 30 + +class MultiParticle : public MoveBase +{ +public: + MultiParticle(System &sys, StaticVals const& statV); + + virtual uint Prep(const double subDraw, const double movPerc); + virtual void CalcEn(); + virtual uint Transform(); + virtual void Accept(const uint rejectState, const uint step); + virtual void PrintAcceptKind(); + void PrepCFCMC(const uint box); + +private: + uint bPick; + uint typePick; + double lambda; + bool initMol[BOX_TOTAL]; + SystemPotential sysPotNew; + XYZArray molTorqueRef; + XYZArray molTorqueNew; + XYZArray atomForceRecNew; + XYZArray molForceRecNew; + XYZArray t_k; + XYZArray r_k; + Coordinates newMolsPos; + COM newCOMs; + vector moveType, moleculeIndex; + const MoleculeLookup& molLookup; + + long double GetCoeff(); + void CalculateTrialDistRot(); + void RotateForceBiased(uint molIndex); + void TranslateForceBiased(uint molIndex); + void SetMolInBox(uint box); + XYZ CalcRandomTransform(XYZ const &lb, double const max); + double CalculateWRatio(XYZ const &lb_new, XYZ const &lb_old, XYZ const &k, + double max); +}; + +inline MultiParticle::MultiParticle(System &sys, StaticVals const &statV) : + MoveBase(sys, statV), + newMolsPos(sys.boxDimRef, newCOMs, sys.molLookupRef, sys.prng, statV.mol), + newCOMs(sys.boxDimRef, newMolsPos, sys.molLookupRef, statV.mol), + molLookup(sys.molLookup) +{ + molTorqueNew.Init(sys.com.Count()); + molTorqueRef.Init(sys.com.Count()); + atomForceRecNew.Init(sys.coordinates.Count()); + molForceRecNew.Init(sys.com.Count()); + + t_k.Init(sys.com.Count()); + r_k.Init(sys.com.Count()); + newMolsPos.Init(sys.coordinates.Count()); + newCOMs.Init(sys.com.Count()); + moveType.resize(sys.com.Count()); + + // set default value for r_max, t_max, and lambda + // the value of lambda is based on the paper + lambda = 0.5; + for(uint b = 0; b < BOX_TOTAL; b++) { + initMol[b] = false; + } +} + +inline void MultiParticle::PrintAcceptKind() +{ + printf("%-37s", "% Accepted MultiParticle "); + for(uint b = 0; b < BOX_TOTAL; b++) { + printf("%10.5f ", 100.0 * moveSetRef.GetAccept(b, mv::MULTIPARTICLE)); + } + std::cout << std::endl; +} + + +inline void MultiParticle::SetMolInBox(uint box) +{ + // NEED to check if atom is not fixed! +#if ENSEMBLE == GCMC || ENSEMBLE == GEMC + moleculeIndex.clear(); + MoleculeLookup::box_iterator thisMol = molLookup.BoxBegin(box); + MoleculeLookup::box_iterator end = molLookup.BoxEnd(box); + while(thisMol != end) { + //Make sure this molecule is not fixed in its position + if(!molLookup.IsFix(*thisMol)) { + moleculeIndex.push_back(*thisMol); + } + thisMol++; + } +#else + if(!initMol[box]) { + moleculeIndex.clear(); + MoleculeLookup::box_iterator thisMol = molLookup.BoxBegin(box); + MoleculeLookup::box_iterator end = molLookup.BoxEnd(box); + while(thisMol != end) { + //Make sure this molecule is not fixed in its position + if(!molLookup.IsFix(*thisMol)) { + moleculeIndex.push_back(*thisMol); + } + thisMol++; + } + } +#endif + initMol[box] = true; +} + +inline uint MultiParticle::Prep(const double subDraw, const double movPerc) +{ + uint state = mv::fail_state::NO_FAIL; +#if ENSEMBLE == GCMC + bPick = mv::BOX0; +#else + prng.PickBox(bPick, subDraw, movPerc); +#endif + + // In each step, we perform either: + // 1- All displacement move. + // 2- All rotation move. + // We can also add another move typr, where in this steps, each molecule + // can displace or rotate, independently from other molecule. To do that, we + // need to change the mp::MPTOTALTYPES variable to 3, in MoveSetting.h + typePick = prng.randIntExc(mp::MPTOTALTYPES); + SetMolInBox(bPick); + + for(uint m = 0; m < moleculeIndex.size(); m++) { + uint length = molRef.GetKind(moleculeIndex[m]).NumAtoms(); + if(length == 1) { + moveType[moleculeIndex[m]] = mp::MPDISPLACE; + } else { + switch(typePick) { + case mp::MPALLDISPLACE: + moveType[moleculeIndex[m]] = mp::MPDISPLACE; + break; + case mp::MPALLROTATE: + moveType[moleculeIndex[m]] = mp::MPROTATE; + break; + case mp::MPALLRANDOM: + moveType[moleculeIndex[m]] = (prng.randInt(1) ? + mp::MPROTATE : mp::MPDISPLACE); + break; + default: + std::cerr << "Error: Something went wrong preping MultiParticle!\n" + << "Type Pick value is not valid!\n"; + exit(EXIT_FAILURE); + } + } + } + + if(moveSetRef.GetSingleMoveAccepted()) { + //Calculate force for long range electrostatic using old position + calcEwald->BoxForceReciprocal(coordCurrRef, atomForceRecRef, molForceRecRef, + bPick); + + //calculate short range energy and force for old positions + calcEnRef.BoxForce(sysPotRef, coordCurrRef, atomForceRef, molForceRef, + boxDimRef, bPick); + + if(typePick != mp::MPALLDISPLACE) { + //Calculate Torque for old positions + calcEnRef.CalculateTorque(moleculeIndex, coordCurrRef, comCurrRef, + atomForceRef, atomForceRecRef, molTorqueRef, + moveType, bPick); + } + } + CalculateTrialDistRot(); + coordCurrRef.CopyRange(newMolsPos, 0, 0, coordCurrRef.Count()); + comCurrRef.CopyRange(newCOMs, 0, 0, comCurrRef.Count()); + return state; +} + +inline void MultiParticle::PrepCFCMC(const uint box) +{ + bPick = box; + typePick = prng.randIntExc(mp::MPTOTALTYPES); + SetMolInBox(bPick); + + for(uint m = 0; m < moleculeIndex.size(); m++) { + uint length = molRef.GetKind(moleculeIndex[m]).NumAtoms(); + if(length == 1) { + moveType[moleculeIndex[m]] = mp::MPDISPLACE; + } else { + switch(typePick) { + case mp::MPALLDISPLACE: + moveType[moleculeIndex[m]] = mp::MPDISPLACE; + break; + case mp::MPALLROTATE: + moveType[moleculeIndex[m]] = mp::MPROTATE; + break; + case mp::MPALLRANDOM: + moveType[moleculeIndex[m]] = (prng.randInt(1) ? + mp::MPROTATE : mp::MPDISPLACE); + break; + default: + std::cerr << "Error: Something went wrong preping MultiParticle!\n" + << "Type Pick value is not valid!\n"; + exit(EXIT_FAILURE); + } + } + } + + if(moveSetRef.GetSingleMoveAccepted()) { + //Calculate force for long range electrostatic using old position + calcEwald->BoxForceReciprocal(coordCurrRef, atomForceRecRef, molForceRecRef, + bPick); + + //calculate short range energy and force for old positions + calcEnRef.BoxForce(sysPotRef, coordCurrRef, atomForceRef, molForceRef, + boxDimRef, bPick); + + if(typePick != mp::MPALLDISPLACE) { + //Calculate Torque for old positions + calcEnRef.CalculateTorque(moleculeIndex, coordCurrRef, comCurrRef, + atomForceRef, atomForceRecRef, molTorqueRef, + moveType, bPick); + } + } + CalculateTrialDistRot(); + coordCurrRef.CopyRange(newMolsPos, 0, 0, coordCurrRef.Count()); + comCurrRef.CopyRange(newCOMs, 0, 0, comCurrRef.Count()); +} + +inline uint MultiParticle::Transform() +{ + // Based on the reference force decided whether to displace or rotate each + // individual particle. + uint state = mv::fail_state::NO_FAIL; + uint m; + + // move particles according to force and torque and store them in the new pos +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(m) +#endif + for(m = 0; m < moleculeIndex.size(); m++) { + if(moveType[moleculeIndex[m]]) { + // rotate + RotateForceBiased(moleculeIndex[m]); + } else { + // displacement + TranslateForceBiased(moleculeIndex[m]); + } + } + return state; +} + +inline void MultiParticle::CalcEn() +{ + // Calculate the new force and energy and we will compare that to the + // reference values in Accept() function + cellList.GridAll(boxDimRef, newMolsPos, molLookup); + + //back up cached fourier term + calcEwald->backupMolCache(); + //setup reciprocate vectors for new positions + calcEwald->BoxReciprocalSetup(bPick, newMolsPos); + + sysPotNew = sysPotRef; + //calculate short range energy and force + sysPotNew = calcEnRef.BoxForce(sysPotNew, newMolsPos, atomForceNew, + molForceNew, boxDimRef, bPick); + //calculate long range of new electrostatic energy + sysPotNew.boxEnergy[bPick].recip = calcEwald->BoxReciprocal(bPick); + //Calculate long range of new electrostatic force + calcEwald->BoxForceReciprocal(newMolsPos, atomForceRecNew, molForceRecNew, + bPick); + + if(typePick != mp::MPALLDISPLACE) { + //Calculate Torque for new positions + calcEnRef.CalculateTorque(moleculeIndex, newMolsPos, newCOMs, atomForceNew, + atomForceRecNew, molTorqueNew, moveType, bPick); + } + sysPotNew.Total(); +} + +inline double MultiParticle::CalculateWRatio(XYZ const &lb_new, XYZ const &lb_old, + XYZ const &k, double max) +{ + double w_ratio = 1.0; + XYZ lbmax = lb_old * max; + //If we used force to bias the displacement or rotation, we include it + if(abs(lbmax.x) > MIN_FORCE && abs(lbmax.x) < MAX_FORCE) { + w_ratio *= lb_new.x * exp(-lb_new.x * k.x) / (2.0 * sinh(lb_new.x * max)); + w_ratio /= lb_old.x * exp(lb_old.x * k.x) / (2.0 * sinh(lb_old.x * max)); + } + + if(abs(lbmax.y) > MIN_FORCE && abs(lbmax.y) < MAX_FORCE) { + w_ratio *= lb_new.y * exp(-lb_new.y * k.y) / (2.0 * sinh(lb_new.y * max)); + w_ratio /= lb_old.y * exp(lb_old.y * k.y) / (2.0 * sinh(lb_old.y * max)); + } + + if(abs(lbmax.z) > MIN_FORCE && abs(lbmax.z) < MAX_FORCE) { + w_ratio *= lb_new.z * exp(-lb_new.z * k.z) / (2.0 * sinh(lb_new.z * max)); + w_ratio /= lb_old.z * exp(lb_old.z * k.z) / (2.0 * sinh(lb_old.z * max)); + } + + return w_ratio; +} + +inline long double MultiParticle::GetCoeff() +{ + // calculate (w_new->old/w_old->new) and return it. + XYZ lbf_old, lbf_new; // lambda * BETA * force + XYZ lbt_old, lbt_new; // lambda * BETA * torque + long double w_ratio = 1.0; + double lBeta = lambda * BETA; + uint m, molNumber; + double r_max = moveSetRef.GetRMAX(bPick); + double t_max = moveSetRef.GetTMAX(bPick); +#ifdef _OPENMP + #pragma omp parallel for default(shared) private(m, molNumber, lbt_old, lbt_new, lbf_old, lbf_new) reduction(*:w_ratio) +#endif + for(m = 0; m < moleculeIndex.size(); m++) { + molNumber = moleculeIndex[m]; + if(moveType[molNumber]) { + // rotate + lbt_old = molTorqueRef.Get(molNumber) * lBeta; + lbt_new = molTorqueNew.Get(molNumber) * lBeta; + w_ratio *= CalculateWRatio(lbt_new, lbt_old, r_k.Get(molNumber), r_max); + } else { + // displace + lbf_old = (molForceRef.Get(molNumber) + molForceRecRef.Get(molNumber)) * + lBeta; + lbf_new = (molForceNew.Get(molNumber) + molForceRecNew.Get(molNumber)) * + lBeta; + w_ratio *= CalculateWRatio(lbf_new, lbf_old, t_k.Get(molNumber), t_max); + } + } + + // In case where force or torque is a large negative number (ex. -800) + // the exp value becomes inf. In these situations we have to return 0 to + // reject the move + // if(!std::isfinite(w_ratio)) { + // // This error can be removed later on once we know this part actually works. + // std::cout << "w_ratio is not a finite number. Auto-rejecting move.\n"; + // return 0.0; + // } + return w_ratio; +} + +inline void MultiParticle::Accept(const uint rejectState, const uint step) +{ + // Here we compare the values of reference and trial and decide whether to + // accept or reject the move + long double MPCoeff = GetCoeff(); + double uBoltz = exp(-BETA * (sysPotNew.Total() - sysPotRef.Total())); + long double accept = MPCoeff * uBoltz; + // cout << "MPCoeff: " << MPCoeff << ", sysPotNew: " << sysPotNew.Total() + // << ", sysPotRef: " << sysPotRef.Total() << ", accept: " << accept <UpdateRecip(bPick); + } else { + cellList.GridAll(boxDimRef, coordCurrRef, molLookup); + calcEwald->exgMolCache(); + } + + moveSetRef.UpdateMoveSettingMultiParticle(bPick, result, typePick); + moveSetRef.AdjustMultiParticle(bPick, typePick); + + moveSetRef.Update(mv::MULTIPARTICLE, result, step, bPick); +} + +inline XYZ MultiParticle::CalcRandomTransform(XYZ const &lb, double const max) +{ + XYZ lbmax = lb * max; + XYZ num; + if(abs(lbmax.x) > MIN_FORCE && abs(lbmax.x) < MAX_FORCE) { + num.x = log(exp(-1.0 * lbmax.x) + 2 * prng() * sinh(lbmax.x)) / lb.x; + } else { + num.x = prng.Sym(max); + } + + if(abs(lbmax.y) > MIN_FORCE && abs(lbmax.y) < MAX_FORCE) { + num.y = log(exp(-1.0 * lbmax.y) + 2 * prng() * sinh(lbmax.y)) / lb.y; + } else { + num.y = prng.Sym(max); + } + + if(abs(lbmax.z) > MIN_FORCE && abs(lbmax.z) < MAX_FORCE) { + num.z = log(exp(-1.0 * lbmax.z) + 2 * prng() * sinh(lbmax.z)) / lb.z; + } else { + num.z = prng.Sym(max); + } + + if(num.Length() >= boxDimRef.axis.Min(bPick)) { + std::cout << "Trial Displacement exceed half of the box length in Multiparticle move.\n"; + std::cout << "Trial transform: " << num; + exit(EXIT_FAILURE); + } else if (!isfinite(num.Length())) { + std::cout << "Trial Displacement is not a finite number in Multiparticle move.\n"; + std::cout << "Trial transform: " << num; + exit(EXIT_FAILURE); + } + + // We can possible bound them + + return num; +} + +inline void MultiParticle::CalculateTrialDistRot() +{ + uint m, molIndex; + double r_max = moveSetRef.GetRMAX(bPick); + double t_max = moveSetRef.GetTMAX(bPick); + XYZ lbf; // lambda * BETA * force * maxTranslate + XYZ lbt; // lambda * BETA * torque * maxRotation + for(m = 0; m < moleculeIndex.size(); m++) { + molIndex = moleculeIndex[m]; + + if(moveType[molIndex]) { // rotate + lbt = molTorqueRef.Get(molIndex) * lambda * BETA; + r_k.Set(molIndex, CalcRandomTransform(lbt, r_max)); + } else { // displace + lbf = (molForceRef.Get(molIndex) + molForceRecRef.Get(molIndex)) * + lambda * BETA; + t_k.Set(molIndex, CalcRandomTransform(lbf, t_max)); + } + } +} + +inline void MultiParticle::RotateForceBiased(uint molIndex) +{ + XYZ rot = r_k.Get(molIndex); + double rotLen = rot.Length(); + RotationMatrix matrix; + + matrix = RotationMatrix::FromAxisAngle(rotLen, rot * (1.0 / rotLen)); + + XYZ center = newCOMs.Get(molIndex); + uint start, stop, len; + molRef.GetRange(start, stop, len, molIndex); + + // Copy the range into temporary array + XYZArray temp(len); + newMolsPos.CopyRange(temp, start, 0, len); + boxDimRef.UnwrapPBC(temp, bPick, center); + + // Do Rotation + for(uint p = 0; p < len; p++) { + temp.Add(p, -center); + temp.Set(p, matrix.Apply(temp[p])); + temp.Add(p, center); + } + boxDimRef.WrapPBC(temp, bPick); + // Copy back the result + temp.CopyRange(newMolsPos, 0, start, len); +} + +inline void MultiParticle::TranslateForceBiased(uint molIndex) +{ + XYZ shift = t_k.Get(molIndex); + + XYZ newcom = newCOMs.Get(molIndex); + uint stop, start, len; + molRef.GetRange(start, stop, len, molIndex); + // Copy the range into temporary array + XYZArray temp(len); + newMolsPos.CopyRange(temp, start, 0, len); + //Shift the coordinate and COM + temp.AddAll(shift); + newcom += shift; + //rewrapping + boxDimRef.WrapPBC(temp, bPick); + newcom = boxDimRef.WrapPBC(newcom, bPick); + //set the new coordinate + temp.CopyRange(newMolsPos, 0, start, len); + newCOMs.Set(molIndex, newcom); +} + +#endif diff --git a/src/moves/Regrowth.h b/src/moves/Regrowth.h index 3c11d6399..122c1d3e7 100644 --- a/src/moves/Regrowth.h +++ b/src/moves/Regrowth.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -104,8 +104,8 @@ inline void Regrowth::CalcEn() correct_new = 0.0; if (newMol.GetWeight() != 0.0 && !overlap) { - correct_new = calcEwald->SwapCorrection(newMol); - correct_old = calcEwald->SwapCorrection(oldMol); + correct_new = calcEwald->SwapCorrection(newMol, molIndex); + correct_old = calcEwald->SwapCorrection(oldMol, molIndex); recipDiff.energy = calcEwald->MolReciprocal(newMol.GetCoords(), molIndex, sourceBox); //self energy is same @@ -125,7 +125,7 @@ inline void Regrowth::Accept(const uint rejectState, const uint step) double Wrat = Wn / Wo * W_recip; //safety to make sure move will be rejected in overlap case - if(newMol.GetWeight() != 0.0 && !overlap) { + if(!overlap) { result = prng() < Wrat; } else result = false; @@ -157,6 +157,7 @@ inline void Regrowth::Accept(const uint rejectState, const uint step) } calcEwald->UpdateRecip(sourceBox); + //Retotal sysPotRef.Total(); } else { diff --git a/src/moves/Rotation.h b/src/moves/Rotation.h index 8f7796c6f..56353912b 100644 --- a/src/moves/Rotation.h +++ b/src/moves/Rotation.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -9,7 +9,6 @@ along with this program, also can be found at . #include "MoveBase.h" - class Rotate : public MoveBase, public MolTransformBase { public: diff --git a/src/moves/Translate.h b/src/moves/Translate.h index a3064233c..f970fd79f 100644 --- a/src/moves/Translate.h +++ b/src/moves/Translate.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . diff --git a/src/moves/VolumeTransfer.h b/src/moves/VolumeTransfer.h index 3ed6333ab..1183f4351 100644 --- a/src/moves/VolumeTransfer.h +++ b/src/moves/VolumeTransfer.h @@ -1,5 +1,5 @@ /******************************************************************************* -GPU OPTIMIZED MONTE CARLO (GOMC) 2.40 +GPU OPTIMIZED MONTE CARLO (GOMC) 2.50 Copyright (C) 2018 GOMC Group A copy of the GNU General Public License can be found in the COPYRIGHT.txt along with this program, also can be found at . @@ -164,14 +164,13 @@ inline void VolumeTransfer::CalcEn() calcEwald->RecipInit(bPick[b], newDim); //setup reciprocate terms calcEwald->BoxReciprocalSetup(bPick[b], newMolsPos); - sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newCOMs, - newDim, bPick[b]); + sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newDim, bPick[b]); } else { calcEwald->RecipInit(bPick[b], newDimNonOrth); //setup reciprocate terms calcEwald->BoxReciprocalSetup(bPick[b], newMolsPos); - sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newCOMs, - newDimNonOrth, bPick[b]); + sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newDimNonOrth, + bPick[b]); } //calculate reciprocate term of electrostatic interaction sysPotNew.boxEnergy[bPick[b]].recip = calcEwald->BoxReciprocal(bPick[b]); @@ -182,14 +181,12 @@ inline void VolumeTransfer::CalcEn() calcEwald->RecipInit(box, newDim); //setup reciprocate terms calcEwald->BoxReciprocalSetup(box, newMolsPos); - sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newCOMs, newDim, - box); + sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newDim, box); } else { calcEwald->RecipInit(box, newDimNonOrth); //setup reciprocate terms calcEwald->BoxReciprocalSetup(box, newMolsPos); - sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newCOMs, - newDimNonOrth, box); + sysPotNew = calcEnRef.BoxInter(sysPotNew, newMolsPos, newDimNonOrth, box); } //calculate reciprocate term of electrostatic interaction sysPotNew.boxEnergy[box].recip = calcEwald->BoxReciprocal(box);