File: petsc.py

package info (click to toggle)
petsc 3.23.1%2Bdfsg1-1exp1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 515,576 kB
  • sloc: ansic: 751,607; cpp: 51,542; python: 38,598; f90: 17,352; javascript: 3,493; makefile: 3,157; sh: 1,502; xml: 619; objc: 445; java: 13; csh: 1
file content (445 lines) | stat: -rwxr-xr-x 20,015 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
#!/usr/bin/env python3
'''
  This is the first try for a hierarchically configured module. The idea is to
add the configure objects from a previously executed framework into the current
framework. However, this necessitates a reorganization of the activities in the
module.

  We must now have three distinct phases: location, construction, and testing.
This is very similar to the current compiler checks. The construction phase is
optional, and only necessary when the package has not been previously configured.
The phases will necessarily interact, as an installation must be located before
testing, however another should be located if the testing fails.

  We will give each installation a unique key, which is returned by the location
method. This will allow us to identify working installations, as well as those
that failed testing.

  There is a weird role reversal that can happen. If we look for PETSc, but
cannot find it, it is reasonable to ask to have it automatically downloaded.
However, in this case, rather than using the configure objects from the existing
PETSc, we contribute objects to the PETSc which will be built.

'''
from __future__ import generators
import config.base

import re
import os

class InvalidPETScError(RuntimeError):
  pass

class Configure(config.base.Configure):
  def __init__(self, framework):
    config.base.Configure.__init__(self, framework)
    self.headerPrefix = ''
    self.substPrefix  = ''
    self.location     = None
    self.trial        = {}
    self.working      = {}
    return

  def __str__(self):
    if self.found:
      desc = ['PETSc:']	
      desc.append('  Type: '+self.name)
      desc.append('  Version: '+self.version)
      desc.append('  Includes: '+str(self.include))
      desc.append('  Library: '+str(self.lib))
      return '\n'.join(desc)+'\n'
    else:
      return ''

  def setupHelp(self, help):
    import nargs
    help.addArgument('PETSc', '-with-petsc=<bool>',                nargs.ArgBool(None, 1, 'Activate PETSc'))
    # Location options
    help.addArgument('PETSc', '-with-petsc-dir=<root dir>',        nargs.ArgDir(None, None, 'Specify the root directory of the PETSc installation'))
    help.addArgument('PETSc', '-with-petsc-arch=<arch>',           nargs.Arg(None, None, 'Specify PETSC_ARCH'))
    # Construction options
    help.addArgument('PETSc', '-download-petsc=<bool>',          nargs.ArgBool(None, 0, 'Install PETSc'))
    # Testing options
    help.addArgument('PETSc', '-with-petsc-shared=<bool>',         nargs.ArgBool(None, 1, 'Require that the PETSc library be shared'))
    return

  def setupPackageDependencies(self, framework):
    import sys

    petscConf = None
    for (name, (petscDir, petscArch)) in self.getLocations():
      petscPythonDir = os.path.join(petscDir, 'config')
      sys.path.append(petscPythonDir)
      confPath = os.path.join(petscDir, petscArch,'lib','petsc','conf')
      petscConf = framework.loadFramework(confPath)
      if petscConf:
        self.logPrint('Loaded PETSc-AS configuration ('+name+') from '+confPath)
        self.location = (petscDir, petscArch)
        self.trial[self.location] = name
        break
      else:
        self.logPrint('PETSc-AS has no cached configuration in '+confPath)
        sys.path.reverse()
        sys.path.remove(petscPythonDir)
        sys.path.reverse()
    if not petscConf:
      self.downloadPETSc()
    framework.addPackageDependency(petscConf, confPath)
    return

  def setupDependencies(self, framework):
    config.base.Configure.setupDependencies(self, framework)
    self.languages  = framework.require('PETSc.options.languages', self)
    self.compilers  = framework.require('config.compilers', self)
    self.headers    = framework.require('config.headers', self)
    self.libraries  = framework.require('config.libraries', self)
    self.blaslapack = framework.require('config.packages.BlasLapack', self)
    self.mpi        = framework.require('config.packages.MPI', self)
    return

  def getPETScArch(self, petscDir):
    '''Return the allowable PETSc architectures for a given root'''
    if 'with-petsc-arch' in self.framework.argDB:
      yield self.framework.argDB['with-petsc-arch']
    elif 'PETSC_ARCH' in os.environ:
      yield os.environ['PETSC_ARCH']
    else:
      raise InvalidPETScError('Must set PETSC_ARCH or use --with-petsc-arch')
    return

  def getLocations(self):
    '''Return all allowable locations for PETSc'''
    if hasattr(self, '_configured'):
      key =(self.dir, self.arch)
      yield (self.working[key], key)
      raise InvalidPETScError('Configured PETSc is not usable')
    if self.framework.argDB['download-petsc'] == 1:
      yield self.downloadPETSc()
      raise InvalidPETScError('Downloaded PETSc is not usable')
    if 'with-petsc-dir' in self.framework.argDB:
      petscDir = self.framework.argDB['with-petsc-dir']
      for petscArch in self.getPETScArch(petscDir):
        yield ('User specified installation root', (petscDir, petscArch))
      raise InvalidPETScError('No working architecitures in '+str(petscDir))
    elif 'PETSC_DIR' in os.environ:
      petscDir = os.environ['PETSC_DIR']
      for petscArch in self.getPETScArch(petscDir):
        yield ('User specified installation root', (petscDir, petscArch))
      raise InvalidPETScError('No working architecitures in '+str(petscDir))
    else:
      for petscArch in self.getPETScArch(petscDir):
        yield ('Default compiler locations', ('', petscArch))
      petscDirRE = re.compile(r'(PETSC|pets)c(-.*)?')
      trialDirs = []
      for packageDir in self.framework.argDB['with-packages-search-path']:
        if os.path.isdir(packageDir):
          for d in os.listdir(packageDir):
            if petscDirRE.match(d):
              trialDirs.append(('Package directory installation root', os.path.join(packageDir, d)))
      usrLocal = os.path.join('/usr', 'local')
      if os.path.isdir(os.path.join('/usr', 'local')):
        trialDirs.append(('Frequent user install location (/usr/local)', usrLocal))
        for d in os.listdir(usrLocal):
          if petscDirRE.match(d):
            trialDirs.append(('Frequent user install location (/usr/local/'+d+')', os.path.join(usrLocal, d)))
      if 'HOME' in os.environ and os.path.isdir(os.environ['HOME']):
        for d in os.listdir(os.environ['HOME']):
          if petscDirRE.match(d):
            trialDirs.append(('Frequent user install location (~/'+d+')', os.path.join(os.environ['HOME'], d)))
    return

  def downloadPETSc(self):
    if self.framework.argDB['download-petsc'] == 0:
      raise RuntimeError('No functioning PETSc located')
    # Download and build PETSc
    #   Use only the already configured objects from this run
    raise RuntimeError('Not implemented')

  def getDir(self):
    if self.location:
      return self.location[0]
    return None
  dir = property(getDir, doc = 'The PETSc root directory')

  def getArch(self):
    if self.location:
      return self.location[1]
    return None
  arch = property(getArch, doc = 'The PETSc architecture')

  def getFound(self):
    return self.location and self.location in self.working
  found = property(getFound, doc = 'Did we find a valid PETSc installation')

  def getName(self):
    if self.location and self.location in self.working:
      return self.working[self.location][0]
    return None
  name = property(getName, doc = 'The PETSc installation type')

  def getInclude(self, useTrial = 0):
    if self.location and self.location in self.working:
      return self.working[self.location][1]
    elif useTrial and self.location and self.location in self.trial:
      return self.trial[self.location][1]
    return None
  include = property(getInclude, doc = 'The PETSc include directories')

  def getLib(self, useTrial = 0):
    if self.location and self.location in self.working:
      return self.working[self.location][2]
    elif useTrial and self.location and self.location in self.trial:
      return self.trial[self.location][2]
    return None
  lib = property(getLib, doc = 'The PETSc libraries')

  def getVersion(self):
    if self.location and self.location in self.working:
      return self.working[self.location][3]
    return None
  version = property(getVersion, doc = 'The PETSc version')

  def getOtherIncludes(self):
    if not hasattr(self, '_otherIncludes'):
      includes = []
      includes.extend([self.headers.getIncludeArgument(inc) for inc in self.mpi.include])
      return ' '.join(includes)
    return self._otherIncludes
  def setOtherIncludes(self, otherIncludes):
    self._otherIncludes = otherIncludes
  otherIncludes = property(getOtherIncludes, setOtherIncludes, doc = 'Includes needed to compile PETSc')

  def getOtherLibs(self):
    if not hasattr(self, '_otherLibs'):
      libs = self.compilers.flibs[:]
      libs.extend(self.mpi.lib)
      libs.extend(self.blaslapack.lib)
      return libs
    return self._otherLibs
  def setOtherLibs(self, otherLibs):
    self._otherLibs = otherLibs
  otherLibs = property(getOtherLibs, setOtherLibs, doc = 'Libraries needed to link PETSc')

  def checkLib(self, libraries):
    '''Check for PETSc creation functions in libraries, which can be a list of libraries or a single library
       - PetscInitialize from libpetsc
       - VecCreate from libpetscvec
       - MatCreate from libpetscmat
       - DMDestroy from libpetscdm
       - KSPCreate from libpetscksp
       - SNESCreate from libpetscsnes
       - TSCreate from libpetscts
       '''
    if not isinstance(libraries, list): libraries = [libraries]
    oldLibs = self.compilers.LIBS
    self.libraries.pushLanguage(self.languages.clanguage)
    found   = (self.libraries.check(libraries, 'PetscInitializeNoArguments', otherLibs = self.otherLibs, prototype = 'int PetscInitializeNoArguments(void);') and
               self.libraries.check(libraries, 'VecDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Vec *Vec;int VecDestroy(Vec*);', call = 'VecDestroy((Vec*) 0)') and
               self.libraries.check(libraries, 'MatDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Mat *Mat;int MatDestroy(Mat*);', call = 'MatDestroy((Mat*) 0)') and
               self.libraries.check(libraries, 'DMDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_DM *DA;int DMDestroy(DA*);', call = 'DMDestroy((DA*) 0)') and
               self.libraries.check(libraries, 'KSPDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_KSP *KSP;int KSPDestroy(KSP*);', call = 'KSPDestroy((KSP*) 0)') and
               self.libraries.check(libraries, 'SNESDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_SNES *SNES;int SNESDestroy(SNES*);', call = 'SNESDestroy((SNES*) 0)') and
               self.libraries.check(libraries, 'TSDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_TS *TS;int TSDestroy(TS*);', call = 'TSDestroy((TS*) 0)'))
    self.libraries.popLanguage()
    self.compilers.LIBS = oldLibs
    return found

  def checkInclude(self, includeDir):
    '''Check that petscsys.h is present'''
    oldFlags = self.compilers.CPPFLAGS
    self.compilers.CPPFLAGS += ' '.join([self.headers.getIncludeArgument(inc) for inc in includeDir])
    if self.otherIncludes:
      self.compilers.CPPFLAGS += ' '+self.otherIncludes
    self.pushLanguage(self.languages.clanguage)
    found = self.checkPreprocess('#include <petscsys.h>\n')
    self.popLanguage()
    self.compilers.CPPFLAGS = oldFlags
    return found

  def checkPETScLink(self, includes, body, cleanup = 1, codeBegin = None, codeEnd = None, shared = None):
    '''Analogous to checkLink(), but the PETSc includes and libraries are automatically provided'''
    success  = 0
    oldFlags = self.compilers.CPPFLAGS
    self.compilers.CPPFLAGS += ' '.join([self.headers.getIncludeArgument(inc) for inc in self.getInclude(useTrial = 1)])
    if self.otherIncludes:
      self.compilers.CPPFLAGS += ' '+self.otherIncludes
    oldLibs  = self.compilers.LIBS
    self.compilers.LIBS = ' '.join([self.libraries.getLibArgument(lib) for lib in self.getLib(useTrial = 1)+self.otherLibs])+' '+self.compilers.LIBS
    if self.checkLink(includes, body, cleanup, codeBegin, codeEnd, shared):
      success = 1
    self.compilers.CPPFLAGS = oldFlags
    self.compilers.LIBS     = oldLibs
    return success

  def checkWorkingLink(self):
    '''Checking that we can link a PETSc executable'''
    self.pushLanguage(self.languages.clanguage)
    if not self.checkPETScLink('#include <petsctime.h>\n', 'PetscLogDouble time;\n\nPetscCall(PetscTime(&time));\n'):
      self.logPrint('PETSc cannot link, which indicates a problem with the PETSc installation')
      return 0
    self.logPrint('PETSc can link with '+self.languages.clanguage)
    self.popLanguage()

    if hasattr(self.compilers, 'CXX') and self.languages.clanguage == 'C':
      self.pushLanguage('C++')
      self.sourceExtension = '.C'
      if not self.checkPETScLink('#include <petsctime.h>\n', 'PetscLogDouble time;\n\nPetscCall(PetscTime(&time));\n'):
        self.logPrint('PETSc cannot link C++ but can link C, which indicates a problem with the PETSc installation')
        self.popLanguage()
        return 0
      self.popLanguage()
      self.logPrint('PETSc can link with C++')

    if hasattr(self.compilers, 'FC'):
      self.pushLanguage('FC')
      self.sourceExtension = '.F'
      if not self.checkPETScLink('', '          integer ierr\n          real time\n          call PetscTime(time, ierr)\n'):
        self.logPrint('PETSc cannot link Fortran, but can link C, which indicates a problem with the PETSc installation\nRun with -with-fc=0 if you do not wish to use Fortran')
        self.popLanguage()
        return 0
      self.popLanguage()
      self.logPrint('PETSc can link with Fortran')
    return 1

  def checkSharedLibrary(self, libraries):
    '''Check that the libraries for PETSc are shared libraries'''
    if config.setCompilers.Configure.isDarwin(self.log):
      # on Apple if you list the MPI libraries again you will generate multiply defined errors
      # since they are already copied into the PETSc dynamic library.
      self.setOtherLibs([])
    self.pushLanguage(self.languages.clanguage)
    isShared = self.libraries.checkShared('#include <petscsys.h>\n', 'PetscInitialize', 'PetscInitialized', 'PetscFinalize', checkLink = self.checkPETScLink, libraries = libraries, initArgs = '&argc, &argv, 0, 0', boolType = 'PetscBool ', executor = self.mpi.mpiexec)
    self.popLanguage()
    return isShared

  def configureVersion(self):
    '''Determine the PETSc version'''
    majorRE    = re.compile(r'^#define PETSC_VERSION_MAJOR([\s]+)(?P<versionNum>\d+)[\s]*$');
    minorRE    = re.compile(r'^#define PETSC_VERSION_MINOR([\s]+)(?P<versionNum>\d+)[\s]*$');
    subminorRE = re.compile(r'^#define PETSC_VERSION_SUBMINOR([\s]+)(?P<versionNum>\d+)[\s]*$');
    dateRE     = re.compile(r'^#define PETSC_VERSION_DATE([\s]+)"(?P<date>(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d\d?, \d\d\d\d)"[\s]*$');
    input   = open(os.path.join(self.dir, 'include', 'petscversion.h'))
    lines   = []
    majorNum = 'Unknown'
    minorNum = 'Unknown'
    subminorNum = 'Unknown'
    self.date = 'Unknown'
    for line in input.readlines():
      m1 = majorRE.match(line)
      m2 = minorRE.match(line)
      m3 = subminorRE.match(line)
      m5 = dateRE.match(line)
      if m1:
        majorNum = int(m1.group('versionNum'))
      elif m2:
        minorNum = int(m2.group('versionNum'))
      elif m3:
        subminorNum = int(m3.group('versionNum'))

      if m5:
        self.date = time.strftime('%b %d, %Y', time.localtime(time.time()))
        lines.append('#define PETSC_VERSION_DATE'+m5.group(1)+'"'+self.date+'"\n')
      else:
        lines.append(line)
    input.close()
    self.logPrint('Found PETSc version (%s,%s,%s) on %s' % (majorNum, minorNum, subminorNum, self.date))
    return '%d.%d.%d' % (majorNum, minorNum, subminorNum)

  def includeGuesses(self, path = None):
    '''Return all include directories present in path or its ancestors'''
    if not path:
      yield []
    while path:
      dir = os.path.join(path, 'include')
      if os.path.isdir(dir):
        yield [dir, os.path.join(path, self.arch,'include')]
      if path == '/':
        return
      path = os.path.dirname(path)
    return

  def libraryGuesses(self, root = None):
    '''Return standard library name guesses for a given installation root'''
    libs = ['ts', 'snes', 'ksp', 'dm', 'mat', 'vec', '']
    if root:
      d = os.path.join(root, 'lib', self.arch)
      if not os.path.isdir(d):
        self.logPrint('', 3, 'petsc')
        return
      yield [os.path.join(d, 'libpetsc'+lib+'.a') for lib in libs]
    else:
      yield ['libpetsc'+lib+'.a' for lib in libs]
    return

  def configureLibrary(self):
    '''Find a working PETSc'''
    for location, name in self.trial.items():
      self.framework.logPrintDivider()
      self.framework.logPrint('Checking for a functional PETSc in '+name+', location/origin '+str(location))
      lib     = None
      include = None
      found   = 0
      for libraries in self.libraryGuesses(location[0]):
        if self.checkLib(libraries):
          lib = libraries
          for includeDir in self.includeGuesses(location[0]):
            if self.checkInclude(includeDir):
              include = includeDir
              self.trial[location] = (name, include, lib, 'Unknown')
              if self.executeTest(self.checkWorkingLink):
                found = 1
                break
              else:
                self.framework.logPrintDivider(single = 1)
                self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkWorkingLink test')
            else:
              self.framework.logPrintDivider(single = 1)
              self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkInclude test with includeDir: '+str(includeDir))
          if not found:
            self.framework.logPrintDivider(single = 1)
            self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkIncludes test')
            continue
        else:
          self.framework.logPrintDivider(single = 1)
          self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkLib test with libraries: '+str(libraries))
          continue
        if self.framework.argDB['with-petsc-shared']:
          if not self.executeTest(self.checkSharedLibrary, [libraries]):
            self.framework.logPrintDivider(single = 1)
            self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkSharedLibrary test with libraries: '+str(libraries))
            found = 0
        if found:
          break
      if found:
        version = self.executeTest(self.configureVersion)
        self.working[location] = (name, include, lib, version)
        break
    if found:
      self.logPrint('Choose PETSc '+self.version+' in '+self.name)
    else:
      raise RuntimeError('Could not locate any functional PETSc')
    return

  def setOutput(self):
    '''Add defines and substitutions
       - HAVE_PETSC is defined if a working PETSc is found
       - PETSC_INCLUDE and PETSC_LIB are command line arguments for the compile and link'''
    if self.found:
      self.addDefine('HAVE_PETSC', 1)
      self.addSubstitution('PETSC_INCLUDE', ' '.join([self.headers.getIncludeArgument(inc) for inc in self.include]))
      self.addSubstitution('PETSC_LIB', ' '.join(map(self.libraries.getLibArgument, self.lib)))
    return

  def configure(self):
    self.executeTest(self.configureLibrary)
    self.setOutput()
    return

if __name__ == '__main__':
  import config.framework
  import sys
  framework = config.framework.Framework(sys.argv[1:])
  framework.setup()
  framework.addChild(Configure(framework))
  framework.configure()
  framework.dumpSubstitutions()