예제 #1
0
    def test_image_num(self):
        module_dir = os.path.dirname(os.path.abspath(__file__))
        test_file_dir = os.path.join(module_dir, "..", "..", "..", "test_files",
                                     "path_finder")
        start_s = Poscar.from_file(os.path.join(test_file_dir, 'LFP_POSCAR_s')).structure
        end_s = Poscar.from_file(os.path.join(test_file_dir, 'LFP_POSCAR_e')).structure
        mid_s = start_s.interpolate(end_s, nimages=2,
                                       interpolate_lattices=False)[1]
        chg = Chgcar.from_file(os.path.join(test_file_dir, 'LFP_CHGCAR.gz'))
        moving_cation_specie = Element('Li')
        relax_sites = []
        for site_i, site in enumerate(start_s.sites):
            if site.specie == moving_cation_specie:
                relax_sites.append(site_i)
        pf = NEBPathfinder(start_s, end_s, relax_sites=relax_sites,
                           v=ChgcarPotential(chg).get_v(), n_images=(8 * 3))
        images = []
        for i, image in enumerate(pf.images):
            if i % 3 == 0:
                images.append(image)
        self.assertEqual(len(images), 9)

        pf_mid = NEBPathfinder(start_s, end_s, relax_sites=relax_sites,
                           v=ChgcarPotential(chg).get_v(), n_images=10, mid_struct=mid_s)
        moving_site = relax_sites[0]
        dists = [s1.sites[moving_site].distance(s2.sites[moving_site]) 
            for s1, s2 in zip(pf.images[:-1], pf.images[1:])]
        # check that all the small distances are about equal
        self.assertTrue(abs(min(dists)-max(dists))/mean(dists) < 0.02)
예제 #2
0
    def find_path(self, end_structure, images, sites=[], charge=None):
        from pymatgen.analysis.path_finder import NEBPathfinder
        from pymatgen.analysis.path_finder import FreeVolumePotential
        from pymatgen.analysis.path_finder import ChgcarPotential
        init_struct = self.poscar.structure
        final_struct = end_structure
        if charge is None:
            dim = [50, 50, 120]
            v = FreeVolumePotential(final_struct, dim)
            obj = NEBPathfinder(init_struct,
                                final_struct,
                                sites,
                                v.get_v(),
                                n_images=images)
            obj.interpolate()
            new_path = obj.images
        else:
            v = ChgcarPotential(charge)
            obj = NEBPathfinder(init_struct,
                                final_struct, [80, 81, 82],
                                v.get_v(),
                                n_images=images)
            obj.interpolate()
            new_path = obj.images

        joblist = []
        for index, item in enumerate(new_path):
            filepath = os.path.join(self.foldname, '{0:02d}'.format(index))
            joblist.append(easyjob(item, filepath))
        return joblist
예제 #3
0
    def _get_pathfinder_from_hop(self, migration_path, n_images=20):
        # get migration pathfinder objects which contains the paths
        ipos = migration_path.isite.frac_coords
        epos = migration_path.esite.frac_coords
        mpos = migration_path.esite.frac_coords

        start_struct = self.base_aeccar.structure.copy()
        end_struct = self.base_aeccar.structure.copy()
        mid_struct = self.base_aeccar.structure.copy()

        # the moving ion is always inserted on the zero index
        start_struct.insert(0,
                            self.migrating_specie,
                            ipos,
                            properties=dict(magmom=0))
        end_struct.insert(0,
                          self.migrating_specie,
                          epos,
                          properties=dict(magmom=0))
        mid_struct.insert(0,
                          self.migrating_specie,
                          mpos,
                          properties=dict(magmom=0))

        chgpot = ChgcarPotential(self.base_aeccar, normalize=False)
        npf = NEBPathfinder(
            start_struct,
            end_struct,
            relax_sites=[0],
            v=chgpot.get_v(),
            n_images=n_images,
            mid_struct=mid_struct,
        )
        return npf
예제 #4
0
    def test_image_num(self):
        os.path.dirname(os.path.abspath(__file__))
        test_file_dir = os.path.join(PymatgenTest.TEST_FILES_DIR,
                                     "path_finder")
        start_s = Poscar.from_file(os.path.join(test_file_dir,
                                                "LFP_POSCAR_s")).structure
        end_s = Poscar.from_file(os.path.join(test_file_dir,
                                              "LFP_POSCAR_e")).structure
        chg = Chgcar.from_file(os.path.join(test_file_dir, "LFP_CHGCAR.gz"))
        moving_cation_specie = Element("Li")
        relax_sites = []
        for site_i, site in enumerate(start_s.sites):
            if site.specie == moving_cation_specie:
                relax_sites.append(site_i)
        pf = NEBPathfinder(
            start_s,
            end_s,
            relax_sites=relax_sites,
            v=ChgcarPotential(chg).get_v(),
            n_images=(8 * 3),
        )
        images = []
        for i, image in enumerate(pf.images):
            if i % 3 == 0:
                images.append(image)
        self.assertEqual(len(images), 9)

        moving_site = relax_sites[0]
        dists = [
            s1.sites[moving_site].distance(s2.sites[moving_site])
            for s1, s2 in zip(pf.images[:-1], pf.images[1:])
        ]
        # check that all the small distances are about equal
        self.assertTrue(abs(min(dists) - max(dists)) / mean(dists) < 0.02)
예제 #5
0
 def test_image_num(self):
     module_dir = os.path.dirname(os.path.abspath(__file__))
     test_file_dir = os.path.join(module_dir, "..", "..", "..",
                                  "test_files", "path_finder")
     start_s = Poscar.from_file(os.path.join(test_file_dir,
                                             'LFP_POSCAR_s')).structure
     end_s = Poscar.from_file(os.path.join(test_file_dir,
                                           'LFP_POSCAR_e')).structure
     chg = Chgcar.from_file(os.path.join(test_file_dir, 'LFP_CHGCAR.gz'))
     moving_cation_specie = Element('Li')
     relax_sites = []
     for site_i, site in enumerate(start_s.sites):
         if site.specie == moving_cation_specie:
             relax_sites.append(site_i)
     pf = NEBPathfinder(start_s,
                        end_s,
                        relax_sites=relax_sites,
                        v=ChgcarPotential(chg).get_v(),
                        n_images=(8 * 3))
     images = []
     for i, image in enumerate(pf.images):
         if i % 3 == 0:
             images.append(image)
     self.assertEqual(len(images), 9)
예제 #6
0
def neb(directory,
        nimages=7,
        functional=("pbe", {}),
        is_metal=False,
        is_migration=False):
    """
    Set up the NEB calculation from the initial and final structures.

    Args:
        directory (str): Directory in which the transition calculations should be
            set up.
        functional (tuple): Tuple with the functional choices. The first element
            contains a string that indicates the functional used ("pbe", "hse", ...),
            whereas the second element contains a dictionary that allows the user
            to specify the various functional tags.
        nimages (int): Number of images to use in the NEB calculation.
        is_metal (bool): Flag that indicates the material being studied is a
            metal, which changes the smearing from Gaussian to second order
            Methfessel-Paxton of 0.2 eV.
        is_migration (bool): Flag that indicates that the transition is a migration
            of an atom in the structure.

    Returns:
        None

    """
    directory = os.path.abspath(directory)

    # Extract the optimized initial and final geometries
    initial_dir = os.path.join(directory, "initial")
    final_dir = os.path.join(directory, "final")

    try:
        # Check to see if the initial final_cathode structure is present
        initial_structure = Cathode.from_file(
            os.path.join(initial_dir,
                         "final_cathode.json")).as_ordered_structure()

    except FileNotFoundError:
        # In case the required json file is not present, check to see if
        # there is VASP output which can be used
        initial_structure = Structure.from_file(
            os.path.join(initial_dir, "CONTCAR"))

        # Add the magnetic configuration to the initial structure
        initial_out = Outcar(os.path.join(initial_dir, "OUTCAR"))
        initial_magmom = [site["tot"] for site in initial_out.magnetization]

        try:
            initial_structure.add_site_property("magmom", initial_magmom)
        except ValueError:
            if len(initial_magmom) == 0:
                print("No magnetic moments found in OUTCAR file. Setting "
                      "magnetic moments to zero.")
                initial_magmom = [0] * len(initial_structure)
                initial_structure.add_site_property("magmom", initial_magmom)
            else:
                raise ValueError("Number of magnetic moments in OUTCAR file "
                                 "do not match the number of sites!")
    except BaseException:
        raise FileNotFoundError("Could not find required structure "
                                "information in " + initial_dir + ".")

    try:
        final_structure = Structure.from_file(
            os.path.join(final_dir, "CONTCAR"))
    except FileNotFoundError:
        final_structure = Cathode.from_file(
            os.path.join(final_dir,
                         "final_cathode.json")).as_ordered_structure()

    # In case the transition is a migration
    if is_migration:
        # Set up the static potential for the Pathfinder from the host charge
        # density
        host_charge_density = Chgcar.from_file(os.path.join(directory, "host"))
        host_potential = ChgcarPotential(host_charge_density)

        migration_site_index = find_migrating_ion(initial_structure,
                                                  final_structure)

        neb_path = NEBPathfinder(start_struct=initial_structure,
                                 end_struct=final_structure,
                                 relax_sites=migration_site_index,
                                 v=host_potential)

        images = neb_path.images
        neb_path.plot_images("neb.vasp")

    # In case an "middle image" has been provided via which to interpolate
    elif os.path.exists(os.path.join(directory, "middle")):
        print("Found a 'middle' directory in the NEB directory. Interpolating "
              "via middle geometry.")
        # Load the middle image
        middle_structure = Structure.from_file(
            os.path.join(directory, "middle", "CONTCAR"))
        # Perform an interpolation via this image
        images_1 = initial_structure.interpolate(
            end_structure=middle_structure,
            nimages=int((nimages + 1) / 2),
            interpolate_lattices=True)
        images_2 = middle_structure.interpolate(end_structure=final_structure,
                                                nimages=int((nimages) / 2 + 1),
                                                interpolate_lattices=True)

        images = images_1[:-1] + images_2

    else:
        # Linearly interpolate the initial and final structures
        images = initial_structure.interpolate(end_structure=final_structure,
                                               nimages=nimages + 1,
                                               interpolate_lattices=True)

    # TODO Add functionality for NEB calculations with changing lattices

    user_incar_settings = {}

    # Set up the functional
    if functional[0] != "pbe":
        functional_config = _load_yaml_config(functional[0] + "Set")
        functional_config["INCAR"].update(functional[1])
        user_incar_settings.update(functional_config["INCAR"])

    # Add the standard Methfessel-Paxton smearing for metals
    if is_metal:
        user_incar_settings.update({"ISMEAR": 2, "SIGMA": 0.2})

    neb_calculation = PybatNEBSet(images,
                                  potcar_functional=DFT_FUNCTIONAL,
                                  user_incar_settings=user_incar_settings)

    # Set up the NEB calculation
    neb_calculation.write_input(directory)

    # Make a file to visualize the transition
    neb_calculation.visualize_transition(
        os.path.join(directory, "transition.cif"))
예제 #7
0
    def run_task(self, fw_spec):
        n_images = self["n_images"]
        end_points_combo = self["end_points_combo"]
        # checks if format of end_points_combo is correct and if so
        # get two desired end_points indexes for end point structures
        try:
            combo = end_points_combo.split("+")
            if len(combo) == 2:
                c = [int(combo[0]), int(combo[1])]
            else:
                raise ValueError(
                    "NEBPathfinder requires exactly two end points")
        except:
            raise ValueError("{} end_points_combo input is incorrect".format(
                str(end_points_combo)))

        # get the database connection
        db_file = env_chk(self["db_file"], fw_spec)
        mmdb = VaspCalcDb.from_db_file(db_file, admin=True)
        mmdb.collection = mmdb.db["approx_neb"]
        wf_uuid = self["approx_neb_wf_uuid"]

        # get end_points and task_id of host from approx_neb collection
        approx_neb_doc = mmdb.collection.find_one({"wf_uuid": wf_uuid}, {
            "end_points": 1,
            "host.task_id": 1,
            "_id": 0
        })
        end_points = approx_neb_doc["end_points"]
        task_id = approx_neb_doc["host"]["task_id"]

        # get potential gradient, v, from host chgcar
        mmdb.collection = mmdb.db["tasks"]
        host_chgcar = mmdb.get_chgcar(task_id)
        v_chgcar = ChgcarPotential(host_chgcar)
        host_v = v_chgcar.get_v()

        # get start and end point structures from end_points
        start_struct = Structure.from_dict(
            end_points[c[0]]["output"]["structure"])
        end_struct = Structure.from_dict(
            end_points[c[1]]["output"]["structure"])

        # checks if inserted site indexes match
        inserted_site_indexes = end_points[c[0]]["inserted_site_indexes"]
        if inserted_site_indexes != end_points[c[1]]["inserted_site_indexes"]:
            raise ValueError(
                "Inserted site indexes of end point structures must match for NEBPathfinder"
            )

        # applies NEBPathFinder to interpolate and get images to store in
        # pathfinder_output.
        neb_pf = NEBPathfinder(
            start_struct,
            end_struct,
            relax_sites=inserted_site_indexes,
            v=host_v,
            n_images=n_images + 1,
        )
        # note NEBPathfinder currently returns n_images+1 images (rather than n_images)
        # and the first and last images generated are very similar to the end points
        # provided so they are discarded
        pathfinder_output = {
            "images":
            [structure.as_dict() for structure in neb_pf.images[1:-1]],
            "relax_site_indexes": inserted_site_indexes,
        }

        # stores images generated by NEBPathFinder in approx_neb collection
        # pathfinder field which is a nested dictionary using
        # end_points_combo as a key.
        mmdb.collection = mmdb.db["approx_neb"]
        pf_subdoc = mmdb.collection.find_one({"wf_uuid": wf_uuid}, {
            "pathfinder": 1,
            "_id": 0
        })
        if "pathfinder" not in pf_subdoc.keys():
            pf_subdoc = {}
        else:
            pf_subdoc = pf_subdoc["pathfinder"]
        pf_subdoc.update({end_points_combo: pathfinder_output})

        mmdb.collection.update_one(
            {"wf_uuid": wf_uuid},
            {
                "$set": {
                    "pathfinder": pf_subdoc,
                    "last_updated": datetime.utcnow()
                }
            },
        )

        return FWAction(
            stored_data={
                "wf_uuid": wf_uuid,
                "end_points_combo": c,
                "pathfinder": pathfinder_output,
            })