Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- Added large strain support for pyFANS [#141](https://github.com/DataAnalyticsEngineering/FANS/pull/141)
- Bugfix: fixes GBDiffusion post MaterialManager update and adds a test case [#147](https://github.com/DataAnalyticsEngineering/FANS/pull/147)
- Bugfix: mem-leak while reading microstructure and added wider CMake support [#140](https://github.com/DataAnalyticsEngineering/FANS/pull/140)
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,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` |

---

Expand Down
30 changes: 30 additions & 0 deletions pyfans/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions pyfans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,60 @@ pyFANS is a Python-wrapped library to control FANS via the [Micro Manager](https

To build FANS as a Micro Manager compatible Python library, set the CMake variable `FANS_LIB` to `ON`. The CMake command to compile FANS would then be `cmake .. -DFANS_LIBRARY_FOR_MICRO_MANAGER=ON`.

To build multiple pyFANS targets configured to load different input and configuration files, respective build directories must be created. Within those, the corresponding CMAKE flags must be set.
In the following example we show how to configure two different builds:

__NOTE__: `MicroSimulationSuffix` may only be set to non-negative integer values within 0 to 10

In the project root directory, create two build folders:

```bash
cd <FANS-ROOT>
mkdir build0
mkdir build1
```

To configure and build the first target:

```bash
cd build0
cmake -DFANS_LIBRARY_FOR_MICRO_MANAGER=ON -DMicroSimulationSuffix=0 ..
cmake --build -j <N> ..
cp pyfans/PyFANS.*.so <Simulation Directory>/PyFANS0.so
cd ..
```

This will result in the following configuration:

| Name | Value |
|----------------------|---------------------|
| CLASS_NAME | MicroSimulation0 |
| INPUT_FILE | input0.json |
| CONFIG_FILE | pyfans-config0.json |
| pybind11 MODULE_NAME | PyFANS0 |

To configure and build the second target:

```bash
cd build1
cmake -DFANS_LIBRARY_FOR_MICRO_MANAGER=ON -DMicroSimulationSuffix=1 ..
cmake --build -j <N> ..
cp pyfans/PyFANS.*.so <Simulation Directory>/PyFANS1.so
cd ..
```

This will result in the following configuration:

| Name | Value |
|----------------------|---------------------|
| CLASS_NAME | MicroSimulation1 |
| INPUT_FILE | input1.json |
| CONFIG_FILE | pyfans-config1.json |
| pybind11 MODULE_NAME | PyFANS1 |

## Usage

pyFANS is intended to be used with the Micro Manager and preCICE for two-scale coupled simulations. However, standalone use of the library is not restricted per se. Look at the [test_pyfans](../test/test_pyfans/) example to see how the library is used in a Python script.

To disable parallelization with MPI, provide a `pyfans-config.json` file containing the field: `no_mpi: true`.
It should be located in the same directed from which for example the Micro Manager is launched.
26 changes: 13 additions & 13 deletions pyfans/micro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ PyFANSConfig load_config(const std::string &file_path)
return config;
}

MicroSimulation::MicroSimulation(int sim_id, bool late_init, const std::string &input_file, const std::string &config_file)
PyFANS_CLASS_NAME::PyFANS_CLASS_NAME(int sim_id, bool late_init, const std::string &input_file, const std::string &config_file)
: _sim_id(sim_id)
{
// initialize fftw mpi
Expand Down Expand Up @@ -76,7 +76,7 @@ std::vector<double> conv_to_vector(const py::array_t<double> &arr, const int siz
return res;
}

py::dict MicroSimulation::solve(const py::dict &macro_data, double dt)
py::dict PyFANS_CLASS_NAME::solve(const py::dict &macro_data, double dt)
{
const bool is_small_strain = std::holds_alternative<MaterialManager<3, 6> *>(matmanager);
// Time step value dt is not used currently, but is available for future use
Expand Down Expand Up @@ -160,14 +160,14 @@ py::dict MicroSimulation::solve(const py::dict &macro_data, double dt)
return micro_write_data;
}

py::dict MicroSimulation::get_state()
py::dict PyFANS_CLASS_NAME::get_state()
{
// TODO populate state
py::dict state;
return state;
}

void MicroSimulation::set_state(const py::dict &state)
void PyFANS_CLASS_NAME::set_state(const py::dict &state)
{
// TODO read from state, not file
reader.FreeMS();
Expand All @@ -191,26 +191,26 @@ void MicroSimulation::set_state(const py::dict &state)
}
}

int MicroSimulation::get_id()
int PyFANS_CLASS_NAME::get_id()
{
return _sim_id;
}

void MicroSimulation::set_id(const int id)
void PyFANS_CLASS_NAME::set_id(const int id)
{
_sim_id = id;
}

PYBIND11_MODULE(PyFANS, m)
PYBIND11_MODULE(PyFANS_MODULE_NAME, m)
{
// optional docstring
m.doc() = "FANS for Micro Manager";

py::class_<MicroSimulation>(m, "MicroSimulation")
py::class_<PyFANS_CLASS_NAME>(m, PyFANS_CLASS_NAME_STR)
.def(py::init<int>())
.def("solve", &MicroSimulation::solve, py::return_value_policy::automatic)
.def("set_state", &MicroSimulation::set_state)
.def("get_state", &MicroSimulation::get_state, py::return_value_policy::automatic)
.def("get_global_id", &MicroSimulation::get_id)
.def("set_global_id", &MicroSimulation::set_id);
.def("solve", &PyFANS_CLASS_NAME::solve, py::return_value_policy::automatic)
.def("set_state", &PyFANS_CLASS_NAME::set_state)
.def("get_state", &PyFANS_CLASS_NAME::get_state, py::return_value_policy::automatic)
.def("get_global_id", &PyFANS_CLASS_NAME::get_id)
.def("set_global_id", &PyFANS_CLASS_NAME::set_id);
}
20 changes: 18 additions & 2 deletions pyfans/micro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,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, bool late_init = false, const std::string &input_file = "input.json", const std::string &config_file = "pyfans-config.json");
PyFANS_CLASS_NAME(int sim_id, bool late_init = false, const std::string &input_file = PyFANS_INPUT_NAME, const std::string &config_file = PyFANS_CONFIG_NAME);
py::dict solve(const py::dict &macro_write_data, double dt);

py::dict get_state();
Expand Down