Skip to content
Snippets Groups Projects
  • akuesters's avatar
    Py feature recipe wo probes (#768) · a6ddd515
    akuesters authored and Benjamin Cumming's avatar Benjamin Cumming committed
    Fixes #760
    
    Wraps arbor recipe (without probes, i.e. num_probes, probe_info, get_probe) including 
    
    - cell_connection, 
    - gap_junction_connection, 
    - recipe with
      - num_cells
      - cell_description
      - cell_kind
      - num_sources
      - num_targets
      - num_gap_junctions_sites
      - event_generators
      - connections_on
      - gap_junctions_on
      - global_properties
    - enum cell_kind in `identifiers.cpp`
    a6ddd515
mpi.cpp 3.03 KiB
#include <sstream>
#include <string>

#include <arbor/version.hpp>

#include <pybind11/pybind11.h>

#ifdef ARB_MPI_ENABLED
#include <mpi.h>

#include <arbor/communication/mpi_error.hpp>

#include "mpi.hpp"

#ifdef ARB_WITH_MPI4PY
#include <mpi4py/mpi4py.h>
#endif
#endif

namespace pyarb {

#ifdef ARB_MPI_ENABLED

// Convert a Python object an MPI Communicator.
// Used to construct mpi_comm_shim from arbitrary Python types.
// Currently only supports mpi4py communicators, but could be extended to
// other types.
MPI_Comm convert_to_mpi_comm(pybind11::object o) {
#ifdef ARB_WITH_MPI4PY
    import_mpi4py();
    if (PyObject_TypeCheck(o.ptr(), &PyMPIComm_Type)) {
        return *PyMPIComm_Get(o.ptr());
    }
#endif
    throw arb::mpi_error(MPI_ERR_OTHER, "Unable to convert to an MPI Communicatior");
}

mpi_comm_shim::mpi_comm_shim(pybind11::object o) {
    comm = convert_to_mpi_comm(o);
}

// Test if a Python object can be converted to an mpi_comm_shim.
bool can_convert_to_mpi_comm(pybind11::object o) {
#ifdef ARB_WITH_MPI4PY
    import_mpi4py();
    if (PyObject_TypeCheck(o.ptr(), &PyMPIComm_Type)) {
        return true;
    }
#endif
    return false;
}

// Some helper functions for initializing and finalizing MPI.
// Arbor requires at least MPI_THREAD_SERIALIZED, because the communication task
// that uses MPI can be run on any thread, and there will never be more than one
// concurrent communication task.

void mpi_init() {
    int provided = MPI_THREAD_SINGLE;
    int ev = MPI_Init_thread(nullptr, nullptr, MPI_THREAD_SERIALIZED, &provided);
    if (ev) {
        throw arb::mpi_error(ev, "MPI_Init_thread");
    }
    else if (provided<MPI_THREAD_SERIALIZED) {
        throw arb::mpi_error(MPI_ERR_OTHER, "MPI_Init_thread: MPI_THREAD_SERIALIZED unsupported");
    }
}

void mpi_finalize() {
    MPI_Finalize();
}

int mpi_is_initialized() {
    int initialized;
    MPI_Initialized(&initialized);
    return initialized;
}

int mpi_is_finalized() {
    int finalized;
    MPI_Finalized(&finalized);
    return finalized;
}
// Define the stringifier for mpi_comm_shim here, to minimise the ifdefication
// elsewhere in this wrapper code.

std::string mpi_comm_string(const mpi_comm_shim& c) {
    std::stringstream s;

    s << "<mpi_comm: ";
    if (c.comm==MPI_COMM_WORLD) s << "MPI_COMM_WORLD>";
    else s << c.comm << ">";
    return s.str();
}

void register_mpi(pybind11::module& m) {
    using namespace std::string_literals;

    pybind11::class_<mpi_comm_shim> mpi_comm(m, "mpi_comm");
    mpi_comm
        .def(pybind11::init<>())
        .def(pybind11::init([](pybind11::object o){return mpi_comm_shim(o);}))
        .def("__str__", &mpi_comm_string)
        .def("__repr__", &mpi_comm_string);

    m.def("mpi_init", &mpi_init, "Initialize MPI with MPI_THREAD_SINGLE, as required by Arbor.");
    m.def("mpi_finalize", &mpi_finalize, "Finalize MPI (calls MPI_Finalize)");
    m.def("mpi_is_initialized", &mpi_is_initialized, "Check if MPI is initialized.");
    m.def("mpi_is_finalized", &mpi_is_finalized, "Check if MPI is finalized.");
}
#endif
} // namespace pyarb