-
Notifications
You must be signed in to change notification settings - Fork 5
Python Interface
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.
In order to compile a version of MaMiCo usable by a Python3 interpreter, follow these simple steps:
- In MaMiCo's root directory, copy
personal_settings.template
topersonal_settings
and adapt it accordingly to your local machine. - From there, go to
coupling/python-bindings/
. - 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.
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].
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)
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)
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.
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].