Beispiel #1
0
def abicomp_ebands(options):
    """
    Plot electron bands on a grid.
    """
    paths, e0 = options.paths, options.e0
    plotter = abilab.ElectronBandsPlotter(key_ebands=[(os.path.relpath(p), p) for p in paths])

    if options.ipython:
        import IPython
        IPython.embed(header=str(plotter) + "\nType `plotter` in the terminal and use <TAB> to list its methods",
                      plotter=plotter)

    elif options.notebook:
        plotter.make_and_open_notebook(daemonize=not options.no_daemon)

    else:
        # Print pandas Dataframe.
        frame = plotter.get_ebands_frame()
        abilab.print_frame(frame)

        # Optionally, print info on gaps and their location
        if not options.verbose:
            print("\nUse --verbose for more information")
        else:
            for ebands in plotter.ebands_list:
                print(ebands)

        # Here I select the plot method to call.
        if options.plot_mode != "None":
            plotfunc = getattr(plotter, options.plot_mode, None)
            if plotfunc is None:
                raise ValueError("Don't know how to handle plot_mode: %s" % options.plot_mode)
            plotfunc(e0=e0)

    return 0
Beispiel #2
0
def abicomp_ebands(options):
    """
    Plot electron bands on a grid.
    """
    paths, e0 = options.paths, options.e0
    plotter = abilab.ElectronBandsPlotter(key_ebands=[(os.path.relpath(p), p) for p in paths])

    if options.ipython:
        import IPython
        IPython.embed(header=str(plotter) + "\nType `plotter` in the terminal and use <TAB> to list its methods",
                      plotter=plotter)

    elif options.notebook:
        plotter.make_and_open_notebook(daemonize=not options.no_daemon)

    else:
        # Print pandas Dataframe.
        frame = plotter.get_ebands_frame()
        abilab.print_frame(frame)

        # Optionally, print info on gaps and their location
        if not options.verbose:
            print("\nUse --verbose for more information")
        else:
            for ebands in plotter.ebands_list:
                print(ebands)

        # Here I select the plot method to call.
        if options.plot_mode != "None":
            plotfunc = getattr(plotter, options.plot_mode, None)
            if plotfunc is None:
                raise ValueError("Don't know how to handle plot_mode: %s" % options.plot_mode)
            plotfunc(e0=e0)

    return 0
Beispiel #3
0
def abicomp_structure(options):
    """
    Compare crystalline structures.
    """
    paths = options.paths
    index = [os.path.relpath(p) for p in paths]

    if options.notebook:
        import nbformat
        nbv = nbformat.v4
        nb = nbv.new_notebook()

        nb.cells.extend([
            nbv.new_code_cell("""\
from __future__ import print_function, division, unicode_literals, absolute_import
import sys
import os

%matplotlib notebook
from IPython.display import display
#import seaborn as sns

from abipy import abilab"""),
            nbv.new_code_cell("dfs = abilab.frames_from_structures(%s, index=%s)" % (paths, index)),
            # Analyze dataframes.
            nbv.new_code_cell("dfs.lattice"),
            nbv.new_code_cell("dfs.coords"),
            nbv.new_code_cell("# for structure in dfs.structures: display(structure)"),
        ])

        import io, tempfile # os,
        _, nbpath = tempfile.mkstemp(prefix="abinb_", suffix='.ipynb', dir=os.getcwd(), text=True)

        # Write notebook
        import nbformat
        with io.open(nbpath, 'wt', encoding="utf8") as fh:
            nbformat.write(nb, fh)

        cmd = "jupyter notebook %s" % nbpath
        return os.system(cmd)

    dfs = abilab.frames_from_structures(paths, index=index)

    if options.ipython:
        import IPython
        IPython.embed(header="Type `dfs` in the terminal and use <TAB> to list its methods", dfs=dfs)
    else:
        print("File list:")
        for i, p in enumerate(paths):
            print("%d %s" % (i, p))
        print()
        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        abilab.print_frame(dfs.coords, title="Atomic positions (columns give the site index):")

    return 0
Beispiel #4
0
def abicomp_structure(options):
    """
    Compare crystalline structures.
    """
    paths = options.paths
    index = [os.path.relpath(p) for p in paths]

    if options.notebook:
        import nbformat
        nbv = nbformat.v4
        nb = nbv.new_notebook()

        nb.cells.extend([
            nbv.new_code_cell("""\
from __future__ import print_function, division, unicode_literals, absolute_import
import sys
import os

%matplotlib notebook
from IPython.display import display
#import seaborn as sns

from abipy import abilab"""),
            nbv.new_code_cell("dfs = abilab.frames_from_structures(%s, index=%s)" % (paths, index)),
            nbv.new_code_cell("dfs.lattice"),
            nbv.new_code_cell("dfs.coords"),
            nbv.new_code_cell("for structure in dfs.structures: display(structure)"),
        ])

        import io, tempfile # os,
        _, nbpath = tempfile.mkstemp(prefix="abinb_", suffix='.ipynb', dir=os.getcwd(), text=True)

        # Write notebook
        import nbformat
        with io.open(nbpath, 'wt', encoding="utf8") as fh:
            nbformat.write(nb, fh)

        cmd = "jupyter notebook %s" % nbpath
        return os.system(cmd)

    dfs = abilab.frames_from_structures(paths, index=index)

    if options.ipython:
        import IPython
        IPython.embed(header="Type `dfs` in the terminal and use <TAB> to list its methods", dfs=dfs)
    else:
        print("File list:")
        for i, p in enumerate(paths):
            print("%d %s" % (i, p))
        print()
        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        abilab.print_frame(dfs.coords, title="Atomic positions (columns give the site index):")

    return 0
Beispiel #5
0
def abicomp_robot(options):
    """
    Analyze multiple files with a robot. The command has two different variants.

    [1] The files can be listed explicitly as in:

            abicomp.py robot out1_GSR.nc out1_GSR.nc

        or, alternatively, with the shell syntax:

            abicomp.py robot *_GSR.nc

    [2] It's possible to use a directory as the first (and only) argument
        of the robot command followed by the Abinit file extension as in:

            abicomp.py robot . GSR.nc

    By default, the script with call `robot.get_dataframe()` to create an print a table
    with the most important results. For finer control, use --ipy to start an ipython
    console to interact with the robot directly or --nb to generate a jupyter notebook.
    """
    # To define an Help action
    #http://stackoverflow.com/questions/20094215/argparse-subparser-monolithic-help-output?rq=1
    paths = options.paths

    # Temporary code
    #robot = abilab.abirobot(".", "GSR")

    if os.path.isdir(paths[0]):
        # Assume directory + extension
        top, ext = paths[:2]
        cls = abilab.Robot.class_for_ext(ext)
        robot = cls.from_dir(top, walk=True)
    else:
        # Assume file list.
        ext = "GSR"
        cls = abilab.Robot.class_for_ext(ext)
        robot = cls.from_files(paths)

    if options.ipython:
        import IPython
        IPython.embed(
            header=str(robot) +
            "\nType `robot` in the terminal and use <TAB> to list its methods",
            robot=robot)
    elif options.notebook:
        robot.make_and_open_notebook(foreground=options.foreground)
    else:
        print(robot)
        abilab.print_frame(robot.get_dataframe())

    return 0
Beispiel #6
0
def abicomp_robot(options):
    """
    Analyze multiple files with a robot. The command has two different variants.

    [1] The files can be listed explicitly as in:

            abicomp.py robot out1_GSR.nc out1_GSR.nc

        or, alternatively, with the shell syntax:

            abicomp.py robot *_GSR.nc

    [2] It's possible to use a directory as the first (and only) argument
        of the robot command followed by the Abinit file extension as in:

            abicomp.py robot . GSR.nc

    By default, the script with call `robot.get_dataframe()` to create an print a table
    with the most important results. For finer control, use --ipy to start an ipython
    console to interact with the robot directly or --nb to generate a jupyter notebook.
    """
    # To define an Help action
    #http://stackoverflow.com/questions/20094215/argparse-subparser-monolithic-help-output?rq=1
    paths = options.paths

    # Temporary code
    #robot = abilab.abirobot(".", "GSR")

    if os.path.isdir(paths[0]):
        # Assume directory + extension
        top, ext = paths[:2]
        cls = abilab.Robot.class_for_ext(ext)
        robot = cls.from_dir(top, walk=True)
    else:
        # Assume file list.
        ext = "GSR"
        cls = abilab.Robot.class_for_ext(ext)
        robot = cls.from_files(paths)

    if options.ipython:
        import IPython
        IPython.embed(header=str(robot) + "\nType `robot` in the terminal and use <TAB> to list its methods",
                     robot=robot)
    elif options.notebook:
        robot.make_and_open_notebook(daemonize=not options.no_daemon)
    else:
        print(robot)
        abilab.print_frame(robot.get_dataframe())

    return 0
Beispiel #7
0
def main():

    def str_examples():
        return """\
Usage example:
    abistruct.py spglib  filepath             => Read the structure from file and analyze it with spglib.
    abistruct.py convert filepath cif         => Read the structure from file and print CIF file.
    abistruct.py convert filepath abivars     => Print the ABINIT variables defining the structure.
    abistruct.py convert out_HIST abivars     => Read the last structure from the HIST file and
                                                 print the corresponding Abinit variables.
    abistrcut.py kpath filepath               => Read structure from filepath and print Abinit variables for k-path.
    abistruct.py bz filepath                  => Read structure from filepath, plot BZ with matplotlib.
    abistruct.py abisanitize FILE             => Read structure from FILE, call abisanitize, compare structures and save
                                                 "abisanitized" structure to file.

    abistruct.py conventional FILE             => Read structure from FILE, generate conventional structure
                                                  following doi:10.1016/j.commatsci.2010.05.010
    abistruct.py visualize filepath xcrysden  => Visualize the structure with XcrysDen.
    abistruct.py ipython filepath             => Read structure from filepath and open Ipython terminal.
    abistruct.py pmgdata mp-149               => Get structure from pymatgen database and print its JSON representation.
"""

    def show_examples_and_exit(err_msg=None, error_code=1):
        """Display the usage of the script."""
        sys.stderr.write(str_examples())
        if err_msg: sys.stderr.write("Fatal Error\n" + err_msg + "\n")
        sys.exit(error_code)

    # Parent parser for commands that need to know the filepath
    path_selector = argparse.ArgumentParser(add_help=False)
    path_selector.add_argument('filepath', nargs="?", help="File with the crystalline structure (netcdf, cif, input files ...)")

    parser = argparse.ArgumentParser(epilog=str_examples(), formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-V', '--version', action='version', version="%(prog)s version " + abilab.__version__)

    spgopt_parser = argparse.ArgumentParser(add_help=False)
    spgopt_parser.add_argument('--symprec', default=1e-3, type=float,
        help="""\
symprec (float): Tolerance for symmetry finding. Defaults to 1e-3,
    which is fairly strict and works well for properly refined
    structures with atoms in the proper symmetry coordinates. For
    structures with slight deviations from their proper atomic
    positions (e.g., structures relaxed with electronic structure
    codes), a looser tolerance of 0.1 (the value used in Materials
    Project) is often needed.""")
    spgopt_parser.add_argument('--angle-tolerance', default=5.0, type=float,
        help="angle_tolerance (float): Angle tolerance for symmetry finding. Default: 5.0")

    # Parent parser for common options.
    copts_parser = argparse.ArgumentParser(add_help=False)
    copts_parser.add_argument('-v', '--verbose', default=0, action='count', # -vv --> verbose=2
                              help='verbose, can be supplied multiple times to increase verbosity')
    copts_parser.add_argument('--loglevel', default="ERROR", type=str,
                              help="set the loglevel. Possible values: CRITICAL, ERROR (default), WARNING, INFO, DEBUG")

    # Create the parsers for the sub-commands
    subparsers = parser.add_subparsers(dest='command', help='sub-command help',
                                       description="Valid subcommands, use command --help for help")

    p_spglib = subparsers.add_parser('spglib', parents=[copts_parser, path_selector, spgopt_parser],
                                      help="Analyze structure with spglib.")

    # Subparser for convert command.
    p_convert = subparsers.add_parser('convert', parents=[copts_parser, path_selector],
                                      help="Convert structure to the specified format.")
    p_convert.add_argument('format', nargs="?", default="cif", type=str,
                           help="Format of the output file (cif, cssr, POSCAR, json, mson, abivars).")

    p_abisanitize = subparsers.add_parser('abisanitize', parents=[copts_parser, path_selector, spgopt_parser],
                                      help="Sanitize structure with abi_sanitize, compare structures and save result to file.")
    p_abisanitize.add_argument("--savefile", default="", type=str,
                               help='Save final structure to file. Format is detected from file extensions e.g. Si.cif')

    p_conventional = subparsers.add_parser('conventional', parents=[copts_parser, path_selector, spgopt_parser],
                                           help="Gives a structure with a conventional cell according to certain standards. "
                                                "The standards are defined in doi:10.1016/j.commatsci.2010.05.010")
    p_conventional.add_argument("--savefile", default="", type=str,
                                help='Save final structure to file. Format is detected from file extensions e.g. Si.cif')

    # Subparser for ipython.
    p_ipython = subparsers.add_parser('ipython', parents=[copts_parser, path_selector],
                                      help="Open IPython shell for advanced operations on structure object.")

    # Subparser for bz.
    p_bz = subparsers.add_parser('bz', parents=[copts_parser, path_selector],
                                 help="Read structure from file, plot Brillouin zone with matplotlib.")

    # Subparser for bz.
    p_kpath = subparsers.add_parser('kpath', parents=[copts_parser, path_selector],
                             help="Read structure from file, generate k-path for band-structure calculations.")

    # Subparser for visualize command.
    p_visualize = subparsers.add_parser('visualize', parents=[copts_parser, path_selector],
                                        help="Visualize the structure with the specified visualizer")
    p_visualize.add_argument('visualizer', nargs="?", default="vesta", type=str, help=("Visualizer name. "
        "List of visualizer supported: %s" % ", ".join(Visualizer.all_visunames())))

    # Subparser for pmgid command.
    p_pmgdata = subparsers.add_parser('pmgdata', parents=[copts_parser],
                                      help="Get structure from the pymatgen database. Requires internet connection and MAPI_KEY")
    p_pmgdata.add_argument("pmgid", type=str, default=None, help="Pymatgen identifier")
    p_pmgdata.add_argument("--mapi-key", default=None, help="Pymatgen MAPI_KEY. Use env variable if not specified.")
    p_pmgdata.add_argument("--endpoint", default="www.materialsproject.org", help="Pymatgen database.")

    # Subparser for animate command.
    p_animate = subparsers.add_parser('animate', parents=[copts_parser, path_selector],
        help="Read structures from HIST or XDATCAR. Print structures in Xrysden AXSF format to stdout")

    # Parse command line.
    try:
        options = parser.parse_args()
    except Exception as exc:
        show_examples_and_exit(error_code=1)

    # loglevel is bound to the string value obtained from the command line argument.
    # Convert to upper case to allow the user to specify --loglevel=DEBUG or --loglevel=debug
    import logging
    numeric_level = getattr(logging, options.loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % options.loglevel)
    logging.basicConfig(level=numeric_level)

    if options.command == "spglib":
        structure = abilab.Structure.from_file(options.filepath)
        print(structure.spglib_summary(verbose=options.verbose))

    elif options.command == "convert":
        structure = abilab.Structure.from_file(options.filepath)

        if options.format == "abivars":
            print(structure.abi_string)
        else:
            s = structure.convert(format=options.format)
            print(s)

    elif options.command == "abisanitize":
        print("\nCalling abi_sanitize to get a new structure in which:")
        print("    * Structure is refined.")
        print("    * Reduced to primitive settings.")
        print("    * Lattice vectors are exchanged if the triple product is negative\n")

        structure = abilab.Structure.from_file(options.filepath)
        sanitized = structure.abi_sanitize(symprec=options.symprec, angle_tolerance=options.angle_tolerance,
                                           primitive=True, primitive_standard=False)
        index = [options.filepath, "abisanitized"]
        dfs = abilab.frames_from_structures([structure, sanitized], index=index, with_spglib=True)

        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        abilab.print_frame(dfs.coords, title="Atomic positions (columns give the site index):")

        if not options.verbose:
            print("\nUse -v for more info")
        else:
            #print("\nDifference between structures:")
            if len(structure) == len(sanitized):
                table = []
                for line1, line2 in zip(str(structure).splitlines(), str(sanitized).splitlines()):
                    table.append([line1, line2])
                print(str(tabulate(table, headers=["Initial structure", "Abisanitized"])))

            else:
                print("\nInitial structure:")
                print(structure)
                print("\nabisanitized structure:")
                print(sanitized)

        # save file.
        if options.savefile:
            print("Saving abisanitized structure as %s" % options.savefile)
            if os.path.exists(options.savefile):
                raise RuntimeError("%s already exists. Cannot overwrite" % options.savefile)
            sanitized.to(filename=options.savefile)

    elif options.command == "conventional":
        print("\nCalling get_conventional_standard_structure to get conventional structure:")
        print("The standards are defined in Setyawan, W., & Curtarolo, S. (2010). ")
        print("High-throughput electronic band structure calculations: Challenges and tools. ")
        print("Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010\n")

        structure = abilab.Structure.from_file(options.filepath)
        conv = structure.get_conventional_standard_structure(international_monoclinic=True,
                                           symprec=options.symprec, angle_tolerance=options.angle_tolerance)
        index = [options.filepath, "conventional"]
        dfs = abilab.frames_from_structures([structure, conv], index=index, with_spglib=True)

        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        #abilab.print_frame(dfs.coords, title="Atomic positions (columns give the site index):")

        if not options.verbose:
            print("\nUse -v for more info")
        else:
            #print("\nDifference between structures:")
            if len(structure) == len(conv):
                table = []
                for line1, line2 in zip(str(structure).splitlines(), str(conv).splitlines()):
                    table.append([line1, line2])
                print(str(tabulate(table, headers=["Initial structure", "Conventional"])))

            else:
                print("\nInitial structure:")
                print(structure)
                print("\nConventional structure:")
                print(conv)

        # save file.
        if options.savefile:
            print("Saving conventional structure as %s" % options.savefile)
            if os.path.exists(options.savefile):
                raise RuntimeError("%s already exists. Cannot overwrite" % options.savefile)
            conv.to(filename=options.savefile)

    elif options.command == "ipython":
        structure = abilab.Structure.from_file(options.filepath)
        print("Invoking Ipython, `structure` object will be available in the Ipython terminal")
        import IPython
        IPython.start_ipython(argv=[], user_ns={"structure": structure})

    elif options.command == "visualize":
        structure = abilab.Structure.from_file(options.filepath)
        print(structure)
        print("Visualizing structure with:", options.visualizer)
        structure.visualize(options.visualizer)

    elif options.command == "kpath":
        structure = abilab.Structure.from_file(options.filepath)
        print("# Abinit Structure")
        print(structure.abi_string)
        print("\n# K-path in reduced coordinates:")
        print("# tolwfr 1e-20 iscf -2 getden ??")
        print(" ndivsm 10")
        print(" kptopt", -(len(structure.hsym_kpoints)-1))
        print(" kptbounds")
        for k in structure.hsym_kpoints:
            print("    %.5f  %.5f  %.5f" % tuple(k.frac_coords), "#", k.name)

    elif options.command == "bz":
        structure = abilab.Structure.from_file(options.filepath)
        structure.show_bz()

    elif options.command == "pmgdata":
        # Get the Structure corresponding the a material_id.
        structure = abilab.Structure.from_material_id(options.pmgid, final=True, api_key=options.mapi_key,
                                                      endpoint=options.endpoint)
        # Convert to json and print it.
        s = structure.convert(format="json")
        print(s)

    elif options.command == "animate":
        from abipy.iotools import xsf_write_structure
        filepath = options.filepath

        if any(filepath.endswith(ext) for ext in ("HIST", "HIST.nc")):
            with abilab.abiopen(filepath) as hist:
                structures = hist.structures

        elif "XDATCAR" in filepath:
            from pymatgen.io.vaspio import Xdatcar
            structures = Xdatcar(filepath).structures
            if not structures:
                raise RuntimeError("Your Xdatcar contains only one structure. Due to a bug "
                    "in the pymatgen routine, your structures won't be parsed correctly"
                    "Solution: Add another structure at the end of the file.")

        else:
            raise ValueError("Don't know how to handle file %s" % filepath)

        xsf_write_structure(sys.stdout, structures)

    else:
        raise ValueError("Unsupported command: %s" % options.command)

    return 0
Beispiel #8
0
def main():
    def str_examples():
        return """\
Usage example:
    abistruct.py spglib  filepath             => Read the structure from file and analyze it with spglib.
    abistruct.py convert filepath cif         => Read the structure from file and print CIF file.
    abistruct.py convert filepath abivars     => Print the ABINIT variables defining the structure.
    abistruct.py convert out_HIST abivars     => Read the last structure from the HIST file and
                                                 print the corresponding Abinit variables.
    abistrcut.py kpath filepath               => Read structure from filepath and print Abinit variables for k-path.
    abistruct.py bz filepath                  => Read structure from filepath, plot BZ with matplotlib.
    abistruct.py abisanitize FILE             => Read structure from FILE, call abisanitize, compare structures and save
                                                 "abisanitized" structure to file.

    abistruct.py conventional FILE             => Read structure from FILE, generate conventional structure
                                                  following doi:10.1016/j.commatsci.2010.05.010
    abistruct.py visualize filepath xcrysden  => Visualize the structure with XcrysDen.
    abistruct.py ipython filepath             => Read structure from filepath and open Ipython terminal.
    abistruct.py pmgdata mp-149               => Get structure from pymatgen database and print its JSON representation.
"""

    def show_examples_and_exit(err_msg=None, error_code=1):
        """Display the usage of the script."""
        sys.stderr.write(str_examples())
        if err_msg: sys.stderr.write("Fatal Error\n" + err_msg + "\n")
        sys.exit(error_code)

    # Parent parser for commands that need to know the filepath
    path_selector = argparse.ArgumentParser(add_help=False)
    path_selector.add_argument(
        'filepath',
        nargs="?",
        help=
        "File with the crystalline structure (netcdf, cif, input files ...)")

    parser = argparse.ArgumentParser(
        epilog=str_examples(),
        formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-V',
                        '--version',
                        action='version',
                        version="%(prog)s version " + abilab.__version__)

    spgopt_parser = argparse.ArgumentParser(add_help=False)
    spgopt_parser.add_argument('--symprec',
                               default=1e-3,
                               type=float,
                               help="""\
symprec (float): Tolerance for symmetry finding. Defaults to 1e-3,
    which is fairly strict and works well for properly refined
    structures with atoms in the proper symmetry coordinates. For
    structures with slight deviations from their proper atomic
    positions (e.g., structures relaxed with electronic structure
    codes), a looser tolerance of 0.1 (the value used in Materials
    Project) is often needed.""")
    spgopt_parser.add_argument(
        '--angle-tolerance',
        default=5.0,
        type=float,
        help=
        "angle_tolerance (float): Angle tolerance for symmetry finding. Default: 5.0"
    )

    # Parent parser for common options.
    copts_parser = argparse.ArgumentParser(add_help=False)
    copts_parser.add_argument(
        '-v',
        '--verbose',
        default=0,
        action='count',  # -vv --> verbose=2
        help='verbose, can be supplied multiple times to increase verbosity')
    copts_parser.add_argument(
        '--loglevel',
        default="ERROR",
        type=str,
        help=
        "set the loglevel. Possible values: CRITICAL, ERROR (default), WARNING, INFO, DEBUG"
    )

    # Create the parsers for the sub-commands
    subparsers = parser.add_subparsers(
        dest='command',
        help='sub-command help',
        description="Valid subcommands, use command --help for help")

    p_spglib = subparsers.add_parser(
        'spglib',
        parents=[copts_parser, path_selector, spgopt_parser],
        help="Analyze structure with spglib.")

    # Subparser for convert command.
    p_convert = subparsers.add_parser(
        'convert',
        parents=[copts_parser, path_selector],
        help="Convert structure to the specified format.")
    p_convert.add_argument(
        'format',
        nargs="?",
        default="cif",
        type=str,
        help=
        "Format of the output file (cif, cssr, POSCAR, json, mson, abivars).")

    p_abisanitize = subparsers.add_parser(
        'abisanitize',
        parents=[copts_parser, path_selector, spgopt_parser],
        help=
        "Sanitize structure with abi_sanitize, compare structures and save result to file."
    )
    p_abisanitize.add_argument(
        "--savefile",
        default="",
        type=str,
        help=
        'Save final structure to file. Format is detected from file extensions e.g. Si.cif'
    )

    p_conventional = subparsers.add_parser(
        'conventional',
        parents=[copts_parser, path_selector, spgopt_parser],
        help=
        "Gives a structure with a conventional cell according to certain standards. "
        "The standards are defined in doi:10.1016/j.commatsci.2010.05.010")
    p_conventional.add_argument(
        "--savefile",
        default="",
        type=str,
        help=
        'Save final structure to file. Format is detected from file extensions e.g. Si.cif'
    )

    # Subparser for ipython.
    p_ipython = subparsers.add_parser(
        'ipython',
        parents=[copts_parser, path_selector],
        help="Open IPython shell for advanced operations on structure object.")

    # Subparser for bz.
    p_bz = subparsers.add_parser(
        'bz',
        parents=[copts_parser, path_selector],
        help="Read structure from file, plot Brillouin zone with matplotlib.")

    # Subparser for bz.
    p_kpath = subparsers.add_parser(
        'kpath',
        parents=[copts_parser, path_selector],
        help=
        "Read structure from file, generate k-path for band-structure calculations."
    )

    # Subparser for visualize command.
    p_visualize = subparsers.add_parser(
        'visualize',
        parents=[copts_parser, path_selector],
        help="Visualize the structure with the specified visualizer")
    p_visualize.add_argument('visualizer',
                             nargs="?",
                             default="vesta",
                             type=str,
                             help=("Visualizer name. "
                                   "List of visualizer supported: %s" %
                                   ", ".join(Visualizer.all_visunames())))

    # Subparser for pmgid command.
    p_pmgdata = subparsers.add_parser(
        'pmgdata',
        parents=[copts_parser],
        help=
        "Get structure from the pymatgen database. Requires internet connection and MAPI_KEY"
    )
    p_pmgdata.add_argument("pmgid",
                           type=str,
                           default=None,
                           help="Pymatgen identifier")
    p_pmgdata.add_argument(
        "--mapi-key",
        default=None,
        help="Pymatgen MAPI_KEY. Use env variable if not specified.")
    p_pmgdata.add_argument("--endpoint",
                           default="www.materialsproject.org",
                           help="Pymatgen database.")

    # Subparser for animate command.
    p_animate = subparsers.add_parser(
        'animate',
        parents=[copts_parser, path_selector],
        help=
        "Read structures from HIST or XDATCAR. Print structures in Xrysden AXSF format to stdout"
    )

    # Parse command line.
    try:
        options = parser.parse_args()
    except Exception as exc:
        show_examples_and_exit(error_code=1)

    # loglevel is bound to the string value obtained from the command line argument.
    # Convert to upper case to allow the user to specify --loglevel=DEBUG or --loglevel=debug
    import logging
    numeric_level = getattr(logging, options.loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % options.loglevel)
    logging.basicConfig(level=numeric_level)

    if options.command == "spglib":
        structure = abilab.Structure.from_file(options.filepath)
        print(structure.spglib_summary(verbose=options.verbose))

    elif options.command == "convert":
        structure = abilab.Structure.from_file(options.filepath)

        if options.format == "abivars":
            print(structure.abi_string)
        else:
            s = structure.convert(format=options.format)
            print(s)

    elif options.command == "abisanitize":
        print("\nCalling abi_sanitize to get a new structure in which:")
        print("    * Structure is refined.")
        print("    * Reduced to primitive settings.")
        print(
            "    * Lattice vectors are exchanged if the triple product is negative\n"
        )

        structure = abilab.Structure.from_file(options.filepath)
        sanitized = structure.abi_sanitize(
            symprec=options.symprec,
            angle_tolerance=options.angle_tolerance,
            primitive=True,
            primitive_standard=False)
        index = [options.filepath, "abisanitized"]
        dfs = abilab.frames_from_structures([structure, sanitized],
                                            index=index,
                                            with_spglib=True)

        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        abilab.print_frame(
            dfs.coords,
            title="Atomic positions (columns give the site index):")

        if not options.verbose:
            print("\nUse -v for more info")
        else:
            #print("\nDifference between structures:")
            if len(structure) == len(sanitized):
                table = []
                for line1, line2 in zip(
                        str(structure).splitlines(),
                        str(sanitized).splitlines()):
                    table.append([line1, line2])
                print(
                    str(
                        tabulate(table,
                                 headers=["Initial structure",
                                          "Abisanitized"])))

            else:
                print("\nInitial structure:")
                print(structure)
                print("\nabisanitized structure:")
                print(sanitized)

        # save file.
        if options.savefile:
            print("Saving abisanitized structure as %s" % options.savefile)
            if os.path.exists(options.savefile):
                raise RuntimeError("%s already exists. Cannot overwrite" %
                                   options.savefile)
            sanitized.to(filename=options.savefile)

    elif options.command == "conventional":
        print(
            "\nCalling get_conventional_standard_structure to get conventional structure:"
        )
        print(
            "The standards are defined in Setyawan, W., & Curtarolo, S. (2010). "
        )
        print(
            "High-throughput electronic band structure calculations: Challenges and tools. "
        )
        print(
            "Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010\n"
        )

        structure = abilab.Structure.from_file(options.filepath)
        conv = structure.get_conventional_standard_structure(
            international_monoclinic=True,
            symprec=options.symprec,
            angle_tolerance=options.angle_tolerance)
        index = [options.filepath, "conventional"]
        dfs = abilab.frames_from_structures([structure, conv],
                                            index=index,
                                            with_spglib=True)

        abilab.print_frame(dfs.lattice, title="Lattice parameters:")
        #abilab.print_frame(dfs.coords, title="Atomic positions (columns give the site index):")

        if not options.verbose:
            print("\nUse -v for more info")
        else:
            #print("\nDifference between structures:")
            if len(structure) == len(conv):
                table = []
                for line1, line2 in zip(
                        str(structure).splitlines(),
                        str(conv).splitlines()):
                    table.append([line1, line2])
                print(
                    str(
                        tabulate(table,
                                 headers=["Initial structure",
                                          "Conventional"])))

            else:
                print("\nInitial structure:")
                print(structure)
                print("\nConventional structure:")
                print(conv)

        # save file.
        if options.savefile:
            print("Saving conventional structure as %s" % options.savefile)
            if os.path.exists(options.savefile):
                raise RuntimeError("%s already exists. Cannot overwrite" %
                                   options.savefile)
            conv.to(filename=options.savefile)

    elif options.command == "ipython":
        structure = abilab.Structure.from_file(options.filepath)
        print(
            "Invoking Ipython, `structure` object will be available in the Ipython terminal"
        )
        import IPython
        IPython.start_ipython(argv=[], user_ns={"structure": structure})

    elif options.command == "visualize":
        structure = abilab.Structure.from_file(options.filepath)
        print(structure)
        print("Visualizing structure with:", options.visualizer)
        structure.visualize(options.visualizer)

    elif options.command == "kpath":
        structure = abilab.Structure.from_file(options.filepath)
        print("# Abinit Structure")
        print(structure.abi_string)
        print("\n# K-path in reduced coordinates:")
        print("# tolwfr 1e-20 iscf -2 getden ??")
        print(" ndivsm 10")
        print(" kptopt", -(len(structure.hsym_kpoints) - 1))
        print(" kptbounds")
        for k in structure.hsym_kpoints:
            print("    %.5f  %.5f  %.5f" % tuple(k.frac_coords), "#", k.name)

    elif options.command == "bz":
        structure = abilab.Structure.from_file(options.filepath)
        structure.show_bz()

    elif options.command == "pmgdata":
        # Get the Structure corresponding the a material_id.
        structure = abilab.Structure.from_material_id(
            options.pmgid,
            final=True,
            api_key=options.mapi_key,
            endpoint=options.endpoint)
        # Convert to json and print it.
        s = structure.convert(format="json")
        print(s)

    elif options.command == "animate":
        from abipy.iotools import xsf_write_structure
        filepath = options.filepath

        if any(filepath.endswith(ext) for ext in ("HIST", "HIST.nc")):
            with abilab.abiopen(filepath) as hist:
                structures = hist.structures

        elif "XDATCAR" in filepath:
            from pymatgen.io.vaspio import Xdatcar
            structures = Xdatcar(filepath).structures
            if not structures:
                raise RuntimeError(
                    "Your Xdatcar contains only one structure. Due to a bug "
                    "in the pymatgen routine, your structures won't be parsed correctly"
                    "Solution: Add another structure at the end of the file.")

        else:
            raise ValueError("Don't know how to handle file %s" % filepath)

        xsf_write_structure(sys.stdout, structures)

    else:
        raise ValueError("Unsupported command: %s" % options.command)

    return 0