From 37716e3a2e5190602364a1c45379bb8380750a25 Mon Sep 17 00:00:00 2001
From: Henry <henry.korb@geo.uu.se>
Date: Mon, 3 Oct 2022 15:09:28 +0200
Subject: [PATCH] update python installation process

---
 .gitignore                                    |   1 +
 pyproject.toml                                |  10 +-
 pythonbindings/CMakeLists.txt                 |  19 +-
 .../src/VirtualFluidsModulesCPU.cpp           |   2 +-
 .../src/VirtualFluidsModulesGPU.cpp           |   2 +-
 .../src/gpu/submodules/actuator_line.cpp      |  53 +++---
 setup.cfg                                     |  21 +++
 setup.py                                      | 169 +++++-------------
 utilities/setup_builder.py                    |  35 ++++
 9 files changed, 149 insertions(+), 163 deletions(-)
 create mode 100644 setup.cfg
 create mode 100644 utilities/setup_builder.py

diff --git a/.gitignore b/.gitignore
index f87c8efbb..1e33ea527 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ dist/
 *.egg-info/
 __pycache__/
 .venv/
+pyfluids*
 
 # IDE
 .vscode/
diff --git a/pyproject.toml b/pyproject.toml
index 8fcb79261..4353b0196 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,2 +1,10 @@
 [build-system]
-requires = ["setuptools", "wheel", "scikit-build"]
\ No newline at end of file
+requires = [
+    "wheel",
+    "cmake>=3.1.0",
+    "setuptools",
+    "setuptools_scm[toml]",
+    "cmake_build_extension"
+]
+build-backend = "setup_builder"
+backend-path = ["utilities"]
\ No newline at end of file
diff --git a/pythonbindings/CMakeLists.txt b/pythonbindings/CMakeLists.txt
index 5a84adef0..f56b2e89e 100644
--- a/pythonbindings/CMakeLists.txt
+++ b/pythonbindings/CMakeLists.txt
@@ -1,24 +1,27 @@
 project(VirtualFluidsPython LANGUAGES CUDA CXX)
 IF(BUILD_VF_GPU)
-    pybind11_add_module(pyfluids src/VirtualFluidsModulesGPU.cpp)
+    pybind11_add_module(python_bindings MODULE src/VirtualFluidsModulesGPU.cpp)
+    set_target_properties(python_bindings PROPERTIES
+    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/pythonbindings/pyfluids
+    OUTPUT_NAME "bindings")
     set_source_files_properties(src/VirtualFluidsModulesGPU.cpp PROPERTIES LANGUAGE CUDA)
 
-    target_link_libraries(pyfluids PRIVATE GridGenerator VirtualFluids_GPU basics lbmCuda logger)
-    target_include_directories(pyfluids PRIVATE ${VF_THIRD_DIR}/cuda_samples/)
+    target_link_libraries(python_bindings PRIVATE GridGenerator VirtualFluids_GPU basics lbmCuda logger)
+    target_include_directories(python_bindings PRIVATE ${VF_THIRD_DIR}/cuda_samples/)
 
 ENDIF()
 IF(BUILD_VF_CPU)
-    pybind11_add_module(pyfluids src/VirtualFluidsModulesCPU.cpp)
+    pybind11_add_module(python_bindings src/VirtualFluidsModulesCPU.cpp)
     pybind11_add_module(pymuparser src/muParser.cpp)
 
     # TODO: Move this to MuParser CMakeLists.txt
     set_target_properties(muparser PROPERTIES POSITION_INDEPENDENT_CODE ON)
 
-    target_compile_definitions(pyfluids PRIVATE VF_METIS VF_MPI)
+    target_compile_definitions(python_bindings PRIVATE VF_METIS VF_MPI)
     target_compile_definitions(pymuparser PRIVATE VF_METIS VF_MPI)
 
-    target_link_libraries(pyfluids PRIVATE simulationconfig VirtualFluidsCore muparser basics)
+    target_link_libraries(python_bindings PRIVATE simulationconfig VirtualFluidsCore muparser basics)
     target_link_libraries(pymuparser PRIVATE muparser)
 ENDIF()
-target_include_directories(pyfluids PRIVATE ${CMAKE_SOURCE_DIR}/src/)
-target_include_directories(pyfluids PRIVATE ${CMAKE_BINARY_DIR})
\ No newline at end of file
+target_include_directories(python_bindings PRIVATE ${CMAKE_SOURCE_DIR}/src/)
+target_include_directories(python_bindings PRIVATE ${CMAKE_BINARY_DIR})
\ No newline at end of file
diff --git a/pythonbindings/src/VirtualFluidsModulesCPU.cpp b/pythonbindings/src/VirtualFluidsModulesCPU.cpp
index 2fba3da49..9201a8ce9 100644
--- a/pythonbindings/src/VirtualFluidsModulesCPU.cpp
+++ b/pythonbindings/src/VirtualFluidsModulesCPU.cpp
@@ -5,7 +5,7 @@ namespace py_bindings
 {
     namespace py = pybind11;
 
-    PYBIND11_MODULE(pyfluids, m)
+    PYBIND11_MODULE(bindings, m)
     {
         cpu::makeModule(m);
     }
diff --git a/pythonbindings/src/VirtualFluidsModulesGPU.cpp b/pythonbindings/src/VirtualFluidsModulesGPU.cpp
index b96971caf..e0320115e 100644
--- a/pythonbindings/src/VirtualFluidsModulesGPU.cpp
+++ b/pythonbindings/src/VirtualFluidsModulesGPU.cpp
@@ -8,7 +8,7 @@ namespace py_bindings
 {
     namespace py = pybind11;
 
-    PYBIND11_MODULE(pyfluids, m)
+    PYBIND11_MODULE(bindings, m)
     {
         basics::makeModule(m);
         gpu::makeModule(m);
diff --git a/pythonbindings/src/gpu/submodules/actuator_line.cpp b/pythonbindings/src/gpu/submodules/actuator_line.cpp
index 3b207df2b..c489654fd 100644
--- a/pythonbindings/src/gpu/submodules/actuator_line.cpp
+++ b/pythonbindings/src/gpu/submodules/actuator_line.cpp
@@ -3,6 +3,8 @@
 #include <pybind11/numpy.h>
 #include <gpu/VirtualFluids_GPU/PreCollisionInteractor/PreCollisionInteractor.h>
 #include <gpu/VirtualFluids_GPU/PreCollisionInteractor/ActuatorLine.h>
+#include <cstdint>
+
 class PyActuatorLine : public ActuatorLine 
 {
 public:
@@ -12,12 +14,14 @@ public:
         PYBIND11_OVERRIDE_NAME(void, ActuatorLine, "calc_blade_forces", calcBladeForces,); 
     }
 };
+
 namespace actuator_line
 {
     namespace py = pybind11;
 
     void makeModule(py::module_ &parentModule)
     {
+
         using arr = py::array_t<float, py::array::c_style>;
         
         py::class_<ActuatorLine, PreCollisionInteractor, PyActuatorLine, std::shared_ptr<ActuatorLine>>(parentModule, "ActuatorLine", py::dynamic_attr())
@@ -29,7 +33,8 @@ namespace actuator_line
                         const real,
                         int,
                         const real,
-                        const real>(), 
+                        const real,
+                        const bool>(), 
                         "n_blades", 
                         "density", 
                         "n_blade_nodes", 
@@ -38,7 +43,8 @@ namespace actuator_line
                         "diameter", 
                         "level", 
                         "delta_t", 
-                        "delta_x")
+                        "delta_x",
+                        "use_host_arrays")
         .def_property("omega", &ActuatorLine::getOmega, &ActuatorLine::setOmega)
         .def_property("azimuth", &ActuatorLine::getAzimuth, &ActuatorLine::setAzimuth)
         .def_property("yaw", &ActuatorLine::getYaw, &ActuatorLine::setYaw)
@@ -63,16 +69,15 @@ namespace actuator_line
         .def("get_blade_forces_x", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesX()); } )
         .def("get_blade_forces_y", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesY()); } )
         .def("get_blade_forces_z", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesZ()); } )
-        .def("get_radii_device", [](ActuatorLine& al){ return arr(al.getNBladeNodes(), al.getBladeRadii()); } )
-        .def("get_blade_coords_x_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeCoordsXD()); } )
-        .def("get_blade_coords_y_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeCoordsYD()); } )
-        .def("get_blade_coords_z_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeCoordsZD()); } )        
-        .def("get_blade_velocities_x_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeVelocitiesXD()); } )
-        .def("get_blade_velocities_y_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeVelocitiesYD()); } )
-        .def("get_blade_velocities_z_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeVelocitiesZD()); } )
-        .def("get_blade_forces_x_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesXD()); } )
-        .def("get_blade_forces_y_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesYD()); } )
-        .def("get_blade_forces_z_device", [](ActuatorLine& al){ return arr({al.getNBlades(), al.getNBladeNodes()}, al.getBladeForcesZD()); } )
+        .def("get_blade_coords_x_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeCoordsXD()); }, py::return_value_policy::reference)
+        .def("get_blade_coords_y_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeCoordsYD()); }, py::return_value_policy::reference)
+        .def("get_blade_coords_z_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeCoordsZD()); }, py::return_value_policy::reference)        
+        .def("get_blade_velocities_x_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeVelocitiesXD()); }, py::return_value_policy::reference)
+        .def("get_blade_velocities_y_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeVelocitiesYD()); }, py::return_value_policy::reference)
+        .def("get_blade_velocities_z_device", [](ActuatorLine& al) -> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeVelocitiesZD()); }, py::return_value_policy::reference)
+        .def("get_blade_forces_x_device", [](ActuatorLine& al)-> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeForcesXD()); }, py::return_value_policy::reference )
+        .def("get_blade_forces_y_device", [](ActuatorLine& al)-> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeForcesYD()); }, py::return_value_policy::reference )
+        .def("get_blade_forces_z_device", [](ActuatorLine& al)-> intptr_t { return reinterpret_cast<intptr_t>(al.getBladeForcesZD()); }, py::return_value_policy::reference )
         .def("set_preinit_blade_radii", [](ActuatorLine& al, arr radii){ al.setPreInitBladeRadii(static_cast<float *>(radii.request().ptr)); } )
         .def("set_blade_coords", [](ActuatorLine& al, arr coordsX, arr coordsY, arr coordsZ)
         { 
@@ -86,18 +91,18 @@ namespace actuator_line
         { 
             al.setBladeForces(static_cast<float *>(forcesX.request().ptr), static_cast<float *>(forcesY.request().ptr), static_cast<float *>(forcesZ.request().ptr));
         })
-        .def("set_blade_coords_device", [](ActuatorLine& al, arr coordsX, arr coordsY, arr coordsZ)
-        { 
-            al.setBladeCoordsD(static_cast<float *>(coordsX.request().ptr), static_cast<float *>(coordsY.request().ptr), static_cast<float *>(coordsZ.request().ptr)); 
-        })
-        .def("set_blade_velocities_device", [](ActuatorLine& al, arr velocitiesX, arr velocitiesY, arr velocitiesZ)
-        { 
-            al.setBladeVelocitiesD(static_cast<float *>(velocitiesX.request().ptr), static_cast<float *>(velocitiesY.request().ptr), static_cast<float *>(velocitiesZ.request().ptr)); 
-        })
-        .def("set_blade_forces_device", [](ActuatorLine& al, arr forcesX, arr forcesY, arr forcesZ)
-        { 
-            al.setBladeForcesD(static_cast<float *>(forcesX.request().ptr), static_cast<float *>(forcesY.request().ptr), static_cast<float *>(forcesZ.request().ptr)); 
-        })
+        // .def("set_blade_coords_device", [](ActuatorLine& al, arr coordsX, arr coordsY, arr coordsZ)
+        // { 
+        //     al.setBladeCoordsD(static_cast<float *>(coordsX.request().ptr), static_cast<float *>(coordsY.request().ptr), static_cast<float *>(coordsZ.request().ptr)); 
+        // })
+        // .def("set_blade_velocities_device", [](ActuatorLine& al, arr velocitiesX, arr velocitiesY, arr velocitiesZ)
+        // { 
+        //     al.setBladeVelocitiesD(static_cast<float *>(velocitiesX.request().ptr), static_cast<float *>(velocitiesY.request().ptr), static_cast<float *>(velocitiesZ.request().ptr)); 
+        // })
+        // .def("set_blade_forces_device", [](ActuatorLine& al, arr forcesX, arr forcesY, arr forcesZ)
+        // { 
+        //     al.setBladeForcesD(static_cast<float *>(forcesX.request().ptr), static_cast<float *>(forcesY.request().ptr), static_cast<float *>(forcesZ.request().ptr)); 
+        // })
         .def("calc_blade_forces", &ActuatorLine::calcBladeForces);
     }
 }
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 000000000..cf060397a
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,21 @@
+[metadata]
+name = pyfluids
+description = Python binding for VirtualFluids
+long_description = file: README.md
+long_description_content_type = text/markdown
+platforms = any
+url = https://git.rz.tu-bs.de/irmb/virtualfluids
+version = 0.0.1
+
+[options]
+zip_safe = False
+packages = find:
+package_dir =
+    =pythonbindings
+python_requires = >=3.6
+install_requires =
+    cmake-build-extension
+
+[options.packages.find]
+where = pythonbindings
+
diff --git a/setup.py b/setup.py
index b26e1c13d..52dc347f1 100644
--- a/setup.py
+++ b/setup.py
@@ -1,137 +1,50 @@
-import os
-import re
+import inspect
 import sys
-import platform
-import subprocess
+from pathlib import Path
 
-from setuptools import setup, Extension
-from setuptools.command.build_ext import build_ext
-from setuptools.command.install import install
-from setuptools.command.develop import develop
-from distutils.version import LooseVersion
+import cmake_build_extension
+import setuptools
 
 """
 Install python wrapper of virtual fluids
-Install GPU backend with option --GPU
-(pass to pip via --install-option="--GPU")
+install via python setup.py install build_ext
+set CMAKE Flags via -DBUILD_VF_GPU:BOOL=1
 """
 
-vf_cmake_args = [
-    "-DBUILD_VF_PYTHON_BINDINGS=ON",
-    "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
-    "-DCMAKE_CUDA_COMPILER_LAUNCHER=ccache",
-    "-DCMAKE_C_COMPILER_LAUNCHER=ccache",
-    "-DBUILD_SHARED_LIBS=OFF",
-    "-DBUILD_WARNINGS_AS_ERRORS=OFF"
-]
-
-vf_cpu_cmake_args = [
-    "-DBUILD_VF_DOUBLE_ACCURACY=ON",
-    "-DBUILD_VF_CPU:BOOL=ON",
-    "-DBUILD_VF_UNIT_TESTS:BOOL=ON",
-    "-DUSE_METIS=ON",
-    "-DUSE_MPI=ON"
-]
-
-vf_gpu_cmake_args = [
-    "-DBUILD_VF_DOUBLE_ACCURACY=OFF",
-    "-DBUILD_VF_GPU:BOOL=ON",
-    "-DBUILD_VF_UNIT_TESTS:BOOL=OFF",
-]
-
-GPU = False
-
-class CommandMixin:
-    user_options = [
-        ('GPU', None, 'compile pyfluids with GPU backend'),
-    ]
-
-    def initialize_options(self):
-        super().initialize_options()
-        self.GPU = False
-
-    def finalize_options(self):
-        super().finalize_options()
-
-    def run(self):
-        global GPU
-        GPU = GPU or self.GPU
-        super().run()
-
-
-class InstallCommand(CommandMixin, install):
-    user_options = getattr(install, 'user_options', []) + CommandMixin.user_options
-
-
-class DevelopCommand(CommandMixin, develop):
-    user_options = getattr(develop, 'user_options', []) + CommandMixin.user_options
-
-
-class CMakeExtension(Extension):
-    def __init__(self, name, sourcedir=''):
-        Extension.__init__(self, name, sources=[])
-        self.sourcedir = os.path.abspath(sourcedir)
-
-
-class CMakeBuild(CommandMixin, build_ext):
-    user_options = getattr(build_ext, 'user_options', []) + CommandMixin.user_options
-
-    def run(self):
-        super().run()
-        try:
-            out = subprocess.check_output(['cmake', '--version'])
-        except OSError:
-            raise RuntimeError("CMake must be installed to build the following extensions: " +
-                               ", ".join(e.name for e in self.extensions))
-
-        if platform.system() == "Windows":
-            cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1))
-            if cmake_version < '3.1.0':
-                raise RuntimeError("CMake >= 3.1.0 is required on Windows")
-
-        for ext in self.extensions:
-            self.build_extension(ext)
-
-    def build_extension(self, ext):
-        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
-        # required for auto-detection of auxiliary "native" libs
-        if not extdir.endswith(os.path.sep):
-            extdir += os.path.sep
-
-        cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
-                      '-DPYTHON_EXECUTABLE=' + sys.executable]
-
-        cfg = 'Debug' if self.debug else 'Release'
-        build_args = ['--config', cfg]
-
-        if platform.system() == "Windows":
-            cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
-            if sys.maxsize > 2**32:
-                cmake_args += ['-A', 'x64']
-            build_args += ['--', '/m']
-        else:
-            cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
-            build_args += ['--', '-j2']
-
-        cmake_args.extend(vf_cmake_args)
-        cmake_args.extend(vf_gpu_cmake_args if GPU else vf_cpu_cmake_args)
-
-        env = os.environ.copy()
-        env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''),
-                                                              self.distribution.get_version())
-        if not os.path.exists(self.build_temp):
-            os.makedirs(self.build_temp)
-        cmake_cache_file = self.build_temp+"/CMakeCache.txt"
-        if os.path.exists(cmake_cache_file):
-            os.remove(cmake_cache_file)
-        subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
-        subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)
-
+init_py = inspect.cleandoc(
+    """
+    import cmake_build_extension
+    with cmake_build_extension.build_extension_env():
+        from .bindings import *
+    """
+)
 
-setup(
-    name='pyfluids',
-    version='0.0.1',
-    ext_modules=[CMakeExtension('pyfluids')],
-    cmdclass={"install": InstallCommand, "develop": DevelopCommand, "build_ext": CMakeBuild},
-    zip_safe=False,
+extra_args = []
+if("cmake_args" in locals()):
+    extra_args.extend([f"{k}={v}" for k,v in locals()["cmake_args"].items()])
+
+setuptools.setup(
+    ext_modules=[
+        cmake_build_extension.CMakeExtension(
+            name="pyfluids",
+            install_prefix="pyfluids",
+            write_top_level_init=init_py,
+            source_dir=str(Path(__file__).parent.absolute()),
+            cmake_configure_options = [
+                f"-DPython3_ROOT_DIR={Path(sys.prefix)}",
+                "-DCALL_FROM_SETUP_PY:BOOL=ON",
+                "-DBUILD_VF_PYTHON_BINDINGS=ON",
+                "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
+                "-DCMAKE_CUDA_COMPILER_LAUNCHER=ccache",
+                "-DCMAKE_C_COMPILER_LAUNCHER=ccache",
+                "-DBUILD_SHARED_LIBS=OFF",
+                "-DBUILD_VF_DOUBLE_ACCURACY=OFF",
+                "-DBUILD_VF_UNIT_TESTS:BOOL=OFF",
+                "-DBUILD_WARNINGS_AS_ERRORS=OFF",
+            ] + extra_args,
+        )
+    ],
+    cmdclass=dict(
+        build_ext=cmake_build_extension.BuildExtension,
+    ),
 )
diff --git a/utilities/setup_builder.py b/utilities/setup_builder.py
new file mode 100644
index 000000000..821d72ede
--- /dev/null
+++ b/utilities/setup_builder.py
@@ -0,0 +1,35 @@
+from setuptools import build_meta
+
+class builder(build_meta._BuildMetaBackend):
+
+    def run_setup(self, setup_script='setup.py'):
+        # Note that we can reuse our build directory between calls
+        # Correctness comes first, then optimization later
+        __file__ = setup_script
+        __name__ = '__main__'
+
+        with build_meta._open_setup_script(__file__) as f:
+            code = f.read().replace(r'\r\n', r'\n')
+        args = locals()
+        args["cmake_args"] = self.extra_args
+        exec(code, args)
+
+
+    def add_settings(self, config_settings):
+        self.extra_args = dict()
+        print(config_settings)
+        if config_settings:
+            self.extra_args = {k:v for k,v in config_settings.items() if k[:2] == "-D"}
+
+    def build_wheel(self, wheel_directory, config_settings=None,
+                    metadata_directory=None):
+        self.add_settings(config_settings)
+        return super().build_wheel(wheel_directory, config_settings, metadata_directory)
+
+    def build_sdist(self, sdist_directory, config_settings=None):
+        self.add_settings(config_settings)
+        return super().build_wheel(sdist_directory, config_settings)
+
+build = builder()
+build_wheel = build.build_wheel
+build_sdist = build.build_sdist
\ No newline at end of file
-- 
GitLab