diff --git a/CHANGELOG.md b/CHANGELOG.md index f981cc4..11e9bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## latest +- Added support to load multiple pyFANS builds in the same Python session [#142](https://github.com/DataAnalyticsEngineering/FANS/pull/142) - Bugfix: mem-leak while reading microstructure and added wider CMake support [#140](https://github.com/DataAnalyticsEngineering/FANS/pull/140) - Added MPI communicator abstraction [#139](https://github.com/DataAnalyticsEngineering/FANS/pull/139) diff --git a/README.md b/README.md index 90b6a6a..7586692 100644 --- a/README.md +++ b/README.md @@ -185,14 +185,15 @@ cd ../test **Build options:** -| CMake Option | Description | Default | -| -------------- | ------------- | --------- | -| `CMAKE_BUILD_TYPE` | Build type: `Debug`, `Release`, `RelWithDebInfo` | `NONE` | -| `CMAKE_INTERPROCEDURAL_OPTIMIZATION` | Enable link-time optimization (LTO) | `ON` (if supported) | -| `FANS_BUILD_STATIC` | Build static library | `OFF` | -| `CMAKE_INSTALL_PREFIX` | Installation directory | System default | -| `FANS_LIBRARY_FOR_MICRO_MANAGER` | Build Python bindings using Pybind11 (needed) | `OFF` | -| `FANS_ENABLE_SANITIZERS` | Enable runtime sanitizers (AddressSanitizer and LeakSanitizer) for memory debugging | `OFF` | +| CMake Option | Description | Default | +|--------------------------------------|-------------------------------------------------------------------------------------------------------|---------------------| +| `CMAKE_BUILD_TYPE` | Build type: `Debug`, `Release`, `RelWithDebInfo` | `NONE` | +| `CMAKE_INTERPROCEDURAL_OPTIMIZATION` | Enable link-time optimization (LTO) | `ON` (if supported) | +| `FANS_BUILD_STATIC` | Build static library | `OFF` | +| `CMAKE_INSTALL_PREFIX` | Installation directory | System default | +| `FANS_LIBRARY_FOR_MICRO_MANAGER` | Build Python bindings (pyFANS) using Pybind11 (needed) | `OFF` | +| `FANS_ENABLE_SANITIZERS` | Enable runtime sanitizers (AddressSanitizer and LeakSanitizer) for memory debugging | `OFF` | +| `MicroSimulationSuffix` | Numeric suffix for pyFANS registration within Pybind11. Required when loading multiple pyFANS builds. | `NONE` | --- diff --git a/pyfans/CMakeLists.txt b/pyfans/CMakeLists.txt index 47882af..978e79e 100644 --- a/pyfans/CMakeLists.txt +++ b/pyfans/CMakeLists.txt @@ -2,6 +2,36 @@ pybind11_add_module(PyFANS micro.hpp micro.cpp) target_include_directories(PyFANS PRIVATE ${HDF5_INCLUDE_DIRS} ${FFTW3_INCLUDE_DIRS}) target_link_libraries(PyFANS PRIVATE FANS::FANS) +# Optional integer option (empty by default) +set(MicroSimulationSuffix "" CACHE STRING "Optional numeric variant suffix") + +# Validate: must be empty or integer +if(MicroSimulationSuffix STREQUAL "") + set(CLASS_NAME MicroSimulation) + set(INPUT_NAME input.json) + set(CONFIG_NAME pyfans-config.json) + set(MODULE_NAME PyFANS) +else() + if(NOT MicroSimulationSuffix MATCHES "^[0-9]+$") + message(FATAL_ERROR "MicroSimulationSuffix must be an integer, got '${MicroSimulationSuffix}'") + endif() + + set(CLASS_NAME MicroSimulation${MicroSimulationSuffix}) + set(INPUT_NAME input${MicroSimulationSuffix}.json) + set(CONFIG_NAME pyfans-config${MicroSimulationSuffix}.json) + set(MODULE_NAME PyFANS${MicroSimulationSuffix}) + message("Using MicroSimulationSuffix: ${MicroSimulationSuffix}") +endif() + +# Add compile definitions +target_compile_definitions(PyFANS PRIVATE + PyFANS_CLASS_NAME=${CLASS_NAME} + PyFANS_CLASS_NAME_STR="${CLASS_NAME}" + PyFANS_INPUT_NAME="${INPUT_NAME}" + PyFANS_CONFIG_NAME="${CONFIG_NAME}" + PyFANS_MODULE_NAME=${MODULE_NAME} +) + add_custom_command( TARGET PyFANS POST_BUILD diff --git a/pyfans/micro.cpp b/pyfans/micro.cpp index 2e9f7b9..ff72c3e 100644 --- a/pyfans/micro.cpp +++ b/pyfans/micro.cpp @@ -44,7 +44,7 @@ PyFANSConfig load_config(const std::string &file_path) return config; } -MicroSimulation::MicroSimulation(int sim_id, const std::string &input_file, const std::string &config_file) +PyFANS_CLASS_NAME::PyFANS_CLASS_NAME(int sim_id, const std::string &input_file, const std::string &config_file) { // initialize fftw mpi fftw_mpi_init(); @@ -66,7 +66,7 @@ MicroSimulation::MicroSimulation(int sim_id, const std::string &input_file, cons solver = createSolver<3, 6>(reader, matmanager); } -py::dict MicroSimulation::solve(py::dict macro_data, double dt) +py::dict PyFANS_CLASS_NAME::solve(py::dict macro_data, double dt) { // Time step value dt is not used currently, but is available for future use @@ -113,12 +113,12 @@ py::dict MicroSimulation::solve(py::dict macro_data, double dt) return micro_write_data; } -PYBIND11_MODULE(PyFANS, m) +PYBIND11_MODULE(PyFANS_MODULE_NAME, m) { // optional docstring m.doc() = "FANS for Micro Manager"; - py::class_(m, "MicroSimulation") + py::class_(m, PyFANS_CLASS_NAME_STR) .def(py::init()) - .def("solve", &MicroSimulation::solve); + .def("solve", &PyFANS_CLASS_NAME::solve); } diff --git a/pyfans/micro.hpp b/pyfans/micro.hpp index 2dbe418..d91c84d 100644 --- a/pyfans/micro.hpp +++ b/pyfans/micro.hpp @@ -17,9 +17,25 @@ namespace py = pybind11; -class MicroSimulation { +#ifndef PyFANS_CLASS_NAME +#define PyFANS_CLASS_NAME MicroSimulation +#endif +#ifndef PyFANS_CLASS_NAME_STR +#define PyFANS_CLASS_NAME_STR "MicroSimulation" +#endif +#ifndef PyFANS_INPUT_NAME +#define PyFANS_INPUT_NAME "input.json" +#endif +#ifndef PyFANS_CONFIG_NAME +#define PyFANS_CONFIG_NAME "pyfans-config.json" +#endif +#ifndef PyFANS_MODULE_NAME +#define PyFANS_MODULE_NAME PyFANS +#endif + +class PyFANS_CLASS_NAME { public: - MicroSimulation(int sim_id, const std::string &input_file = "input.json", const std::string &config_file = "pyfans-config.json"); + PyFANS_CLASS_NAME(int sim_id, const std::string &input_file = PyFANS_INPUT_NAME, const std::string &config_file = PyFANS_CONFIG_NAME); py::dict solve(py::dict macro_write_data, double dt); private: