Skip to content

Python Interface

Felix Maurer edited this page Jul 26, 2021 · 7 revisions

Introduction

In an effort to make MaMiCo more accessible and to increase the compatibility to other solvers and analytics modules such as filters, an interface allowing easy integration into Python context is provided. This interface makes use of the open-source binding library pybind11[1], which enables a lightweight and easily expandable user interface to all necessary parts of MaMiCo's coupling system.
The KVS test scenario makes use of this interface, utilizing it in order to couple SimpleMD to lbmpy [3], a Lattice-Boltzmann macroscopic solver implemented in Python. This page will first give compilation instructions and then go over to providing a list containing parts of MaMiCo that are accessible using this interface and provide short code examples to give an idea regarding how to use it as a Python developer.

Compilation

In order to compile a version of MaMiCo usable by a Python3 interpreter, follow these simple steps:

  1. In MaMiCo's root directory, copy personal_settings.template to personal_settings and adapt it accordingly to your local machine.
  2. From there, go to coupling/python-bindings/.
  3. Execute make.sh.

You will now find a shared library in your current working directory, which can be imported via a python3 interpreter or script. Make sure the location of this library is part of sys.path when trying to import it.

sys.path.append('../../python-binding')  
import mamico.tarch.configuration 
import mamico.tarch.utils
import mamico.coupling

You can also compile this Python library for testing purposes using CMake via MaMiCo's Compilation Testing module. Note that it is planned to make CMake the primary build system, replacing the make.sh script mentioned above in the future.

Code Examples

This section will contain code examples from the Kármán Vortex Street (KVS) test scenario, which is implemented in Python exclusively by the use of this interface. Following the examples, one can get a good understanding of how to create all parts relevant to a coupled scenario in Python. Their context can be inspected in [4].

Solver Initialisation

After reading several configuration files, one is able to initialize macro- and microscopic solvers and coupling service classes using simple Python directives.
Taking microscopic solver initialisation in the KVS scenario as an example, construction of services, SimpleMD instances and solver interfaces is implemented as follows:

# init MultiMDService
multiMDService = mamico.tarch.utils.MultiMDService(numberProcesses = numProcs, totalNumberMDSimulations = numMD)

# init SimpleMD instances
simpleMD = [mamico.coupling.getMDSimulation(simpleMDConfig, mamicoConfig, multiMDService.getLocalCommunicator()) for i in range(localMDInstances)]

# init interfaces microscopic solvers
mdSolverInterface = [mamico.coupling.getMDSolverInterface(simpleMDConfig, mamicoConfig, simpleMD[i]) for i in range(localMDInstances)]

# init interfaces of macroscopic solvers
macroscopicSolverInterface = CouetteSolverInterface(globalNumberMacroscopicCells, 
            mamicoConfig.getMomentumInsertionConfiguration().getInnerOverlap())
mamico.coupling.setMacroscopicSolverInterface(self.macroscopicSolverInterface)

# init MultiMDCellService
multiMDCellService = MultiMDCellService(mdSolverInterface, macroscopicSolverInterface, 
            simpleMDConfig, rank, numberMDSimulations,
            mamicoConfig, "kvs.xml", multiMDService)

Coupling Cycles

Advancing the simulation by one coupling cycle consists of three core steps, which can easily be implemented as three separate functions in Python. One coupling step in the KVS scenario can thus be expressed as:

def runOneCouplingCycle(cycle):
    # advance the macroscopic solver
    advanceMacro(cycle)

    # advance the microscopic solver
    advanceMicro(cycle)

    # couple the two simulations using two-way-coupling
    twoWayCoupling(cycle)

advanceMicro and advanceMacro

The implementation of these two functions depends on the scenario and what solvers are in use. In our case with multi-instance SimpleMD for the microscopic part and lbmpy for simulation of the macroscopic domain, the advancement functions are implemented as follows:

def advanceMicro(cycle):
    # get number of timesteps to be simulated by SimpleMD
    numT = self.simpleMDConfig.getSimulationConfiguration().getNumberOfTimesteps()

    # advance all SimpleMD instances
    for i in range(localMDInstances):
        #...
        self.simpleMD[i].simulateTimesteps(numT,mdStepCounter)
        #...
        mdStepCounter = mdStepCounter + numT

    # send micro data to macro solver. uses kvs-test's global buffer 'buf'
    multiMDCellService.sendFromMD2Macro(buf)


def advanceMacro(cycle):
    # access lbmpy from master rank only
    if rank==0:
        #...
        # advance lbmpy by previously calculated number of steps
        self.macroscopicSolver.advance(steps) 
        #...

        # fill buffer with macro data in order to send it to micro solver. mdpos refers to location of center of md domain.
        buf.store2send(cellmass,
            macroscopicSolver.scen.velocity[
             mdpos[0]:mdpos[0]+numcells[0], 
             mdpos[1]:mdpos[1]+numcells[1], 
             mdpos[2]:mdpos[2]+numcells[2], 
            :].data * (self.dx / self.dt_LB),
            macroscopicSolver.scen.density[
             mdpos[0]:mdpos[0]+numcells[0], 
             mdpos[1]:mdpos[1]+numcells[1], 
             mdpos[2]:mdpos[2]+numcells[2]
            ].data
       )

       # send buffer filled with data from lbmpy to microscopic solver
       multiMDCellService.sendFromMacro2MD(buf)

Note the seamless integration of lbmby here: Because lbmpy's user interface is a Python library as well, smooth interoperability is guaranteed.

Type Conversions

The C++ implementation of MaMiCo uses both C-Style and standard template library (STL) data structures. To the latter, no implicit conversion exists in Python. The conversion between types of the two languages is therefore largely a consequence of the automatic type translation of STL types provided by pybind11. Further details on this can be found in [2].

References