pymatgen.cli.pmg_config — pymatgen 2020.7.3 documentation

# Source code for pymatgen.cli.pmg_config

    #!/usr/bin/env python
    # coding: utf-8
    # Copyright (c) Pymatgen Development Team.
    # Distributed under the terms of the MIT License.

    """
    Implementation for `pmg config` CLI.
    """

    import os
    import sys
    import glob
    import shutil
    import subprocess
    from monty.serialization import loadfn, dumpfn

    from pymatgen import SETTINGS_FILE
    from urllib.request import urlretrieve

    def setup_potcars(args):
        """
        Setup POTCAR directirt,

        :param args: args from command.
        """
        pspdir, targetdir = [os.path.abspath(d) for d in args.potcar_dirs]
        try:
            os.makedirs(targetdir)
        except OSError:
            r = input("Destination directory exists. Continue (y/n)? ")
            if r != "y":
                print("Exiting ...")
                sys.exit(0)

        print("Generating pymatgen resources directory...")

        name_mappings = {
            "potpaw_PBE": "POT_GGA_PAW_PBE",
            "potpaw_PBE_52": "POT_GGA_PAW_PBE_52",
            "potpaw_PBE_54": "POT_GGA_PAW_PBE_54",
            "potpaw_PBE.52": "POT_GGA_PAW_PBE_52",
            "potpaw_PBE.54": "POT_GGA_PAW_PBE_54",
            "potpaw_LDA": "POT_LDA_PAW",
            "potpaw_LDA.52": "POT_LDA_PAW_52",
            "potpaw_LDA.54": "POT_LDA_PAW_54",
            "potpaw_LDA_52": "POT_LDA_PAW_52",
            "potpaw_LDA_54": "POT_LDA_PAW_54",
            "potUSPP_LDA": "POT_LDA_US",
            "potpaw_GGA": "POT_GGA_PAW_PW91",
            "potUSPP_GGA": "POT_GGA_US_PW91"
        }

        for (parent, subdirs, files) in os.walk(pspdir):
            basename = os.path.basename(parent)
            basename = name_mappings.get(basename, basename)
            for subdir in subdirs:
                filenames = glob.glob(os.path.join(parent, subdir, "POTCAR*"))
                if len(filenames) > 0:
                    try:
                        basedir = os.path.join(targetdir, basename)
                        if not os.path.exists(basedir):
                            os.makedirs(basedir)
                        fname = filenames[0]
                        dest = os.path.join(basedir, os.path.basename(fname))
                        shutil.copy(fname, dest)
                        ext = fname.split(".")[-1]
                        if ext.upper() in ["Z", "GZ"]:
                            subprocess.Popen(["gunzip", dest]).communicate()
                        elif ext.upper() in ["BZ2"]:
                            subprocess.Popen(["bunzip2", dest]).communicate()
                        if subdir == "Osmium":
                            subdir = "Os"
                        dest = os.path.join(basedir, "POTCAR.{}".format(subdir))
                        shutil.move(os.path.join(basedir, "POTCAR"), dest)
                        subprocess.Popen(["gzip", "-f", dest]).communicate()
                    except Exception as ex:
                        print("An error has occured. Message is %s. Trying to "
                              "continue... " % str(ex))

        print("")
        print("PSP resources directory generated. It is recommended that you "
              "run 'pmg config --add PMG_VASP_PSP_DIR %s'" % os.path.abspath(targetdir))
        print("Start a new terminal to ensure that your environment variables "
              "are properly set.")

    def build_enum(fortran_command="gfortran"):
        """
        Build enum.

        :param fortran_command:
        """
        currdir = os.getcwd()
        state = True
        try:
            subprocess.call(["git", "clone", "--recursive",
                             "https://github.com/msg-byu/enumlib.git"])
            os.chdir(os.path.join(currdir, "enumlib", "symlib", "src"))
            os.environ["F90"] = fortran_command
            subprocess.call(["make"])
            enumpath = os.path.join(currdir, "enumlib", "src")
            os.chdir(enumpath)
            subprocess.call(["make"])
            for f in ["enum.x", "makestr.x"]:
                subprocess.call(["make", f])
                shutil.copy(f, os.path.join("..", ".."))
        except Exception as ex:
            print(str(ex))
            state = False
        finally:
            os.chdir(currdir)
            shutil.rmtree("enumlib")
        return state

    def build_bader(fortran_command="gfortran"):
        """
        Build bader package.

        :param fortran_command:
        """
        bader_url = "http://theory.cm.utexas.edu/henkelman/code/bader/download/bader.tar.gz"
        currdir = os.getcwd()
        state = True
        try:
            urlretrieve(bader_url, "bader.tar.gz")
            subprocess.call(["tar", "-zxf", "bader.tar.gz"])
            os.chdir("bader")
            subprocess.call(
                ["cp", "makefile.osx_" + fortran_command, "makefile"])
            subprocess.call(["make"])
            shutil.copy("bader", os.path.join("..", "bader_exe"))
            os.chdir("..")
            shutil.rmtree("bader")
            os.remove("bader.tar.gz")
            shutil.move("bader_exe", "bader")
        except Exception as ex:
            print(str(ex))
            state = False
        finally:
            os.chdir(currdir)
        return state

    def install_software(args):
        """
        Install all optional external software.

        :param args:
        """
        try:
            subprocess.call(["ifort", "--version"])
            print("Found ifort")
            fortran_command = "ifort"
        except Exception:
            try:
                subprocess.call(["gfortran", "--version"])
                print("Found gfortran")
                fortran_command = "gfortran"
            except Exception as ex:
                print(str(ex))
                print("No fortran compiler found.")
                sys.exit(-1)

        enum = None
        bader = None
        if args.install == "enumlib":
            print("Building enumlib")
            enum = build_enum(fortran_command)
            print("")
        elif args.install == "bader":
            print("Building bader")
            bader = build_bader(fortran_command)
            print("")
        if bader or enum:
            print("Please add {} to your PATH or move the executables multinum.x, "
                  "makestr.x and/or bader to a location in your PATH."
                  .format(os.path.abspath(".")))
            print("")

    def add_config_var(args):
        """
        Add configuration args.

        :param args:
        """
        d = {}
        if os.path.exists(SETTINGS_FILE):
            shutil.copy(SETTINGS_FILE, SETTINGS_FILE + ".bak")
            print("Existing %s backed up to %s"
                  % (SETTINGS_FILE, SETTINGS_FILE + ".bak"))
            d = loadfn(SETTINGS_FILE)
        toks = args.var_spec
        if len(toks) % 2 != 0:
            print("Bad variable specification!")
            sys.exit(-1)
        for i in range(int(len(toks) / 2)):
            d[toks[2 * i]] = toks[2 * i + 1]
        dumpfn(d, SETTINGS_FILE, default_flow_style=False)
        print("New %s written!" % (SETTINGS_FILE))

    def configure_pmg(args):
        """
        Handle configure command.

        :param args:
        """
        if args.potcar_dirs:
            setup_potcars(args)
        elif args.install:
            install_software(args)
        elif args.var_spec:
            add_config_var(args)