Esempio n. 1
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
Esempio n. 2
0
def sbatch(jobfile, folder, kwargs):
    """Submits jobs to a supercomputer with sbatch. 
    
    Args:
       jobfile (str): the name of jobfile that needs to be submitted.
       folder (str): the path to the jobfile.
       kwargs (dict): other arguments to pass to the execute function.
    """

    execute(["sbatch", jobfile], **kwargs)
Esempio n. 3
0
def vasp_to_xyz(folder,
                outfile="output.xyz",
                recalc=0,
                properties=["species", "pos", "z", "dft_force"],
                parameters=["dft_energy", "dft_virial"],
                config_type=None):
    """Creates an extended XYZ file for the calculated structure in
    OUTCAR for the given folder.

    Args:
        folder (str): path to the folder to convert.

        outfile (str): name of the XYZ file to create. The file will
          be created in the same folder as the original if no absolute
          path is given.

        recalc (bool): when True, re-convert the OUTCAR file, even if
          the target XYZ file already exists.

    """

    from matdb.atoms import Atoms

    if not path.isabs(outfile):
        #Convert to absolute path if one wasn't given.
        outfile = path.join(folder, outfile)

    if (path.isfile(outfile) and stat(outfile).st_size > 100 and recalc <= 0):
        return True

    p = ','.join(properties)
    P = ','.join(parameters)
    renames = [("energy", "vasp_energy"), ("force", "vasp_force"),
               ("virial", "vasp_virial")]
    sargs = ["convert.py", "-I", "vasprun.xml", "-p", p, "-P", P, "-f", "xyz"]
    for s, d in renames:
        sargs.append("-n")
        sargs.append(s)
        sargs.append(d)

    sargs.extend(["-o", outfile, "vasprun.xml"])

    from matdb.utility import execute
    execute(sargs, folder, errignore="OMP_STACKSIZE")

    if config_type is not None:
        #We need to load the XYZ file, add the config_type parameter and then
        #save it again. The -e parameter of convert.py should save this, but in
        #our experiments, it doesn't :(.
        a = Atoms(outfile)
        a.params["config_type"] = config_type
        a.write(outfile)

    return path.isfile(outfile) and stat(outfile).st_size > 100
Esempio n. 4
0
def test_execute():
    """Tests missing lines of execute.
    """
    from matdb.utility import execute

    # test early breaking on stderr and stdout.
    temp=execute(('which','python'),'.',nlines=0)
    temp=execute(('python','enum.x'),'.',nlines=0)
    
    temp=execute(('which','python'),'.',env_vars={"POTENTIALS_DIR":'1'})
    assert "bin/python\n" in temp['output'][0]
    assert temp['error'] == []
Esempio n. 5
0
def test_execution():
    """Tests the execution via shell subprocess in a different folder.
    """
    from matdb.utility import execute, reporoot
    sargs = ["pwd"]
    target = path.join(reporoot, "matdb")
    xres = execute(sargs, target, nlines=1, env_vars={"VASP_PP_PATH":"~/."})
    assert xres["output"][0].strip() == target

    sargs = ["cat dummy-file"]
    target = path.join(reporoot, "matdb")
    xres = execute(sargs, target, nlines=1)
    assert len(xres["error"]) > 0
Esempio n. 6
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.")
Esempio n. 7
0
def test_cat(tmpdir):
    """Tests concatenation of multiple files.
    """
    from matdb.utility import cat, execute, reporoot
    files = [path.join(reporoot, "tests", "files", f)
             for f in ["A.txt", "B.txt"]]
    outfile = str(tmpdir.join("cat_C.txt"))
    cat(files, outfile)

    sargs = ["diff", "C.txt", outfile]
    xres = execute(sargs, path.join(reporoot, "tests/files"))
    assert len(xres["output"]) == 0
Esempio n. 8
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)
Esempio n. 9
0
    def execute(self, dryrun=False):
        """Submits the job script for the currently configured potential training.

        Args:
            dryrun (bool): when True, simulate the submission without actually
              submitting.

        Returns:
            bool: True if the submission generated a job id (considered
            successful).
        """
        if self.ready():
            msg.info(
                "Trainer {} is already done;".format(self.root) +
                "skipping execute step.", 2)
            return

        if not path.isfile(self._jobfile):
            return False

        if not path.isfile(self._trainfile):
            msg.std("train.h5 missing in {}; can't execute.".format(self.root))
            return False

        # We must have what we need to execute. Compile the command and submit.

        shell_command = self.controller.db.shell_command
        # We suport 'bash' and 'sbatch' shell commands, if it's neighter one
        # of them, default to 'bash'
        if shell_command not in ['bash', 'sbatch']:
            shell_command = 'bash'
        cargs = [shell_command, self._jobfile]

        if dryrun:
            msg.okay("Executed {} in {}".format(' '.join(cargs), self.root))
            return True
        else:
            xres = execute(cargs, self.root)

        # supercompute will return "Submitted"
        if len(xres["output"]) > 0 and "Submitted" in xres["output"][0]:
            msg.okay("{}: {}".format(self.root, xres["output"][0].strip()))
            return True
        # local computer
        elif len(xres["error"]) == 0:
            return True
        else:
            return False
Esempio n. 10
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"]))
Esempio n. 11
0
    def execute(self, dryrun=False, recovery=False, env_vars=None):
        """Submits the job script for each of the folders in this
        database if they are ready to run.

        Args:
            dryrun (bool): when True, simulate the submission without actually submitting.
            recovery (bool): when True, submit the script for running recovery jobs.
            env_vars (dict): `dict` of environment variables to set before calling the execution. The variables will be set back after execution.

        Returns:
            bool: True if the submission generated a job id (considered successful).
        """

        self._expand_sequence()
        if len(self.sequence) == 0:
            jobfile = "recovery.sh" if recovery else "jobfile.sh"
            if not path.isfile(path.join(self.root, jobfile)):
                return False

            if not recovery:
                if not all(
                        a.calc.can_execute(self.configs[i])
                        for i, a in self.config_atoms.items()):
                    return False

                #We also need to check that we haven't already submitted this
                #job. Check to see if it is executing.
                if any(
                        a.calc.is_executing(self.configs[i])
                        for i, a in self.config_atoms.items()):
                    return False

                #Make sure that the calculation isn't complete.
                if self.last_config_atoms is not None:
                    if any(
                            a.calc.can_extract(self.last_iteration[i])
                            for i, a in self.last_config_atoms.items()):
                        return False

            # We must have what we need to execute. Compile the command and
            # submit.
            from matdb.utility import execute
            cargs = [self.database.parent.shell_command, jobfile]
            if dryrun:
                from matdb.msg import okay
                okay("Executed {} in {}".format(' '.join(cargs), self.root))
                return True
            else:
                xres = execute(cargs, self.root, env_vars=env_vars)

            if len(xres["output"]) > 0 and "Submitted" in xres["output"][0]:
                from matdb.msg import okay
                okay("{}: {}".format(self.root, xres["output"][0].strip()))
                return True
            else:  #pragma: no cover
                return False

        else:  #pragma: no cover, enumerated database shouldn't take seeds
            already_executed = []
            for group in self.sequence.values():
                already_executed.append(
                    group.execute(dryrun=dryrun,
                                  recovery=recovery,
                                  env_vars=env_vars))
            return all(already_executed)
Esempio n. 12
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
Esempio n. 13
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