def test_automatic_runner(self):
        test_dir = os.path.join(PymatgenTest.TEST_FILES_DIR, "bader")

        summary = bader_analysis_from_path(test_dir)
        """
        Reference summary dict (with bader 1.0)
        summary_ref = {
            'magmom': [4.298761, 4.221997, 4.221997, 3.816685, 4.221997, 4.298763, 0.36292,
                       0.370516, 0.36292, 0.36292, 0.36292, 0.36292, 0.36292, 0.370516],
            'min_dist': [0.835789, 0.92947, 0.92947, 0.973007, 0.92947, 0.835789, 0.94067,
                         0.817381, 0.94067, 0.94067, 0.94067, 0.94067, 0.94067, 0.817381],
            'vacuum_charge': 0.0,
            'vacuum_volume': 0.0,
            'atomic_volume': [9.922887, 8.175158, 8.175158, 9.265802, 8.175158, 9.923233, 12.382546,
                              12.566972, 12.382546, 12.382546, 12.382546, 12.382546, 12.382546, 12.566972],
            'charge': [12.248132, 12.26177, 12.26177, 12.600596, 12.26177, 12.248143, 7.267303,
                       7.256998, 7.267303, 7.267303, 7.267303, 7.267303, 7.267303, 7.256998],
            'bader_version': 1.0,
            'reference_used': True
        }
        """

        self.assertEqual(
            set(summary.keys()),
            {
                "magmom",
                "min_dist",
                "vacuum_charge",
                "vacuum_volume",
                "atomic_volume",
                "charge",
                "bader_version",
                "reference_used",
            },
        )
        self.assertTrue(summary["reference_used"])
        self.assertAlmostEqual(sum(summary["magmom"]), 28, places=1)
Exemple #2
0
    def process_vasprun(self, dir_name, taskname, filename):
        """
        Adapted from matgendb.creator

        Process a vasprun.xml file.
        """
        vasprun_file = os.path.join(dir_name, filename)

        vrun = Vasprun(vasprun_file, parse_potcar_file=self.parse_potcar_file)

        d = vrun.as_dict()

        # rename formula keys
        for k, v in {
                "formula_pretty": "pretty_formula",
                "composition_reduced": "reduced_cell_formula",
                "composition_unit_cell": "unit_cell_formula"
        }.items():
            d[k] = d.pop(v)

        for k in ["eigenvalues", "projected_eigenvalues"
                  ]:  # large storage space breaks some docs
            if k in d["output"]:
                del d["output"][k]

        comp = Composition(d["composition_unit_cell"])
        d["formula_anonymous"] = comp.anonymized_formula
        d["formula_reduced_abc"] = comp.reduced_composition.alphabetical_formula
        d["dir_name"] = os.path.abspath(dir_name)
        d["completed_at"] = str(
            datetime.datetime.fromtimestamp(os.path.getmtime(vasprun_file)))
        d["density"] = vrun.final_structure.density

        # replace 'crystal' with 'structure'
        d["input"]["structure"] = d["input"].pop("crystal")
        d["output"]["structure"] = d["output"].pop("crystal")
        for k, v in {
                "energy": "final_energy",
                "energy_per_atom": "final_energy_per_atom"
        }.items():
            d["output"][k] = d["output"].pop(v)

        # Process bandstructure and DOS
        if self.bandstructure_mode != False:
            bs = self.process_bandstructure(vrun)
            if bs:
                d["bandstructure"] = bs

        if self.parse_dos != False:
            dos = self.process_dos(vrun)
            if dos:
                d["dos"] = dos

        # Parse electronic information if possible.
        # For certain optimizers this is broken and we don't get an efermi resulting in the bandstructure
        try:
            bs = vrun.get_band_structure()
            bs_gap = bs.get_band_gap()
            d["output"]["vbm"] = bs.get_vbm()["energy"]
            d["output"]["cbm"] = bs.get_cbm()["energy"]
            d["output"]["bandgap"] = bs_gap["energy"]
            d["output"]["is_gap_direct"] = bs_gap["direct"]
            d["output"]["is_metal"] = bs.is_metal()
            if not bs_gap["direct"]:
                d["output"]["direct_gap"] = bs.get_direct_band_gap()
            if isinstance(bs, BandStructureSymmLine):
                d["output"]["transition"] = bs_gap["transition"]

        except Exception:
            logger.warning("Error in parsing bandstructure")
            if vrun.incar["IBRION"] == 1:
                logger.warning(
                    "Vasp doesn't properly output efermi for IBRION == 1")
            if self.bandstructure_mode is True:
                logger.error(traceback.format_exc())
                logger.error("Error in " + os.path.abspath(dir_name) + ".\n" +
                             traceback.format_exc())
                raise

        # Should roughly agree with information from .get_band_structure() above, subject to tolerances
        # If there is disagreement, it may be related to VASP incorrectly assigning the Fermi level
        try:
            band_props = vrun.eigenvalue_band_properties
            d["output"]["eigenvalue_band_properties"] = {
                "bandgap": band_props[0],
                "cbm": band_props[1],
                "vbm": band_props[2],
                "is_gap_direct": band_props[3]
            }
        except Exception:
            logger.warning("Error in parsing eigenvalue band properties")

        # store run name and location ,e.g. relax1, relax2, etc.
        d["task"] = {"type": taskname, "name": taskname}

        # include output file names
        d["output_file_paths"] = self.process_raw_data(dir_name,
                                                       taskname=taskname)

        # parse axially averaged locpot
        if "locpot" in d["output_file_paths"] and self.parse_locpot:
            locpot = Locpot.from_file(
                os.path.join(dir_name, d["output_file_paths"]["locpot"]))
            d["output"]["locpot"] = {
                i: locpot.get_average_along_axis(i)
                for i in range(3)
            }

        if self.store_volumetric_data:
            for file in self.store_volumetric_data:
                if file in d["output_file_paths"]:
                    try:
                        # assume volumetric data is all in CHGCAR format
                        data = Chgcar.from_file(
                            os.path.join(dir_name,
                                         d["output_file_paths"][file]))
                        d[file] = data.as_dict()
                    except:
                        raise ValueError("Failed to parse {} at {}.".format(
                            file, d["output_file_paths"][file]))

        # parse force constants
        if hasattr(vrun, "force_constants"):
            d["output"]["force_constants"] = vrun.force_constants.tolist()
            d["output"][
                "normalmode_eigenvals"] = vrun.normalmode_eigenvals.tolist()
            d["output"][
                "normalmode_eigenvecs"] = vrun.normalmode_eigenvecs.tolist()

        # perform Bader analysis using Henkelman bader
        if self.parse_bader and "chgcar" in d["output_file_paths"]:
            suffix = '' if taskname == 'standard' else ".{}".format(taskname)
            bader = bader_analysis_from_path(dir_name, suffix=suffix)
            d["bader"] = bader

        return d
Exemple #3
0
    def process_vasprun(self, dir_name, taskname, filename):
        """
        Adapted from matgendb.creator

        Process a vasprun.xml file.
        """
        vasprun_file = os.path.join(dir_name, filename)

        vrun = Vasprun(vasprun_file)

        d = vrun.as_dict()

        # rename formula keys
        for k, v in {"formula_pretty": "pretty_formula",
                     "composition_reduced": "reduced_cell_formula",
                     "composition_unit_cell": "unit_cell_formula"}.items():
            d[k] = d.pop(v)

        for k in ["eigenvalues", "projected_eigenvalues"]:  # large storage space breaks some docs
            if k in d["output"]:
                del d["output"][k]

        comp = Composition(d["composition_unit_cell"])
        d["formula_anonymous"] = comp.anonymized_formula
        d["formula_reduced_abc"] = comp.reduced_composition.alphabetical_formula
        d["dir_name"] = os.path.abspath(dir_name)
        d["completed_at"] = str(datetime.datetime.fromtimestamp(os.path.getmtime(vasprun_file)))
        d["density"] = vrun.final_structure.density

        # replace 'crystal' with 'structure'
        d["input"]["structure"] = d["input"].pop("crystal")
        d["output"]["structure"] = d["output"].pop("crystal")
        for k, v in {"energy": "final_energy", "energy_per_atom": "final_energy_per_atom"}.items():
            d["output"][k] = d["output"].pop(v)

        # Process bandstructure and DOS
        if self.bandstructure_mode != False:
            bs = self.process_bandstructure(vrun)
            if bs:
                d["bandstructure"] = bs

        if self.parse_dos != False:
            dos = self.process_dos(vrun)
            if dos:
                d["dos"] = dos

        # Parse electronic information if possible.
        # For certain optimizers this is broken and we don't get an efermi resulting in the bandstructure
        try:
            bs = vrun.get_band_structure()
            bs_gap = bs.get_band_gap()
            d["output"]["vbm"] = bs.get_vbm()["energy"]
            d["output"]["cbm"] = bs.get_cbm()["energy"]
            d["output"]["bandgap"] = bs_gap["energy"]
            d["output"]["is_gap_direct"] = bs_gap["direct"]
            d["output"]["is_metal"] = bs.is_metal()
            if not bs_gap["direct"]:
                d["output"]["direct_gap"] = bs.get_direct_band_gap()
            if isinstance(bs, BandStructureSymmLine):
                d["output"]["transition"] = bs_gap["transition"]

        except Exception:
            logger.warning("Error in parsing bandstructure")
            if vrun.incar["IBRION"] == 1:
                logger.warning("Vasp doesn't properly output efermi for IBRION == 1")
            if self.bandstructure_mode is True:
                logger.error(traceback.format_exc())
                logger.error("Error in " + os.path.abspath(dir_name) + ".\n" + traceback.format_exc())
                raise

        # store run name and location ,e.g. relax1, relax2, etc.
        d["task"] = {"type": taskname, "name": taskname}

        # include output file names
        d["output_file_paths"] = self.process_raw_data(dir_name, taskname=taskname)

        # parse axially averaged locpot
        if "locpot" in d["output_file_paths"] and self.parse_locpot:
            locpot = Locpot.from_file(os.path.join(dir_name, d["output_file_paths"]["locpot"]))
            d["output"]["locpot"] = {i: locpot.get_average_along_axis(i) for i in range(3)}

        if self.parse_chgcar != False:
            # parse CHGCAR file only for static calculations
            # TODO require static run later
            # if self.parse_chgcar == True and vrun.incar.get("NSW", 0) < 1:
            try:
                chgcar = self.process_chgcar(os.path.join(dir_name, d["output_file_paths"]["chgcar"]))
            except:
                raise ValueError("No valid charge data exist")
            d["chgcar"] = chgcar

        if self.parse_aeccar != False:
            try:
                chgcar = self.process_chgcar(os.path.join(dir_name, d["output_file_paths"]["aeccar0"]))
            except:
                raise ValueError("No valid charge data exist")
            d["aeccar0"] = chgcar
            try:
                chgcar = self.process_chgcar(os.path.join(dir_name, d["output_file_paths"]["aeccar2"]))
            except:
                raise ValueError("No valid charge data exist")
            d["aeccar2"] = chgcar

        # parse force constants
        if hasattr(vrun, "force_constants"):
            d["output"]["force_constants"] = vrun.force_constants.tolist()
            d["output"]["normalmode_eigenvals"] = vrun.normalmode_eigenvals.tolist()
            d["output"]["normalmode_eigenvecs"] = vrun.normalmode_eigenvecs.tolist()

        # Try and perform bader
        if self.parse_bader:
            try:
                bader = bader_analysis_from_path(dir_name, suffix=".{}".format(taskname))
            except Exception as e:
                bader = "Bader analysis failed: {}".format(e)
            d["bader"] = bader

        return d
Exemple #4
0
if os.path.isfile(OUT_JSON):
    with open(OUT_JSON) as jsonfile:
        out = json.load(jsonfile.read())
else:
    out = {}

for start_id, path_dict in paths.items():
    if start_id not in out.keys():
        logging.info('{}:'.format(start_id))
        paths_lst = []
        for tt in ['static', 'uniform', 'band structure']:
            paths = [p for p in path_dict[tt] if os.path.isdir(p)]
            paths_lst.extend(zip([tt] * len(paths), paths))
        for task_type, path in paths_lst:
            try:
                result = bader_analysis_from_path(path)
                out.update({
                    start_id: {
                        'task_type': task_type,
                        'path': path,
                        'result': result
                    }
                })
                logging.info('  Done within {} path:\n  {}\n'.format(
                    task_type, path))
                break
            except:
                with open(OUT_JSON, 'w') as jsonfile:
                    json.dump(out, jsonfile, indent=4, separators=(',', ': '))
        else:
            logging.warn('  Vaild path not found.\n')
Exemple #5
0
    def process_item(self, item):
        """
        Calculates StructureGraphs (bonding information) for a material
        """

        topology_docs = []
        bader_doc = None

        task_id = item['task_id']
        structure = Structure.from_dict(item['output']['structure'])

        # bonding first

        self.logger.debug("Calculating bonding for {}".format(task_id))

        # try all local_env strategies
        strategies = NearNeighbors.__subclasses__()
        for strategy in strategies:
            try:
                topology_docs.append({
                    'task_id':
                    task_id,
                    'method':
                    strategy.__name__,
                    'graph':
                    StructureGraph.with_local_env_strategy(
                        structure, strategy()).as_dict(),
                    'successful':
                    True
                })
            except Exception as e:

                topology_docs.append({
                    'task_id': task_id,
                    'method': strategy.__name__,
                    'successful': False,
                    'error_message': str(e)
                })

                self.logger.warning(e)
                self.logger.warning("Failed to calculate bonding for {} using "
                                    "{} local_env strategy.".format(
                                        task_id, strategy.__name__))

        # and also critic2 with sum of atomic charge densities
        if self.critic2_available:

            try:
                c2 = Critic2Caller(structure,
                                   user_input_settings=self.critic2_settings)

                topology_docs.append({
                    'task_id':
                    task_id,
                    'method':
                    'critic2_promol',
                    'graph':
                    c2.output.structure_graph().as_dict(),
                    'succesful':
                    True
                })

            except Exception as e:

                topology_docs.append({
                    'task_id': task_id,
                    'method': 'critic2_promol',
                    'critic2_settings': self.critic2_settings,
                    'successful': False,
                    'error_message': str(e)
                })

                self.logger.warning(e)
                self.logger.warning(
                    "Failed to calculate bonding for {} using "
                    "critic2 and sum of atomic charge densities.".format(
                        task_id))

        if self.use_chgcars:

            root_dir = item['calcs_reversed'][0]['dir_name']
            root_dir = self.rewrite_dir(root_dir)

            if not os.path.isdir(root_dir):

                self.logger.error(
                    "Cannot find or cannot access {} for {}.".format(
                        root_dir, task_id))

            else:

                if 'output_file_paths' in item['calcs_reversed'][0]:
                    # we know what output files we have

                    paths = item['calcs_reversed'][0]['output_file_paths']
                    chgcar = paths.get('chgcar', None)
                    aeccar0 = paths.get('aeccar0', None)
                    aeccar2 = paths.get('aeccar2', None)

                else:
                    # we have to search manually

                    self.logger.info(
                        "Searching {} for charge density files for {}.".format(
                            root_dir, task_id))

                    chgcar = glob.glob(root_dir + '/*CHGCAR*')
                    chgcar = chgcar[0] if chgcar else None
                    aeccar0 = glob.glob(root_dir + '/*AECCAR0*')
                    aeccar0 = aeccar0[0] if aeccar0 else None
                    aeccar2 = glob.glob(root_dir + '/*AECCAR2*')
                    aeccar2 = aeccar2[0] if aeccar2 else None

                if chgcar and aeccar0 and aeccar2:

                    try:
                        c2 = Critic2Caller(
                            structure,
                            user_input_settings=self.critic2_settings)

                        topology_docs.append({
                            'task_id':
                            task_id,
                            'method':
                            'critic2_chgcar',
                            'graph':
                            c2.output.structure_graph().as_dict(),
                            'critic2_settings':
                            self.critic2_settings,
                            'critic2_stdout':
                            c2._stdout,
                            'successful':
                            True
                        })

                    except Exception as e:

                        topology_docs.append({
                            'task_id': task_id,
                            'method': 'critic2_chgcar',
                            'critic2_settings': self.critic2_settings,
                            'successful': False,
                            'error_message': str(e)
                        })

                        self.logger.warning(e)
                        self.logger.warning(
                            "Failed to calculate bonding for {} "
                            "using critic2 from CHGCAR.".format(task_id))

                    if self.bader_available:

                        try:
                            bader_doc = bader_analysis_from_path(root_dir)
                            bader_doc['task_id'] = task_id
                            bader_doc['successful'] = True
                        except Exception as e:

                            bader_doc = {
                                'task_id': task_id,
                                'successful': False,
                                'error_message': str(e)
                            }

                            self.logger.warning(e)
                            self.logger.warning(
                                "Failed to perform bader analysis "
                                "for {}".format(task_id))

                else:
                    self.logger.warning(
                        "Not all files necessary for charge analysis "
                        "are present.")
                    if not chgcar:
                        self.logger.warning(
                            "Could not find CHGCAR for {}.".format(task_id))
                    else:
                        self.logger.warning(
                            "CHGCAR found for {}, but AECCAR0 "
                            "or AECCAR2 not present.".format(task_id))

        return {'topology_docs': topology_docs, 'bader_doc': bader_doc}