diff --git a/CMakeLists.txt b/CMakeLists.txt index 80eaaeb3a..a180f836d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,57 +59,24 @@ INCLUDE_DIRECTORIES($ENV{NETCDF_FORTRAN_HOME}/include;$ENV{NETCDF_HOME}/include) ############################################################ - # Define some directories SET(SRC ${CMAKE_SOURCE_DIR}/src) SET(LIB ${CMAKE_SOURCE_DIR}/lib) SET(BIN ${CMAKE_SOURCE_DIR}/bin) SET(MOD ${CMAKE_SOURCE_DIR}/include) #SET(TEST ${CMAKE_SOURCE_DIR}/test) -SET(PY ${CMAKE_SOURCE_DIR}/src/python) +SET(PY ${CMAKE_SOURCE_DIR}/python) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIB}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN}) - -FUNCTION(REPLACE_VERSION IN_FILE LANGUAGE) - # Make list of strings from file - FILE(STRINGS ${IN_FILE} LINES) - - # Replace file with new one - FILE(WRITE ${IN_FILE} "") - - # Look for the word "VERSION" and replace the line with the updated one - FOREACH(LINE IN LISTS LINES) - IF (LANGUAGE STREQUAL "Fortran") # This is the version found in the swiftest driver program - STRING(FIND "${LINE}" " VERSION =" LINE_HAS_VER) - IF (LINE_HAS_VER GREATER_EQUAL 0) # This is the version line - FILE(APPEND ${IN_FILE} " character(*), parameter :: VERSION = \"${CMAKE_PROJECT_VERSION}\" !! Swiftest version\n") - ELSE () - FILE(APPEND ${IN_FILE} "${LINE}\n") # No match. Put this line back like we found it - ENDIF () - ELSEIF (LANGUAGE STREQUAL "Python") # This is the version found in the Python package - STRING(FIND "${LINE}" "version=" LINE_HAS_VER) - IF (LINE_HAS_VER GREATER_EQUAL 0) # This is the version line - FILE(APPEND ${IN_FILE} " version='${CMAKE_PROJECT_VERSION}',\n") - ELSE () # No match. Put this line back like we found it - STRING(REPLACE ";" "\n" LINE "${LINE}") # Fixes an issue when parsing the list that defines the pydriver extension module - FILE(APPEND ${IN_FILE} "${LINE}\n") - ENDIF () - ENDIF () - ENDFOREACH () -ENDFUNCTION () - -REPLACE_VERSION(${SRC}/globals/globals_module.f90 "Fortran" ) -REPLACE_VERSION(${PY}/setup.py "Python") - - # Have the .mod files placed in the lib folder SET(CMAKE_Fortran_MODULE_DIRECTORY ${MOD}) # The source for the SWIFTEST binary and have it placed in the bin folder ADD_SUBDIRECTORY(${SRC} ${BIN}) +ADD_SUBDIRECTORY(${PY}) # # Set up test directory # ENABLE_TESTING() diff --git a/Dockerfile b/Dockerfile index 2962229ce..ef73edf90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -130,11 +130,9 @@ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-py311_23.5.2-0-Linux-x86 -DBUILD_SHARED_LIBS=OFF \ ${EXTRA_CMAKE_OPTIONS} && \ cmake --build build && \ - cmake --install build && \ - mv bin/CMakeFiles/swiftest.dir/__/python/swiftest/f2py/driver.f90.o python/swiftest/f2py/ && \ - mv bin/CMakeFiles/swiftest.dir/__/python/swiftest/f2py/pydriver.f90.o python/swiftest/f2py/ + cmake --install build -RUN cd swiftest/python/swiftest/f2py && \ +RUN cd swiftest/python && \ python setup.py build_ext --inplace @@ -167,17 +165,17 @@ ENV LD_LIBRARY_PATH="/usr/local/lib" COPY --from=build_driver /usr/local/bin/swiftest_driver /opt/conda/bin/swiftest_driver COPY --from=build_driver /usr/local/lib/libswiftest.a /opt/conda/lib/libswiftest.a COPY --from=build_driver /swiftest/include/ /opt/conda/include/swiftest/ -COPY ./python/. /opt/conda/pkgs/ +COPY ./python/. /opt/conda/pkgs/swiftest/ COPY environment.yml . RUN conda update --all -y && \ conda install conda-libmamba-solver -y && \ conda config --set solver libmamba && \ - conda install conda-build -y && \ conda env create -f environment.yml && \ - cd /opt/conda/pkgs/swiftest && conda develop . --name swiftest-env && \ conda init bash && \ echo "conda activate swiftest-env" >> ~/.bashrc && \ + conda install -c anaconda cython -y && \ + cd /opt/conda/pkgs/swiftest && pip install -e . && \ conda clean --all -y && \ mkdir -p /.astropy && \ chmod -R 777 /.astropy && \ diff --git a/distclean.cmake b/distclean.cmake index a06162074..f85a44027 100644 --- a/distclean.cmake +++ b/distclean.cmake @@ -45,6 +45,7 @@ FILE(GLOB_RECURSE MAKEFILE "${TOPDIR}/*Makefile") FILE(GLOB_RECURSE CMAKETESTFILES "${TOPDIR}/*CTestTestfile.cmake") SET(TOPDIRECTORIES "${TOPDIR}/lib" "${TOPDIR}/bin" + "${TOPDIR}/include" ) # CMake has trouble finding directories recursively, so locate these diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 000000000..1bfe6dba0 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,10 @@ +FIND_PACKAGE(Python COMPONENTS Interpreter REQUIRED) +IF(NOT ${PYTHON}) + find_program(PYTHON "python") +ENDIF() + +# Communicate version number and other CMake build variables to the source code +SET(SETUP_PY_IN "${PY}/setup.py.in") +SET(SETUP_PY_OUT "${PY}/setup.py") + +CONFIGURE_FILE(${SETUP_PY_IN} ${SETUP_PY_OUT}) diff --git a/src/python/setup.py b/python/setup.py similarity index 51% rename from src/python/setup.py rename to python/setup.py index 3ec0cb128..5abe8d762 100644 --- a/src/python/setup.py +++ b/python/setup.py @@ -14,16 +14,20 @@ from Cython.Build import cythonize import os -# Build the pydriver extension that allows us to run the Fortran driver as a Python module. -root_dir = 'pydriver' -include_dir = os.path.join(root_dir,'include') -lib_dir = os.path.join(root_dir,'lib') -pydriver_extension = [Extension('swiftest.pydriver', - [os.path.join(root_dir,'pydriver.pyx')], - extra_compile_args=['-fPIC', '-O3'], - library_dirs=[lib_dir], - libraries=['swiftest','netcdff','netcdf','hdf5_hl','hdf5','m','z'], - include_dirs=[include_dir], +# Build the pybindings extension that allows us to run the Fortran driver as a Python module. +root_dir = 'pybindings' +include_dirs = "/Users/daminton/git/swiftest/apple_install/usr/local/include;/Users/daminton/git/swiftest/apple_install/usr/local/include" +include_dirs = include_dirs.split() +include_dirs.append(root_dir) +link_flags = "-lswiftest /Users/daminton/git/swiftest/apple_install/usr/local/lib/libnetcdff.a /Users/daminton/git/swiftest/apple_install/usr/local/lib/libnetcdf.a -L/Users/daminton/git/swiftest/apple_install/usr/local/lib -lhdf5_hl -lhdf5 -lm -lz -lbz2 -lxml2 -lcurl" +link_flags = link_flags.split() + +pybindings_extension = [Extension('swiftest.bindings', + [os.path.join(root_dir,'pybindings.pyx')], + extra_compile_args=['-fPIC', '-O3','-fopenmp'], + extra_link_args=link_flags, + libraries=['gfortran','omp'], + include_dirs=include_dirs, )] setup(name='swiftest', @@ -31,5 +35,5 @@ author='David A. Minton', author_email='daminton@purdue.edu', url='https://github.itap.purdue.edu/MintonGroup/swiftest', - ext_modules = cythonize(pydriver_extension), + ext_modules = cythonize(pybindings_extension), packages=find_packages()) diff --git a/python/swiftest/.idea/.gitignore b/python/swiftest/.idea/.gitignore deleted file mode 100644 index 26d33521a..000000000 --- a/python/swiftest/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/src/python/swiftest/__init__.py b/python/swiftest/__init__.py similarity index 89% rename from src/python/swiftest/__init__.py rename to python/swiftest/__init__.py index 4636057ba..2b9720bd7 100644 --- a/src/python/swiftest/__init__.py +++ b/python/swiftest/__init__.py @@ -9,5 +9,6 @@ If not, see: https://www.gnu.org/licenses. """ -from swiftest.constants import * -from swiftest.simulation_class import Simulation \ No newline at end of file +from constants import * +from simulation_class import Simulation +from bindings import driver \ No newline at end of file diff --git a/src/python/swiftest/constants.py b/python/swiftest/constants.py similarity index 100% rename from src/python/swiftest/constants.py rename to python/swiftest/constants.py diff --git a/src/python/swiftest/init_cond.py b/python/swiftest/init_cond.py similarity index 100% rename from src/python/swiftest/init_cond.py rename to python/swiftest/init_cond.py diff --git a/src/python/swiftest/io.py b/python/swiftest/io.py similarity index 100% rename from src/python/swiftest/io.py rename to python/swiftest/io.py diff --git a/src/python/swiftest/simulation_class.py b/python/swiftest/simulation_class.py similarity index 100% rename from src/python/swiftest/simulation_class.py rename to python/swiftest/simulation_class.py diff --git a/src/python/swiftest/tool.py b/python/swiftest/tool.py similarity index 100% rename from src/python/swiftest/tool.py rename to python/swiftest/tool.py diff --git a/src/python/swiftest/visualize.py b/python/swiftest/visualize.py similarity index 100% rename from src/python/swiftest/visualize.py rename to python/swiftest/visualize.py diff --git a/src/python/tests/test_suite.py b/python/tests/test_suite.py similarity index 100% rename from src/python/tests/test_suite.py rename to python/tests/test_suite.py diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d15ef2ffb..f9307b7ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,9 +7,10 @@ # You should have received a copy of the GNU General Public License along with Swiftest. # If not, see: https://www.gnu.org/licenses. -######################################## -# Set up how to compile the source files -######################################## +# Communicate version number and other CMake build variables to the source code +set(GLOBAL_MODULE_IN ${SRC}/globals/globals_module.f90.in) +set(GLOBAL_MODULE_OUT ${SRC}/globals/globals_module.f90) +CONFIGURE_FILE(${GLOBAL_MODULE_IN} ${GLOBAL_MODULE_OUT}) # Add the source files SET(STRICT_MATH_FILES @@ -48,8 +49,7 @@ SET(STRICT_MATH_FILES ${SRC}/whm/whm_gr.f90 ${SRC}/whm/whm_kick.f90 ${SRC}/whm/whm_step.f90 - ${PY}/pydriver/src/driver.f90 - ${PY}/pydriver/src/pydriver.f90 + ${SRC}/bindings/bindings_module.f90 ) SET(FAST_MATH_FILES @@ -96,7 +96,7 @@ SET(COARRAY_FILES SET(DRIVER_src ${SRC}/main/main.f90) -# Combine all source files (includ) +# Combine all source files set(SWIFTEST_src ${FAST_MATH_FILES} ${STRICT_MATH_FILES}) IF(USE_COARRAY) set(SWIFTEST_src ${SWIFTEST_src} ${COARRAY_FILES} ) @@ -117,20 +117,25 @@ ADD_EXECUTABLE(${SWIFTEST_DRIVER} ${DRIVER_src}) # Add the needed libraries and special compiler flags ##################################################### -TARGET_LINK_LIBRARIES(${SWIFTEST_DRIVER} PRIVATE swiftest ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_LIBRARIES} $ENV{LIBS}) +TARGET_LINK_LIBRARIES(${SWIFTEST_DRIVER} PRIVATE ${SWIFTEST_LIBRARY} ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_LIBRARIES} $ENV{LIBS}) IF(USE_OPENMP) - SET_PROPERTY(TARGET swiftest ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY COMPILE_FLAGS "${OpenMP_Fortran_FLAGS} ") - SET_PROPERTY(TARGET swiftest ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY LINK_FLAGS "${OpenMP_Fortran_FLAGS} ") + SET_PROPERTY(TARGET ${SWIFTEST_LIBRARY} ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY COMPILE_FLAGS "${OpenMP_Fortran_FLAGS} ") + SET_PROPERTY(TARGET ${SWIFTEST_LIBRARY} ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY LINK_FLAGS "${OpenMP_Fortran_FLAGS} ") ENDIF(USE_OPENMP) IF(USE_COARRAY) - TARGET_COMPILE_DEFINITIONS(swiftest PRIVATE -DCOARRAY) + TARGET_COMPILE_DEFINITIONS(${SWIFTEST_LIBRARY} PRIVATE -DCOARRAY) TARGET_COMPILE_DEFINITIONS(${SWIFTEST_DRIVER} PRIVATE -DCOARRAY) - SET_PROPERTY(TARGET swiftest ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY COMPILE_FLAGS "${Coarray_Fortran_FLAGS} ") - SET_PROPERTY(TARGET swiftest ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY LINK_FLAGS "${Coarray_Fortran_FLAGS} ") + SET_PROPERTY(TARGET ${SWIFTEST_LIBRARY} ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY COMPILE_FLAGS "${Coarray_Fortran_FLAGS} ") + SET_PROPERTY(TARGET ${SWIFTEST_LIBRARY} ${SWIFTEST_DRIVER} APPEND_STRING PROPERTY LINK_FLAGS "${Coarray_Fortran_FLAGS} ") ENDIF(USE_COARRAY) +STRING(REPLACE ";" " " SWIFTEST_LINK_FLAGS "${CMAKE_LINK_LIBRARY_FLAG}${SWIFTEST_LIBRARY} ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_LIBRARIES} $ENV{LIBS}") +SET(SWIFTEST_LINK_FLAGS "${SWIFTEST_LINK_FLAGS}" CACHE STRING "Link flags to pass to setup.py") + +GET_TARGET_PROPERTY(SWIFTEST_INCLUDE_DIR ${SWIFTEST_DRIVER} INCLUDE_DIRECTORIES) +SET(SWIFTEST_INCLUDE_DIR "${SWIFTEST_INCLUDE_DIR}" CACHE STRING "Include directory needed for linking the library to an executable") #Set strict vs fast math flags STRING(TOUPPER "${CMAKE_BUILD_TYPE}" BT) @@ -182,8 +187,3 @@ INSTALL(TARGETS ${SWIFTEST_DRIVER} ${SWIFTEST_LIBRARY} INCLUDES DESTINATION include ) -INSTALL(TARGETS ${SWIFTEST_LIBRARY} - LIBRARY DESTINATION "${PY}/pydriver/lib" - ARCHIVE DESTINATION "${PY}/pydriver/lib" - INCLUDES DESTINATION "${PY}pydriver/include" - ) diff --git a/src/bindings/bindings_module.f90 b/src/bindings/bindings_module.f90 new file mode 100644 index 000000000..9664c87fe --- /dev/null +++ b/src/bindings/bindings_module.f90 @@ -0,0 +1,44 @@ +module bindings_module + use iso_c_binding, only : c_char, c_null_char, c_int + use swiftest, only : swiftest_driver, swiftest_io_get_args, STRMAX + implicit none + contains + + subroutine bindings_c2f_string(c_string, f_string) + implicit none + ! Arguments + character(len=1,kind=c_char), intent(in) :: c_string(*) + character(len=:), allocatable, intent(out) :: f_string + ! Internals + integer :: i + character(len=STRMAX) :: tmp_string + + i=1 + tmp_string = '' + do while(c_string(i) /= c_null_char .and. i <= STRMAX) + tmp_string(i:i) = c_string(i) + i=i+1 + end do + + if (i > 1) then + f_string = trim(tmp_string) + else + f_string = "" + end if + + return + end subroutine bindings_c2f_string + + subroutine bindings_c_driver(c_integrator, c_param_file_name, c_display_style) bind(c) + implicit none + character(kind=c_char), dimension(*), intent(in) :: c_integrator, c_param_file_name, c_display_style + character(len=:), allocatable :: integrator, param_file_name, display_style + + call bindings_c2f_string(c_integrator, integrator) + call bindings_c2f_string(c_param_file_name, param_file_name) + call bindings_c2f_string(c_display_style, display_style) + + call swiftest_io_get_args(integrator,param_file_name,display_style,from_cli=.false.) + call swiftest_driver(integrator,param_file_name,display_style) + end subroutine bindings_c_driver +end module bindings_module diff --git a/src/main/main.f90 b/src/main/main.f90 index 250c05023..1b8572c3f 100644 --- a/src/main/main.f90 +++ b/src/main/main.f90 @@ -20,7 +20,7 @@ program main character(len=:), allocatable :: display_style !! Style of the output display {"STANDARD", "COMPACT", "PROGRESS"}). Default is "STANDARD" ! Read in command line arguments - call swiftest_io_get_args(integrator, param_file_name, display_style) + call swiftest_io_get_args(integrator, param_file_name, display_style, from_cli=.true.) ! Execute the driver call swiftest_driver(integrator, param_file_name, display_style) diff --git a/src/python/pydriver/src/driver.f90 b/src/python/pydriver/src/driver.f90 deleted file mode 100644 index fade662d3..000000000 --- a/src/python/pydriver/src/driver.f90 +++ /dev/null @@ -1,26 +0,0 @@ -module pydriver_module - use swiftest, only : swiftest_driver - implicit none - - contains - subroutine driver(integrator, param_file_name, display_style) - - !! author: David A. Minton - !! - !! Driver program for the Swiftest integrators. Unlike the earlier Swift and Swifter drivers, in Swiftest all integrators - !! are run from this single program. - !! - !! Adapted from Swifter by David E. Kaufmann's Swifter driver programs swifter_[bs,helio,ra15,rmvs,symba,tu4,whm].f90 - !! Adapted from Hal Levison and Martin Duncan's Swift driver programs - implicit none - - ! Arguments - character(len=:), intent(in), allocatable :: integrator !! Symbolic code of the requested integrator - character(len=:), intent(in), allocatable :: param_file_name !! Name of the input parameters file - character(len=:), intent(in), allocatable :: display_style !! Style of the output display {"STANDARD", "COMPACT", "PROGRESS"}). Default is "STANDARD") - - call swiftest_driver(integrator, param_file_name, display_style) - - return - end subroutine driver -end module pydriver_module \ No newline at end of file diff --git a/src/python/pydriver/src/pydriver.f90 b/src/python/pydriver/src/pydriver.f90 deleted file mode 100644 index 2ab035387..000000000 --- a/src/python/pydriver/src/pydriver.f90 +++ /dev/null @@ -1,9 +0,0 @@ -module pydriver_interface - use iso_c_binding, only : c_char - use pydriver_module, only : driver - implicit none - contains - subroutine c_driver(integrator, param_file_name, display_style) bind(c) - character(kind=c_char) :: integrator(:), param_file_name(:), display_style(:) - end subroutine c_driver -end module pydriver_interface diff --git a/src/swiftest/swiftest_io.f90 b/src/swiftest/swiftest_io.f90 index 64731a7eb..ebd394149 100644 --- a/src/swiftest/swiftest_io.f90 +++ b/src/swiftest/swiftest_io.f90 @@ -446,7 +446,7 @@ module subroutine swiftest_io_dump_storage(self, param) end subroutine swiftest_io_dump_storage - module subroutine swiftest_io_get_args(integrator, param_file_name, display_style) + module subroutine swiftest_io_get_args(integrator, param_file_name, display_style, from_cli) !! author: David A. Minton !! !! Reads in the name of the parameter file from command line arguments. @@ -455,20 +455,41 @@ module subroutine swiftest_io_get_args(integrator, param_file_name, display_styl character(len=:), intent(inout), allocatable :: integrator !! Symbolic code of the requested integrator character(len=:), intent(inout), allocatable :: param_file_name !! Name of the input parameters file character(len=:), intent(inout), allocatable :: display_style !! Style of the output display {"STANDARD", "COMPACT", "PROGRESS"}). Default is "STANDARD" + logical, intent(in) :: from_cli !! If true, get command-line arguments. Otherwise, use the values of the input variables ! Internals character(len=STRMAX), dimension(:), allocatable :: arg + character(len=STRMAX), dimension(3) :: tmp_arg integer(I4B), dimension(:), allocatable :: ierr integer :: i,narg - narg = command_argument_count() - if (narg > 0) then - allocate(arg(narg),ierr(narg)) - do i = 1,narg - call get_command_argument(i, arg(i), status = ierr(i)) - end do - if (any(ierr /= 0)) call base_util_exit(USAGE) + if (from_cli) then + narg = command_argument_count() + if (narg > 0) then + allocate(arg(narg),ierr(narg)) + do i = 1,narg + call get_command_argument(i, arg(i), status = ierr(i)) + end do + if (any(ierr /= 0)) call base_util_exit(USAGE) + else + call base_util_exit(USAGE) + end if else - call base_util_exit(USAGE) + narg = 0 + if (len(integrator) > 0) then + narg = narg + 1 + tmp_arg(narg) = integrator + end if + if (len(param_file_name) > 0) then + narg = narg + 1 + tmp_arg(narg) = param_file_name + end if + if (len(display_style) > 0) then + narg = narg + 1 + tmp_arg(narg) = display_style + end if + allocate(arg(narg)) + arg(1:narg) = tmp_arg(1:narg) + deallocate(integrator, param_file_name, display_style) end if if (narg == 1) then diff --git a/src/swiftest/swiftest_module.f90 b/src/swiftest/swiftest_module.f90 index ffb60a593..f3f282123 100644 --- a/src/swiftest/swiftest_module.f90 +++ b/src/swiftest/swiftest_module.f90 @@ -639,11 +639,12 @@ module subroutine swiftest_io_dump_storage(self, param) class(swiftest_parameters), intent(inout) :: param !! Current run configuration parameters end subroutine swiftest_io_dump_storage - module subroutine swiftest_io_get_args(integrator, param_file_name, display_style) + module subroutine swiftest_io_get_args(integrator, param_file_name, display_style, from_cli) implicit none character(len=:), allocatable, intent(inout) :: integrator !! Symbolic code of the requested integrator character(len=:), allocatable, intent(inout) :: param_file_name !! Name of the input parameters file character(len=:), allocatable, intent(inout) :: display_style !! Style of the output display {"STANDARD", "COMPACT"}). Default is "STANDARD" + logical, intent(in) :: from_cli !! If true, get command-line arguments. Otherwise, use the values of the input variables end subroutine swiftest_io_get_args module function swiftest_io_get_token(buffer, ifirst, ilast, ierr) result(token)