Exemplo n.º 1
0
 def _get_basis(self, basis):
     """Determines the atomic basis vectors for the system.
     
     Args:
         basis (list or None): A list of the atomic basis vectors or None,
             if None then the default basis is assumed.
     """
     if basis is not None and isinstance(basis, list):
         if len(basis[0]) == 3:
             self.basis = basis
         else:  #pragma: no cover
             msg.err(
                 "The atomic basis must be a list of lists that is nx3 where n is "
                 "the number of atoms in the basis.")
     elif basis is None:
         if self.lattice is not None:
             if self.lattice_name != "hcp":
                 self.basis = [[0, 0, 0]]
             else:
                 self.basis = [[0, 0, 0],
                               [0.5, 0.28867513459, 0.81649658093]]
         else:
             self.basis = [[0, 0, 0]]
     else:  #pragma: no cover
         msg.err(
             "The atomic basis must be a list of lists that is nx3 where n is "
             "the number of atoms in the basis or left blank.")
Exemplo n.º 2
0
def run(args):
    """Runs the matdb setup and cleanup to produce database files.
    """

    import numpy as np
    import json
    from matdb import msg
    from os import path
    from matdb.fitting.mtp import create_to_relax

    print("matdb  Copyright (C) 2019  HALL LABS")
    print("This program comes with ABSOLUTELY NO WARRANTY.")
    print(
        "This is free software, and you are welcome to redistribute it under "
        "certain conditions.")
    if path.isfile('to_relax.json'):
        with open('to_relax.json', "r") as f:
            settings = json.load(f)
            create_to_relax(settings)

    else:
        msg.err("Could not find 'to_relax.json' file needed for computation.")
        return

    if "status" in args:
        cdb.trainers.status()
Exemplo n.º 3
0
    def H(self):
        """Returns the Hessian matrix extracted from the frozen phonon calculations.
        """
        if self._H is not None:
            return self._H

        #Otherwise, we need to calculate it from scratch. This depends on
        #whether we are using DFPT or frozen phonons.
        if not self.dfpt:
            self.calc_forcesets()
            dim = ' '.join(map(str, self.supercell))
            xargs = ["phonopy", '--dim="{}"'.format(dim), "--writefc"]
            execute(xargs, self.phonodir, venv=True)

            if not path.isfile(path.join(self.phonodir, "FORCE_CONSTANTS")):
                msg.err("Cannot convert FORCE_SETS to FORCE_CONSTANTS")
                msg.err(''.join(xargs["output"]))
        else:
            self.calc_fc()

        with chdir(self.phonodir):
            result = file_IO.parse_FORCE_CONSTANTS()

        self._H = unroll_fc(result)
        self.save_pkl(self._H, self.H_file)
        return self._H
Exemplo n.º 4
0
    def _create_train_cfg(self, atm, target):
        """Creates a 'train.cfg' file for the calculation stored at the target
        directory.
        Args:
            atm (matdb.atoms.Atoms): an atoms object to write to the cfg file.
            target (str): the path to the desierd "train.cfg" file.            
        """
        temp_cfg = path.join(self.root, "temp.cfg")
        type_map = {}
        symbols = list(np.unique(atm.get_chemical_symbols()))
        for i, s in enumerate(np.unique(self.species)):
            if s in symbols:
                type_map[symbols.index(s)] = i
        atoms_to_cfg(atm, temp_cfg, type_map=type_map)

        # If a configuration doesn't generate a temp_cfg file, issue an error message and return.
        # This will make it continue processing the rest configurations.
        if not path.isfile(temp_cfg):  #pragma: no cover
            msg.err("Failed to create cfg file for atoms object stored "
                    "at: {0}".format(atm.calc.folder))
            return

        if path.isfile(target):
            cat([temp_cfg, target], path.join(self.root, "temp2.cfg"))
            rename(path.join(self.root, "temp2.cfg"), target)
        else:
            rename(temp_cfg, target)

        if path.isfile(temp_cfg):
            remove(temp_cfg)
        if path.isfile(path.join(self.root, "temp2.cfg")):  #pragma: no cover
            # This line should only get run if something goes terribly
            # wrong up above. I'm keeping it here purely as a fail
            # safe.
            remove(path.join(self.root, "temp2.cfg"))
Exemplo n.º 5
0
    def calc_DOS(self, recalc=False):
        """Calculates the *total* density of states.

        Args:
            recalc (bool): when True, recalculate the DOS, even if the
              file already exists.
        """
        dosfile = path.join(self.phonodir, "mesh.yaml")
        if not recalc and path.isfile(dosfile):
            return

        #Make sure we have calculated the force sets already.
        self.calc_forcesets(recalc)
        settings = {
            "ATOM_NAME": ' '.join(self.database.parent.species),
            "DIM": ' '.join(map(str, self.supercell)),
            "MP": ' '.join(map(str, self.dosmesh))
        }
        with open(path.join(self.phonodir, "dos.conf"), 'w') as f:
            for k, v in settings.items():
                f.write("{} = {}\n".format(k, v))

        sargs = ["phonopy", "-p", "dos.conf", "-s"]
        xres = execute(sargs, self.phonodir, venv=True)
        #Make sure that phonopy actually produced files; otherwise show the output
        #(phonopy doesn't write to stderr, only stdout).
        if not path.isfile(dosfile):  #pragma: no cover
            msg.std(''.join(xres["error"]))
            msg.err("could not calculate the DOS; see errors.")
Exemplo n.º 6
0
    def _get_perms(self, size):
        """Finds the allowed permutations of the atomic species that need to
        be permuted over.

        Args:
            size (str): Any of "unary", "binary", or "ternary".
        """

        from itertools import permutations
        res = None
        if size == "unary":
            r = 1
            res = self.permutations[
                "unary"] if "unary" in self.permutations.keys() else None
        elif size == "binary":
            r = 2
            res = self.permutations[
                "binary"] if "binary" in self.permutations.keys() else None
        elif size == "ternary":
            r = 3
            res = self.permutations[
                "ternary"] if "ternary" in self.permutations.keys() else None
        else:  # pragma: no cover
            msg.err("{} is not a valid prototype size.".format(size))
            return None

        possible_perms = [list(i) for i in permutations(self.species, r=r)]
        perms_copy = deepcopy(possible_perms)
        if res is not None:
            for i in perms_copy:
                if i not in res:
                    possible_perms.remove(i)

        return possible_perms
Exemplo n.º 7
0
def run(args):
    """Runs the matdb setup and cleanup to produce database files.
    """
    print("matdb  Copyright (C) 2019  HALL LABS")
    print("This program comes with ABSOLUTELY NO WARRANTY.")
    print(
        "This is free software, and you are welcome to redistribute it under "
        "certain conditions.")
    if args is None:
        return

    #No matter what other options the user has chosen, we will have to create a
    #database controller for the specification they have given us.
    from matdb.database import Controller
    cdb = Controller(args["dbspec"])

    if args["t"]:
        matches = []
        for pattern in args["p"]:
            for entry in cdb.trainers.find(pattern):
                matches.append(entry)
        if len(matches) != 1:
            msg.err("Can only move a single trainer at a time."
                    "Found {} trainers.".format(len(matches)))
            exit(-1)

        _move_trainer(matches[0], **args)
Exemplo n.º 8
0
def parse_path(root, seeds, ran_seed=None):
    """Finds the full path to the seed files for this system.

    Args:
        root (str): the root directory for the database.
        seeds (str or list): the seeds for the database that need to be parsed and have the root folder found.
        ran_seed (optional): the seed for the random generator if used.

    Returns:
        list: a list of the seed files for the database.
    """
    # from matdb.utility import special_values
    # from itertools import product

    seed_files = []
    for seed in seeds:
        # if there is a '/' in the seed then this is a path to the
        # seed file configuration, otherwise we assume the user put
        # the seed file in the `seed` directory in root.
        if len(seed.split("/")) > 1:
            this_seeds = []
            seed_path = root
            for segment in seed.split("/"):
                if "*" in segment:
                    if len(this_seeds) >= 1:
                        this_level = []
                        for t_path in segment:
                            this_level.extend(
                                glob(path.join(seed_path, t_path, segment)))
                    else:  #pragma: no cover. This could never happens, len(this_seeds) will be at least 1 if goes into this path
                        this_level = glob(path.join(seed_path, segment))
                else:
                    this_level = [segment]
                if len(this_seeds) >= 1:
                    this_seeds.extend([
                        path.join(*i) for i in product(this_seeds, this_level)
                    ])
                else:
                    this_seeds.extend(this_level)

        else:
            seed_path = path.join(root, "seed")
            to_parse = seed
            if "*" in to_parse:
                this_seeds = glob(path.join(seed_path, to_parse))
            else:
                this_seeds = [to_parse]

        for ts in this_seeds:
            t_seed = path.join(seed_path, ts)
            if path.isfile(t_seed):
                seed_files.append(t_seed)
            else:  #pragma: no cover
                msg.err("The seed file {} could not be found.".format(t_seed))

        return seed_files
Exemplo n.º 9
0
    def _setup_configs(self, rerun):
        """Displaces the seed configuration preparatory to calculating the force
        sets for phonon spectra.

        .. note:: This method *appears* to be VASP-specific. However, the
          configurations that are generated by `phonopy` as VASP `POSCAR` files
          are turned into :class:`~matdb.atoms.Atoms` objects before they are
          passed to the super class that sets up the actual calculations. So, it
          is quite general.

        Args:
            rerun (int): when > 1, recreate the folders even if they
              already exist. If > 0, recreate the jobfile.
        """
        #We also don't want to setup again if we have the results already.
        if self.ready() and rerun == 0:
            return

        if not self.is_setup() or rerun > 1:
            from ase.io import write
            write(path.join(self.phonodir, "POSCAR"), self.atoms, "vasp")
            scell = ' '.join(map(str, self.supercell))
            sargs = ["phonopy", "-d", '--dim="{}"'.format(scell)]
            pres = execute(sargs, self.phonodir, venv=True)

            #Make sure that phonopy produced the supercell. If it didn't, it
            #should have printed an error to *stdout* because it doesn't know
            #about stderr...
            if not path.isfile(path.join(self.phonodir, "SPOSCAR")):
                msg.err('\n'.join(pres["output"]))

            from matdb.atoms import Atoms
            if not self.dfpt:
                #Frozen phonons, create a config execution folder for each of
                #the displacements.
                from glob import glob
                with chdir(self.phonodir):
                    for dposcar in glob("POSCAR-*"):
                        dind = int(dposcar.split('-')[1])
                        datoms = Atoms(dposcar, format="vasp")
                        self.create(datoms)
            else:
                #Pull the perfect supercell and set it up for executing with
                #DFPT parameters.
                with chdir(self.phonodir):
                    datoms = Atoms("SPOSCAR", format="vasp")
                    self.create(datoms)

        # Last of all, create the job file to execute the job array.
        self.jobfile(rerun)
Exemplo n.º 10
0
def get_calc_class(name):
    """Gets the class definition objects for the calculator that has the
    specified name.
    """
    globs = globals()
    try:
        for k, v in globs.items():
            if k.lower() == name.lower() and k != name.lower():
                target = v
                break
    except KeyError:  # pragma: no cover
        msg.err("Cannot import calculator {}. ".format(name) +
                "Does not exist at package level.")

    return target
Exemplo n.º 11
0
    def read(self, target, **kwargs):
        """Reads an atoms object in from file.
        
        Args:
            target (str): The path to the target file.
            kwargs (dict): A dictionary of arguments to pass to the ase 
              read function.
        """
        frmt = target.split('.')[-1]
        if frmt == "h5" or frmt == "hdf5":
            from matdb.io import load_dict_from_h5
            with h5py.File(target, "r") as hf:
                data = load_dict_from_h5(hf)
            # If the data was read in from an hdf5 file written by the
            # AtomsList object then each atom will have a tag with it. We check
            # for this by looking for the word 'atom' inside the first key, if
            # it's present we assume that all the contents of the file are an
            # atoms list. If it's not then we assume this is a single atoms
            # object.

            #NB! It is possible that the atoms list object could be an *empty*
            #AtomsList that was written to disk. In that case, just use an empty
            #list.
            if len(data) == 0:
                atoms = []
            elif "atom" in list(data.keys())[0]:
                if isinstance(list(data.values())[0], dict):
                    atoms = [Atoms(**d) for d in data.values()]
                elif isinstance(list(data.values())[0], string_types):
                    atoms = [Atoms(d) for d in data.values()]
                else:  #pragma: no cover
                    msg.err(
                        "The data format {} isn't supported for reading AtomLists "
                        "from hdf5 files.".format(type((data.values())[0])))
            else:
                atoms = [Atoms(target, **kwargs)]
            if len(self) > 0:
                self.extend(atoms)
            else:
                self.__init__(atoms)
        else:
            atoms = [Atoms(d) for d in io.read(target, index=':', **kwargs)]
            if len(self) > 0:
                self.extend(atoms)
            else:
                self.__init__(atoms)
Exemplo n.º 12
0
    def calc_fc(self, recalc=False):
        """Extracts the force constants from a DFPT Hessian matrix.
        """
        fcfile = path.join(self.phonodir, "FORCE_CONSTANTS")
        if not recalc and path.isfile(fcfile):
            return

        from matdb.calculators import get_calculator_module
        mod = get_calculator_module(self.calcargs)
        call = getattr(mod, "extract_force_constants")
        xres = call(self.configs, self.phonodir)

        #Make sure that phonopy actually produced files; otherwise show the
        #output (phonopy doesn't write to stderr, only stdout).
        if not path.isfile(fcfile):  #pragma: no cover
            msg.std(''.join(xres["error"]))
            msg.err("could not calculate the force constants from DFPT.")
Exemplo n.º 13
0
def modulate_atoms(db):
    """Generates modulated configurations using the dynamical matrix of the
    :class:`DynMatrix` instance.
    Args:
        db (Group): database with parameters needed to module the
            atoms, (Calibration or Modulation databases).
    """
    #Generating the modulation file. We need to sample the DOS in order to
    #compute that correctly.
    dosfile = path.join(db.base.phonodir, "mesh.yaml")
    qvecs = sample_dos(dosfile, sampling=db.sampling, nfreqs=db.nconfigs)
    conffile = path.join(db.base.phonodir, db.confname)

    modstr = [' '.join(map(str, db.base.supercell))]
    for iq, (bandi, qvec) in enumerate(qvecs):
        mstr = "{0:.7f} {1:.7f} {2:.7f} {3:d} {4:.7f} {5:.7f}"
        if hasattr(db, "_amplitudes"):
            A = db._amplitudes[iq]
        else:
            A = np.random.normal(1, 0.25) * db.amplitude

        phi = np.random.uniform(0, 180)
        args = qvec + [bandi, A, phi]
        modstr.append(mstr.format(*args))

    phondict = db.phonons.copy()
    phondict["dim"] = db.base.supercell
    phondict["atom_name"] = ' '.join(db.base.parent.species)
    phondict["modulation"] = ', '.join(modstr)
    with open(conffile, 'w') as f:
        for k, v in phondict.items():
            if isinstance(v, (list, tuple, set)):
                value = ' '.join(map(str, v))
                f.write("{} = {}\n".format(k.upper(), value))
            else:
                f.write("{} = {}\n".format(k.upper(), v))

    from matdb.utility import execute
    sargs = ["phonopy", db.confname]
    xres = execute(sargs, db.base.phonodir, venv=True)

    #Make sure that phonopy actually produced files; otherwise show the output
    #(phonopy doesn't write to stderr, only stdout).
    testmod = path.join(db.base.phonodir, "MPOSCAR-001")
    if not path.isfile(testmod):  #pragma: no cover
        msg.err(''.join(xres["output"]))
Exemplo n.º 14
0
    def can_extract(self, folder):
        """Returns True if the specified folder has completed
        executing and the results are available for use.
        """
        if not path.isdir(folder):
            return False

        if path.isfile(path.join(folder, "CRASH")):
            msg.err(
                "The QE calculation in {0} has crashed. Error message:".format(
                    folder))
            with open(path.join(folder, "CRASH"), 'r') as f:
                for line in f:  #pragma: no cover, we just need to test
                    #that the CRASH file is found. We don't
                    #need to test the error write out.
                    msg.err(line.strip())
            return False
        #If we can extract a final total energy from the OUTCAR file, we
        #consider the calculation to be finished.
        outxml = path.join(folder, "{0}.xml".format(self.out_file))
        if not path.isfile(outxml):
            if path.isfile(path.join(folder, "pwscf.xml")):
                rename(path.join(folder, "pwscf.xml"), outxml)
                rename(path.join(folder, "pwscf.save"),
                       path.join(folder, self.out_dir))
            else:
                return False

        line = None
        with open(outxml, 'r') as f:
            # memory-map the file, size 0 means whole file
            m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
            i = m.rfind(b'</closed>')
            # we look for this second line to verify that VASP wasn't
            # terminated during runtime for memory or time
            # restrictions
            if i > 0:
                # seek to the location and get the rest of the line.
                m.seek(i)
                line = m.readline()

        if line is not None:
            return True
        else:
            return False
Exemplo n.º 15
0
    def _get_arrows(self, arrows):
        """Parses the arrow concentrations passed in by the user.

        Args:
            arrows (list): a list of the arrow concentration on each element.
        """
        if arrows is not None and len(arrows) == len(self.species):
            if isinstance(arrows[0], (int, float)) and arrows[0] <= 1:
                self.arrows = arrows
                self.arrow_res = True
            else:  #pragma: no cover
                msg.err("The arrows must be a list of values <= 1.")
        elif arrows is None:
            self.arrows = arrows
            self.arrow_res = False
        else:  #pragma: no cover
            msg.err(
                "The number of species and arrow concentrations must have the "
                "same length.")
Exemplo n.º 16
0
    def _get_concs(self, concs):
        """Parses the atomic concentrations passed in by the user.

        Args:
            concs (list): a list of the atomic concentrations.
        """
        if concs is not None and len(concs) == len(self.species):
            if len(concs[0]) == 3:
                self.concs = concs
                self.conc_res = True
            else:  #pragma: no cover
                msg.err("The concetrations must be a nx3 list.")
        elif concs is None:
            self.concs = concs
            self.conc_res = False
        else:  #pragma: no cover
            msg.err(
                "The number of species and the concentrations must be have the "
                "same length.")
Exemplo n.º 17
0
def slicer(obj, args):
    """Slices the object along the path defined in args.

    Args:
        obj (iterable): an object to be sliced or divided.
        args (iterable): the locations where the slices should be.
    """
    from itertools import islice
    if not isinstance(args, (list, tuple)):
        msg.err("The slicer args must be a list or a tuple.")
        return
    result = []
    if len(args) % 2 == 0:
        for a in range(0, len(args), 2):
            for b, c in islice(((args[a], args[a + 1]), ), 0, None, 2):
                result.extend(obj[slice(b, c)])
    else:
        msg.err(
            "Could not parse slice {} without start and stop values.".format(
                args))
    return result
Exemplo n.º 18
0
    def _convert_mem(self):
        """Converts memory to the correct values including units."""

        if "mem_per_cpu" in self.execution:
            return self.execution["mem_per_cpu"]

        init_mem, final_unit = self.execution[
            "total_mem"][:-2], self.execution["total_mem"][-2:]
        final_mem = float(init_mem) / self.execution["ntasks"]
        while final_mem < 1:
            final_mem = final_mem * 1000
            if final_unit.lower() == "gb":
                final_unit = "MB"
            elif final_unit.lower() == "mb":
                final_unit = "KB"
            else:  #pragma: no cover
                msg.err("Unrecognized memory size {}".format(final_unit))

        final_mem = int(np.round(final_mem))

        return "{0}{1}".format(final_mem, final_unit)
Exemplo n.º 19
0
    def calc_forcesets(self, recalc=False):
        """Extracts the force sets from the displacement calculations.

        Args:
            recalc (bool): when True, recalculate the force sets, even if the
              file already exists.
        """
        fsets = path.join(self.phonodir, "FORCE_SETS")
        if not recalc and path.isfile(fsets):
            return

        from matdb.calculators import get_calculator_module
        mod = get_calculator_module(self.calcargs)
        call = getattr(mod, "extract_force_sets")
        xres = call(self.configs, self.phonodir)

        #Make sure that phonopy actually produced files; otherwise show the output
        #(phonopy doesn't write to stderr, only stdout).
        if not path.isfile(fsets):  #pragma: no cover
            msg.std(''.join(xres["output"]))
            msg.err("Couldn't create the FORCE_SETS in {}.".format(
                self.phonodir))
Exemplo n.º 20
0
def check_deps():
    """Checks that the needed dependencies are installed and were all installed by pip.

    Returns:
        A dictionary of the dependencies and their version numbers.
    """

    req_pckgs = required_packages()
    versions = {}
    instld_pckgs = [
        l.strip() for l in execute(["pip3 freeze"], ".", venv=True)["output"]
    ]

    for pkg in instld_pckgs:
        if "==" in pkg:
            name, version = pkg.strip().split("==")
        else:
            name, version = pkg, None
        if name in req_pckgs:
            count = version.count(".")
            if version is not None and (
                    count in [1, 2, 3] and
                ("/" not in version and ":" not in version
                 and "-" not in version and ".com" not in version)):
                versions[name] = version
                req_pckgs.remove(name)
            else:  #pragma: no cover There won't be locally installed
                #packages on the test machines.
                msg.err("Cannot run `matdb` with locally installed package {} "
                        "as it would not be reproducable.".format(pkg))
    if len(req_pckgs) >= 1:
        for pkg in req_pckgs:
            msg.err(
                "Could not find required package {} in environment.".format(
                    pkg))

    return versions
Exemplo n.º 21
0
    def _setup_to_relax_cfg(self):
        """Creates the list of files to relax to check the mtp against.
        """

        target = path.join(self.root, "to-relax.cfg")

        if path.isfile(target):
            remove(target)

        setup_args = {}

        if len(self.species) > 4:
            msg.err("The MTP relaxation isn't setup to create a to-relax.cfg "
                    "file for systems with more than 4 elements.")
            return
        args = {
            "config": "t",
            "species": self.species,
            "structures": "all",
            "debug": False,
            "example": False,
            "displace": 0.0,
            "mink": True,
            "outfile": target,
            "verbose": None,
            "rattle": 0.0,
            "mapping": None,
        }
        setup_args["phenum_args"] = args
        setup_args["crystals"] = self.crystals_to_relax
        setup_args["species"] = self.species
        setup_args["min_atoms"] = self.relax_min_atoms
        setup_args["max_atoms"] = self.relax_max_atoms
        setup_args["root"] = self.root

        with open(path.join(self.root, "to_relax.json"), "w+") as f:
            json.dump(setup_args, f)
Exemplo n.º 22
0
 def _get_lattice(self, lattice):
     """Gets the lattice vectors for the system.
     
     Args:
         lattice (str or list): either a string containing the lattice 
             name or a 3x3 list of the vectors as [a1,a2,a3].
     """
     # determine the lattice.
     if isinstance(lattice, string_types):
         if lattice.lower() == "fcc":
             self.lattice = [[0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]]
             self.lattice_name = "fcc"
         elif lattice.lower() == "bcc":
             self.lattice = [[0.5, 0.5, -0.5], [0.5, -0.5, 0.5],
                             [-0.5, 0.5, 0.5]]
             self.lattice_name = "bcc"
         elif lattice.lower() == "sc":
             self.lattice = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
             self.lattice_name = "sc"
         elif lattice.lower() == "hcp":
             self.lattice = [[1, 0, 0], [.5, 0.866025403784439, 0],
                             [0, 0, 1.6329931618554521]]
             self.lattice_name = "hcp"
         else:  #pragma: no cover
             msg.err(
                 "The lattice type {} is unsupported. Please enter your lattice vectors "
                 "as a 3x3 matrix with the vectors as rows in the config file "
                 "(i.e. [a1,a2,a3]).".format(lattice))
     elif isinstance(lattice, list):
         if len(lattice) == 3 and len(
                 lattice[0]) == 3 and np.linalg.det(lattice) != 0:
             self.lattice = lattice
             self.lattice_name = "custom"
         else:  #pragma: no cover
             msg.err(
                 "The lattice vectors must be a 3x3 matrix with the vectors as rows "
                 "and the vectors must be linearly independent.")
     elif lattice is not None:  #pragma: no cover
         msg.err(
             "The lattice vectors must either be a string of 'sc', 'fcc', 'hcp', 'bcc', "
             "or a 3x3 matrix with the vectors a rows.")
     else:
         self.lattice = None
         self.lattice_name = None
Exemplo n.º 23
0
def _move_trainer(trainer, dbspec=None, dupe=False, to=None, **kwargs):
    """Moves the specified trainer.

    Args:
        trainer (matdb.fitting.basic.Trainer): trainer whose directory structure
          should be moved.
        dbspec (str): path to the spec yml file for the entire `matdb`.
        dupe (bool): when True, duplicate the trainer so that the existing
          trainer remains at its current location and a copy is placed under the
          new name.
        to (str): name of the new trainer; all references, including those in
          configuration files, will be changed and folders will be moved.
    """
    #We need to:
    # 1) Rename the trainer in the yml settings that defined the trainer.
    # 2) If duplicating, copy the yml settings file, add its reference to the
    #    system yml file.
    # 3) Move and/or duplicate the folder structure for the trainer.
    origspec = dbspec
    dbspec = path.abspath("{}.yml".format(dbspec))
    with open(dbspec) as f:
        spec = yaml.load(f)
    root = path.dirname(dbspec)

    fits, targets, fitroot = _get_targets(root, spec)
    if to in fits:
        raise ValueError("The specified fit name is already taken! "
                         "Choose a different new name for the fit.")
    mvname = trainer.parent.name
    try:
        settings = targets[mvname]
    except KeyError:
        msg.err("Cannot find trainer specification for {}.".format(mvname))

    #Handle duplication if that was specified. Otherwise, just move the config
    #specification and rename the trainer within it.
    if dupe:
        dsettings = settings.copy()
        dsettings["name"] = to
        if fitroot is not None:
            with open(path.join(fitroot, "{}.yml".format(to)), 'w') as f:
                yaml.dump(dsettings, f)
            spec["fitting"]["fits"].append(":{}".format(to))
        else:
            spec["fitting"]["fits"].append(dsettings)
    else:
        settings["name"] = to
        if fitroot is not None:
            with open(path.join(fitroot, "{}.yml".format(to)), 'w') as f:
                yaml.dump(settings, f)
            remove(path.join(fitroot, "{}.yml".format(mvname)))
            spec["fitting"]["fits"].append(":{}".format(to))
        else:
            spec["fitting"]["fits"].append(settings)

    #Next, move the actual directory. We have to be careful because the first
    #level of subdirectories also have the name in them, but they also may have
    #seed directives for the special `*` and `^` notation. If duplication is
    #switched on, we *still* move the folder because a new one with the same
    #settings will be created to replace the original one.
    src = path.join(root, mvname)
    dst = path.join(root, to)
    move(src, dst)

    for objname in listdir(dst):
        _src = path.join(dst, objname)
        if path.isdir(_src):
            if mvname == objname[0:len(mvname)]:
                newname = to + objname[len(mvname):]
                _dst = path.join(dst, newname)
                move(_src, _dst)

    #Rewrite the `matdb.yml` file to include the new fit
    #specifications.
    if not dupe:
        spec["fitting"]["fits"].remove(":{}".format(mvname))
    with open(dbspec, 'w') as f:
        yaml.dump(spec, f)
Exemplo n.º 24
0
def make_primitive(atm, eps=None):
    """Finds the primitive cell of a given crystal and the HNF needed to
    convert the primitive to the current crystal.

    Args:
        atm (matdb.atoms.Atoms): an atoms object.
        esp (float): floating point tolerance.

    Returns:
        The lattice and atomic basis vectors of the primitive cell along
        with the HNF needed to map the primitive cell to the original cell.
    """
    #extract data from the atoms object to be used.
    a_vecs = atm.cell
    atom_pos = atm.positions
    atom_type = None
    new_vecs = None
    unique_pos = None
    unique_types = None
    try:
        atom_type = atm.get_chemical_symbols()
        atom_type = [i for i in atom_type if i != "X"]
        # mapping = {k:v for v, k in enumerate(np.unique(temp))}
        # atom_type = [mapping[i] for i in types]
    except:  #pragma: no cover
        atom_type = atm.numbers
        # mapping = {k:v for v, k in enumerate(np.unique(temp))}
        # atom_type = [mapping[i] for i in types]

    if atom_type is None or len(atom_type) == 0:
        raise ValueError(
            "The atoms object doesn't contain species information. "
            "The primitive cell can't be found without the species information."
        )
    if eps is None:
        eps = 1E-3

    #Armed with the data we can now make the cell primitive.
    num_atoms = len(atom_pos)

    latt_to_cart, cart_to_latt = _get_transformations(a_vecs)

    #Ensure that all the basis atoms are in the unit cell.
    atom_pos = np.array([
        bring_into_cell(pos, cart_to_latt, latt_to_cart, eps)
        for pos in atom_pos
    ])

    #If the cell isn't primitive then there will be lattice vectors
    #inside the cell. If a lattice vector is inside the unit cell then
    #it will bring any atom inside the cell back unto itself via a
    #fractional transaltion. Such a translation must exist for each
    #atom of the same type.
    fracts = []
    for i_atom, a_type in enumerate(atom_type):
        #Only proceed for atoms of the same type as the first type of
        #atom that aren't the first atom.
        if not a_type == atom_type[0] or i_atom == 0:
            continue

        fract = atom_pos[i_atom] - atom_pos[0]
        fract = np.array(
            bring_into_cell(fract, cart_to_latt, latt_to_cart, eps))
        for j_atom, this_type in enumerate(atom_type):
            #Find the new location of the atom after the fractional
            #translation.
            new_pos = fract + atom_pos[j_atom]
            new_pos = np.array(
                bring_into_cell(new_pos, cart_to_latt, latt_to_cart, eps))
            mapped = _does_mapping_exist(new_pos, this_type, atom_pos,
                                         atom_type, eps)
            if not mapped:
                break

        #If the loop was successfull (mapped==True) then this
        #translation takes all atoms to another of the same type so it
        #is a valid fractional translation and should be kept.
        if mapped:
            fracts.append(list(fract))

    #If the lattice isn't primitive then extra lattice points, i.e.,
    #fractional rotations, were found above.
    if len(fracts) > 0:
        #Collect all lattices points, i.e., potential new primitive vectors.
        lattice_points = deepcopy(fracts)
        lattice_points.extend(a_vecs)

        #Consider all possible triplets of the lattices points to see
        #if the form a set of primitive basis vectors. A triplet will
        #form a basis set if all lattice points will be integer
        #combinations of the triplet.
        for new_vecs in combinations(lattice_points, 3):
            try:
                cart_to_latt = np.linalg.inv(np.transpose(new_vecs))
            except:  #pragma: no cover
                continue
            for point in lattice_points:
                vec = np.matmul(cart_to_latt, point)

                #Check if the new vecs are all integers. If not then
                #this lattice point wasn't preserved, move on to the
                #next one.
                mapped = True
                if not np.allclose(vec, np.rint(vec),
                                   rtol=eps):  #pragma: no cover
                    # I could never get this flag to fire.
                    mapped = False
                    msg.err(
                        "Reached portion of code that has never been tested. "
                        "Please submit following to developers for testing: "
                        "a_vecs: {0}, atom_pos: {1}, atom_types: "
                        "{2}".format(a_vecs, atom_pos, atom_type))
                    break

            #If all lattice points were mapped then we've found a valid new basis and can exit.
            if mapped:
                break

        if not mapped:  #pragma: no cover
            raise LogicError("Error in make_primitive. Valid basis not found.")

        #Bring all the atoms into the new cell.
        atom_pos = np.array([
            bring_into_cell(pos, cart_to_latt, np.transpose(new_vecs), eps)
            for pos in atom_pos
        ])
        # Check for redundant atoms in the basis. If any are present then remove them.
        unique_pos = []
        unique_types = []
        removed = np.zeros(len(atom_type))
        for i_atom, this_type in enumerate(atom_type):
            pos = atom_pos[i_atom]
            mapped = False
            for j in range(i_atom + 1, len(atom_type)):
                if (atom_type[j] == this_type) and (np.allclose(
                        pos, atom_pos[j], rtol=eps)):
                    mapped = True
                    removed[i_atom] = i_atom + 1

            if not mapped:
                unique_pos.append(pos)
                unique_types.append(this_type)

    if new_vecs is None:
        new_vecs = a_vecs
    if unique_pos is None:
        unique_pos = atom_pos
    if unique_types is None:
        unique_types = atom_type
    #Now that we have the new atomic and lattice basis we need to
    #define the HNF that mapps the primitive to the supercell.
    n = np.rint(
        np.matmul(np.linalg.inv(np.transpose(new_vecs)), np.transpose(a_vecs)))
    hnf, b = hermite_normal_form(n)
    hnf = hnf.astype(int)
    return (new_vecs, unique_pos, unique_types, hnf)
Exemplo n.º 25
0
    def command(self):
        """Returns the command that is needed to train the GAP
        potentials specified by this object.
        .. note:: This method also configures the directory that the command
          will run in so that it has the relevant files.
        """

        self._set_root()

        if not path.isfile(path.join(self.root, "status.txt")):
            self.iter_status = "train"
            self.iter_count = 1
            self.cell_iter = 0
        else:
            with open(path.join(self.root, "status.txt"), "r") as f:
                for line in f:
                    old_status = line.strip().split()

            if old_status[0] == "done":
                self.iter_status = "train"
                # iter_count is the total iteration number for the whole process, not limited to one cell
                self.iter_count = int(old_status[1]) + 1
                self.cell_iter = int(old_status[2])

                if int(old_status[3]) <= self.next_cell_threshold:
                    self.cell_iter = int(old_status[2]) + 1
                    if len(self.cell_sizes) > self.cell_iter:
                        self.relax_max_atoms = self.cell_sizes[self.cell_iter]
                    else:  #pragma: no cover
                        msg.err(
                            "The MTP process has finished for the cell sizes "
                            "specified in the YML file.")
                    target1 = path.join(self.root, "unrelaxed.cfg")
                    target2 = path.join(self.root, "to-relax.cfg")
                    if path.isfile(target1):
                        remove(target1)
                    if path.isfile(target2):
                        remove(target2)

                if self.iter_count >= self.iter_threshold:  #pragma: no cover
                    msg.err(
                        "The number of iterations has exceeded "
                        "the allowed number of {0}. To increase this limit "
                        "use the iteration_threshold aption in the YML "
                        "file.".format(self.iter_threshold))
            else:
                self.iter_status = old_status[0]
                self.iter_count = int(old_status[1])
                self.cell_iter = int(old_status[2])

        #if we're at the start of a training iteration use the command to train the potential
        if self.iter_status == "train":
            self._make_train_cfg(self.iter_count)

            #remove the selected.cfg and rexaed.cfg from the last iteration if they exist
            if path.isfile(path.join(self.root, "new_training.cfg")):
                remove(path.join(self.root, "new_training.cfg"))
            if path.isfile(path.join(self.root, "relaxed.cfg")):
                remove(path.join(self.root, "relaxed.cfg"))

            if not path.isfile(path.join(self.root, "relax.ini")):
                self._make_relax_ini()
            if not path.isfile(path.join(self.root, "pot.mtp")):
                self._make_pot_initial()
            template = self._train_template()
            with open(path.join(self.root, "status.txt"), "w+") as f:
                f.write("relax_setup {0} {1}".format(self.iter_count,
                                                     self.cell_iter))

        if self.iter_status == "relax_setup":
            # If pot has been trained
            if path.isfile(path.join(self.root, "Trained.mtp_")):
                rename(path.join(self.root, "Trained.mtp_"),
                       path.join(self.root, "pot.mtp"))

            # Calculate the grad of the training configurations.
            calc_grade = self._calc_grade_template()
            execute(calc_grade.split(), self.root)
            if path.isfile(path.join(self.root, "temp1.cfg")):
                remove(path.join(self.root, "temp1.cfg"))
            if not path.isfile(path.join(self.root,
                                         "state.mvs")):  #pragma: no cover
                raise MlpError(
                    "mlp failed to produce the 'state.mvs` file with command "
                    "'mlp calc-grade pot.mtp train.cfg train.cfg temp1.cfg'")

            # if the unrelaxed.cfg file exists we need to move it to
            # replace the existing 'to-relax.cfg' otherwise we need to
            # create the 'to-relax.cfg' file.
            if path.isfile(path.join(self.root,
                                     "unrelaxed.cfg")) and self.use_unrelaxed:
                rename(path.join(self.root, "unrelaxed.cfg"),
                       path.join(self.root, "to-relax.cfg"))
                self.iter_status = "relax"
            elif not path.isfile(path.join(self.root, "to-relax.cfg")):
                self._setup_to_relax_cfg()
                template = "matdb_mtp_to_relax.py > create-to-relax.txt"
                with open(path.join(self.root, "status.txt"), "w+") as f:
                    f.write("relax {0} {1}".format(self.iter_count,
                                                   self.cell_iter))
            else:
                self.iter_status = "relax"

        if self.iter_status == "relax":
            # command to relax structures
            if path.isfile(path.join(self.root, "candidate.cfg")):
                remove(path.join(self.root, "candidate.cfg"))

            template = self._relax_template()
            with open(path.join(self.root, "status.txt"), "w+") as f:
                f.write("select {0} {1}".format(self.iter_count,
                                                self.cell_iter))

        # if relaxation is done
        if self.iter_status == "select":
            cand_files = glob(path.join(self.root, "candidate.cfg_*"))
            cat(cand_files, path.join(self.root, "candidate.cfg"))
            for cfile in cand_files:
                remove(cfile)

            # command to select next training set.
            template = self._select_template()
            with open(path.join(self.root, "status.txt"), "w+") as f:
                f.write("add {0} {1}".format(self.iter_count, self.cell_iter))

        if self.iter_status == "add":
            with chdir(self.root):
                # Now add the selected atoms to the Active database.
                # input filename sould always be entered before output filename
                execute([
                    "mlp", "convert-cfg", "new_training.cfg", "POSCAR",
                    "--output-format=vasp-poscar"
                ], self.root)
                if path.isfile("new_training.cfg"):
                    rename("new_training.cfg",
                           "new_training.cfg_iter_{}".format(self.iter_count))
                if path.isfile("relaxed.cfg"):
                    rename("relaxed.cfg",
                           "relaxed.cfg_iter_{}".format(self.iter_count))

                new_configs = []
                new_POSCARS = glob("POSCAR*")
                # Here We need to re-write the POSCARs so that they
                # have the correct species.
                for POSCAR in new_POSCARS:
                    # We need to put the correct species into the
                    # title of the POSCAR so that ASE can read it
                    pos_file = []
                    with open(POSCAR, 'r') as f:
                        for line in f:
                            pos_file.append(line)
                    pos_file[0] = "{0} : {1}".format(" ".join(self.species),
                                                     pos_file[0])
                    with open(POSCAR, 'w+') as f:
                        for line in pos_file:
                            f.write(line)

                    new_configs.append(Atoms(POSCAR, format="vasp"))
                    remove(POSCAR)

            self.active.add_configs(new_configs, self.iter_count)
            self.active.setup()
            if len(self.active.configs
                   ) != self.active.nconfigs:  #pragma: no cover
                raise LogicError(
                    "The active database failed to setup the calculations "
                    "for iteration {0}".format(self.iter_count))
            self.active.execute()
            with open(path.join(self.root, "status.txt"), "w+") as f:
                f.write("done {0} {1} {2}".format(self.iter_count,
                                                  self.cell_iter,
                                                  len(new_configs)))
            template = ''

        return template
Exemplo n.º 26
0
    def __init__(self, atoms, folder, contr_dir, ran_seed, *args, **kwargs):

        # the "name" attribute must be the same as the local name for the module imported in __init__.py
        self.name = "Qe"

        if contr_dir == '$control$':
            contr_dir = config_specs["cntr_dir"]
        if path.isdir(contr_dir):
            self.contr_dir = path.abspath(path.expanduser(contr_dir))
        else:
            msg.err("{} is not a valid directory.".format(contr_dir))

        if '$control$' in folder:
            folder = folder.replace('$control$', self.contr_dir)
        self.folder = path.abspath(path.expanduser(folder))
        self.kpoints = None

        self.in_kwargs = kwargs.copy()
        self.args = args

        if "kpoints" in kwargs:
            self.kpoints = kwargs.pop("kpoints")
        if self.kpoints["method"] == "mueller":
            raise NotImplementedError(
                "The Mueller server does not support QE at this time.")
        elif self.kpoints["method"] == "MP":
            kpts = tuple(self.kpoints["divisions"].split(" "))
            kspacing = None
        elif self.kpoints["method"] == "kspacing":
            kpts = None
            kspacing = self.kpoints["spacing"]

        if "offset" in self.kpoints:
            koffset = self.kpoints["offset"]
        else:
            koffset = 0

        self.potcars = kwargs.pop("potcars")
        if "directory" in self.potcars:
            if "." == self.potcars["directory"][0]:
                pseudo_dir = path.abspath(self.potcars["directory"])
            else:
                pseudo_dir = path.expanduser(self.potcars["directory"])
            self.potcars["directory"] = pseudo_dir
        else:  #pragma: no cover
            pseudo_dir = None
        pseudopotentials = self.potcars["potentials"]

        if "input_data" in kwargs:
            if "prefix" in kwargs["input_data"]["control"]:
                self.out_file = kwargs["input_data"]["control"]["prefix"]
            else:
                self.out_file = "pwscf"

            if "control" in kwargs["input_data"]:
                if "outdir" in kwargs["input_data"]["control"]:
                    self.out_dir = kwargs["input_data"]["control"]["outdir"]
                    self.out_file = path.join(self.out_dir, self.out_file)
                else:
                    self.out_dir = "{0}.save".format(self.out_file)

            input_data = kwargs.pop("input_data")

            # set default values for tprnfor and tstress so that the QE
            # calculates the forces and stresses unless over-written by
            # user.
            input_data[
                "tprnfor"] = kwargs["tprnfor"] if "tprnfor" in kwargs else True
            input_data[
                "tstress"] = kwargs["tstress"] if "tstress" in kwargs else True
        else:
            input_data = None
            self.out_file = "pwscf"
            self.out_dir = "{0}.save".format(self.out_file)

        self.ran_seed = ran_seed
        self.version = None
        super(AsyncQe, self).__init__(pseudopotentials=pseudopotentials,
                                      pseudo_dir=pseudo_dir,
                                      input_data=input_data,
                                      kpts=kpts,
                                      koffset=koffset,
                                      kspacing=kspacing,
                                      **kwargs)

        if not path.isdir(self.folder):
            mkdir(self.folder)

        self.atoms = atoms

        self.tarball = ["{0}.xml".format(self.out_file)]
        self._check_potcars()
Exemplo n.º 27
0
    def _set_local_attributes(self, mtpargs):
        """Sets the attributes of the mtp object from the input dictionary.
        Args:
            mtpargs (dict): the input dictionary of arguments.
        """

        if "relax_ini" in mtpargs and mtpargs["relax_ini"] is not None:
            self._set_relax_ini(mtpargs["relax_ini"])
        else:
            self._set_relax_ini({})

        if "relax" in mtpargs and mtpargs["relax"] is not None:
            self.relax_args = mtpargs["relax"]
        else:
            self.relax_args = {}

        self.ran_seed = mtpargs["ran_seed"] if "ran_seed" in mtpargs else 0

        if "train" in mtpargs and mtpargs["train"] is not None:
            self.train_args = mtpargs["train"]
        else:
            self.train_args = {}

        if "run_as_root" in mtpargs and mtpargs["run_as_root"] is not None:
            self.run_as_root = mtpargs["run_as_root"]
        else:
            self.run_as_root = False

        if "smallest_relax_cell" in mtpargs:
            self.relax_min_atoms = mtpargs["smallest_relax_cell"]
        else:
            self.relax_min_atoms = 1

        if "largest_relax_cell" in mtpargs:
            if isinstance(mtpargs["largest_relax_cell"], list):
                self.cell_sizes = mtpargs["largest_relax_cell"]
                if len(self.cell_sizes) > self.cell_iter:
                    self.relax_max_atoms = self.cell_sizes[self.cell_iter]
                else:  #pragma: no cover
                    msg.err(
                        "The MTP process has finished for the cell sizes {0}"
                        "specified in the YML file.".format(self.cell_sizes))
            else:
                self.cell_sizes = [mtpargs["largest_relax_cell"]]
                self.relax_max_atoms = mtpargs["largest_relax_cell"]
        else:
            self.relax_max_atoms = None
            self.cell_sizes = None

        if "iteration_threshold" in mtpargs and mtpargs[
                "iteration_threshold"] is not None:
            self.iter_threshold = mtpargs["iteration_threshold"]
        else:
            if self.cell_sizes is None:
                self.iter_threshold = 50
            else:
                self.iter_threshold = 50 * len(self.cell_sizes)

        if "next_cell_threshold" in mtpargs and mtpargs[
                "next_cell_threshold"] is not None:
            self.next_cell_threshold = mtpargs["next_cell_threshold"]
        else:
            self.next_cell_threshold = 0

        if (self.relax_max_atoms is not None and
            (self.relax_max_atoms < self.relax_min_atoms
             or self.relax_max_atoms < 0)) or self.relax_min_atoms < 0:
            raise ValueError(
                "The max and min number of atoms to be relaxed must be "
                "larger than 0 and the max must be larger than the min.")

        if "calc-grade" in mtpargs and mtpargs["calc-grade"] is not None:
            self.grade_args = mtpargs["calc-grade"]
        else:
            self.grade_args = {}

        if "select" in mtpargs and mtpargs["select"] is not None:
            self.select_args = mtpargs["select"]
        else:
            self.select_args = {}

        if "use_mpi" in mtpargs and mtpargs["use_mpi"] is not None:
            self.use_mpi = False if "false" in mtpargs["use_mpi"].lower(
            ) else True
        else:
            self.use_mpi = True

        if "use_unrelaxed" in mtpargs:
            self.use_unrelaxed = True if "true" in mtpargs[
                "use_unrelaxed"].lower() else False
        else:
            self.use_unrelaxed = False

        self.crystals_to_relax = mtpargs[
            "crystals_to_relax"] if "crystals_to_relax" in mtpargs else [
                "sc", "fcc", "bcc", "hcp", "prototypes"
            ]
Exemplo n.º 28
0
    def _parse_md(self, target):
        """Extracts every frame from XDATCAR that is a multiple of `N` and
        constructs a POSCAR for it.

        Args:
            target (str): path to the directory storing `XDATCAR`.
        """
        from os import path
        from subprocess import Popen, PIPE

        header = []
        li = 0
        Natoms = 0
        current = None
        writes = []

        from tqdm import tqdm
        pbar = tqdm(total=self.nsteps)

        try:
            with open(path.join(target, "XDATCAR")) as f:
                for line in f:
                    if li <= 6:
                        if li == 0:
                            header.append("{} ! MD: {{}}")
                        else:
                            header.append(line.strip())
                        li += 1

                    if li == 7:
                        header[0] = header[0].format(header[5])
                        header.remove(header[5])
                        Natoms = sum(map(int, header[5].split()))
                        li += 1

                    if "configuration" in line:
                        vals = line.split()
                        if len(vals) == 3:
                            NL = int(vals[2])
                        else:
                            NL = int(vals[1].split('=')[-1])

                        #Write the POSCAR for this frame.
                        if current is not None:
                            NC = eval(current[0].split()[-1])
                            outfile = path.join(target, "POSCAR.{}".format(NC/self.samplerate))
                            with open(outfile, 'w') as f:
                                f.write('\n'.join(current))
                            writes.append(outfile + '\n')
                            pbar.update(1)

                            if len(current) != Natoms + 7:
                                emsg = "ERROR: MD {} has {} lines."
                                msg.err(emsg.format(NC, len(current)))

                        if NL % self.samplerate == 0:
                            current = list(header)
                            current[0] = current[0].format(NL)
                            current.append("Direct")
                        else:
                            current = None

                    elif current is not None:
                        current.append(line.strip())

            #Handle the last frame in the set.
            if current is not None:
                NC = eval(current[0].split()[-1])
                outfile = path.join(target, "POSCAR.{}".format(NC))
                with open(outfile, 'w') as f:
                    f.write('\n'.join(current))
                writes.append(outfile + '\n')
                pbar.update(1)
                if len(current) != Natoms + 7:
                    emsg = "ERROR: MD {} has {} lines."
                    msg.err(emsg.format(NC, len(current)))
        finally:
            pbar.close()

        return writes
Exemplo n.º 29
0
    def __init__(self,
                 name="prototype",
                 structures=None,
                 ran_seed=None,
                 permutations=None,
                 **dbargs):
        self.name = name
        self.seeded = False
        dbargs["prefix"] = "P"
        dbargs["cls"] = Prototypes
        if "Prototypes" not in dbargs['root']:
            from os import mkdir
            new_root = path.join(dbargs['root'], "Prototypes")
            if not path.isdir(new_root):
                mkdir(new_root)
            dbargs['root'] = new_root
        super(Prototypes, self).__init__(**dbargs)

        self.in_structures = structures
        self.ran_seed = ran_seed
        self.permutations = permutations
        self.species = self.database.parent.species

        #Make sure that we override the global calculator default values with
        #those settings that we know are needed for good phonon calculations.
        calcargs = self.database.calculator.copy()
        if "calculator" in dbargs:
            if dbargs["calculator"] is not None and "name" in dbargs[
                    "calculator"]:
                calcargs.update(dbargs["calculator"])
                dbargs["calculator"] = calcargs

        # The prototypes are saved into the file prototypes.tar.gz, if
        # this is the first time prototypes has been run we need to unpack it.
        template_root = path.join(_get_reporoot(), "matdb", "templates")
        if not path.isdir(path.join(template_root, "uniqueUnaries")):
            import tarfile
            with chdir(template_root):
                tarf = "prototypes.tar.gz"
                tar = tarfile.open(tarf, "r:gz")
                tar.extractall()
                tar.close()

        # parse the structures to make a list of paths to the source folders for the
        if self.ran_seed is not None:
            import random
            random.seed(self.ran_seed)

        self.puuids = None
        self._load_puuids()
        self.nconfigs = 0

        self.structures = {}
        for k, v in structures.items():
            if k.lower() == "unary":
                cand_path = path.join(template_root, "uniqueUnaries")
            elif k.lower() == "binary":
                cand_path = path.join(template_root, "uniqueBinaries")
            elif k.lower() == "ternary":
                cand_path = path.join(template_root, "uniqueTernaries")
            else:  # pragma: no cover
                msg.warn(
                    "Must specify the system size, i.e., unary, binary, or "
                    "ternary. {} not recognized".format(k))
                continue
            if isinstance(v, list):
                self.structures[k.lower()] = []
                for prot in v:
                    files = glob("{0}/*{1}*".format(cand_path, prot))
                    if len(files) < 1:  # pragma: no cover
                        msg.warn(
                            "No prototypes of size {0} matched the string "
                            "{1}".format(k, prot))
                    else:
                        self.structures[k.lower()].extend(files)
            elif isinstance(v, str) and v == "all":
                files = glob("{0}/*".format(cand_path))
                self.structures[k.lower()] = files
            elif isinstance(v, int):
                from random import shuffle
                files = glob("{0}/*".format(cand_path))
                shuffle(files)
                keep = files[:v]
                self.structures[k.lower()] = keep
            else:  #pragma: no cover
                msg.err(
                    "Couldn't parse {0} structures for {1} case. Must be either "
                    "a list of file names, 'all', or an int.".format(v, k))

            if self.permutations is not None and k.lower(
            ) in self.permutations.keys():
                self.nconfigs += len(self.structures[k.lower()]) * len(
                    self.permutations[k.lower()])
            else:
                if k.lower() == "unary":
                    self.nconfigs += len(self.structures[k.lower()]) * 3
                elif k.lower() == "binary" or k.lower() == "ternary":
                    self.nconfigs += len(self.structures[k.lower()]) * 6
                else:  #pragma: no cover
                    continue
Exemplo n.º 30
0
def _calc_bands(atoms, hessian, supercell=(1, 1, 1), outfile=None, grid=None):
    """Calculates the band structure for the given Hessian matrix.

    Args:
        atoms (matdb.atoms.Atoms): atoms object corresponding to the *primitive*
          cell. The specified supercell matrix should  result in a number
          of atoms that matches the dimensionality of the Hessian.
        supercell (tuple): tuple of `int` supercell matrix components; can have either
          3 or 9 components.
        hessian (numpy.ndarray): with shape `(natoms*3, natoms*3)`.
        grid (list): list of `int` specifying the number of divisions in k-space
          along each reciprocal unit vector.
        outfile (str): path to the output `band.yaml` file that should be
          created by this function.

    Returns:
        If `outfile` is None, then this method returns a dictionary that has the
        same format as :func:`from_yaml`.
    """
    #Create a temporary directory in which to work.
    target = mkdtemp()
    bandfile = path.join(target, "band.yaml")

    if grid is None:
        grid = [13, 13, 13]
    if isinstance(supercell, np.ndarray):
        supercell = supercell.flatten()

    #First, roll up the Hessian and write it as a FORCE_CONSTANTS file.
    with chdir(target):
        HR = roll(hessian)
        write_FORCE_CONSTANTS(HR)
        atoms.write("POSCAR", format="vasp")

        #We need to create the band.conf file and write the special
        #paths in k-space at which the phonons should be calculated.
        atom_types = _ordered_unique(atoms.get_chemical_symbols())
        settings = [("FORCE_CONSTANTS", "READ"),
                    ("ATOM_NAME", ' '.join(atom_types)),
                    ("DIM", ' '.join(map(str, supercell))),
                    ("MP", ' '.join(map(str, grid)))]

        labels, bands = parsed_kpath(atoms)
        bandfmt = "{0:.3f} {1:.3f} {2:.3f}"
        sband = []
        for Q in bands:
            sband.append(bandfmt.format(*Q))

        settings.append(("BAND", "  ".join(sband)))
        settings.append(("BAND_LABELS", ' '.join(labels)))

        with open("band.conf", 'w') as f:
            for k, v in settings:
                f.write("{} = {}\n".format(k, v))

    sargs = ["phonopy", "band.conf"]
    xres = execute(sargs, target, venv=True)

    if not path.isfile(bandfile):  #pragma: no cover
        msg.err("could not calculate phonon bands; see errors.")
        msg.std(''.join(xres["output"]))

    result = None
    if outfile is not None:
        #Move the band.yaml file to the new target location.
        from shutil import move
        move(bandfile, outfile)
    else:
        result = from_yaml(bandfile)

    #Remove the temporary directory that we created and return the result.
    rmtree(target)
    return result