def from_dict(cls, d):
        # Programmatic access to enumeration members in Enum class.
        structure = d["structure"]
        if isinstance(structure, dict):
            structure = Structure.from_dict(structure)

        return cls(structure=structure,
                   xc=Xc.from_string(d["xc"]),
                   task=Task.from_string(d["task"]),
                   kpoints=d["kpoints"],
                   potcar=Potcar.from_dict(d["potcar"]),
                   incar_settings=d["incar_settings"],
                   files_to_transfer=d["files_to_transfer"],
                   **d["kwargs"])
Example #2
0
def parse_args(args):
    vise_yaml_files = '\n'.join(["* " + str(f) for f in defaults.yaml_files])
    parser = argparse.ArgumentParser(
        description=f"""
Vise is a package that helps researchers to do first-principles calculations
with the VASP code.

Author: Yu Kumagai
Version: {__version__}
    """,
        epilog=f"The parsed vise.yaml files are:\n{vise_yaml_files}",
        formatter_class=argparse.RawTextHelpFormatter)

    subparsers = parser.add_subparsers()

    # -- parent parser: vasprun
    vasprun_parser = argparse.ArgumentParser(description="", add_help=False)
    vasprun_parser.add_argument(
        "-v", "--vasprun", type=Path, default=defaults.vasprun,
        help="vasprun.xml file name.")
    # -- parent parser: outcar
    outcar_parser = argparse.ArgumentParser(description="", add_help=False)
    outcar_parser.add_argument(
        "-o", "--outcar", type=Path, default=defaults.outcar,
        help="OUTCAR file name.")

    # -- structure_info ---------------------------------------------------
    parser_structure_info = subparsers.add_parser(
        name="structure_info",
        description="Tools showing structure info or conventional cell.",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['si'])

    parser_structure_info.add_argument(
        "-p", "--poscar", type=str, default="POSCAR",
        help="POSCAR-type file name to be read.", metavar="FILE")
    parser_structure_info.add_argument(
        "-s", "--symprec", type=float,
        default=defaults.symmetry_length_tolerance,
        help="Length tolerance for symmetry analysis.")
    parser_structure_info.add_argument(
        "-a", "--angle_tolerance", type=float,
        default=defaults.symmetry_angle_tolerance,
        help="Angle tolerance in degree for symmetry analysis.")
    parser_structure_info.add_argument(
        "-c", "--show_conventional", action="store_true",
        help="Set when showing the conventional cell.")

    parser_structure_info.set_defaults(func=structure_info)

    # -- get_poscar -----------------------------------------------------------
    parser_get_poscar = subparsers.add_parser(
        name="get_poscar",
        description="Tools for generating a POSCAR file.",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['gp'])

    parser_get_poscar.add_argument(
        "-p", "--poscar", type=str, default="POSCAR",
        help="POSCAR-type file name.", metavar="FILE")
    parser_get_poscar.add_argument(
        "-pi", "--prior_info", type=Path, default=Path("prior_info.yaml"),
        help="prior_info.yaml file name.", metavar="FILE")
    parser_get_poscar.add_argument(
        "-m", "--mpid", type=str, required=True,
        help="MP entry id with prefix, e.g., mp-1234.")

    parser_get_poscar.set_defaults(func=get_poscar_from_mp)

    # -- make_atom_poscars -----------------------------------------------------
    parser_make_atom_poscars = subparsers.add_parser(
        name="make_atom_poscars",
        description="Tools for generating POSCAR files for atom calculations.",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['map'])

    parser_make_atom_poscars.add_argument(
        "-d", "--dirname", type=Path, default=Path.cwd(),
        help="Directory name where atom calculation directories are created.")
    parser_make_atom_poscars.add_argument(
        "-e", "--elements", type=Element, default=None, nargs="+",
        help="Element names. When not set, all atom directories are created")

    parser_make_atom_poscars.set_defaults(func=make_atom_poscars)

    # -- vasp_set ---------------------------------------------------------
    parser_vasp_set = subparsers.add_parser(
        name="vasp_set",
        description="Tools for constructing vasp input set",
        parents=[vasprun_parser, outcar_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['vs'])

    parser_vasp_set.add_argument(
        "-p", "--poscar", default="POSCAR", type=Path,
        help="POSCAR-type input structure file name.")
    parser_vasp_set.add_argument(
        "-t", dest="task", default=defaults.task, type=Task,
        choices=Task.name_list(),
        help=f"Task name.")
    parser_vasp_set.add_argument(
        "-x", dest="xc", default=defaults.xc, type=Xc, choices=Xc.name_list(),
        help=f"Exchange-correlation (XC) interaction.")
    parser_vasp_set.add_argument(
        # Default must be None to use the insulator_kpoint_density.
        # defaults.kpoints_density is used at structure_kpoints_generator.py
        "-k", "--kpt_density", type=float,
        help="K-point density in Angstrom along each direction. "
             f"Defaults of metal and insulators are {defaults.kpoint_density}, "
             f"and {defaults.insulator_kpoint_density}.")
    parser_vasp_set.add_argument(
        "--potcar", dest="overridden_potcar",
        default=defaults.overridden_potcar, type=str, nargs="+",
        help="User specifying POTCAR set. E.g., Mg_pv O_h")
    parser_vasp_set.add_argument(
        "-uis", "--user_incar_settings", type=str, nargs="+",
        help="""Overwritten incar settings described with pairs of INCAR tag 
        names and values. These can also be set by vise.yaml, but they are 
        overwritten by this command line options.""")
    parser_vasp_set.add_argument(
        "-d", "--prev_dir", type=Path,
        help="Parse the previous calculations for better input settings.")
    parser_vasp_set.add_argument(
        "--options", type=str, nargs="+",
        help="Manual options used at CategorizedInputOptions class.")
    parser_vasp_set.add_argument(
        "--uniform_kpt_mode", action="store_true",
        help="""Kpoints with uniform k-point sampling. The k-point sampling mesh 
        and centering are determined based on the given lattice. Note that only 
        when the angles are 90 degrees, the centering is shifted along the 
        perpendicular direction. This mode is useful when calculating the 
        supercells. """)
    parser_vasp_set.add_argument(
        "--file_transfer_type", type=str, nargs="+",
        help="File transfer which is used with the prev_dir argument and can "
             "be written with pairs of file names and transfer types of "
             "m (move), c (copy) or l (link). An example is: "
             "--file_transfer_type POSCAR c WAVECAR l ")

    parser_vasp_set.set_defaults(func=VaspSet)

    # -- plot_band -------------------------------------------------------------
    parser_plot_band = subparsers.add_parser(
        name="plot_band",
        description="Tools for plotting band structures",
        parents=[vasprun_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['pb'])

    parser_plot_band.add_argument(
        "-k", "--kpoints", dest="kpoints_filename", default="KPOINTS", type=str,
        help="KPOINTS file name.")
    parser_plot_band.add_argument(
        "-y", "--y_range", nargs="+", default=[-10, 10], type=float,
        help="Energy range, requiring two values.")
    parser_plot_band.add_argument(
        "-f", "--filename", type=str, default="band.pdf", help="Pdf file name.")

    parser_plot_band.set_defaults(func=plot_band)

    # -- plot_dos -----------------------------------------------------------
    parser_plot_dos = subparsers.add_parser(
        name="plot_dos",
        description="Tools for plotting density of states",
        parents=[vasprun_parser, outcar_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['pd'])

    parser_plot_dos.add_argument(
        "-t", "--type",
        type=AtomGroupingType,
        default=AtomGroupingType.non_equiv_sites,
        choices=AtomGroupingType.name_list(),
        help="How to group atoms for pdos.")
    parser_plot_dos.add_argument(
        "-l", "--legend", type=str2bool, default=True,
        help="Whether to show the figure legend.")
    parser_plot_dos.add_argument(
        "-c", "--crop_first_value", type=str2bool, default=True,
        help="Whether to crop the first value in DOS.")
    parser_plot_dos.add_argument(
        "-x", "--x_range", nargs="+", type=float,
        help="Set minimum and maximum energies.")
    parser_plot_dos.add_argument(
        "-y", "--y_max_ranges", nargs="+", type=float,
        help="Set max energies for each plot. AssertionError is raised when"
             "the number of ranges does not fit to the number of figures.")
    parser_plot_dos.add_argument(
        "--target", type=str, nargs="+",
        help="""Show specific PDOS. The input depends on AtomGroupingType.\n 
        AtomGroupingType.atoms:   1,2 3 \n
        AtomGroupingType.elements: Mg O""")
    parser_plot_dos.add_argument(
        "-f", "--filename", type=str, default="dos.pdf", help="Pdf file name.")
    parser_plot_dos.add_argument(
        "-b", "--base_energy", type=float,
        help="Set when showing the figure in the absolute energies scale.")
    parser_plot_dos.set_defaults(func=plot_dos)

    # -- plot_absorption -------------------------------------------------------
    parser_plot_absorption = subparsers.add_parser(
        name="plot_absorption",
        description="Tools for plotting absorption coefficient",
        parents=[vasprun_parser, outcar_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['pa'])

    parser_plot_absorption.add_argument(
        "-f", "--filename", type=str, default="absorption.pdf",
        help="Pdf file name.")
    parser_plot_absorption.add_argument(
        "-y", "--y_ranges", nargs="+", default=[10**3, 10**8],
        type=lambda x: 10 ** float(x),
        help="Exponential parts of base-10 for energy range in cm-1, "
             "requiring two values.")
    parser_plot_absorption.add_argument(
        "-ckk", "--calc_kk", action="store_true",
        help="Set when real part of dielectric function is explicitly "
             "calculated using the Kramers-Kronig transformation.")
    parser_plot_absorption.add_argument(
        "-i", "--ita", type=float, default=0.01,
        help="Complex shift η in the Kramers-Kronig transformation.")

    parser_plot_absorption.set_defaults(func=plot_absorption)

    # -- effective_mass -------------------------------------------------------
    parser_effective_mass = subparsers.add_parser(
        name="effective_mass",
        description="Tools for calculating effective masses",
        parents=[vasprun_parser, outcar_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['em'])

    parser_effective_mass.add_argument(
        "-t", "--temperature", type=float, default=300,
        help="Temperature in K.")
    parser_effective_mass.add_argument(
        "-c", "--concentrations", type=lambda x: 10 ** float(x), nargs="+",
        help="Exponential parts of base-10 for concentrations in cm-3")

    parser_effective_mass.set_defaults(func=calc_effective_mass)

    # -- band_edge -------------------------------------------------------------
    parser_band_edge = subparsers.add_parser(
        name="band_edge",
        description="Calculate the band edge positions",
        parents=[vasprun_parser, outcar_parser],
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        aliases=['be'])

    parser_band_edge.set_defaults(func=band_edge_properties)

    try:
        import argcomplete
        argcomplete.autocomplete(parser)
        # https://github.com/kislyuk/argcomplete#activating-global-completion
        # This supports bash/zsh autocompletion. To enable this, pip install
        # argcomplete, activate global completion, or add
        #      eval "$(register-python-argcomplete vise)"
        # into your .bash_profile or .bashrc
    except ImportError:
        pass

    return parser.parse_args(args)
def vasp_set(args):
    if args.print_json:
        vis = ViseInputSet.load_json(args.print_json)
        print(vis)
        return

    flags = [str(s) for s in list(Element)]
    ldauu = list2dict(args.ldauu, flags)
    ldaul = list2dict(args.ldaul, flags)
    potcar_set = potcar_str2dict(args.potcar_set)
    task = Task.from_string(args.task)
    xc = Xc.from_string(args.xc)

    base_kwargs = {"kpt_density": args.kpt_density,
                   "standardize_structure": args.standardize,
                   "ldauu": ldauu,
                   "ldaul": ldaul}

    flags = list(ViseInputSet.ALL_OPTIONS.keys())
    base_kwargs.update(list2dict(args.vise_opts, flags))

    flags = list(chain.from_iterable(incar_flags.values()))
    base_user_incar_settings = list2dict(args.user_incar_setting, flags)

    original_dir = os.getcwd()
    dirs = args.dirs or ["."]

    for d in dirs:
        os.chdir(os.path.join(original_dir, d))
        logger.info(f"Constructing vasp set in {d}")
        user_incar_settings = deepcopy(base_user_incar_settings)
        kwargs = deepcopy(base_kwargs)

        if args.prior_info:
            if os.path.exists("prior_info.json"):
                prior_info = PriorInfo.load_json("prior_info.json")
                kwargs["band_gap"] = prior_info["band_gap"]
                kwargs["is_magnetization"] = \
                    abs(prior_info["total_magnetization"]) > 0.1

        if args.prev_dir:
            files = {"CHGCAR": "C", "WAVECAR": "M", "WAVEDER": "M"}
            input_set = ViseInputSet.from_prev_calc(args.prev_dir,
                                                    task=task,
                                                    xc=xc,
                                                    charge=args.charge,
                                                    files_to_transfer=files,
                                                    **kwargs)
        else:
            s = Structure.from_file(args.poscar)
            input_set = \
                ViseInputSet.make_input(structure=s,
                                        task=task,
                                        xc=xc,
                                        charge=args.charge,
                                        user_incar_settings=user_incar_settings,
                                        override_potcar_set=potcar_set,
                                        **kwargs)

        input_set.write_input(".")

    os.chdir(original_dir)
Example #4
0
 def task(self):
     return Task(self._task)
Example #5
0
def test_task_name_list():
    expected = 'structure_opt, structure_opt_rough, structure_opt_tight, ' \
               'cluster_opt, phonon_force, defect, band, dos, ' \
               'dielectric_dfpt, dielectric_finite_field, dielectric_function'
    assert Task.names_string() == expected
Example #6
0
def test_task_from_string_raise_error():
    with pytest.raises(AttributeError):
        Task.from_string("not_exist_member")
Example #7
0
def test_task_from_string():
    assert Task.dos == Task.from_string("dos")
    def make_input(cls,
                   structure: Structure,
                   task: Union[str, Task] = Task.structure_opt,
                   xc: Union[str, Xc] = Xc.pbe,
                   prev_set: "ViseInputSet" = None,
                   abs_files_to_transfer: Optional[dict] = None,
                   user_incar_settings: Optional[dict] = None,
                   **kwargs) -> "ViseInputSet":
        """Construct ViseInputSet from some options including xc and task.

        To make a simple but practical rule for inheriting the previous
        calculation condition and effectively use its results, we adopt
        fail-safe rule.

        When, the prev_set is set, we compare task and xc between current
        input and prev_set, and inherit some options depending on whether
        task and/or xc are common. For instance, when we calculate the band
        structure with the HSE06 hybrid functional, it would be better to
        generate the WAVECAR file with e.g., PBE functional to reduce the
        computational cost. Then, the initial task was done with task=Task.band
        and xc=Xc.pbe with user_incar_setting={"LWAVE": True}, and next task is
        performed with task=Task.band, xc=Xc.hs. Then, task is common, so
        input set related to TaskStructureKpoints and TaskIncarSettings are
        inherited. Note that, CommonIncarSettings options are always inherited.

        Other notes are as follows.

        Note1: Charge set to structure is ignored when determining NELECT. For
               this purpose, charge option needs to be used.
        Note2: When the structure is changed via find_spglib_standard_primitive,
               all the site properties are removed.
        Note3: When different version of ViseInputSet with different defaults,
               the consistency of input set is destroyed, so the same version
               must be used when options are inherited.
        Note4: Other INCAR flags than those defined by TaskIncarSettings,
               XcIncarSettings, XcTaskIncarSettings, and CommonIncarSettings are
               not inherited. When some of them need to be inherited, they
               should be added to COMMON_OPTIONAL_FLAGS.
        Note5: user_incar_settings is not inherited from prev_set. One needs
               to explicitly specify it, again, if needed.

        Args:
            structure (Structure):
                The Structure to create inputs for.
            task (Task):
                Task defined in Task.
            xc (Xc):
                Exchange-correlation (xc) defined in Xc.
            prev_set (ViseInputSet):
                Previous ViseInputSet.
            abs_files_to_transfer (dict):
                Keys are file names with absolute paths to be transferred.
                Values mean the transfer modes, where there are three modes,
                "c": copy file, "m": move file, "l": make symbolic link
                e.g., {"/..../CHGCAR": "c", "/..../WAVECAR": "l", ..}
            user_incar_settings (dict):
                User INCAR settings.
                e.g., {"NSW": 100, "LWAVE": True, "LASPH": "A", ..}
            kwargs (dict):
                OPTION arguments.

        Return:
            ViseInputSet class object.
        """
        abs_files_to_transfer = abs_files_to_transfer or {}
        user_incar_settings = user_incar_settings or {}

        if isinstance(task, str):
            task = Task.from_string(task)
        if isinstance(xc, str):
            xc = Xc.from_string(xc)

        # First, set default.
        opts = deepcopy(cls.ALL_OPTIONS)

        # Second, override with previous condition
        if prev_set:
            key_set = set(cls.COMMON_OPTIONS.keys())
            if prev_set.task == task:
                key_set.update(cls.TASK_OPTIONS.keys())
            if prev_set.xc == xc:
                key_set.update(cls.XC_OPTIONS.keys())
            if prev_set.task == task and prev_set.xc == xc:
                key_set.update(cls.XC_TASK_OPTIONS)

            for k in key_set:
                opts[k] = prev_set.kwargs[k]

        # Third, override with keyword arguments
        for k in kwargs:
            if k not in opts.keys():
                logger.warning(
                    f"Keyword {k} is not adequate for ViseInputSet.")
        opts.update(kwargs)

        task_str_kpt = TaskStructureKpoints.from_options(
            task=task,
            original_structure=structure,
            standardize_structure=opts["standardize_structure"],
            sort_structure=opts["sort_structure"],
            is_magnetization=opts["is_magnetization"],
            kpt_mode=opts["kpt_mode"],
            kpt_density=opts["kpt_density"],
            kpt_shift=opts["kpt_shift"],
            only_even=opts["only_even"],
            band_ref_dist=opts["band_ref_dist"],
            factor=opts["factor"],
            symprec=opts["symprec"],
            angle_tolerance=opts["angle_tolerance"])

        orig_matrix = structure.lattice.matrix
        matrix = task_str_kpt.structure.lattice.matrix
        structure_changed = \
            not np.allclose(orig_matrix, matrix, atol=opts["symprec"])

        if structure_changed and opts["charge"] != 0:
            raise ValueError("Structure is changed but charge is set.")

        if structure_changed:
            # The following files are useless when lattice is changed.
            pattern = \
                re.compile("|".join([r"CHGCAR$", r"WAVECAR$", r"WAVEDER"]))
            for f in abs_files_to_transfer:
                if pattern.match(f):
                    abs_files_to_transfer.pop(f, None)

        # unique_justseen https://docs.python.org/ja/3/library/itertools.html
        # ["H", "H", "O", "O", "H"] -> ['H', 'O', 'H']
        symbol_list = get_symbol_list(task_str_kpt.structure)

        xc_task_potcar = XcTaskPotcar.from_options(
            xc=xc,
            symbol_list=symbol_list,
            potcar_set_name=opts["potcar_set_name"],
            override_potcar_set=opts["override_potcar_set"])

        task_settings = TaskIncarSettings.from_options(
            task=task,
            structure=task_str_kpt.structure,
            potcar=xc_task_potcar.potcar,
            num_kpoints=task_str_kpt.num_kpts,
            max_enmax=xc_task_potcar.max_enmax,
            is_magnetization=opts["is_magnetization"],
            vbm_cbm=opts["vbm_cbm"],
            npar_kpar=opts["npar_kpar"],
            num_nodes=opts["num_nodes"],
            encut=opts["encut"],
            structure_opt_encut_factor=opts["structure_opt_encut_factor"],
            dos_step_size=opts["dos_step_size"])

        xc_settings = XcIncarSettings.from_options(
            xc=xc,
            symbol_list=symbol_list,
            factor=task_str_kpt.factor,
            aexx=opts["aexx"],
            hubbard_u=opts["hubbard_u"],
            ldauu=opts["ldauu"],
            ldaul=opts["ldaul"],
            ldaul_set_name=opts["ldaul_set_name"])

        xc_task_settings = XcTaskIncarSettings.from_options()

        common_settings = CommonIncarSettings.from_options(
            potcar=xc_task_potcar.potcar,
            composition=task_str_kpt.structure.composition,
            charge=opts["charge"])

        incar_settings = \
            {**task_settings.settings, **xc_settings.settings,
             **xc_task_settings.settings, **common_settings.settings}

        # user_incar_settings is the top prioritized.
        incar_settings.update(user_incar_settings)

        # TODO: tweak the unfavorable combination of the input set.
        # e.g., Avoiding ICHARG = 11 is a must for hybrid functional.

        return cls(structure=task_str_kpt.structure,
                   task=task,
                   xc=xc,
                   kpoints=task_str_kpt.kpoints,
                   potcar=xc_task_potcar.potcar,
                   incar_settings=incar_settings,
                   files_to_transfer=abs_files_to_transfer,
                   **opts)