xref: /petsc/config/BuildSystem/config/packages/MUMPS.py (revision 6c0b2abcb0ecd3d20fed510a4eb7341974870f21)
1import config.package
2
3class Configure(config.package.Package):
4  def __init__(self, framework):
5    config.package.Package.__init__(self, framework)
6    self.version          = '5.8.2'
7    self.minversion       = '5.2.1'
8    self.versionname      = 'MUMPS_VERSION'
9    self.requiresversion  = 1
10    self.gitcommit        = 'v'+self.version
11    self.download         = ['https://mumps-solver.org/MUMPS_'+self.version+'.tar.gz',
12                             'https://web.cels.anl.gov/projects/petsc/download/externalpackages/MUMPS_'+self.version+'.tar.gz']
13    self.downloaddirnames = ['petsc-pkg-mumps','MUMPS']
14    self.buildLanguages   = ['C','FC']
15    self.downloadonWindows= 1
16    self.hastests         = 1
17    self.hastestsdatafiles= 1
18    self.license          = 'https://mumps-solver.org/index.php?page=dwnld#form'
19    return
20
21  def setupHelp(self, help):
22    import nargs
23    config.package.Package.setupHelp(self, help)
24    help.addArgument('MUMPS', '-download-mumps-openmp', nargs.ArgBool(None, 1, 'Let MUMPS use OpenMP if available'))
25    help.addArgument('MUMPS', '-download-mumps-avoid-mpi-in-place', nargs.ArgBool(None, 0, 'Let MUMPS not use MPI_IN_PLACE. Since MUMPS-5.6.2, it can be used to avoid a bug in MPICH older than 4.0b1'))
26    return
27
28  def setupDependencies(self, framework):
29    config.package.Package.setupDependencies(self, framework)
30    self.flibs            = framework.require('config.packages.flibs',self)
31    self.blasLapack       = framework.require('config.packages.BlasLapack',self)
32    self.mpi              = framework.require('config.packages.MPI',self)
33    self.metis            = framework.require('config.packages.METIS',self)
34    self.parmetis         = framework.require('config.packages.ParMETIS',self)
35    self.ptscotch         = framework.require('config.packages.PTSCOTCH',self)
36    self.scalapack        = framework.require('config.packages.ScaLAPACK',self)
37    self.hwloc            = framework.require('config.packages.hwloc',self)
38    self.openmp           = framework.require('config.packages.OpenMP',self)
39    self.scalartypes      = framework.require('PETSc.options.scalarTypes',self)
40    if not self.argDB['with-mpi']:
41      self.deps           = [self.blasLapack,self.flibs]
42      self.odeps          = [self.metis,self.openmp]
43    else:
44      self.deps           = [self.scalapack,self.mpi,self.blasLapack,self.flibs]
45      self.odeps          = [self.metis,self.parmetis,self.ptscotch,self.hwloc,self.openmp]
46    return
47
48  def configureLibrary(self):
49    for arg in ['with-64-bit-blas-indices','known-64-bit-blas-indices']:
50      if self.argDB.get(arg):
51        raise RuntimeError('MUMPS cannot be used with %s' % arg)
52    liblist_common = [['libmumps_common.a','libpord.a','libpthread.a'],
53                      ['libmumps_common.a','libpord.a'],
54                      ['libmumps_common.a','libpord.a','libmpiseq.a'],
55                      ['libmumps_common.a','libpord.a','libpthread.a','libmpiseq.a']]
56    try: # Check if the MUMPS installation supports all precisions with either complex or real, in other words, it is a full installation.
57      self.functions = ['smumps_c', 'dmumps_c', 'cmumps_c', 'zmumps_c',]
58      self.includes  = ['smumps_c.h', 'dmumps_c.h', 'cmumps_c.h', 'zmumps_c.h']
59      self.liblist   = []
60      for libc in liblist_common:
61        self.liblist.append(['libsmumps.a', 'libdmumps.a', 'libcmumps.a', 'libzmumps.a'] + libc)
62      config.package.Package.configureLibrary(self)
63      self.addDefine('HAVE_MUMPS_MIXED_PRECISION',1)
64    except Exception as e:
65      self.log.write('MUMPS mixed precision with '+str(e)+'. Now only try the precision PetscScalar uses.\n')
66      if self.scalartypes.precision == 'single':
67        if self.scalartypes.scalartype == 'real': l = 's'
68        else: l = 'c'
69      elif self.scalartypes.precision == 'double':
70        if self.scalartypes.scalartype == 'real': l = 'd'
71        else: l = 'z'
72      else: raise RuntimeError('With a partial installation of MUMPS, you can only use single or double precision')
73      self.functions = [l+'mumps_c']
74      self.includes  = [l+'mumps_c.h']
75      self.liblist   = []
76      for libc in liblist_common:
77        self.liblist.append(['lib'+l+'mumps.a'] + libc)
78      config.package.Package.configureLibrary(self)
79
80  def Install(self):
81    import os
82
83    if self.openmp.found and self.argDB['download-mumps-openmp']:
84      #  MUMPS has no make flags for turning on/off OpenMP it just uses it if it can
85      #  we only pass OpenMP compiler flags to MUMPS if self.usesopenmp is yes
86      self.usesopenmp = 'yes'
87      # use OMP_NUM_THREADS to control the number of threads used
88
89    if not self.fortran.FortranDefineCompilerOption:
90      raise RuntimeError('Fortran compiler cannot handle preprocessing directives from command line.')
91    g = open(os.path.join(self.packageDir,'Makefile.inc'),'w')
92    g.write('LPORDDIR   = $(topdir)/PORD/lib/\n')
93    g.write('IPORD      = -I$(topdir)/PORD/include/\n')
94    g.write('LPORD      = -L$(LPORDDIR) -lpord\n')
95    g.write('PLAT       = \n')
96    orderingsc = '-Dpord'
97    orderingsf = self.fortran.FortranDefineCompilerOption+'pord'
98    if self.metis.found:
99      g.write('IMETIS = '+self.headers.toString(self.metis.include)+'\n')
100      g.write('LMETIS = '+self.libraries.toString(self.metis.lib)+'\n')
101      orderingsc += ' -Dmetis'
102      orderingsf += ' '+self.fortran.FortranDefineCompilerOption+'metis'
103    if self.parmetis.found:
104      g.write('IPARMETIS = '+self.headers.toString(self.parmetis.include)+'\n')
105      g.write('LPARMETIS = '+self.libraries.toString(self.parmetis.lib)+'\n')
106      orderingsc += ' -Dparmetis'
107      orderingsf += ' '+self.fortran.FortranDefineCompilerOption+'parmetis'
108    if self.ptscotch.found:
109      g.write('ISCOTCH = '+self.headers.toString(self.ptscotch.include)+'\n')
110      g.write('LSCOTCH = '+self.libraries.toString(self.ptscotch.lib)+'\n')
111      orderingsc += ' -Dscotch -Dptscotch'
112      orderingsf += ' '+self.fortran.FortranDefineCompilerOption+'scotch '+self.fortran.FortranDefineCompilerOption+'ptscotch'
113
114    g.write('ORDERINGSC = '+orderingsc+'\n')
115    g.write('ORDERINGSF = '+orderingsf+'\n')
116    g.write('LORDERINGS  = $(LPARMETIS) $(LMETIS) $(LPORD) $(LSCOTCH)\n')
117    g.write('IORDERINGSC = $(IPARMETIS) $(IMETIS) $(IPORD) $(ISCOTCH)\n')
118    g.write('IORDERINGSF = $(ISCOTCH)\n')
119
120    g.write('RM = '+self.programs.RM+'\n')
121    self.pushLanguage('C')
122    g.write('CC = '+self.getCompiler()+'\n')
123    g.write('OPTC    = '+self.updatePackageCFlags(self.getCompilerFlags())+'\n')
124    g.write('OUTC = -o \n')
125    self.popLanguage()
126    if not self.fortran.fortranIsF90:
127      raise RuntimeError('Installing MUMPS requires a F90 compiler')
128    self.pushLanguage('FC')
129    g.write('FC = '+self.getCompiler()+'\n')
130    g.write('FL = '+self.getCompiler()+'\n')
131    if self.usesopenmp == 'yes':
132      g.write('OPTF    = '+self.updatePackageFFlags(self.getCompilerFlags())+'\n')
133    else:
134      g.write('OPTF    = '+self.updatePackageFFlags(' '.join(self.removeOpenMPFlag(self.getCompilerFlags().split())))+'\n')
135    if self.blasLapack.checkForRoutine('dgemmt'):
136      g.write('OPTF   += -DGEMMT_AVAILABLE\n')
137    g.write('OUTF = -o \n')
138    self.popLanguage()
139
140    # set fortran name mangling
141    # this mangling information is for both BLAS and the Fortran compiler so cannot use the BlasLapack mangling flag
142    if self.compilers.fortranManglingDoubleUnderscore:
143      g.write('CDEFS   = -DAdd__\n')
144    elif self.compilers.fortranMangling == 'underscore':
145      g.write('CDEFS   = -DAdd_\n')
146    elif self.compilers.fortranMangling == 'caps':
147      g.write('CDEFS   = -DUPPPER\n')
148
149    g.write('AR      = '+self.setCompilers.AR+' '+self.setCompilers.AR_FLAGS+' \n')
150    g.write('LIBEXT  = .'+self.setCompilers.AR_LIB_SUFFIX+'\n')
151    g.write('RANLIB  = '+self.setCompilers.RANLIB+'\n')
152    g.write('SCALAP  = '+self.libraries.toString(self.scalapack.lib)+'\n')
153    if not self.mpi.usingMPIUni:
154      g.write('INCPAR  = '+self.headers.toString(self.mpi.include)+'\n')
155      g.write('LIBPAR  = $(SCALAP) '+self.libraries.toString(self.mpi.lib)+'\n')
156    else:
157      g.write('INCPAR  = -I../libseq\n')
158    g.write('INCSEQ  = -I$(topdir)/libseq\n')
159    g.write('LIBSEQ  =  $(LAPACK) -L$(topdir)/libseq -lmpiseq\n')
160    g.write('LIBBLAS = '+self.libraries.toString(self.blasLapack.dlib)+'\n')
161    g.write('OPTL    = '+self.getLinkerFlags()+'\n')
162    g.write('INCS = $(INCPAR)\n')
163    g.write('LIBS = $(LIBPAR)\n')
164    if self.mpi.usingMPIUni:
165      g.write('LIBSEQNEEDED = libseqneeded\n')
166      g.write('LIBS = $(LIBSEQ)\n')
167    else:
168      g.write('LIBSEQNEEDED =\n')
169      if self.openmp.found and self.hwloc.found:
170        g.write('LIBS += '+self.libraries.toString(self.hwloc.lib)+'\n')
171        g.write('OPTF += -DUSE_LIBHWLOC\n')
172        g.write('OPTC += -DUSE_LIBHWLOC\n')
173      # To avoid a bug related to MPI_IN_PLACE and old MPICH releases, see MR 4410
174      self.avoid_mpi_in_place = 0
175      if 'download-mumps-avoid-mpi-in-place' in self.framework.clArgDB: # user-provided value takes precedence
176        self.avoid_mpi_in_place = self.framework.clArgDB['download-mumps-avoid-mpi-in-place']
177      elif hasattr(self.mpi, 'mpich_numversion') and int(self.mpi.mpich_numversion) < 40000101:
178        self.avoid_mpi_in_place = 1
179      if self.avoid_mpi_in_place:
180        g.write('OPTF += -DAVOID_MPI_IN_PLACE\n') # only take effect since mumps-5.6.2
181        self.addDefine('HAVE_MUMPS_AVOID_MPI_IN_PLACE', 1)
182    g.close()
183    if self.installNeeded('Makefile.inc'):
184      try:
185        output1,err1,ret1  = config.package.Package.executeShellCommand('make clean', cwd=self.packageDir, timeout=60, log = self.log)
186      except RuntimeError as e:
187        pass
188      try:
189        self.logPrintBox('Compiling MUMPS; this may take several minutes')
190        output2,err2,ret2 = config.package.Package.executeShellCommand(self.make.make_jnp+' prerequisites', cwd=self.packageDir, timeout=2500, log = self.log)
191        output3,err3,ret3 = config.package.Package.executeShellCommand(self.make.make_jnp+' all', cwd=os.path.join(self.packageDir,'src'), timeout=2500, log = self.log)
192        libDir     = self.libDir
193        includeDir = os.path.join(self.installDir, self.includedir)
194        self.logPrintBox('Installing MUMPS; this may take several minutes')
195        output,err,ret = config.package.Package.executeShellCommandSeq(
196          ['mkdir -p '+libDir+' '+includeDir,
197           'cp -f lib/*.* '+libDir,
198           'cp -f include/*.* '+includeDir
199          ], cwd=self.packageDir, timeout=60, log = self.log)
200        if self.mpi.usingMPIUni:
201          output,err,ret = config.package.Package.executeShellCommand(['cp', '-f', 'libseq/libmpiseq.a', libDir+'/.'], cwd=self.packageDir, timeout=60, log = self.log)
202      except RuntimeError as e:
203        self.logPrint('Error running make on MUMPS: '+str(e))
204        raise RuntimeError('Error running make on MUMPS')
205      self.postInstall(output2+err2+output3+err3,'Makefile.inc')
206    return self.installDir
207