Create an Python-importable class from your micro simulation code.
Updated 12 Jul 24

The Micro Manager requires that the micro simulation code be in a predefined class structure. As the Micro Manager is written in Python, micro simulation codes written in Python are the easiest to prepare. For micro simulation codes not written in Python, look at the C++ micro simulation section below.

Restructure your micro simulation code into a Python class with the structure given below. The docstring of each function gives information on what it should do and what its input and output should be.

class MicroSimulation: # Name is fixed
    def __init__(self, sim_id):
        """
        Constructor of class MicroSimulation.

        Parameters
        ----------
        sim_id : int
            ID of the simulation instance, that the Micro Manager has set for it.
        """

    def initialize(self) -> dict:
        """
        Initialize the micro simulation and return initial data which will be used in computing adaptivity before the first time step.

        Defining this function is OPTIONAL.

        Returns
        -------
        initial_data : dict
            Dictionary with names of initial data as keys and the initial data itself as values.
        """

    def solve(self, macro_data: dict, dt: float) -> dict:
        """
        Solve one time step of the micro simulation for transient problems or solve until steady state for steady-state problems.

        Parameters
        ----------
        macro_data : dict
            Dictionary with names of macro data as keys and the data as values.
        dt : float
            Current time step size.

        Returns
        -------
        micro_data : dict
            Dictionary with names of micro data as keys and the updated micro data a values.
        """

    def set_state(self, state):
        """
        Set the state of the micro simulation.
        """

    def get_state(self):
        """
        Return the state of the micro simulation.
        """

    def output(self):
        """
        This function writes output of the micro simulation in some form.
        It will be called with frequency set by configuration option `simulation_params: micro_output_n`
        This function is *optional*.
        """

A dummy code of a sample MicroSimulation class can be found in the examples/python-dummy/micro_dummy.py directory.

Create an Python-importable class from your micro simulation code written in C++

A dummy C++ dummy micro simulation code having a Python-importable class structure is provided in examples/cpp-dummy/micro_cpp_dummy.cpp. It uses pybind11 to enable control and use from Python. Restructuring a C++ micro simulation code has the following steps

  1. Create a C++ class which implements the functions given in the code snippet above. The solve() function should have the following signature:

     py::dict MicroSimulation::solve(py::dict macro_data, double dt)
    

    py::dict is a Python dictionary which can be used to pass data between Python and C++. Cast the data to the correct type before using it in C++ and vice versa.

  2. Export the C++ class to Python using pybind11. Follow the instructions to exporting classes in the pybind11 documentation or read their first steps to get started.

  3. Compile the C++ library including pybind11. For example, for the solverdummy, the command is

     c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) micro_cpp_dummy.cpp -o micro_dummy$(python3-config --extension-suffix)
    

    This will create a shared library micro_dummy.so which can be directly imported in Python. For more information on compiling C++ libraries, see the pybind11 documentation.

Initializing micro simulations

Micro simulations can be initialized before the actual coupling starts. To initialize a micro simulation, define an initialize() function in the code. The Micro Manager calls the initialize function for every micro simulation. If the macro simulation writes initial data to preCICE, the Micro Manager attempts to pass it to the micro simulation. If the initialize() function does not have input parameters, the initial data will not be passed. The initialize() function can return data to the Micro Manager. This data is only relevant to compute the adaptivity before the coupling starts. Therefore, if the initialize() functions returns data, it must be the data expected by the adaptivity.

Next step

After restructuring your micro simulation code into a Python-importable class structure, configure the Micro Manager.