diff options
Diffstat (limited to 'var/spack/repos/builtin/packages/mfem/package.py')
-rw-r--r-- | var/spack/repos/builtin/packages/mfem/package.py | 423 |
1 files changed, 309 insertions, 114 deletions
diff --git a/var/spack/repos/builtin/packages/mfem/package.py b/var/spack/repos/builtin/packages/mfem/package.py index a7be7fba8b..2f25a7cc21 100644 --- a/var/spack/repos/builtin/packages/mfem/package.py +++ b/var/spack/repos/builtin/packages/mfem/package.py @@ -22,22 +22,31 @@ # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -import re from spack import * +import os +import shutil class Mfem(Package): """Free, lightweight, scalable C++ library for finite element methods.""" + tags = ['FEM', 'finite elements', 'high-order', 'AMR', 'HPC'] + homepage = 'http://www.mfem.org' url = 'https://github.com/mfem/mfem' + maintainers = ['goxberry', 'tzanio', 'markcmiller86', 'acfisher', + 'v-dobrev'] + + # Recommended mfem builds to test when updating this file: see the shell + # script 'test_builds.sh' in the same directory as this file. + # mfem is downloaded from a URL shortener at request of upstream # author Tzanio Kolev <tzanio@llnl.gov>. See here: # https://github.com/mfem/mfem/issues/53 # # The following procedure should be used to verify security when a - # new verison is added: + # new version is added: # # 1. Verify that no checksums on old versions have changed. # @@ -51,6 +60,11 @@ class Mfem(Package): # If this quick verification procedure fails, additional discussion # will be required to verify the new version. + # 'develop' is a special version that is always larger (or newer) than any + # other version. + version('develop', + git='https://github.com/mfem/mfem', branch='master') + version('3.3.2', '01a762a5d0a2bc59ce4e2f59009045a4', url='https://goo.gl/Kd7Jk8', extension='.tar.gz', @@ -71,8 +85,19 @@ class Mfem(Package): '841ea5cf58de6fae4de0f553b0e01ebaab9cd9c67fa821e8a715666ecf18fc57', url='http://goo.gl/xrScXn', extension='.tar.gz') + variant('static', default=True, + description='Build static library') + variant('shared', default=False, + description='Build shared library') variant('mpi', default=True, description='Enable MPI parallelism') + # Can we make the default value for 'metis' to depend on the 'mpi' value? + variant('metis', default=True, + description='Enable METIS support') + # TODO: The 'hypre' variant is the same as 'mpi', we may want to remove it. + # For now, keep the 'hypre' variant while ignoring its setting. This + # is done to preserve compatibility with other packages that refer to + # it, e.g. xSDK. variant('hypre', default=True, description='Required for MPI parallelism') variant('openmp', default=False, @@ -83,10 +108,13 @@ class Mfem(Package): ' May cause minor performance issues.')) variant('superlu-dist', default=False, description='Enable MPI parallel, sparse direct solvers') + # Placeholder for STRUMPACK, support added in mfem v3.3.2: + # variant('strumpack', default=False, + # description='Enable support for STRUMPACK') variant('suite-sparse', default=False, description='Enable serial, sparse direct solvers') variant('petsc', default=False, - description='Enable PETSc solvers, preconditioners, etc..') + description='Enable PETSc solvers, preconditioners, etc.') variant('sundials', default=False, description='Enable Sundials time integrators') variant('mpfr', default=False, @@ -97,43 +125,77 @@ class Mfem(Package): description='Build debug instead of optimized version') variant('netcdf', default=False, description='Enable Cubit/Genesis reader') + variant('conduit', default=False, + description='Enable binary data I/O using Conduit') variant('gzstream', default=True, description='Support zip\'d streams for I/O') + variant('gnutls', default=False, + description='Enable secure sockets using GnuTLS') + variant('libunwind', default=False, + description='Enable backtrace on error support using Libunwind') + variant('timer', default='auto', + values=('auto', 'std', 'posix', 'mac', 'mpi'), + description='Timing functions to use in mfem::StopWatch') variant('examples', default=False, description='Build and install examples') variant('miniapps', default=False, description='Build and install miniapps') - conflicts('+mpi', when='~hypre') - conflicts('+suite-sparse', when='~lapack') - conflicts('+superlu-dist', when='@:3.1') + conflicts('+shared', when='@:3.3.2') + conflicts('~static~shared') + conflicts('~threadsafe', when='+openmp') + conflicts('+netcdf', when='@:3.1') + conflicts('+superlu-dist', when='@:3.1') + conflicts('+gnutls', when='@:3.1') + conflicts('+gzstream', when='@:3.2') + conflicts('+mpfr', when='@:3.2') + conflicts('+petsc', when='@:3.2') + conflicts('+sundials', when='@:3.2') + conflicts('timer=mac', when='@:3.3.0') + conflicts('timer=mpi', when='@:3.3.0') + conflicts('~metis+mpi', when='@:3.3.0') + conflicts('+metis~mpi', when='@:3.3.0') + conflicts('+conduit', when='@:3.3.2') + + conflicts('+superlu-dist', when='~mpi') + conflicts('+petsc', when='~mpi') + conflicts('timer=mpi', when='~mpi') - depends_on('hypre', when='+hypre') + depends_on('mpi', when='+mpi') + depends_on('hypre', when='+mpi') + depends_on('metis', when='+metis') depends_on('blas', when='+lapack') - depends_on('blas', when='+suite-sparse') depends_on('lapack', when='+lapack') - depends_on('lapack', when='+suite-sparse') - - depends_on('mpi', when='+mpi') - depends_on('metis') - depends_on('parmetis', when='+superlu-dist') - depends_on('metis@5:', when='+superlu-dist') - depends_on('metis@5:', when='+suite-sparse ^suite-sparse@4.5:') - depends_on('sundials@2.7:+hypre', when='+sundials') + depends_on('sundials@2.7.0', when='@:3.3.0+sundials~mpi') + depends_on('sundials@2.7.0+mpi+hypre', when='@:3.3.0+sundials+mpi') + depends_on('sundials@2.7.0:', when='@3.3.2:+sundials~mpi') + depends_on('sundials@2.7.0:+mpi+hypre', when='@3.3.2:+sundials+mpi') depends_on('suite-sparse', when='+suite-sparse') - depends_on('superlu-dist', when='@3.2: +superlu-dist') - depends_on('petsc@3.8:', when='+petsc') - + depends_on('superlu-dist', when='+superlu-dist') + # The PETSc tests in MFEM will fail if PETSc is not configured with + # SuiteSparse and MUMPS. On the other hand, if we require the variants + # '+suite-sparse+mumps' of PETSc, the xsdk package concretization fails. + depends_on('petsc@3.8:+mpi+double+hypre', when='+petsc') + # Recommended when building outside of xsdk: + # depends_on('petsc@3.8:+mpi+double+hypre+suite-sparse+mumps', + # when='+petsc') depends_on('mpfr', when='+mpfr') - depends_on('netcdf', when='@3.2: +netcdf') - depends_on('zlib', when='@3.2: +netcdf') - depends_on('hdf5', when='@3.2: +netcdf') - depends_on('libunwind', when='+debug') + depends_on('netcdf', when='+netcdf') + depends_on('libunwind', when='+libunwind') depends_on('zlib', when='+gzstream') + depends_on('gnutls', when='+gnutls') + depends_on('conduit@0.3.1:', when='+conduit') + depends_on('conduit+mpi', when='+conduit+mpi') + + patch('mfem_ppc_build.patch', when='@3.2:3.3.0 arch=ppc64le') + + phases = ['configure', 'build', 'install'] - patch('mfem_ppc_build.patch', when='@3.2:3.3 arch=ppc64le') + def setup_environment(self, spack_env, run_env): + spack_env.unset('MFEM_DIR') + spack_env.unset('MFEM_BUILD_DIR') # # Note: Although MFEM does support CMake configuration, MFEM @@ -142,30 +204,42 @@ class Mfem(Package): # likely to be up to date in supporting *all* of MFEM's # configuration options. So, don't use CMake # - def install(self, spec, prefix): + def configure(self, spec, prefix): def yes_no(varstr): return 'YES' if varstr in self.spec else 'NO' - metis5_str = 'NO' - if '+superlu-dist' in spec or \ - spec.satisfies('+suite-sparse ^suite-sparse@4.5:') or \ - spec['metis'].satisfies('@5:'): - metis5_str = 'YES' + # We need to add rpaths explicitly to allow proper export of link flags + # from within MFEM. - threadsafe_str = 'NO' - if '+openmp' in spec or '+threadsafe' in spec: - threadsafe_str = 'YES' + # Similar to spec[pkg].libs.ld_flags but prepends rpath flags too. + def ld_flags_from_LibraryList(libs_list): + flags = ['-Wl,-rpath,%s' % dir for dir in libs_list.directories] + flags += [libs_list.ld_flags] + return ' '.join(flags) + + def ld_flags_from_dirs(pkg_dirs_list, pkg_libs_list): + flags = ['-Wl,-rpath,%s' % dir for dir in pkg_dirs_list] + flags += ['-L%s' % dir for dir in pkg_dirs_list] + flags += ['-l%s' % lib for lib in pkg_libs_list] + return ' '.join(flags) + + metis5_str = 'NO' + if ('+metis' in spec) and spec['metis'].satisfies('@5:'): + metis5_str = 'YES' options = [ 'PREFIX=%s' % prefix, 'MFEM_USE_MEMALLOC=YES', 'MFEM_DEBUG=%s' % yes_no('+debug'), + # NOTE: env['CXX'] is the spack c++ compiler wrapper. The real + # compiler is defined by env['SPACK_CXX']. 'CXX=%s' % env['CXX'], - 'MFEM_USE_LIBUNWIND=%s' % yes_no('+debug'), + 'MFEM_USE_LIBUNWIND=%s' % yes_no('+libunwind'), 'MFEM_USE_GZSTREAM=%s' % yes_no('+gzstream'), + 'MFEM_USE_METIS=%s' % yes_no('+metis'), 'MFEM_USE_METIS_5=%s' % metis5_str, - 'MFEM_THREAD_SAFE=%s' % threadsafe_str, + 'MFEM_THREAD_SAFE=%s' % yes_no('+threadsafe'), 'MFEM_USE_MPI=%s' % yes_no('+mpi'), 'MFEM_USE_LAPACK=%s' % yes_no('+lapack'), 'MFEM_USE_SUPERLU=%s' % yes_no('+superlu-dist'), @@ -174,115 +248,175 @@ class Mfem(Package): 'MFEM_USE_PETSC=%s' % yes_no('+petsc'), 'MFEM_USE_NETCDF=%s' % yes_no('+netcdf'), 'MFEM_USE_MPFR=%s' % yes_no('+mpfr'), - 'MFEM_USE_OPENMP=%s' % yes_no('+openmp')] + 'MFEM_USE_GNUTLS=%s' % yes_no('+gnutls'), + 'MFEM_USE_OPENMP=%s' % yes_no('+openmp'), + 'MFEM_USE_CONDUIT=%s' % yes_no('+conduit')] + + cxxflags = spec.compiler_flags['cxxflags'] + if cxxflags: + # The cxxflags are set by the spack c++ compiler wrapper. We also + # set CXXFLAGS explicitly, for clarity, and to properly export the + # cxxflags in the variable MFEM_CXXFLAGS in config.mk. + options += ['CXXFLAGS=%s' % ' '.join(cxxflags)] + + if '~static' in spec: + options += ['STATIC=NO'] + if '+shared' in spec: + options += ['SHARED=YES', 'PICFLAG=%s' % self.compiler.pic_flag] if '+mpi' in spec: options += ['MPICXX=%s' % spec['mpi'].mpicxx] - - if '+hypre' in spec: hypre = spec['hypre'] - hypre_flag_list = (hypre.libs + - hypre['lapack'].libs + - hypre['blas'].libs) - options += ['HYPRE_DIR=%s' % hypre.prefix, - 'HYPRE_OPT=-I%s' % hypre.prefix.include, - 'HYPRE_LIB=%s' % hypre_flag_list.ld_flags] + # The hypre package always links with 'blas' and 'lapack'. + all_hypre_libs = hypre.libs + hypre['lapack'].libs + \ + hypre['blas'].libs + options += [ + 'HYPRE_OPT=-I%s' % hypre.prefix.include, + 'HYPRE_LIB=%s' % ld_flags_from_LibraryList(all_hypre_libs)] + + if '+metis' in spec: + options += [ + 'METIS_OPT=-I%s' % spec['metis'].prefix.include, + 'METIS_LIB=%s' % + ld_flags_from_dirs([spec['metis'].prefix.lib], ['metis'])] if '+lapack' in spec: - lapack_lib = (spec['lapack'].libs + spec['blas'].libs).ld_flags # NOQA: ignore=E501 + lapack_blas = spec['lapack'].libs + spec['blas'].libs options += [ - 'LAPACK_OPT=-I%s' % spec['lapack'].prefix.include, - 'LAPACK_LIB=%s' % lapack_lib] + # LAPACK_OPT is not used + 'LAPACK_LIB=%s' % ld_flags_from_LibraryList(lapack_blas)] if '+superlu-dist' in spec: - metis_lib = '-L%s -lparmetis -lmetis' % spec['parmetis'].prefix.lib - options += [ - 'METIS_DIR=%s' % spec['parmetis'].prefix, - 'METIS_OPT=-I%s' % spec['parmetis'].prefix.include, - 'METIS_LIB=%s' % metis_lib] - superlu_lib = '-L%s' % spec['superlu-dist'].prefix.lib - superlu_lib += ' -lsuperlu_dist' options += [ - 'SUPERLU_DIR=%s' % spec['superlu-dist'].prefix, - 'SUPERLU_OPT=-I%s' % spec['superlu-dist'].prefix.include, - 'SUPERLU_LIB=%s' % superlu_lib] - else: - metis_lib = '-L%s -lmetis' % spec['metis'].prefix.lib - options += [ - 'METIS_DIR=%s' % spec['metis'].prefix, - 'METIS_OPT=-I%s' % spec['metis'].prefix.include, - 'METIS_LIB=%s' % metis_lib] + 'SUPERLU_OPT=-I%s -I%s' % + (spec['superlu-dist'].prefix.include, + spec['parmetis'].prefix.include), + 'SUPERLU_LIB=-L%s -L%s -lsuperlu_dist -lparmetis' % + (spec['superlu-dist'].prefix.lib, + spec['parmetis'].prefix.lib)] if '+suite-sparse' in spec: - ssp = spec['suite-sparse'].prefix - ss_lib = '-L%s' % ssp.lib - if '@3.2:' in spec: - ss_lib += ' -lklu -lbtf' - ss_lib += (' -lumfpack -lcholmod -lcolamd' + - ' -lamd -lcamd -lccolamd -lsuitesparseconfig') - no_rt = spec.satisfies('platform=darwin') - if not no_rt: - ss_lib += ' -lrt' - ss_lib += (' ' + metis_lib + ' ' + lapack_lib) + ss_spec = 'suite-sparse:' + self.suitesparse_components options += [ - 'SUITESPARSE_DIR=%s' % ssp, - 'SUITESPARSE_OPT=-I%s' % ssp.include, - 'SUITESPARSE_LIB=%s' % ss_lib] + 'SUITESPARSE_OPT=-I%s' % spec[ss_spec].prefix.include, + 'SUITESPARSE_LIB=%s' % + ld_flags_from_LibraryList(spec[ss_spec].libs)] if '+sundials' in spec: - sundials_libs = ( - '-lsundials_arkode -lsundials_cvode' - ' -lsundials_nvecserial -lsundials_kinsol') - if '+mpi' in spec: - sundials_libs += ( - ' -lsundials_nvecparhyp -lsundials_nvecparallel') + sun_spec = 'sundials:' + self.sundials_components options += [ - 'SUNDIALS_DIR=%s' % spec['sundials'].prefix, - 'SUNDIALS_OPT=-I%s' % spec['sundials'].prefix.include, - 'SUNDIALS_LIB=-L%s %s' % (spec['sundials'].prefix.lib, - sundials_libs)] + 'SUNDIALS_OPT=%s' % spec[sun_spec].headers.cpp_flags, + 'SUNDIALS_LIB=%s' % + ld_flags_from_LibraryList(spec[sun_spec].libs)] if '+petsc' in spec: - f = open('%s/lib/pkgconfig/PETSc.pc' % spec['petsc'].prefix, 'r') - for line in f: - if re.search('^\s*Cflags: ', line): - petsc_opts = re.sub('^\s*Cflags: (.*)', '\\1', line) - elif re.search('^\s*Libs.*: ', line): - petsc_libs = re.sub('^\s*Libs.*: (.*)', '\\1', line) - f.close() + # options += ['PETSC_DIR=%s' % spec['petsc'].prefix] options += [ - 'PETSC_DIR=%s' % spec['petsc'].prefix, - 'PETSC_OPT=%s' % petsc_opts, - 'PETSC_LIB=-L%s -lpetsc %s' % - (spec['petsc'].prefix.lib, petsc_libs)] + 'PETSC_OPT=%s' % spec['petsc'].headers.cpp_flags, + 'PETSC_LIB=%s' % + ld_flags_from_LibraryList(spec['petsc'].libs)] if '+netcdf' in spec: - np = spec['netcdf'].prefix - zp = spec['zlib'].prefix - h5p = spec['hdf5'].prefix - nlib = '-L%s -lnetcdf ' % np.lib - nlib += '-L%s -lhdf5_hl -lhdf5 ' % h5p.lib - nlib += '-L%s -lz' % zp.lib options += [ - 'NETCDF_DIR=%s' % np, - 'HDF5_DIR=%s' % h5p, - 'ZLIB_DIR=%s' % zp, - 'NETCDF_OPT=-I%s' % np.include, - 'NETCDF_LIB=%s' % nlib] + 'NETCDF_OPT=-I%s' % spec['netcdf'].prefix.include, + 'NETCDF_LIB=%s' % + ld_flags_from_dirs([spec['netcdf'].prefix.lib], ['netcdf'])] + + if '+gzstream' in spec: + if "@:3.3.2" in spec: + options += ['ZLIB_DIR=%s' % spec['zlib'].prefix] + else: + options += [ + 'ZLIB_OPT=-I%s' % spec['zlib'].prefix.include, + 'ZLIB_LIB=%s' % + ld_flags_from_LibraryList(spec['zlib'].libs)] if '+mpfr' in spec: - options += ['MPFR_LIB=-L%s -lmpfr' % spec['mpfr'].prefix.lib] + options += [ + 'MPFR_OPT=-I%s' % spec['mpfr'].prefix.include, + 'MPFR_LIB=%s' % + ld_flags_from_dirs([spec['mpfr'].prefix.lib], ['mpfr'])] + + if '+gnutls' in spec: + options += [ + 'GNUTLS_OPT=-I%s' % spec['gnutls'].prefix.include, + 'GNUTLS_LIB=%s' % + ld_flags_from_dirs([spec['gnutls'].prefix.lib], ['gnutls'])] + + if '+libunwind' in spec: + libunwind = spec['libunwind'] + headers = find_headers('libunwind', libunwind.prefix.include) + headers.add_macro('-g') + libs = find_libraries('libunwind', libunwind.prefix.lib, + shared=True, recursive=True) + if not libs: + libs = find_libraries('libunwind', libunwind.prefix.lib, + shared=False, recursive=True) + # When mfem uses libunwind, it also needs 'libdl'. + libs += LibraryList(find_system_libraries('libdl')) + options += [ + 'LIBUNWIND_OPT=%s' % headers.cpp_flags, + 'LIBUNWIND_LIB=%s' % ld_flags_from_LibraryList(libs)] if '+openmp' in spec: - options += ['OPENMP_OPT = %s' % self.compiler.openmp_flag] + options += ['OPENMP_OPT=%s' % self.compiler.openmp_flag] + + timer_ids = {'std': '0', 'posix': '2', 'mac': '4', 'mpi': '6'} + timer = spec.variants['timer'].value + if timer != 'auto': + options += ['MFEM_TIMER_TYPE=%s' % timer_ids[timer]] + + if '+conduit' in spec: + conduit = spec['conduit'] + headers = HeaderList(find(conduit.prefix.include, 'conduit.hpp', + recursive=True)) + conduit_libs = ['libconduit', 'libconduit_relay', + 'libconduit_blueprint'] + libs = find_libraries(conduit_libs, conduit.prefix.lib, + shared=('+shared' in conduit)) + libs += LibraryList(find_system_libraries('libdl')) + if '+hdf5' in conduit: + hdf5 = conduit['hdf5'] + headers += find_headers('hdf5', hdf5.prefix.include) + libs += hdf5.libs + options += [ + 'CONDUIT_OPT=%s' % headers.cpp_flags, + 'CONDUIT_LIB=%s' % ld_flags_from_LibraryList(libs)] - make('config', *options) + make('config', *options, parallel=False) + make('info', parallel=False) + + def build(self, spec, prefix): make('lib') - if self.run_tests: - make('check') + @run_after('build') + def check_or_test(self): + # Running 'make check' or 'make test' may fail if MFEM_MPIEXEC or + # MFEM_MPIEXEC_NP are not set appropriately. + if not self.run_tests: + # check we can build ex1 (~mpi) or ex1p (+mpi). + make('-C', 'examples', 'ex1p' if ('+mpi' in self.spec) else 'ex1', + parallel=False) + # make('check', parallel=False) + else: + make('all') + make('test', parallel=False) - make('install') + def install(self, spec, prefix): + make('install', parallel=False) + + # TODO: The way the examples and miniapps are being installed is not + # perfect. For example, the makefiles do not work. + + install_em = ('+examples' in spec) or ('+miniapps' in spec) + if install_em and ('+shared' in spec): + make('examples/clean', 'miniapps/clean') + # This is a hack to get the examples and miniapps to link with the + # installed shared mfem library: + with working_dir('config'): + os.rename('config.mk', 'config.mk.orig') + shutil.copyfile(str(self.config_mk), 'config.mk') + shutil.copystat('config.mk.orig', 'config.mk') if '+examples' in spec: make('examples') @@ -291,3 +425,64 @@ class Mfem(Package): if '+miniapps' in spec: make('miniapps') install_tree('miniapps', join_path(prefix, 'miniapps')) + + if install_em: + install_tree('data', join_path(prefix, 'data')) + + @property + def suitesparse_components(self): + """Return the SuiteSparse components needed by MFEM.""" + ss_comps = 'umfpack,cholmod,colamd,amd,camd,ccolamd,suitesparseconfig' + if self.spec.satisfies('@3.2:'): + ss_comps = 'klu,btf,' + ss_comps + return ss_comps + + @property + def sundials_components(self): + """Return the SUNDIALS components needed by MFEM.""" + sun_comps = 'arkode,cvode,nvecserial,kinsol' + if '+mpi' in self.spec: + sun_comps += ',nvecparhyp,nvecparallel' + return sun_comps + + @property + def headers(self): + """Export the main mfem header, mfem.hpp. + """ + hdrs = HeaderList(find(self.prefix.include, 'mfem.hpp', + recursive=False)) + return hdrs or None + + @property + def libs(self): + """Export the mfem library file. + """ + libs = find_libraries('libmfem', root=self.prefix.lib, + shared=('+shared' in self.spec), recursive=False) + return libs or None + + @property + def config_mk(self): + """Export the location of the config.mk file. + This property can be accessed using spec['mfem'].package.config_mk + """ + dirs = [self.prefix, self.prefix.share.mfem] + for d in dirs: + f = join_path(d, 'config.mk') + if os.access(f, os.R_OK): + return FileList(f) + return FileList(find(self.prefix, 'config.mk', recursive=True)) + + @property + def test_mk(self): + """Export the location of the test.mk file. + This property can be accessed using spec['mfem'].package.test_mk. + In version 3.3.2 and newer, the location of test.mk is also defined + inside config.mk, variable MFEM_TEST_MK. + """ + dirs = [self.prefix, self.prefix.share.mfem] + for d in dirs: + f = join_path(d, 'test.mk') + if os.access(f, os.R_OK): + return FileList(f) + return FileList(find(self.prefix, 'test.mk', recursive=True)) |