xref: /petsc/config/BuildSystem/config/packages/MFEM.py (revision 4e8208cbcbc709572b8abe32f33c78b69c819375)
1import config.package
2
3class Configure(config.package.Package):
4  def __init__(self, framework):
5    config.package.Package.__init__(self, framework)
6    #disable version checking
7    #self.minversion             = '4.6'
8    #self.version                = '4.6'
9    #self.versionname            = 'MFEM_VERSION_STRING'
10    #self.versioninclude         = 'mfem/config.hpp'
11    self.gitcommit              = '9cd8f53dc6183c1421be3b67d07b02325d0eef4b' # https://github.com/mfem/mfem/pull/5215
12    self.download               = ['git://https://github.com/mfem/mfem.git','https://github.com/mfem/mfem/archive/'+self.gitcommit+'.tar.gz']
13    self.linkedbypetsc          = 0
14    self.downloadonWindows      = 1
15    self.buildLanguages         = ['Cxx']
16    self.minCxxVersion          = 'c++17'
17    self.skippackagewithoptions = 1
18    self.useddirectly           = 0
19    self.builtafterpetsc        = 1
20    self.noMPIUni               = 1
21    self.precisions             = ['single', 'double']
22    return
23
24  def setupHelp(self, help):
25    import nargs
26    config.package.Package.setupHelp(self, help)
27    help.addArgument('MFEM', '-download-mfem-ghv-cxx=<prog>', nargs.Arg(None, None, 'CXX Front-end compiler to compile get_hypre_version'))
28    return
29
30  def setupDependencies(self, framework):
31    config.package.Package.setupDependencies(self, framework)
32    self.hypre        = framework.require('config.packages.hypre',self)
33    self.mpi          = framework.require('config.packages.MPI',self)
34    self.metis        = framework.require('config.packages.METIS',self)
35    self.slepc        = framework.require('config.packages.SLEPc',self)
36    self.ceed         = framework.require('config.packages.libCEED',self)
37    self.cuda         = framework.require('config.packages.CUDA',self)
38    self.hip          = framework.require('config.packages.HIP',self)
39    self.openmp       = framework.require('config.packages.OpenMP',self)
40    self.superlu_dist = framework.require('config.packages.SuperLU_DIST',self)
41    self.netcdf       = framework.require('config.packages.netCDF',self)
42    self.scalar = framework.require('PETSc.options.scalarTypes',self)
43    self.deps   = [self.mpi,self.hypre,self.metis]
44    self.odeps  = [self.slepc,self.ceed,self.cuda,self.openmp,self.superlu_dist,self.netcdf]
45    return
46
47  def writeConfig(self, g, lib_name, lib_data):
48    lib_name_upper = lib_name.upper()
49    if lib_data.found:
50      g.write('MFEM_USE_{0} = YES\n'.format(lib_name_upper))
51      g.write('{0}_DIR = {1}\n'.format(lib_name_upper, lib_data.directory))
52      g.write('{0}_OPT = {1}\n'.format(lib_name_upper, self.headers.toString(lib_data.include)))
53      g.write('{0}_LIB = {1}\n'.format(lib_name_upper, self.libraries.toString(lib_data.lib)))
54      if self.cuda.found:
55        g.write('{0}_LIB := $(subst -Wl,-Xlinker=,$({0}_LIB))\n'.format(lib_name_upper))
56
57  def Install(self):
58    import os
59    import re
60
61    buildDir = os.path.join(self.packageDir,'petsc-build')
62    configDir = os.path.join(buildDir,'config')
63    if not os.path.exists(configDir):
64      os.makedirs(configDir)
65
66    if self.framework.argDB['prefix'] and not 'package-prefix-hash' in self.argDB:
67      PETSC_DIR  = os.path.abspath(os.path.expanduser(self.argDB['prefix']))
68      PETSC_ARCH = ''
69      prefix     = os.path.abspath(os.path.expanduser(self.argDB['prefix']))
70    else:
71      PETSC_DIR  = self.petscdir.dir
72      PETSC_ARCH = self.arch
73      prefix     = os.path.join(self.petscdir.dir,self.arch)
74
75    PETSC_OPT = self.headers.toStringNoDupes([os.path.join(PETSC_DIR,'include'),os.path.join(PETSC_DIR,PETSC_ARCH,'include')])
76
77    self.pushLanguage('Cxx')
78    cxx = self.getCompiler()
79    cxxflags = self.updatePackageCxxFlags(self.getCompilerFlags())
80    self.popLanguage()
81    if 'download-mfem-ghv-cxx' in self.argDB and self.argDB['download-mfem-ghv-cxx']:
82      ghv = self.argDB['download-mfem-ghv-cxx']
83    else:
84      ghv = cxx
85
86    # On CRAY with shared libraries, libmfem.so is linked as
87    # $ cc -shared -o libmfem.so ...a bunch of .o files.... ...libraries.... -dynamic
88    # The -dynamic at the end makes cc think it is creating an executable
89    ldflags = self.setCompilers.LDFLAGS.replace('-dynamic','')
90
91    strip_rpath=''
92    if self.cuda.found:
93      strip_rpath=' | sed "s/-Wl,-rpath,/-Xlinker=-rpath,/g"'
94      if self.openmp.found:
95        ldflags = ldflags.replace(self.openmp.ompflag,'')
96
97    makedepend = ''
98    with open(os.path.join(configDir,'user.mk'),'w') as g:
99      g.write('PREFIX = '+prefix+'\n')
100      g.write('MPICXX = '+cxx+'\n')
101      g.write('GHV_CXX = '+ghv+'\n')
102      if not self.hip.found: #MFEM uses hipcc as compiler for everything
103        g.write('CXXFLAGS = '+cxxflags+'\n')
104      if self.argDB['with-shared-libraries']:
105        g.write('SHARED = YES\n')
106        g.write('STATIC = NO\n')
107      else:
108        g.write('SHARED = NO\n')
109        g.write('STATIC = YES\n')
110      g.write('AR = '+self.setCompilers.AR+'\n')
111      g.write('ARFLAGS = '+self.setCompilers.AR_FLAGS+'\n')
112      g.write('LDFLAGS = '+ldflags+'\n')
113      if self.cuda.found:
114        g.write('LDFLAGS := $(addprefix -Xlinker ,$(LDFLAGS))\n')
115      g.write('MFEM_USE_MPI = YES\n')
116      g.write('MFEM_MPIEXEC = '+self.mpi.getMakeMacro('MPIEXEC')+'\n')
117      g.write('MFEM_USE_METIS_5 = YES\n')
118      g.write('MFEM_USE_METIS = YES\n')
119      if self.scalar.precision == 'single':
120        g.write('MFEM_PRECISION = single\n')
121      g.write('MFEM_USE_PETSC = YES\n')
122      g.write('HYPRE_OPT = '+self.headers.toString(self.hypre.include)+'\n')
123      g.write('HYPRE_LIB = '+self.libraries.toString(self.hypre.lib)+'\n')
124      g.write('METIS_OPT = '+self.headers.toString(self.metis.include)+'\n')
125      g.write('METIS_LIB = '+self.libraries.toString(self.metis.lib)+'\n')
126      if self.cuda.found:
127        g.write('HYPRE_LIB := $(subst -Wl,-Xlinker=,$(HYPRE_LIB))\n')
128        g.write('METIS_LIB := $(subst -Wl,-Xlinker=,$(METIS_LIB))\n')
129      g.write('PETSC_VARS = '+prefix+'/lib/petsc/conf/petscvariables\n')
130      g.write('PETSC_OPT = '+PETSC_OPT+'\n')
131      # MFEM's config/defaults.mk overwrites these
132      g.write('PETSC_DIR = '+PETSC_DIR+'\n')
133      g.write('PETSC_ARCH = '+PETSC_ARCH+'\n')
134      # Adding all externals should not be needed when PETSc is a shared library, but it is no harm.
135      # When the HYPRE library is built statically, we need to resolve blas symbols
136      # It would be nice to have access to the conf variables during postProcess, and access petsclib and other variables, instead of using a shell here
137      # but I do not know how to do so
138      petscext = '$(shell sed -n "s/PETSC_EXTERNAL_LIB_BASIC = *//p" $(PETSC_VARS)'+strip_rpath+')'
139
140      if self.argDB['with-single-library']:
141        petsclib = '-L'+prefix+'/lib -lpetsc'
142      else:
143        petsclib = '-L'+prefix+'/lib -lpetscml -lpetsctao -lpetscts -lpetscsnes -lpetscksp -lpetscdm -lpetscmat -lpetscvec -lpetscsys'
144      if self.argDB['with-shared-libraries']:
145        if self.cuda.found:
146          petscrpt = '-Xlinker=-rpath,'+prefix+'/lib'
147        else:
148          petscrpt = '-Wl,-rpath,'+prefix+'/lib'
149      else:
150        petscrpt = ''
151      g.write('PETSC_LIB = '+petscrpt+' '+petsclib+' '+petscext+'\n')
152      if self.slepc.found:
153        g.write('MFEM_USE_SLEPC = YES\n')
154        g.write('SLEPC_OPT = '+PETSC_OPT+'\n')
155        g.write('SLEPC_DIR = '+PETSC_DIR+'\n')
156        g.write('SLEPC_ARCH = '+PETSC_ARCH+'\n')
157        g.write('SLEPC_VARS = '+prefix+'/lib/slepc/conf/slepc_variables\n')
158        slepclib = '-L'+prefix+'/lib -lslepc'
159        slepcext = ''
160        g.write('SLEPC_LIB = '+petscrpt+' '+slepclib+' '+slepcext+' $(PETSC_LIB)\n')
161        if self.argDB['prefix'] and not 'package-prefix-hash' in self.argDB:
162          makedepend = 'slepc-install'
163        else:
164          makedepend = 'slepc-build'
165      self.writeConfig(g, 'ceed', self.ceed)
166      self.writeConfig(g, 'superlu', self.superlu_dist)
167      self.writeConfig(g, 'netcdf', self.netcdf)
168
169      if self.cuda.found:
170        self.pushLanguage('CUDA')
171        petscNvcc = self.getCompiler()
172        cudaFlags = self.updatePackageCUDAFlags(self.getCompilerFlags())
173        self.popLanguage()
174        cudaFlags = re.sub(r'-std=([^\s]+) ','',cudaFlags)
175        g.write('MFEM_USE_CUDA = YES\n')
176        g.write('CUDA_CXX = '+petscNvcc+'\n')
177        g.write('CXXFLAGS := '+cudaFlags+' $(addprefix -Xcompiler ,$(CXXFLAGS))\n')
178        g.write('CUDA_ARCH = sm_' + self.cuda.cudaArchSingle() + '\n')
179      if self.hip.found:
180        self.pushLanguage('HIP')
181        hipcc = self.getCompiler()
182        hipFlags = self.updatePackageCxxFlags(self.getCompilerFlags())
183        self.popLanguage()
184        hipFlags = re.sub(r'-std=([^\s]+) ','',hipFlags)
185        g.write('MFEM_USE_HIP = YES\n')
186        g.write('HIP_CXX = '+hipcc+'\n')
187        hipFlags = hipFlags.replace('-fvisibility=hidden','')
188        g.write('HIP_FLAGS = '+hipFlags+'\n')
189        g.write('MPI_OPT = '+self.mpi.includepaths+'\n')
190        g.write('MPI_LIB = '+self.mpi.libpaths+' '+self.mpi.mpilibs+'\n')
191      g.close()
192
193    with open(os.path.join(configDir,'petsc.mk'),'w') as f:
194      f.write('''
195MAKEOVERRIDES := $(filter-out CXXFLAGS=%,$(MAKEOVERRIDES))
196unexport CXXFLAGS
197unexport CPPFLAGS
198.PHONY: run-config
199run-config:
200\t$(MAKE) -f {mfile} config MFEM_DIR={mfemdir}
201'''.format(mfile=os.path.join(self.packageDir,'makefile'), mfemdir=self.packageDir))
202
203    self.addPost(buildDir, ['${OMAKE} -f ' + os.path.join(configDir,'petsc.mk') + ' run-config',
204                            '${OMAKE} clean',
205                            self.make.make_jnp,
206                            '${OMAKE} install'])
207    # this checks MFEM using the pre-installed libraries, I think that is wrong and it should use the post-installed prefix location
208    self.addMakeCheck(os.path.join(buildDir, 'examples', 'petsc'), '${OMAKE} -i ex1p-test-par ex9p-test-par')
209    self.logClearRemoveDirectory()
210    self.logPrintBox('MFEM examples are available at ' + os.path.join(self.packageDir,'examples'))
211    self.logResetRemoveDirectory()
212    return self.installDir
213