diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..f91aa0e --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,48 @@ +version: 2 +jobs: + build: + machine: true + steps: + - checkout + + - restore_cache: + key: v2-miniconda-{{ .Branch }} + + - run: + name: install miniconda + command: | + if [[ ! -d /home/circleci/miniconda ]]; then + wget https://repo.continuum.io/miniconda/Miniconda3-4.5.1-Linux-x86_64.sh -O miniconda.sh && + bash miniconda.sh -b -f -p /home/circleci/miniconda; + else + echo "Using cached miniconda"; + fi + source ~/miniconda/bin/activate root + conda config --set always_yes yes + conda config --add channels conda-forge + conda config --add channels spectralDNS + conda clean --lock + conda install --yes --quiet conda-forge-ci-setup=1 + source run_conda_forge_build_setup + + - save_cache: + key: v2-miniconda-{{ .Branch }} + paths: + - /home/circleci/miniconda + + - run: + name: Build and test + command: | + source ~/miniconda/bin/activate root + cd /home/circleci/project + conda build --python 2.7 ./conf/conda + conda build --python 3.6 ./conf/conda + + - run: + name: Upload packages + command: | + source ~/miniconda/bin/activate root + cd /home/circleci/project + upload_or_check_non_existence ./conf/conda spectralDNS --channel main + export CONDA_PY=36 + upload_or_check_non_existence ./conf/conda spectralDNS --channel main diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3932b59 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +VERSION=$(shell python3 -c "import mpiFFT4py; print(mpiFFT4py.__version__)") + +default: + python setup.py build_ext -i + +pip: + rm -f dist/* + python setup.py sdist + twine upload dist/* + +tag: + git tag $(VERSION) + git push --tags + +publish: tag pip + +clean: + git clean mpiFFT4py -fx + git clean tests -fx + cd docs && make clean && cd .. + @rm -rf *.egg-info/ build/ dist/ .eggs/ \ No newline at end of file diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 008559c..0000000 --- a/circle.yml +++ /dev/null @@ -1,39 +0,0 @@ -machine: - environment: - CONDA_ROOT: /home/ubuntu/miniconda2 - PATH: ${CONDA_ROOT}/bin:${PATH} - -dependencies: - cache_directories: - - /home/ubuntu/miniconda2 - - override: - - > - if [[ ! -d ${CONDA_ROOT} ]]; then - echo "Installing Miniconda..."; - wget --quiet https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh && - bash Miniconda-latest-Linux-x86_64.sh -b -p ${CONDA_ROOT}; - else - echo "Using cached Miniconda install"; - fi - - conda config --set always_yes yes - - conda update -q conda - - conda config --add channels conda-forge - #- conda config --add channels mpi4py - - conda install -n root conda-build - - conda install --yes --quiet conda-forge-build-setup - - source run_conda_forge_build_setup - -test: - override: - - conda build conf/conda - - conda build --python 3.6 conf/conda - -deployment: - master: - branch: master - commands: - - upload_or_check_non_existence ./conf/conda spectralDNS --channel main - - | - export CONDA_PY=36 - upload_or_check_non_existence ./conf/conda spectralDNS --channel main diff --git a/conf/conda/conda_build_config.yaml b/conf/conda/conda_build_config.yaml new file mode 100644 index 0000000..1043105 --- /dev/null +++ b/conf/conda/conda_build_config.yaml @@ -0,0 +1,2 @@ +numpy: + - 1.15 diff --git a/conf/conda/meta.yaml b/conf/conda/meta.yaml index fa76c5c..b2e0bb2 100644 --- a/conf/conda/meta.yaml +++ b/conf/conda/meta.yaml @@ -1,6 +1,6 @@ package: name: mpifft4py - version: "1.1.0" + version: "{{ GET_DESCRIBE_TAG }}" source: git_url: ../../ @@ -19,9 +19,7 @@ requirements: - numpy - scipy - mpi4py - - mpich #[unix] - - openmpi ==1.6.3 #[osx] - - cython + - fftw - pyfftw test: diff --git a/mpiFFT4py/__init__.py b/mpiFFT4py/__init__.py index 528e857..847d391 100644 --- a/mpiFFT4py/__init__.py +++ b/mpiFFT4py/__init__.py @@ -5,3 +5,4 @@ from .mpibase import work_arrays, datatypes, empty, zeros from numpy.fft import fftfreq, rfftfreq +__version__ = '1.1.0' diff --git a/mpiFFT4py/slab.py b/mpiFFT4py/slab.py index 60b2544..87157e2 100644 --- a/mpiFFT4py/slab.py +++ b/mpiFFT4py/slab.py @@ -145,9 +145,9 @@ def complex_local_slice(self): def complex_local_wavenumbers(self): """Returns local wavenumbers of complex space""" - return (fftfreq(self.N[0], 1./self.N[0]), - fftfreq(self.N[1], 1./self.N[1])[self.complex_local_slice()[1]], - rfftfreq(self.N[2], 1./self.N[2])) + return (fftfreq(self.N[0], 1./self.N[0]).astype(self.float), + fftfreq(self.N[1], 1./self.N[1])[self.complex_local_slice()[1]].astype(self.float), + rfftfreq(self.N[2], 1./self.N[2]).astype(self.float)) def get_local_mesh(self): """Returns the local decomposed physical mesh""" @@ -170,13 +170,15 @@ def get_local_wavenumbermesh(self, scaled=False, broadcast=False, eliminate_high """ kx, ky, kz = self.complex_local_wavenumbers() if eliminate_highest_freq: - ky = fftfreq(self.N[1], 1./self.N[1]) + ky = fftfreq(self.N[1], 1./self.N[1].astype(self.float)) for i, k in enumerate((kx, ky, kz)): if self.N[i] % 2 == 0: k[self.N[i]//2] = 0 ky = ky[self.complex_local_slice()[1]] Ks = np.meshgrid(kx, ky, kz, indexing='ij', sparse=True) + for i in range(3): + Ks[i] = Ks[i].astype(self.float) if scaled: Lp = 2*np.pi/self.L for i in range(3): diff --git a/setup.py b/setup.py index 498817a..7b22671 100644 --- a/setup.py +++ b/setup.py @@ -1,45 +1,57 @@ #!/usr/bin/env python -import os, sys, platform -from distutils.core import setup, Extension -from Cython.Distutils import build_ext -from Cython.Build import cythonize +import os +import re +import subprocess +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext from numpy import get_include -# Version number -major = 1 -minor = 1 -version = 0 - cwd = os.path.abspath(os.path.dirname(__file__)) cdir = os.path.join(cwd, "mpiFFT4py", "cython") -cmdclass = {} +def has_flag(compiler, flagname): + """Return a boolean indicating whether a flag name is supported on + the specified compiler. + """ + devnull = open(os.devnull, "w") + p = subprocess.Popen([compiler.compiler[0], '-E', '-'] + [flagname], + stdin=subprocess.PIPE, stdout=devnull, stderr=devnull, + shell=True) + p.communicate("") + return True if p.returncode == 0 else False + class build_ext_subclass(build_ext): def build_extensions(self): - extra_compile_args = ['-w', '-Ofast'] - cmd = "echo | %s -E - %s &>/dev/null" % ( - self.compiler.compiler[0], " ".join(extra_compile_args)) - try: - subprocess.check_call(cmd, shell=True) - except: - extra_compile_args = ['-w', '-O3'] + extra_compile_args = ['-g0'] + for c in ['-w', '-Ofast', '-ffast-math', '-march=native']: + if has_flag(self.compiler, c): + extra_compile_args.append(c) + for e in self.extensions: - e.extra_compile_args = extra_compile_args + e.extra_compile_args += extra_compile_args + e.include_dirs.extend([get_include()]) build_ext.build_extensions(self) -ext = cythonize(os.path.join(cdir, "*.pyx")) -[e.include_dirs.extend([get_include()]) for e in ext] -cmdclass = {'build_ext': build_ext_subclass} +ext = [Extension('mpiFFT4py.cython.maths', + sources=[os.path.join(cdir, "maths.pyx")])] + +def version(): + srcdir = os.path.join(cwd, 'mpiFFT4py') + with open(os.path.join(srcdir, '__init__.py')) as f: + m = re.search(r"__version__\s*=\s*'(.*)'", f.read()) + return m.groups()[0] + +with open("README.md", "r") as fh: + long_description = fh.read() setup(name = "mpiFFT4py", - version = "%d.%d.%d" % (major, minor, version), + version = version(), description = "mpiFFT4py -- Parallel 3D FFT in Python using MPI for Python", - long_description = "", + long_description = long_description, author = "Mikael Mortensen", author_email = "mikaem@math.uio.no", url = 'https://github.com/spectralDNS/mpiFFT4py', - download_url = "https://github.com/spectralDNS/mpiFFT4py/archive/mpiFFT4py-1.1.0.tar.gz", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -47,6 +59,8 @@ def build_extensions(self): 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Software Development :: Libraries :: Python Modules', @@ -57,5 +71,5 @@ def build_extensions(self): ], package_dir = {"mpiFFT4py": "mpiFFT4py"}, ext_modules = ext, - cmdclass = cmdclass + cmdclass = {'build_ext': build_ext_subclass} )