Skip to content

Commit 9a681f8

Browse files
committed
Implement python bindings for 4C mixture module
1 parent e8663b4 commit 9a681f8

File tree

10 files changed

+570
-0
lines changed

10 files changed

+570
-0
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,10 @@ add_subdirectory(tests/benchmark_tests)
198198
add_subdirectory(tests/cut_test)
199199
include(tests/list_of_tests.cmake)
200200

201+
# Python bindings
202+
if(FOUR_C_WITH_PYBIND11)
203+
add_subdirectory(pybind11)
204+
endif()
205+
201206
# Installation
202207
include(cmake/setup_install.cmake)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# This file is part of 4C multiphysics licensed under the
2+
# GNU Lesser General Public License v3.0 or later.
3+
#
4+
# See the LICENSE.md file in the top-level for license information.
5+
#
6+
# SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
# Automatically creates a python binding submodule for all sources and headers in the current directory. The target
9+
# will be named based on the folder name. If this function is called recursively inside an already
10+
# defined submodule, the sources are appended to the already defined submodule. The submodule name is returned
11+
# in the variable AUTO_DEFINED_SUBMODULE_NAME which is set at the call site.
12+
function(four_c_auto_define_pybind11_module)
13+
if(NOT FOUR_C_WITH_PYBIND11)
14+
return()
15+
endif()
16+
17+
set(options "")
18+
set(oneValueArgs MODULE)
19+
set(multiValueArgs "")
20+
cmake_parse_arguments(
21+
_parsed
22+
"${options}"
23+
"${oneValueArgs}"
24+
"${multiValueArgs}"
25+
${ARGN}
26+
)
27+
if(DEFINED _parsed_UNPARSED_ARGUMENTS)
28+
message(FATAL_ERROR "There are unparsed arguments: ${_parsed_UNPARSED_ARGUMENTS}")
29+
endif()
30+
31+
if(_parsed_MODULE)
32+
set(_bindings_for_module ${_parsed_MODULE})
33+
else()
34+
if("${FOUR_C_CURRENTLY_DEFINED_PARENT_SUBMODULE}" STREQUAL "")
35+
message(
36+
FATAL_ERROR
37+
"No parent module is set. Either give the module these bindings belongs to or call this functions inside a module."
38+
)
39+
endif()
40+
41+
set(_bindings_for_module "${FOUR_C_CURRENTLY_DEFINED_PARENT_SUBMODULE}")
42+
endif()
43+
44+
if(NOT TARGET "${_bindings_for_module}_objs")
45+
message(
46+
FATAL_ERROR
47+
"Tried to add bindings for a module named '${_bindings_for_module}' which is not a known module name."
48+
)
49+
endif()
50+
51+
# create the name of the current binding
52+
set(_target pybind11_${_bindings_for_module})
53+
54+
# create an object target for the current submodule (and fill it with a dummy cpp-file)
55+
add_library(${_target}_objs OBJECT ${PROJECT_SOURCE_DIR}/cmake/dummy.cpp)
56+
57+
# Add all source files of this folder to this target
58+
file(GLOB_RECURSE _sources CONFIGURE_DEPENDS *.cpp)
59+
target_sources(${_target}_objs PRIVATE ${_sources})
60+
61+
# Link the python binding configuration to this target
62+
target_link_libraries(${_target}_objs PUBLIC pybind11_config)
63+
64+
# Link all dependencies of the respective 4C module to this python binding submodule
65+
target_link_libraries(${_target}_objs PRIVATE ${_bindings_for_module}_unit_test_deps)
66+
67+
# Python bindings require position independent code so that Python can dynamically link the library
68+
set_target_properties(${_target}_objs PROPERTIES POSITION_INDEPENDENT_CODE ON)
69+
70+
# Add the current submodule object file to the main python bindings target
71+
target_link_libraries(${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME} PRIVATE ${_target}_objs)
72+
73+
# Now check if there are more directories that contain CMakeLists.txt. If yes, we also add those.
74+
# For this action, we become the parent submodule of the sub-submodules we are about to define.
75+
set(FOUR_C_CURRENTLY_DEFINED_PARENT_SUBMODULE ${_target})
76+
# Recursively add all subdirectories that contain CMakeLists.txt files.
77+
# N.B. We need to directly glob for CMakeLists.txt files here to ensure
78+
# the glob reruns when a new CMakeLists.txt is added.
79+
file(
80+
GLOB children
81+
RELATIVE ${CMAKE_CURRENT_LIST_DIR}
82+
CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*/CMakeLists.txt
83+
)
84+
foreach(child ${children})
85+
get_filename_component(_subdir ${child} DIRECTORY)
86+
add_subdirectory(${_subdir})
87+
endforeach()
88+
89+
# Simulate a "return" by setting a variable at the call site
90+
set(AUTO_DEFINED_SUBMODULE_NAME
91+
${_target}
92+
PARENT_SCOPE
93+
)
94+
endfunction()

pybind11/4C_main.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file is part of 4C multiphysics licensed under the
2+
// GNU Lesser General Public License v3.0 or later.
3+
//
4+
// See the LICENSE.md file in the top-level for license information.
5+
//
6+
// SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
#include "4C_config_submodule_registry.hpp"
9+
10+
#include <pybind11/pybind11.h>
11+
#include <pybind11/stl.h>
12+
13+
namespace py = pybind11;
14+
15+
FOUR_C_NAMESPACE_OPEN
16+
17+
PYBIND11_MODULE(py4C, module)
18+
{
19+
module.doc() = "Python bindings for 4C";
20+
21+
// initialize all submodules
22+
for (const auto& entry : Py4C::get_submodule_registry())
23+
{
24+
auto submodule = module.def_submodule(entry.name.c_str(), entry.doc.c_str());
25+
entry.init_funct(submodule);
26+
}
27+
}
28+
FOUR_C_NAMESPACE_CLOSE

pybind11/CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# This file is part of 4C multiphysics licensed under the
2+
# GNU Lesser General Public License v3.0 or later.
3+
#
4+
# See the LICENSE.md file in the top-level for license information.
5+
#
6+
# SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
set(FOUR_C_PYTHON_BINDINGS_PROJECT_NAME py4C)
9+
10+
# create python bindings module
11+
pybind11_add_module(${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME} 4C_main.cpp)
12+
13+
add_subdirectory(config)
14+
add_subdirectory(mixture)
15+
16+
# link python bindings to 4C libs
17+
target_link_libraries(${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME} PUBLIC lib4C)
18+
target_link_libraries(${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME} PUBLIC pybind11_config)
19+
20+
set_target_properties(
21+
${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME}
22+
PROPERTIES PREFIX ""
23+
LIBRARY_OUTPUT_DIRECTORY
24+
"${PROJECT_BINARY_DIR}/${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME}"
25+
)
26+
27+
# Add setup.py to make python package installable
28+
configure_file(
29+
${PROJECT_SOURCE_DIR}/pybind11/setup.py.in
30+
${PROJECT_BINARY_DIR}/${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME}/setup.py
31+
@ONLY
32+
)
33+
34+
# Add __init__.py and import from there all pybind11 functions and classes
35+
configure_file(
36+
${PROJECT_SOURCE_DIR}/pybind11/__init__.py.in
37+
${PROJECT_BINARY_DIR}/${FOUR_C_PYTHON_BINDINGS_PROJECT_NAME}/__init__.py
38+
)

pybind11/__init__.py.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This file is part of 4C multiphysics licensed under the
2+
# GNU Lesser General Public License v3.0 or later.
3+
#
4+
# See the LICENSE.md file in the top-level for license information.
5+
#
6+
# SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
from .@FOUR_C_PYTHON_BINDINGS_PROJECT_NAME@ import *
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// This file is part of 4C multiphysics licensed under the
2+
// GNU Lesser General Public License v3.0 or later.
3+
//
4+
// See the LICENSE.md file in the top-level for license information.
5+
//
6+
// SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
#ifndef FOUR_C_CONFIG_SUBMODULE_REGISTRY_HPP
9+
#define FOUR_C_CONFIG_SUBMODULE_REGISTRY_HPP
10+
11+
#include "4C_config.hpp"
12+
13+
#include <pybind11/pybind11.h>
14+
15+
#include <functional>
16+
#include <vector>
17+
18+
FOUR_C_NAMESPACE_OPEN
19+
20+
21+
namespace Py4C
22+
{
23+
struct SubmoduleRegistryEntry
24+
{
25+
std::string name;
26+
std::string doc;
27+
std::function<void(pybind11::module_&)> init_funct;
28+
};
29+
30+
inline std::vector<SubmoduleRegistryEntry>& get_submodule_registry()
31+
{
32+
static std::vector<SubmoduleRegistryEntry> registry;
33+
return registry;
34+
}
35+
} // namespace Py4C
36+
37+
/*!
38+
* @brief Register a new submodule for 4C Python bindings
39+
*
40+
* This macro simplifies the registration of new submodules by automatically
41+
* registering the submodule at the global 4C Python bindings module upon initialization.
42+
*
43+
* @param name The name of the submodule
44+
* @param doc A brief documentation string for the submodule
45+
* @param init_func A functional with signature `void(pybind11::module_&)` that initializes all
46+
* bindings
47+
*/
48+
#define FOUR_C_PYBIND_REGISTER_SUBMODULE(name, doc, init_func) \
49+
static bool _four_c_pybind_submodule_register_##name = []() -> bool \
50+
{ \
51+
Py4C::get_submodule_registry().emplace_back(#name, doc, init_func); \
52+
return true; \
53+
}();
54+
55+
FOUR_C_NAMESPACE_CLOSE
56+
57+
#endif

pybind11/config/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This file is part of 4C multiphysics licensed under the
2+
# GNU Lesser General Public License v3.0 or later.
3+
#
4+
# See the LICENSE.md file in the top-level for license information.
5+
#
6+
# SPDX-License-Identifier: LGPL-3.0-or-later
7+
8+
add_library(pybind11_config INTERFACE)
9+
target_include_directories(pybind11_config INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
10+
# Add the special config target as a dependency
11+
target_link_libraries(pybind11_config INTERFACE config_deps)
12+
13+
set_target_properties(pybind11_config PROPERTIES POSITION_INDEPENDENT_CODE ON)

0 commit comments

Comments
 (0)