diff --git a/README.md b/README.md index 6497cc0..1c5c33e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![license](https://img.shields.io/github/license/Lyncs-API/lyncs.mpi?logo=github&logoColor=white)](https://github.com/Lyncs-API/lyncs.mpi/blob/master/LICENSE) [![build & test](https://img.shields.io/github/workflow/status/Lyncs-API/lyncs.mpi/build%20&%20test?logo=github&logoColor=white)](https://github.com/Lyncs-API/lyncs.mpi/actions) [![codecov](https://img.shields.io/codecov/c/github/Lyncs-API/lyncs.mpi?logo=codecov&logoColor=white)](https://codecov.io/gh/Lyncs-API/lyncs.mpi) -[![pylint](https://img.shields.io/badge/pylint%20score-9.6%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) +[![pylint](https://img.shields.io/badge/pylint%20score-9.5%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) [![black](https://img.shields.io/badge/code%20style-black-000000.svg?logo=codefactor&logoColor=white)](https://github.com/ambv/black) This package provides tools for interfacing to MPI libraries based on `mpi4py` and `dask`: @@ -54,7 +54,6 @@ pip install [--user] lyncs_mpi ## Documentation - In this package we implement several low-level tools for supporting classes distributed over MPI. These are described in this [guide]() for developers. In the following we describe the high-level tools provided in this package. @@ -110,4 +109,4 @@ where `[2,2]` are the dimensions of the multi-dimensional grid where the process Cartesian communicators directly support [Dask arrays](https://docs.dask.org/en/latest/array.html) and e.g. `cart.zeros([4,4,3,2,1])` instantiates a distributed Dask array assigned to the workers -of the communicator with local shape (chunks) `(2,2,3,2,1)`. \ No newline at end of file +of the communicator with local shape (chunks) `(2,2,3,2,1)`. diff --git a/lyncs_mpi/__init__.py b/lyncs_mpi/__init__.py index fdb4366..5df36ef 100644 --- a/lyncs_mpi/__init__.py +++ b/lyncs_mpi/__init__.py @@ -2,7 +2,7 @@ Utils for interfacing to MPI libraries using mpi4py and dask """ -__version__ = "0.1.4" +__version__ = "0.1.5" from . import abc from .lib import * diff --git a/lyncs_mpi/comm.py b/lyncs_mpi/comm.py index 60b2e60..7da7b70 100644 --- a/lyncs_mpi/comm.py +++ b/lyncs_mpi/comm.py @@ -7,6 +7,7 @@ from lyncs_utils import compute_property from .distributed import Distributed, results +from .lib import MPI class Comm(Distributed): @@ -22,9 +23,6 @@ def __init__(self, comms): @property def type(self): "Returns the MPI type of the class" - # pylint: disable=import-outside-toplevel - from mpi4py import MPI - return MPI.Comm @property @@ -85,9 +83,6 @@ def __init__(self, comms): @property def type(self): "Returns the MPI type of the class" - # pylint: disable=import-outside-toplevel - from mpi4py import MPI - return MPI.Cartcomm @property diff --git a/lyncs_mpi/include/pympivendor.h b/lyncs_mpi/include/pympivendor.h new file mode 100644 index 0000000..55812ba --- /dev/null +++ b/lyncs_mpi/include/pympivendor.h @@ -0,0 +1,156 @@ +/* SOURCE: mpi4py */ + +/* Author: Lisandro Dalcin */ +/* Contact: dalcinl@gmail.com */ + +/* Copyright (c) 2021, Lisandro Dalcin. */ +/* All rights reserved. */ + +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ + +/* * Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ + +/* * Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ + +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS */ +/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ +/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */ +/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +static std::string PyMPI_Get_vendor(int *version_major, + int *version_minor, + int *version_micro) +{ + const char *name = "unknown"; + int major=0, minor=0, micro=0; + +#if defined(I_MPI_VERSION) + + name = "Intel MPI"; + #if defined(I_MPI_NUMVERSION) + {int version = I_MPI_NUMVERSION/1000; + major = version/10000; version -= major*10000; + minor = version/100; version -= minor*100; + micro = version/1; version -= micro*1; } + #else + (void)sscanf(I_MPI_VERSION,"%d.%d Update %d",&major,&minor,µ); + #endif + +#elif defined(PLATFORM_MPI) + + name = "Platform MPI"; + major = (PLATFORM_MPI>>24)&0xff; + minor = (PLATFORM_MPI>>16)&0xff; + micro = (PLATFORM_MPI>> 8)&0xff; + major = (major/16)*10+(major%16); + +#elif defined(MSMPI_VER) + + name = "Microsoft MPI"; + major = MSMPI_VER >> 8; + minor = MSMPI_VER & 0xFF; + +#elif defined(MVAPICH2_VERSION) || defined(MVAPICH2_NUMVERSION) + + name = "MVAPICH2"; + #if defined(MVAPICH2_NUMVERSION) + {int version = MVAPICH2_NUMVERSION/1000; + major = version/10000; version -= major*10000; + minor = version/100; version -= minor*100; + micro = version/1; version -= micro*1; } + #elif defined(MVAPICH2_VERSION) + (void)sscanf(MVAPICH2_VERSION,"%d.%d.%d",&major,&minor,µ); + #endif + +#elif defined(MPICH_NAME) && (MPICH_NAME == 3) + + name = "MPICH"; + #if defined(MPICH_NUMVERSION) + {int version = MPICH_NUMVERSION/1000; + major = version/10000; version -= major*10000; + minor = version/100; version -= minor*100; + micro = version/1; version -= micro*1; } + #elif defined(MPICH_VERSION) + (void)sscanf(MPICH_VERSION,"%d.%d.%d",&major,&minor,µ); + #endif + +#elif defined(MPICH_NAME) && (MPICH_NAME == 2) + + name = "MPICH2"; + #if defined(MPICH2_NUMVERSION) + {int version = MPICH2_NUMVERSION/1000; + major = version/10000; version -= major*10000; + minor = version/100; version -= minor*100; + micro = version/1; version -= micro*1; } + #elif defined(MPICH2_VERSION) + (void)sscanf(MPICH2_VERSION,"%d.%d.%d",&major,&minor,µ); + #endif + +#elif defined(MPICH_NAME) && (MPICH_NAME == 1) + + name = "MPICH1"; + #if defined(MPICH_VERSION) + (void)sscanf(MPICH_VERSION,"%d.%d.%d",&major,&minor,µ); + #endif + +#elif defined(OPEN_MPI) + + name = "Open MPI"; + #if defined(OMPI_MAJOR_VERSION) + major = OMPI_MAJOR_VERSION; + #endif + #if defined(OMPI_MINOR_VERSION) + minor = OMPI_MINOR_VERSION; + #endif + #if defined(OMPI_RELEASE_VERSION) + micro = OMPI_RELEASE_VERSION; + #endif + + #if defined(OMPI_MAJOR_VERSION) + #if OMPI_MAJOR_VERSION >= 10 + name = "Spectrum MPI"; + #endif + #endif + +#elif defined(LAM_MPI) + + name = "LAM/MPI"; + #if defined(LAM_MAJOR_VERSION) + major = LAM_MAJOR_VERSION; + #endif + #if defined(LAM_MINOR_VERSION) + minor = LAM_MINOR_VERSION; + #endif + #if defined(LAM_RELEASE_VERSION) + micro = LAM_RELEASE_VERSION; + #endif + +#endif + + //if (vendor_name) *vendor_name = name; + if (version_major) *version_major = major; + if (version_minor) *version_minor = minor; + if (version_micro) *version_micro = micro; + + return name; +} + +/* + Local variables: + c-basic-offset: 2 + indent-tabs-mode: nil + End: +*/ diff --git a/lyncs_mpi/lib.py b/lyncs_mpi/lib.py index 5a72d15..4b0cb7a 100644 --- a/lyncs_mpi/lib.py +++ b/lyncs_mpi/lib.py @@ -5,15 +5,55 @@ "default_comm", "initialized", "finalized", + "get_comm", + "MPI", ] +from array import array from ctypes import c_int +import numpy as np from lyncs_cppyy import Lib +from lyncs_utils import static_property, lazy_import +from lyncs_cppyy.ll import cast +from . import __path__ from .config import MPI_INCLUDE_DIRS, MPI_LIBRARIES -lib = Lib( +PATHS = list(__path__) +MPI = lazy_import("mpi4py.MPI") + + +def get_comm(comm): + assert isinstance(comm, MPI.Comm) + return lib.MPI_Comm(MPI._handleof(comm)) + + +class MPILib(Lib): + def load(self): + super().load() + + if MPI.get_vendor() != self.get_vendor(): + print("mpi4py vendor:", MPI.get_vendor()) + print("lyncs_mpi vendor:", self.get_vendor()) + raise RuntimeError( + """ + mpi4py and lyncs_mpi are not using the same MPI + Try to recompily mpi4py using: + pip install --force-reinstall --no-cache-dir mpi4py + """ + ) + + def get_vendor(self): + major = array("i", [0]) + minor = array("i", [0]) + micro = array("i", [0]) + name = self.PyMPI_Get_vendor(major, minor, micro) + return str(name), (major[0], minor[0], micro[0]) + + +lib = MPILib( + path=PATHS, include=MPI_INCLUDE_DIRS.split(";"), - header="mpi.h", + header=["mpi.h", "pympivendor.h"], library=MPI_LIBRARIES.split(";"), c_include=False, check="MPI_Init", @@ -25,11 +65,9 @@ def default_comm(): "Returns the default communicator to be used (MPI_COMM_WORLD by default)" - # pylint: disable=import-outside-toplevel,no-name-in-module,redefined-outer-name,global-statement global COMM if not COMM: - from mpi4py.MPI import COMM_WORLD as COMM - + return MPI.COMM_WORLD return COMM diff --git a/setup.py b/setup.py index b26bf22..15a15f2 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,11 @@ +import glob from lyncs_setuptools import setup, CMakeExtension requirements = [ "lyncs-setuptools", "mpi4py", "lyncs-cppyy", - "lyncs-utils", + "lyncs-utils>=0.2.2", "dask", "distributed", "dask[array]", @@ -18,7 +19,12 @@ "lyncs_mpi", ext_modules=[CMakeExtension("lyncs_mpi.lib", ".")], exclude=["*.config"], - data_files=[(".", ["config.py.in"])], + data_files=[ + (".", ["config.py.in"]), + ("lyncs_mpi", glob.glob("lyncs_mpi/include/*.h")), + ], install_requires=requirements, extras_require={"test": ["pytest", "pytest-cov", "pytest-benchmark"]}, + package_data={"lyncs_mpi": ["include/*.h"]}, + include_package_data=True, ) diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000