1import os
2import sys
3import numpy
4import platform
5import sysconfig
6
7try:
8    from setuptools import setup, Extension
9    use_setuptools = True
10    print("setuptools is used.")
11except ImportError:
12    from distutils.core import setup, Extension
13    use_setuptools = False
14    print("distutils is used.")
15
16try:
17    from setuptools_scm import get_version
18except ImportError:
19    git_num = None
20
21if 'setuptools_scm' in sys.modules.keys():
22    try:
23        git_ver = get_version()
24        git_num = int(git_ver.split('.')[3].split('+')[0].replace("dev", ""))
25    except:
26        git_num = None
27
28include_dirs_numpy = [numpy.get_include()]
29extra_link_args = []
30# Workaround Python issue 21121
31config_var = sysconfig.get_config_var("CFLAGS")
32if (config_var is not None and
33    "-Werror=declaration-after-statement" in config_var):
34    os.environ['CFLAGS'] = config_var.replace(
35        "-Werror=declaration-after-statement", "")
36
37extra_compile_args = ['-fopenmp', ]
38include_dirs = ['c', ] + include_dirs_numpy
39define_macros = []
40
41extra_link_args_lapacke = []
42include_dirs_lapacke = []
43
44
45use_mkl = False
46# C macro definitions:
47# - MULTITHREADED_BLAS
48#   This deactivates OpenMP multithread harmonic phonon calculation,
49#   since inside each phonon calculation, zheev is called.
50#   When using multithread BLAS, this macro has to be set and
51#   by this all phonons on q-points should be calculated in series.
52# - MKL_LAPACKE:
53#   This sets definitions and functions needed when using MKL lapacke.
54#   Phono3py complex values are handled based on those provided by Netlib
55#   lapacke. However MKL lapacke doesn't provide some macros and functions
56#   that provided Netlib. This macro defines those used in phono3py among them.
57if os.path.isfile("setup_mkl.py"):
58    # This supposes that MKL multithread BLAS is used.
59    # This is invoked when setup_mkl.py exists on the current directory.
60
61    print("MKL LAPACKE is to be used.")
62    print("Use of icc is assumed (CC='icc').")
63
64    from setup_mkl import mkl_extra_link_args_lapacke, mkl_include_dirs_lapacke
65
66    #### Examples of setup_mkl.py ####
67    # For 2015
68    # intel_root = "/opt/intel/composer_xe_2015.7.235"
69    # mkl_root = "%s/mkl" % intel_root
70    # compiler_root = "%s/compiler" % intel_root
71    #
72    # For 2016
73    # intel_root = "/opt/intel/parallel_studio_xe_2016"
74    # mkl_root = "%s/mkl" % intel_root
75    # compiler_root = "%s" % intel_root
76    #
77    # For both
78    # mkl_extra_link_args_lapacke = ['-L%s/lib/intel64' % mkl_root,
79    #                                '-lmkl_rt']
80    # mkl_extra_link_args_lapacke += ['-L%s/lib/intel64' % compiler_root,
81    #                                 '-lsvml',
82    #                                 '-liomp5',
83    #                                 '-limf',
84    #                                 '-lpthread']
85    # mkl_include_dirs_lapacke = ["%s/include" % mkl_root]
86
87    use_mkl = True
88    extra_link_args_lapacke += mkl_extra_link_args_lapacke
89    include_dirs_lapacke += mkl_include_dirs_lapacke
90
91    if use_setuptools:
92        extra_compile_args += ['-DMKL_LAPACKE',
93                               '-DMULTITHREADED_BLAS']
94    else:
95        define_macros += [('MKL_LAPACKE', None),
96                          ('MULTITHREADED_BLAS', None)]
97elif os.path.isfile("libopenblas.py"):
98    # This supposes that multithread openBLAS is used.
99    # This is invoked when libopenblas.py exists on the current directory.
100
101    #### Example of libopenblas.py ####
102    # extra_link_args_lapacke += ['-lopenblas']
103
104    from libopenblas import extra_link_args_lapacke, include_dirs_lapacke
105    include_dirs_lapacke += []
106    if use_setuptools:
107        extra_compile_args += ['-DMULTITHREADED_BLAS']
108    else:
109        define_macros += [('MULTITHREADED_BLAS', None)]
110elif (platform.system() == 'Darwin' and
111      os.path.isfile('/opt/local/lib/libopenblas.a')):
112    # This supposes lapacke with single-thread openBLAS provided by MacPort is
113    # used.
114    # % sudo port install gcc6
115    # % sudo port select --set gcc mp-gcc
116    # % sudo port install OpenBLAS +gcc6
117    extra_link_args_lapacke += ['/opt/local/lib/libopenblas.a']
118    include_dirs_lapacke += ['/opt/local/include']
119elif ('CONDA_PREFIX' in os.environ and
120      (os.path.isfile(os.path.join(os.environ['CONDA_PREFIX'],
121                                   'lib', 'liblapacke.dylib')) or
122       os.path.isfile(os.path.join(os.environ['CONDA_PREFIX'],
123                                   'lib', 'liblapacke.so')))):
124    # This is for the system prepared with conda openblas.
125    extra_link_args_lapacke += ['-llapacke']
126    include_dirs_lapacke += [
127        os.path.join(os.environ['CONDA_PREFIX'], 'include'), ]
128    if os.path.isfile(os.path.join(os.environ['CONDA_PREFIX'],
129                                   'include', 'mkl.h')):
130        use_mkl = True
131        if use_setuptools:
132            extra_compile_args += ['-DMKL_LAPACKE',
133                                   '-DMULTITHREADED_BLAS']
134        else:
135            define_macros += [('MKL_LAPACKE', None),
136                              ('MULTITHREADED_BLAS', None)]
137    else:
138        if use_setuptools:
139            extra_compile_args += ['-DMULTITHREADED_BLAS']
140        else:
141            define_macros += [('MULTITHREADED_BLAS', None)]
142elif os.path.isfile('/usr/lib/liblapacke.so'):
143    # This supposes that lapacke with single-thread BLAS is installed on
144    # system.
145    extra_link_args_lapacke += ['-llapacke', '-llapack', '-lblas']
146    include_dirs_lapacke += []
147else:
148    # Here is the default lapacke linkage setting.
149    # Please modify according to your system environment.
150    # Without using multithreaded BLAS, DMULTITHREADED_BLAS is better to be
151    # removed to activate OpenMP multithreading harmonic phonon calculation,
152    # but this is not mandatory.
153    #
154    # The below supposes that lapacke with multithread openblas is used.
155    # Even if using single-thread BLAS and deactivating OpenMP
156    # multithreading for harmonic phonon calculation, the total performance
157    # decrease is considered marginal.
158    #
159    # For conda: Try installing with dynamic link library of openblas by
160    # % conda install numpy scipy h5py pyyaml matplotlib openblas libgfortran
161    extra_link_args_lapacke += ['-lopenblas', '-lgfortran']
162    if 'CONDA_PREFIX' in os.environ:
163        include_dirs_lapacke += [
164            os.path.join(os.environ['CONDA_PREFIX'], 'include'), ]
165    if use_setuptools:
166        extra_compile_args += ['-DMULTITHREADED_BLAS']
167    else:
168        define_macros += [('MULTITHREADED_BLAS', None)]
169
170cc = None
171lib_omp = None
172if 'CC' in os.environ:
173    if 'clang' in os.environ['CC']:
174        cc = 'clang'
175        if not use_mkl:
176            lib_omp = '-lomp'
177        # lib_omp = '-liomp5'
178    if 'gcc' in os.environ['CC'] or 'gnu-cc' in os.environ['CC']:
179        cc = 'gcc'
180if cc == 'gcc' or cc is None:
181    lib_omp = '-lgomp'
182
183    if 'CC' in os.environ and 'gcc-' in os.environ['CC']:
184        # For macOS & homebrew gcc:
185        # Using conda's gcc is more recommended though. Suppose using
186        # homebrew gcc whereas conda is used as general environment.
187        # This is to avoid linking conda libgomp that is incompatible
188        # with homebrew gcc.
189        try:
190            v = int(os.environ['CC'].split('-')[1])
191        except ValueError:
192            pass
193        else:
194            ary = [os.sep, "usr", "local", "opt", "gcc@%d" % v, "lib", "gcc",
195                   "%d" % v, "libgomp.a"]
196            libgomp_a = os.path.join(*ary)
197            if os.path.isfile(libgomp_a):
198                lib_omp = libgomp_a
199
200if lib_omp:
201    extra_link_args.append(lib_omp)
202
203## Uncomment below to measure reciprocal_to_normal_squared_openmp performance
204# define_macros += [('MEASURE_R2N', None)]
205
206extra_link_args += extra_link_args_lapacke
207include_dirs += include_dirs_lapacke
208
209print("extra_link_args", extra_link_args)
210sources_phono3py = ['c/_phono3py.c',
211                    'c/collision_matrix.c',
212                    'c/fc3.c',
213                    'c/imag_self_energy_with_g.c',
214                    'c/interaction.c',
215                    'c/isotope.c',
216                    'c/kgrid.c',
217                    'c/kpoint.c',
218                    'c/lapack_wrapper.c',
219                    'c/mathfunc.c',
220                    'c/phono3py.c',
221                    'c/phonoc_utils.c',
222                    'c/pp_collision.c',
223                    'c/real_self_energy.c',
224                    'c/real_to_reciprocal.c',
225                    'c/reciprocal_to_normal.c',
226                    'c/tetrahedron_method.c',
227                    'c/triplet.c',
228                    'c/triplet_iw.c',
229                    'c/triplet_kpoint.c']
230extension_phono3py = Extension(
231    'phono3py._phono3py',
232    include_dirs=include_dirs,
233    extra_compile_args=extra_compile_args,
234    extra_link_args=extra_link_args,
235    define_macros=define_macros,
236    sources=sources_phono3py)
237
238sources_phononmod = ['c/_phononmod.c',
239                     'c/dynmat.c',
240                     'c/lapack_wrapper.c',
241                     'c/phonon.c',
242                     'c/phononmod.c']
243extension_phononmod = Extension(
244    'phono3py._phononmod',
245    include_dirs=include_dirs,
246    extra_compile_args=extra_compile_args,
247    extra_link_args=extra_link_args,
248    sources=sources_phononmod)
249
250sources_lapackepy = ['c/_lapackepy.c',
251                     'c/lapack_wrapper.c']
252extension_lapackepy = Extension(
253    'phono3py._lapackepy',
254    extra_compile_args=extra_compile_args,
255    extra_link_args=extra_link_args,
256    include_dirs=include_dirs,
257    sources=sources_lapackepy)
258
259packages_phono3py = ['phono3py',
260                     'phono3py.cui',
261                     'phono3py.interface',
262                     'phono3py.other',
263                     'phono3py.phonon',
264                     'phono3py.phonon3',
265                     'phono3py.sscha']
266scripts_phono3py = ['scripts/phono3py',
267                    'scripts/phono3py-load',
268                    'scripts/phono3py-kaccum',
269                    'scripts/phono3py-kdeplot',
270                    'scripts/phono3py-coleigplot']
271
272########################
273# _lapackepy extension #
274########################
275
276if __name__ == '__main__':
277    version_nums = [None, None, None]
278    with open("phono3py/version.py") as w:
279        for line in w:
280            if "__version__" in line:
281                for i, num in enumerate(
282                        line.split()[2].strip('\"').split('.')):
283                    version_nums[i] = num
284                break
285
286    # To deploy to pypi by travis-CI
287    if os.path.isfile("__nanoversion__.txt"):
288        nanoversion = 0
289        with open('__nanoversion__.txt') as nv:
290            try:
291                for line in nv:
292                    nanoversion = int(line.strip())
293                    break
294            except ValueError:
295                nanoversion = 0
296        if nanoversion != 0:
297            version_nums.append(nanoversion)
298    elif git_num:
299        version_nums.append(git_num)
300
301    if None in version_nums:
302        print("Failed to get version number in setup.py.")
303        raise
304
305    version = ".".join(["%s" % n for n in version_nums[:3]])
306    if len(version_nums) > 3:
307        version += "-%d" % version_nums[3]
308
309    if use_setuptools:
310        setup(name='phono3py',
311              version=version,
312              description='This is the phono3py module.',
313              author='Atsushi Togo',
314              author_email='atz.togo@gmail.com',
315              url='http://phonopy.github.io/phono3py/',
316              packages=packages_phono3py,
317              install_requires=['numpy', 'scipy', 'PyYAML', 'matplotlib',
318                                'h5py', 'spglib', 'phonopy>=2.9.3,<2.10'],
319              provides=['phono3py'],
320              scripts=scripts_phono3py,
321              ext_modules=[extension_phono3py,
322                           extension_lapackepy,
323                           extension_phononmod])
324    else:
325        setup(name='phono3py',
326              version=version,
327              description='This is the phono3py module.',
328              author='Atsushi Togo',
329              author_email='atz.togo@gmail.com',
330              url='http://phonopy.github.io/phono3py/',
331              packages=packages_phono3py,
332              requires=['numpy', 'scipy', 'PyYAML', 'matplotlib', 'h5py',
333                        'phonopy', 'spglib'],
334              provides=['phono3py'],
335              scripts=scripts_phono3py,
336              ext_modules=[extension_phono3py,
337                           extension_lapackepy,
338                           extension_phononmod])
339