1#!/usr/bin/env python 2 3""" 4PETSc: Portable, Extensible Toolkit for Scientific Computation 5============================================================== 6 7The Portable, Extensible Toolkit for Scientific Computation (PETSc), 8is a suite of data structures and routines for the scalable (parallel) 9solution of scientific applications modeled by partial differential 10equations. It employs the Message Passing Interface (MPI) standard for 11all message-passing communication. 12 13.. note:: 14 15 To install ``PETSc`` and ``petsc4py`` (``mpi4py`` is optional 16 but highly recommended) use:: 17 18 $ python -m pip install numpy mpi4py (or pip install numpy mpi4py) 19 $ python -m pip install petsc petsc4py (or pip install petsc petsc4py) 20 21.. tip:: 22 23 You can also install the in-development versions with:: 24 25 $ python -m pip install Cython numpy mpi4py 26 $ python -m pip install --no-deps https://gitlab.com/petsc/petsc/-/archive/main/petsc-main.tar.gz 27 28 To set the MPI compilers use the environmental variables ``MPICC``, ``MPICXX``, ``MPIF90``. 29 30 Provide any ``PETSc`` ./configure options using the environmental variable ``PETSC_CONFIGURE_OPTIONS``. 31 32 Do not use the ``PETSc`` ``./configure`` options ``--with-cc``, ``--with-cxx``, ``--with-fc``, or ``--with-mpi-dir``. 33 34 If ``mpi4py`` is installed the compilers will obtained from that installation and ``MPICC``, ``MPICXX``, ``MPIF90`` will be ignored. 35 36""" 37 38import sys, os 39from setuptools import setup 40from setuptools.command.install import install as _install 41from distutils.util import get_platform, split_quoted 42from distutils.spawn import find_executable 43from distutils import log 44 45init_py = """\ 46# Author: PETSc Team 47# Contact: petsc-maint@mcs.anl.gov 48 49def get_petsc_dir(): 50 import os 51 return os.path.dirname(__file__) 52 53def get_config(): 54 conf = {} 55 conf['PETSC_DIR'] = get_petsc_dir() 56 return conf 57""" 58 59metadata = { 60 'provides' : ['petsc'], 61 'zip_safe' : False, 62} 63 64CONFIGURE_OPTIONS = [] 65 66def bootstrap(): 67 # Set PETSC_DIR and PETSC_ARCH 68 PETSC_DIR = os.path.abspath(os.getcwd()) 69 PETSC_ARCH = 'arch-python-' + get_platform() 70 os.environ['PETSC_DIR'] = PETSC_DIR 71 os.environ['PETSC_ARCH'] = PETSC_ARCH 72 sys.path.insert(0, os.path.join(PETSC_DIR, 'config')) 73 sys.path.insert(0, os.path.join(PETSC_DIR, 'lib','petsc','conf')) 74 # Generate package __init__.py file 75 from distutils.dir_util import mkpath 76 pkgdir = os.path.join('config', 'pypi') 77 if not os.path.exists(pkgdir): mkpath(pkgdir) 78 pkgfile = os.path.join(pkgdir, '__init__.py') 79 fh = open(pkgfile, 'w') 80 fh.write(init_py) 81 fh.close() 82 # Configure options 83 options = os.environ.get('PETSC_CONFIGURE_OPTIONS', '') 84 CONFIGURE_OPTIONS.extend(split_quoted(options)) 85 for i in CONFIGURE_OPTIONS: 86 if i.startswith('--with-mpi-dir='): 87 raise RuntimeError("Do not use --with-mpi-dir, use the environmental variables MPICC, MPICXX, MPIF90") 88 if i.startswith('--with-cc='): 89 raise RuntimeError("Do not use --with-cc, use the environmental variable MPICC") 90 if i.startswith('--with-cxx=') and i != "--with-cxx=0": 91 raise RuntimeError("Do not use --with-cxx, use the environmental variable MPICXX") 92 if i.startswith('--with-fc=') and i != "--with-fc=0": 93 raise RuntimeError("Do not use --with-fc, use the environmental variable MPIF90") 94 95 if '--with-mpi=0' not in CONFIGURE_OPTIONS: 96 # Simple-minded lookup for MPI and mpi4py 97 mpi4py = mpicc = None 98 try: 99 import mpi4py 100 conf = mpi4py.get_config() 101 mpicc = conf.get('mpicc') 102 except ImportError: # mpi4py is not installed 103 mpi4py = None 104 mpicc = (os.environ.get('MPICC') or 105 find_executable('mpicc')) 106 except AttributeError: # mpi4py is too old 107 pass 108 if not mpi4py and mpicc: 109 metadata['install_requires'] = ['mpi4py>=1.2.2'] 110 111def config(prefix, dry_run=False): 112 log.info('PETSc: configure') 113 options = [ 114 '--prefix=' + prefix, 115 'PETSC_ARCH='+os.environ['PETSC_ARCH'], 116 '--with-shared-libraries=1', 117 '--with-debugging=0', 118 '--with-c2html=0', # not needed 119 ] 120 if '--with-fc=0' in CONFIGURE_OPTIONS: 121 options.append('--with-sowing=0') 122 if '--with-mpi=0' not in CONFIGURE_OPTIONS: 123 try: 124 import mpi4py 125 conf = mpi4py.get_config() 126 mpicc = conf.get('mpicc') 127 mpicxx = conf.get('mpicxx') 128 mpif90 = conf.get('mpif90') 129 except (ImportError, AttributeError): 130 mpicc = os.environ.get('MPICC') or find_executable('mpicc') 131 mpicxx = os.environ.get('MPICXX') or find_executable('mpicxx') 132 mpif90 = os.environ.get('MPIF90') or find_executable('mpif90') 133 if mpicc: 134 options.append('--with-cc='+mpicc) 135 if '--with-cxx=0' not in CONFIGURE_OPTIONS: 136 if mpicxx: 137 options.append('--with-cxx='+mpicxx) 138 else: 139 options.append('--with-cxx=0') 140 if '--with-fc=0' not in CONFIGURE_OPTIONS: 141 if mpif90: 142 options.append('--with-fc='+mpif90) 143 else: 144 options.append('--with-fc=0') 145 options.append('--with-sowing=0') 146 else: 147 options.append('--with-mpi=0') 148 options.extend(CONFIGURE_OPTIONS) 149 # 150 log.info('configure options:') 151 for opt in options: 152 log.info(' '*4 + opt) 153 # Run PETSc configure 154 if dry_run: return 155 use_config_py = False 156 if use_config_py: 157 import configure 158 configure.petsc_configure(options) 159 import logger 160 logger.Logger.defaultLog = None 161 else: 162 python = sys.executable 163 command = [python, './configure'] + options 164 status = os.system(" ".join(command)) 165 if status != 0: raise RuntimeError(status) 166 167def build(dry_run=False): 168 log.info('PETSc: build') 169 # Run PETSc build 170 if dry_run: return 171 use_builder_py = False 172 if use_builder_py: 173 import builder 174 builder.PETScMaker().run() 175 import logger 176 logger.Logger.defaultLog = None 177 else: 178 make = find_executable('make') 179 command = [make, 'all'] 180 status = os.system(" ".join(command)) 181 if status != 0: raise RuntimeError(status) 182 183def install(dry_run=False): 184 log.info('PETSc: install') 185 # Run PETSc installer 186 if dry_run: return 187 use_install_py = False 188 if use_install_py: 189 import install 190 install.Installer().run() 191 import logger 192 logger.Logger.defaultLog = None 193 else: 194 make = find_executable('make') 195 command = [make, 'install'] 196 status = os.system(" ".join(command)) 197 if status != 0: raise RuntimeError(status) 198 199class context(object): 200 def __init__(self): 201 self.sys_argv = sys.argv[:] 202 self.wdir = os.getcwd() 203 def enter(self): 204 del sys.argv[1:] 205 pdir = os.environ['PETSC_DIR'] 206 os.chdir(pdir) 207 return self 208 def exit(self): 209 sys.argv[:] = self.sys_argv 210 os.chdir(self.wdir) 211 212class cmd_install(_install): 213 214 def initialize_options(self): 215 _install.initialize_options(self) 216 self.optimize = 1 217 218 def finalize_options(self): 219 _install.finalize_options(self) 220 self.install_lib = self.install_platlib 221 self.install_libbase = self.install_lib 222 self.old_and_unmanageable = True 223 224 def run(self): 225 root_dir = os.path.abspath(self.install_lib) 226 prefix = os.path.join(root_dir, 'petsc') 227 # 228 ctx = context().enter() 229 try: 230 config(prefix, self.dry_run) 231 build(self.dry_run) 232 install(self.dry_run) 233 finally: 234 ctx.exit() 235 # 236 self.outputs = [] 237 for dirpath, _, filenames in os.walk(prefix): 238 for fn in filenames: 239 self.outputs.append(os.path.join(dirpath, fn)) 240 # 241 _install.run(self) 242 243 def get_outputs(self): 244 outputs = getattr(self, 'outputs', []) 245 outputs += _install.get_outputs(self) 246 return outputs 247 248def version(): 249 import re 250 version_re = { 251 'major' : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"), 252 'minor' : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"), 253 'micro' : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"), 254 'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+([-]*\d+)"), 255 } 256 petscversion_h = os.path.join('include','petscversion.h') 257 data = open(petscversion_h, 'r').read() 258 major = int(version_re['major'].search(data).groups()[0]) 259 minor = int(version_re['minor'].search(data).groups()[0]) 260 micro = int(version_re['micro'].search(data).groups()[0]) 261 release = int(version_re['release'].search(data).groups()[0]) 262 if release: 263 v = "%d.%d.%d" % (major, minor, micro) 264 else: 265 v = "%d.%d.0.dev%d" % (major, minor+1, 0) 266 return v 267 268def tarball(): 269 VERSION = version() 270 if '.dev' in VERSION: return None 271 return ('https://web.cels.anl.gov/projects/petsc/download/release-snapshots/' 272 'petsc-%s.tar.gz#egg=petsc-%s' % (VERSION, VERSION)) 273 274description = __doc__.split('\n')[1:-1]; del description[1:3] 275classifiers = """ 276Development Status :: 5 - Production/Stable 277Intended Audience :: Developers 278Intended Audience :: Science/Research 279License :: OSI Approved :: BSD License 280Operating System :: POSIX 281Programming Language :: C 282Programming Language :: C++ 283Programming Language :: Fortran 284Programming Language :: Python 285Topic :: Scientific/Engineering 286Topic :: Software Development :: Libraries 287""" 288 289if 'bdist_wheel' in sys.argv: 290 sys.stderr.write("petsc: this package cannot be built as a wheel\n") 291 sys.exit(1) 292 293bootstrap() 294setup(name='petsc', 295 version=version(), 296 description=description.pop(0), 297 long_description='\n'.join(description), 298 classifiers= classifiers.split('\n')[1:-1], 299 keywords = ['PETSc', 'MPI'], 300 platforms=['POSIX'], 301 license='BSD', 302 303 url='https://petsc.org/', 304 download_url=tarball(), 305 306 author='PETSc Team', 307 author_email='petsc-maint@mcs.anl.gov', 308 maintainer='Lisandro Dalcin', 309 maintainer_email='dalcinl@gmail.com', 310 311 packages = ['petsc'], 312 package_dir = {'petsc': 'config/pypi'}, 313 cmdclass={'install': cmd_install}, 314 **metadata) 315