summaryrefslogblamecommitdiff
path: root/var/spack/repos/builtin/packages/mfem/package.py
blob: d9e88be3d092bb94e70442b1d3ce99aa06fd9fac (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                         
                                                                         
 

                                              
                   

             
 



                                                                             

                                                                 
                                    
                                                 
 

                                                    
 

                                 


                                                                            




                                                                      
                           












                                                                        

                                                                              
                                       
 

                                                                               
                                                              

                           

                                                                               
                                                              
 




                                                                               
                                                              
 


                                                            
                    
                                                                                      
                                                              
 
                    
                                                                                      
                                                            
 

                                                            
 
                  
                                                                                      
                                                           
 
                  
                                                                                      
                                                           
 
                  
                                                                                      
                                                           
 
                                   
                                               
                                    
                                               
                                
                                                 

                                                                             
                                               
                                    
                                                    


                                                                     

                                                                     

                                                                           
                                                                                   
 
                                        


                                                                 
                                          
                                                                     

                                                       
                                          
                                                               
                                   
                                                                      
                                      
                                                           
                                  
                                                             

                                                              
                                  
                                                              
                                    
                                                            
                                   
                                                                   
                                    
                                                      
                                     
                                                               
                                 
                                                         
                                    
                                                             
                                       
                                                                            
                                    

                                                                     
                                      
                                                     
                                      
                                                     
 



                                            
                                        
                                      
                                            


                                                                             
                                      
                                    


                                        
                                      
                                        




                                           



                                              

                                           

                                           
                                        
                                    
                                   
                                       
 
                                  
                                                           
                                         
 
                                      
                                      
                                             
 

                                    



                                                                        

                                                                             

                                                                    



                                                           
                                                    
                                                    
                                                     






                                                                            
                                    
                                                 
                                           
                                    
                                        
                                                         

                                                  

                                                                        



                                                                           


                                                                    
 

                                                   
                                              
 



                                                      

                                                       



                                                    
 






                                                             
                                                                 

                                          
                                                   
 




                                                                       
                                              
 


                                           
 






                                                                
                                      
 


                                                         



















                                                                             

                                                                               
 
                                                                          


                                                                              
                                                  





                                                                   


                                                             



                                                                            


                                                            








                                                                        


                                                                 
 


                                                                        



                                               

                                                                          
                                  
                                                           
                                                  
                                                   
                                               
                                                          


                                                            
                                                           


                                                                
                                                 
                                                   

                                                     

                                                     



                                                       
                                                 

                                                     
 
                                                  
 
                    














                                                                             







                                                                        







                                                                              


                                                                       
 

                                                         
                                 




                                                                      
                                                                            




                                                                
                                                               
 
                             
                                                                 
                        
                                        
                                                                          
 
                                   
                                                                 
                        


                                                     



                                                                     
                                                          
 





























                                                                         
                                   
                                                                   
                        

                                                                      
                                                               

                               
                                                             
                        

                                                                     
                                                                

                            

                                  
                            

                                                                            
                 
                                                          
 
                           











                                                                         
 
                             






                                                                         
                        
                                                                    
                                            
 
                           





                                                                  
                                                                  
 
                           











                                                                            
                                      

                                                                         
                                                                       



                                                               
                                                                      

                             

                                                                    




                                                                              
                           
                                                                      




                                                                     
                                                                      



                                                                     








                                                                  










                                                                          

















                                                                            


























                                                                            
                        
                                                     
                                                                    
 



                                                

                   











                                                                              
 












                                                                              
                                                      
                                                              
 

                                                         

                               
                                                                         


                               
                                                                         

                      
                                                                 
 

























                                                                             


                                                         





                                                                        










                                                                              
                                                     
                               



                                                         

                                    










































                                                                               
# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

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'
    git      = 'https://github.com/mfem/mfem.git'

    maintainers = ['v-dobrev', 'tzanio', 'acfisher',
                   'goxberry', 'markcmiller86']

    test_requires_compiler = True

    # 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 version is added:
    #
    # 1. Verify that no checksums on old versions have changed.
    #
    # 2. Verify that the shortened URL for the new version is listed at:
    #    http://mfem.org/download/
    #
    # 3. Use http://getlinkinfo.com or similar to verify that the
    #    underling download link for the latest version comes has the
    #    prefix: http://mfem.github.io/releases
    #
    # 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', branch='master')

    version('4.2.0',
            '4352a225b55948d2e73a5ee88cece0e88bdbe7ba6726a23d68b2736d3221a86d',
            url='https://bit.ly/mfem-4-2', extension='tar.gz',
            preferred=True)

    version('4.1.0',
            '4c83fdcf083f8e2f5b37200a755db843cdb858811e25a8486ad36b2cbec0e11d',
            url='https://bit.ly/mfem-4-1', extension='tar.gz')

    # Tagged development version used by xSDK
    version('4.0.1-xsdk', commit='c55c80d17b82d80de04b849dd526e17044f8c99a')

    version('4.0.0',
            'df5bdac798ea84a263979f6fbf79de9013e1c55562f95f98644c3edcacfbc727',
            url='https://bit.ly/mfem-4-0', extension='tar.gz')

    # Tagged development version used by the laghos package:
    version('3.4.1-laghos-v2.0', tag='laghos-v2.0')

    version('3.4.0',
            sha256='4e73e4fe0482636de3c5dc983cd395839a83cb16f6f509bd88b053e8b3858e05',
            url='https://bit.ly/mfem-3-4', extension='tar.gz')

    version('3.3.2',
            sha256='b70fa3c5080b9ec514fc05f4a04ff74322b99ac4ecd6d99c229f0ed5188fc0ce',
            url='https://goo.gl/Kd7Jk8', extension='tar.gz')

    # Tagged development version used by the laghos package:
    version('3.3.1-laghos-v1.0', tag='laghos-v1.0')

    version('3.3',
            sha256='b17bd452593aada93dc0fee748fcfbbf4f04ce3e7d77fdd0341cc9103bcacd0b',
            url='http://goo.gl/Vrpsns', extension='tar.gz')

    version('3.2',
            sha256='2938c3deed4ec4f7fd5b5f5cfe656845282e86e2dcd477d292390058b7b94340',
            url='http://goo.gl/Y9T75B', extension='tar.gz')

    version('3.1',
            sha256='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')
    variant('openmp', default=False,
            description='Enable OpenMP parallelism')
    variant('cuda', default=False, description='Enable CUDA support')
    variant('cuda_arch', default='sm_60',
            description='CUDA architecture to compile for')
    variant('occa', default=False, description='Enable OCCA backend')
    variant('raja', default=False, description='Enable RAJA backend')
    variant('libceed', default=False, description='Enable libCEED backend')
    variant('umpire', default=False, description='Enable Umpire support')
    variant('amgx', default=False, description='Enable NVIDIA AmgX solver support')

    variant('threadsafe', default=False,
            description=('Enable thread safe features.'
                         ' Required for OpenMP.'
                         ' May cause minor performance issues.'))
    variant('superlu-dist', default=False,
            description='Enable MPI parallel, sparse direct solvers')
    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.')
    variant('sundials', default=False,
            description='Enable Sundials time integrators')
    variant('pumi', default=False,
            description='Enable functionality based on PUMI')
    variant('gslib', default=False,
            description='Enable functionality based on GSLIB')
    variant('mpfr', default=False,
            description='Enable precise, 1D quadrature rules')
    variant('lapack', default=False,
            description='Use external blas/lapack routines')
    variant('debug', default=False,
            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('zlib', 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('+shared', when='@:3.3.2')
    conflicts('~static~shared')
    conflicts('~threadsafe', when='+openmp')

    conflicts('+cuda', when='@:3.99.99')
    conflicts('+netcdf', when='@:3.1')
    conflicts('+superlu-dist', when='@:3.1')
    # STRUMPACK support was added in mfem v3.3.2, however, here we allow only
    # strumpack v3 support which is available starting with mfem v4.0:
    conflicts('+strumpack', when='@:3.99.99')
    conflicts('+gnutls', when='@:3.1')
    conflicts('+zlib', when='@:3.2')
    conflicts('+mpfr', when='@:3.2')
    conflicts('+petsc', when='@:3.2')
    conflicts('+sundials', when='@:3.2')
    conflicts('+pumi', when='@:3.3.2')
    conflicts('+gslib', when='@:4.0.99')
    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('+occa', when='mfem@:3.99.99')
    conflicts('+raja', when='mfem@:3.99.99')
    conflicts('+libceed', when='mfem@:4.0.99')
    conflicts('+umpire', when='mfem@:4.0.99')
    conflicts('+amgx', when='mfem@:4.1.99')
    conflicts('+amgx', when='~cuda')

    conflicts('+superlu-dist', when='~mpi')
    conflicts('+strumpack', when='~mpi')
    conflicts('+petsc', when='~mpi')
    conflicts('+pumi', when='~mpi')
    conflicts('timer=mpi', when='~mpi')

    depends_on('mpi', when='+mpi')
    depends_on('hypre@2.10.0:2.13.99', when='@:3.3.99+mpi')
    depends_on('hypre', when='@3.4:+mpi')

    depends_on('metis', when='+metis')
    depends_on('blas', when='+lapack')
    depends_on('lapack@3.0:', when='+lapack')

    depends_on('cuda', when='+cuda')

    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('sundials@5.0.0:', when='@4.0.1-xsdk:+sundials~mpi')
    depends_on('sundials@5.0.0:+mpi+hypre', when='@4.0.1-xsdk:+sundials+mpi')
    depends_on('sundials@5.4.0:+cuda', when='@4.2.0:+sundials+cuda')
    depends_on('pumi@2.2.3', when='@4.2.0:+pumi')
    depends_on('pumi', when='+pumi~shared')
    depends_on('pumi+shared', when='+pumi+shared')
    depends_on('gslib@1.0.5:+mpi', when='+gslib+mpi')
    depends_on('gslib@1.0.5:~mpi~mpiio', when='+gslib~mpi')
    depends_on('suite-sparse', when='+suite-sparse')
    depends_on('superlu-dist', when='+superlu-dist')
    depends_on('strumpack@3.0.0:', when='+strumpack')
    # 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-c@4.1.3:', when='+netcdf')
    depends_on('unwind', when='+libunwind')
    depends_on('zlib', when='+zlib')
    depends_on('gnutls', when='+gnutls')
    depends_on('conduit@0.3.1:,master:', when='+conduit')
    depends_on('conduit+mpi', when='+conduit+mpi')

    # The MFEM 4.0.0 SuperLU interface fails when using hypre@2.16.0 and
    # superlu-dist@6.1.1. See https://github.com/mfem/mfem/issues/983.
    # This issue was resolved in v4.1.
    conflicts('+superlu-dist',
              when='mfem@:4.0.99 ^hypre@2.16.0: ^superlu-dist@6:')
    # The STRUMPACK v3 interface in MFEM seems to be broken as of MFEM v4.1
    # when using hypre version >= 2.16.0.
    # This issue is resolved in v4.2.
    conflicts('+strumpack', when='mfem@4.0.0:4.1.99 ^hypre@2.16.0:')

    depends_on('occa@1.0.8:', when='@:4.1.99+occa')
    depends_on('occa@1.1.0:', when='@4.2.0:+occa')
    depends_on('occa+cuda', when='+occa+cuda')

    depends_on('raja@0.10.0:', when='@4.0.1:+raja')
    depends_on('raja@0.7.0:0.9.0', when='@4.0.0+raja')
    depends_on('raja+cuda', when='+raja+cuda')

    depends_on('libceed@0.6:', when='@:4.1.99+libceed')
    depends_on('libceed@0.7:', when='@4.2.0:+libceed')
    depends_on('libceed+cuda', when='+libceed+cuda')

    depends_on('umpire@2.0.0:', when='+umpire')
    depends_on('umpire+cuda', when='+umpire+cuda')

    depends_on('amgx', when='+amgx')
    # MPI is enabled by default
    depends_on('amgx~mpi', when='+amgx~mpi')
    for sm_ in CudaPackage.cuda_arch_values:
        depends_on('amgx cuda_arch={0}'.format(sm_),
                   when='+amgx cuda_arch=sm_{0}'.format(sm_))

    patch('mfem_ppc_build.patch', when='@3.2:3.3.0 arch=ppc64le')
    patch('mfem-3.4.patch', when='@3.4.0')
    patch('mfem-3.3-3.4-petsc-3.9.patch',
          when='@3.3.0:3.4.0 +petsc ^petsc@3.9.0:')

    # Patch to fix MFEM makefile syntax error. See
    # https://github.com/mfem/mfem/issues/1042 for the bug report and
    # https://github.com/mfem/mfem/pull/1043 for the bugfix contributed
    # upstream.
    patch('mfem-4.0.0-makefile-syntax-fix.patch', when='@4.0.0')
    phases = ['configure', 'build', 'install']

    def setup_build_environment(self, env):
        env.unset('MFEM_DIR')
        env.unset('MFEM_BUILD_DIR')

    #
    # Note: Although MFEM does support CMake configuration, MFEM
    # development team indicates that vanilla GNU Make is the
    # preferred mode of configuration of MFEM and the mode most
    # likely to be up to date in supporting *all* of MFEM's
    # configuration options. So, don't use CMake
    #
    def configure(self, spec, prefix):

        def yes_no(varstr):
            return 'YES' if varstr in self.spec else 'NO'

        # See also find_system_libraries in lib/spack/llnl/util/filesystem.py
        # where the same list of paths is used.
        sys_lib_paths = [
            '/lib64',
            '/lib',
            '/usr/lib64',
            '/usr/lib',
            '/usr/local/lib64',
            '/usr/local/lib']

        def is_sys_lib_path(dir):
            return dir in sys_lib_paths

        xcompiler = ''
        xlinker = '-Wl,'
        if '+cuda' in spec:
            xcompiler = '-Xcompiler='
            xlinker = '-Xlinker='
        cuda_arch = spec.variants['cuda_arch'].value

        # We need to add rpaths explicitly to allow proper export of link flags
        # from within MFEM.

        # Similar to spec[pkg].libs.ld_flags but prepends rpath flags too.
        # Also does not add system library paths as defined by 'sys_lib_paths'
        # above -- this is done to avoid issues like this:
        # https://github.com/mfem/mfem/issues/1088.
        def ld_flags_from_library_list(libs_list):
            flags = ['%s-rpath,%s' % (xlinker, dir)
                     for dir in libs_list.directories
                     if not is_sys_lib_path(dir)]
            flags += ['-L%s' % dir for dir in libs_list.directories
                      if not is_sys_lib_path(dir)]
            flags += [libs_list.link_flags]
            return ' '.join(flags)

        def ld_flags_from_dirs(pkg_dirs_list, pkg_libs_list):
            flags = ['%s-rpath,%s' % (xlinker, dir) for dir in pkg_dirs_list
                     if not is_sys_lib_path(dir)]
            flags += ['-L%s' % dir for dir in pkg_dirs_list
                      if not is_sys_lib_path(dir)]
            flags += ['-l%s' % lib for lib in pkg_libs_list]
            return ' '.join(flags)

        def find_optional_library(name, prefix):
            for shared in [True, False]:
                for path in ['lib64', 'lib']:
                    lib = find_libraries(name, join_path(prefix, path),
                                         shared=shared, recursive=False)
                    if lib:
                        return lib
            return LibraryList([])

        metis5_str = 'NO'
        if ('+metis' in spec) and spec['metis'].satisfies('@5:'):
            metis5_str = 'YES'

        zlib_var = 'MFEM_USE_ZLIB' if (spec.satisfies('@4.1.0:')) else \
                   'MFEM_USE_GZSTREAM'

        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('+libunwind'),
            '%s=%s' % (zlib_var, yes_no('+zlib')),
            'MFEM_USE_METIS=%s' % yes_no('+metis'),
            'MFEM_USE_METIS_5=%s' % metis5_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'),
            'MFEM_USE_STRUMPACK=%s' % yes_no('+strumpack'),
            'MFEM_USE_SUITESPARSE=%s' % yes_no('+suite-sparse'),
            'MFEM_USE_SUNDIALS=%s' % yes_no('+sundials'),
            'MFEM_USE_PETSC=%s' % yes_no('+petsc'),
            'MFEM_USE_PUMI=%s' % yes_no('+pumi'),
            'MFEM_USE_GSLIB=%s' % yes_no('+gslib'),
            'MFEM_USE_NETCDF=%s' % yes_no('+netcdf'),
            'MFEM_USE_MPFR=%s' % yes_no('+mpfr'),
            'MFEM_USE_GNUTLS=%s' % yes_no('+gnutls'),
            'MFEM_USE_OPENMP=%s' % yes_no('+openmp'),
            'MFEM_USE_CONDUIT=%s' % yes_no('+conduit'),
            'MFEM_USE_CUDA=%s' % yes_no('+cuda'),
            'MFEM_USE_OCCA=%s' % yes_no('+occa'),
            'MFEM_USE_RAJA=%s' % yes_no('+raja'),
            'MFEM_USE_AMGX=%s' % yes_no('+amgx'),
            'MFEM_USE_CEED=%s' % yes_no('+libceed'),
            'MFEM_USE_UMPIRE=%s' % yes_no('+umpire')]

        cxxflags = spec.compiler_flags['cxxflags']

        if cxxflags:
            # Add opt/debug flags if they are not present in global cxx flags
            opt_flag_found = any(f in self.compiler.opt_flags
                                 for f in cxxflags)
            debug_flag_found = any(f in self.compiler.debug_flags
                                   for f in cxxflags)

            if '+debug' in spec:
                if not debug_flag_found:
                    cxxflags.append('-g')
                if not opt_flag_found:
                    cxxflags.append('-O0')
            else:
                if not opt_flag_found:
                    cxxflags.append('-O2')

            cxxflags = [(xcompiler + flag) for flag in cxxflags]
            if '+cuda' in spec:
                cxxflags += [
                    '-x=cu --expt-extended-lambda -arch=%s' % cuda_arch,
                    '-ccbin %s' % (spec['mpi'].mpicxx if '+mpi' in spec
                                   else env['CXX'])]
            if self.spec.satisfies('@4.0.0:'):
                cxxflags.append(self.compiler.cxx11_flag)
            # 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.cxx_pic_flag
            ]

        if '+mpi' in spec:
            options += ['MPICXX=%s' % spec['mpi'].mpicxx]
            hypre = spec['hypre']
            # 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_library_list(all_hypre_libs)]

        if '+metis' in spec:
            options += [
                'METIS_OPT=-I%s' % spec['metis'].prefix.include,
                'METIS_LIB=%s' %
                ld_flags_from_library_list(spec['metis'].libs)]

        if '+lapack' in spec:
            lapack_blas = spec['lapack'].libs + spec['blas'].libs
            options += [
                # LAPACK_OPT is not used
                'LAPACK_LIB=%s' % ld_flags_from_library_list(lapack_blas)]

        if '+superlu-dist' in spec:
            lapack_blas = spec['lapack'].libs + spec['blas'].libs
            options += [
                'SUPERLU_OPT=-I%s -I%s' %
                (spec['superlu-dist'].prefix.include,
                 spec['parmetis'].prefix.include),
                'SUPERLU_LIB=%s %s' %
                (ld_flags_from_dirs([spec['superlu-dist'].prefix.lib,
                                     spec['parmetis'].prefix.lib],
                                    ['superlu_dist', 'parmetis']),
                 ld_flags_from_library_list(lapack_blas))]

        if '+strumpack' in spec:
            strumpack = spec['strumpack']
            sp_opt = ['-I%s' % strumpack.prefix.include]
            sp_lib = [ld_flags_from_library_list(strumpack.libs)]
            # Parts of STRUMPACK use fortran, so we need to link with the
            # fortran library and also the MPI fortran library:
            if '~shared' in strumpack:
                if os.path.basename(env['FC']) == 'gfortran':
                    sp_lib += ['-lgfortran']
                if '^mpich' in strumpack:
                    sp_lib += ['-lmpifort']
                elif '^openmpi' in strumpack:
                    sp_lib += ['-lmpi_mpifh']
            if '+openmp' in strumpack:
                sp_opt += [self.compiler.openmp_flag]
            if '^scalapack' in strumpack:
                scalapack = strumpack['scalapack']
                sp_opt += ['-I%s' % scalapack.prefix.include]
                sp_lib += [ld_flags_from_dirs([scalapack.prefix.lib],
                                              ['scalapack'])]
            if '+butterflypack' in strumpack:
                bp = strumpack['butterflypack']
                sp_opt += ['-I%s' % bp.prefix.include]
                sp_lib += [ld_flags_from_dirs([bp.prefix.lib],
                                              ['dbutterflypack',
                                               'zbutterflypack'])]
            options += [
                'STRUMPACK_OPT=%s' % ' '.join(sp_opt),
                'STRUMPACK_LIB=%s' % ' '.join(sp_lib)]

        if '+suite-sparse' in spec:
            ss_spec = 'suite-sparse:' + self.suitesparse_components
            options += [
                'SUITESPARSE_OPT=-I%s' % spec[ss_spec].prefix.include,
                'SUITESPARSE_LIB=%s' %
                ld_flags_from_library_list(spec[ss_spec].libs)]

        if '+sundials' in spec:
            sun_spec = 'sundials:' + self.sundials_components
            options += [
                'SUNDIALS_OPT=%s' % spec[sun_spec].headers.cpp_flags,
                'SUNDIALS_LIB=%s' %
                ld_flags_from_library_list(spec[sun_spec].libs)]

        if '+petsc' in spec:
            petsc = spec['petsc']
            if '+shared' in petsc:
                options += [
                    'PETSC_OPT=%s' % petsc.headers.cpp_flags,
                    'PETSC_LIB=%s' % ld_flags_from_library_list(petsc.libs)]
            else:
                options += ['PETSC_DIR=%s' % petsc.prefix]

        if '+pumi' in spec:
            pumi_libs = ['pumi', 'crv', 'ma', 'mds', 'apf', 'pcu', 'gmi',
                         'parma', 'lion', 'mth', 'apf_zoltan', 'spr']
            options += [
                'PUMI_OPT=-I%s' % spec['pumi'].prefix.include,
                'PUMI_LIB=%s' %
                ld_flags_from_dirs([spec['pumi'].prefix.lib], pumi_libs)]

        if '+gslib' in spec:
            options += [
                'GSLIB_OPT=-I%s' % spec['gslib'].prefix.include,
                'GSLIB_LIB=%s' %
                ld_flags_from_dirs([spec['gslib'].prefix.lib], ['gs'])]

        if '+netcdf' in spec:
            lib_flags = ld_flags_from_dirs([spec['netcdf-c'].prefix.lib],
                                           ['netcdf'])
            hdf5 = spec['hdf5:hl']
            if hdf5.satisfies('~shared'):
                hdf5_libs = hdf5.libs
                hdf5_libs += LibraryList(find_system_libraries('libdl'))
                lib_flags += " " + ld_flags_from_library_list(hdf5_libs)
            options += [
                'NETCDF_OPT=-I%s' % spec['netcdf-c'].prefix.include,
                'NETCDF_LIB=%s' % lib_flags]

        if '+zlib' 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_library_list(spec['zlib'].libs)]

        if '+mpfr' in spec:
            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['unwind']
            headers = find_headers('libunwind', libunwind.prefix.include)
            headers.add_macro('-g')
            libs = find_optional_library('libunwind', libunwind.prefix)
            # 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_library_list(libs)]

        if '+openmp' in spec:
            options += ['OPENMP_OPT=%s' % self.compiler.openmp_flag]

        if '+cuda' in spec:
            options += [
                'CUDA_CXX=%s' % join_path(spec['cuda'].prefix, 'bin', 'nvcc'),
                'CUDA_ARCH=%s' % cuda_arch]

        if '+occa' in spec:
            options += ['OCCA_OPT=-I%s' % spec['occa'].prefix.include,
                        'OCCA_LIB=%s' %
                        ld_flags_from_dirs([spec['occa'].prefix.lib],
                                           ['occa'])]

        if '+raja' in spec:
            options += ['RAJA_OPT=-I%s' % spec['raja'].prefix.include,
                        'RAJA_LIB=%s' %
                        ld_flags_from_dirs([spec['raja'].prefix.lib],
                                           ['RAJA'])]

        if '+amgx' in spec:
            amgx = spec['amgx']
            if '+shared' in amgx:
                options += ['AMGX_OPT=-I%s' % amgx.prefix.include,
                            'AMGX_LIB=%s' %
                            ld_flags_from_library_list(amgx.libs)]
            else:
                options += ['AMGX_DIR=%s' % amgx.prefix]

        if '+libceed' in spec:
            options += ['CEED_OPT=-I%s' % spec['libceed'].prefix.include,
                        'CEED_LIB=%s' %
                        ld_flags_from_dirs([spec['libceed'].prefix.lib],
                                           ['ceed'])]

        if '+umpire' in spec:
            options += ['UMPIRE_OPT=-I%s' % spec['umpire'].prefix.include,
                        'UMPIRE_LIB=%s' %
                        ld_flags_from_library_list(spec['umpire'].libs)]

        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

            ##################
            # cyrush note:
            ##################
            # spack's HeaderList is applying too much magic, undermining us:
            #
            #  It applies a regex to strip back to the last "include" dir
            #  in the path. In our case we need to pass the following
            #  as part of the CONDUIT_OPT flags:
            #
            #    -I<install_path>/include/conduit
            #
            #  I tried several ways to present this path to the HeaderList,
            #  but the regex always kills the trailing conduit dir
            #  breaking build.
            #
            #  To resolve the issue, we simply join our own string with
            #  the headers results (which are important b/c they handle
            #  hdf5 paths when enabled).
            ##################

            # construct proper include path
            conduit_include_path = conduit.prefix.include.conduit
            # add this path to the found flags
            conduit_opt_flags = "-I{0} {1}".format(conduit_include_path,
                                                   headers.cpp_flags)

            options += [
                'CONDUIT_OPT=%s' % conduit_opt_flags,
                'CONDUIT_LIB=%s' % ld_flags_from_library_list(libs)]

        make('config', *options, parallel=False)
        make('info', parallel=False)

    def build(self, spec, prefix):
        make('lib')

    @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)

    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')
                copy(str(self.config_mk), 'config.mk')
                shutil.copystat('config.mk.orig', 'config.mk')

        prefix_share = join_path(prefix, 'share', 'mfem')

        if '+examples' in spec:
            make('examples')
            install_tree('examples', join_path(prefix_share, 'examples'))

        if '+miniapps' in spec:
            make('miniapps')
            install_tree('miniapps', join_path(prefix_share, 'miniapps'))

        if install_em:
            install_tree('data', join_path(prefix_share, 'data'))

    examples_src_dir = 'examples'
    examples_data_dir = 'data'

    @run_after('install')
    def cache_test_sources(self):
        """Copy the example source files after the package is installed to an
        install test subdirectory for use during `spack test run`."""
        self.cache_extra_test_sources([self.examples_src_dir,
                                       self.examples_data_dir])

    def test(self):
        test_dir = join_path(self.install_test_root, self.examples_src_dir)
        with working_dir(test_dir, create=False):
            # MFEM has many examples to serve as a suitable smoke check. ex10
            # was chosen arbitrarily among the examples that work both with
            # MPI and without it
            test_exe = 'ex10p' if ('+mpi' in self.spec) else 'ex10'
            make('CONFIG_MK={0}/share/mfem/config.mk'.format(self.prefix),
                 test_exe, parallel=False)
            self.run_test('./{0}'.format(test_exe),
                          ['--mesh', '../{0}/beam-quad.mesh'.format(
                              self.examples_data_dir)],
                          [], installed=True, purpose='Smoke test for mfem',
                          skip_missing=False, work_dir='.')
            make('clean')

    # this patch is only needed for mfem 4.1, where a few
    # released files include byte order marks
    @when('@4.1.0')
    def patch(self):
        # Remove the byte order mark since it messes with some compilers
        filter_file(u'\uFEFF', '', 'fem/gslib.hpp')
        filter_file(u'\uFEFF', '', 'fem/gslib.cpp')
        filter_file(u'\uFEFF', '', 'linalg/hiop.hpp')

    @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,cvodes,nvecserial,kinsol'
        if '+mpi' in self.spec:
            if self.spec.satisfies('@4.2:'):
                sun_comps += ',nvecparallel,nvecmpiplusx'
            else:
                sun_comps += ',nvecparhyp,nvecparallel'
        if '+cuda' in self.spec:
            sun_comps += ',nveccuda'
        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))