Source code for abipy.scripts.oncv

#!/usr/bin/env python
"""
Script to generate/analyze/plot ONCVPSP pseudopotentials.
"""
from __future__ import annotations

import sys
import os
import argparse
import shutil
import abipy.tools.cli_parsers as cli

from pprint import pformat
from monty.termcolor import cprint
from abipy.flowtk.pseudos import Pseudo
from abipy.ppcodes.ppgen import OncvGenerator
from abipy.ppcodes.oncv_parser import OncvParser
from abipy.ppcodes.oncv_plotter import OncvPlotter, oncv_make_open_notebook, MultiOncvPlotter


def _find_oncv_output(path: str) -> str:
    """
    Fix possible error in the specification of filepath when we want a `.out` file.
    Return output path.
    """
    if path.endswith(".out"): return path
    root, _ = os.path.splitext(path)
    new_path = root + ".out"
    if not os.path.exists(new_path):
        raise ValueError("Cannot find neither %s nor %s" % (path, new_path))
    cprint("Maybe you meant %s" % new_path, color="yellow")
    return new_path


[docs] def oncv_notebook(options): """ Generate jupyter notebook to plot data. Requires oncvpsp output file. """ out_path = _find_oncv_output(options.filepath) return oncv_make_open_notebook(out_path, foreground=options.foreground, classic_notebook=options.classic_notebook, no_browser=options.no_browser)
[docs] def oncv_gnuplot(options): """ Plot data with gnuplot. """ out_path = _find_oncv_output(options.filepath) # Parse output file. onc_parser = OncvParser(out_path).scan() if not onc_parser.run_completed: cprint("oncvpsp output is not completed. Exiting", color="red") return 1 onc_parser.gnuplot() return 0
[docs] def oncv_print(options) -> int: """ Parse oncvps output file and print results to terminal. """ out_path = _find_oncv_output(options.filepath) p = OncvParser(out_path).scan() if not p.run_completed: raise RuntimeError("ocnvpsp output file is not completed") print(p.to_string(verbose=options.verbose)) return 0
[docs] def oncv_plot(options) -> int: """ Plot data with matplotlib. Requires oncvpsp output file. """ cli.customize_mpl(options) out_path = _find_oncv_output(options.filepath) plotter = OncvPlotter.from_file(out_path) #plotter.plotly_atan_logders().show() #return 0 plotter.expose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout, use_web=options.expose_web, verbose=options.verbose) return 0
[docs] def oncv_plot_pseudo(options) -> int: """ Plot data with matplotlib. Requires pseudopotential file (UPF2 or pawxml). """ cli.customize_mpl(options) pseudo = Pseudo.from_file(options.filepath) print(pseudo) exposer = "panel" if options.expose_web else "mpl" from abipy.tools.plotting import Exposer with Exposer.as_exposer(exposer) as e: e.add_obj_with_yield_figs(pseudo) return 0
[docs] def oncv_compare(options) -> int: """ Compare multiple oncvpsp output files. """ cli.customize_mpl(options) out_paths = [_find_oncv_output(p) for p in options.filepaths] plotter = MultiOncvPlotter.from_files(out_paths) # Plot data plotter.expose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout, use_web=options.expose_web, verbose=options.verbose) return 0
#@flowtk.flow_main #def main(options): # return build_flow(options) #def oncv_hints(options): # """ # """ # from abipy import flowtk # from abipy.flowtk.pseudo_works import GsEcutConvWork # flow = flowtk.Flow(workdir=options.workdir) # # ecut_list = [35, 40, 45, 50, 55, 60, 65] # #ecut_list = [35, 40, 45] # work = GsEcutConvWork.from_pseudo(pseudo, ecut_list) # flow.register_work(work) # return flow
[docs] def oncv_run(options): """ Run oncvpsp, generate djrepo file, plot results. Requires oncvps input file. """ # Build names of psp8 and djson files from input and relativistic mode. in_path = options.filepath root, _ = os.path.splitext(in_path) # Enforce convention on output files. calc_type = None if options.rel == "nor": if not root.endswith("_nor"): root += "_nor" elif options.rel == "fr": if not root.endswith("_r"): root += "_r" cprint("FR calculation with input file without `_r` suffix. Will add `_r` to output files", color="yellow") elif options.rel == "from_file": calc_type = "scalar-relativistic" if root.endswith("_r"): calc_type = "fully-relativistic" if root.endswith("_nor"): calc_type = "non-relativistic" # Build names of output files. psp8_path = root + ".psp8" djrepo_path = root + ".djrepo" out_path = root + ".out" if os.path.exists(psp8_path): cprint("%s already exists and will be overwritten" % os.path.relpath(psp8_path), color="yellow") if os.path.exists(djrepo_path): cprint("%s already exists and will be overwritten" % os.path.relpath(djrepo_path), color="yellow") if os.path.exists(out_path): cprint("%s already exists and will be overwritten" % os.path.relpath(out_path), color="yellow") # Select calc_type if calc_type is None: calc_type = dict(nor="non-relativistic", sr="scalar-relativistic", fr="fully-relativistic")[options.rel] # Build Generator and start generation. psgen = OncvGenerator.from_file(in_path, calc_type, options.use_mgga, workdir=None) print(psgen.input_str) print("Using executable:\n\t", psgen.executable) print(f"Output files produced in directory:\n\t{psgen.workdir}") if retcode := psgen.start_and_wait() != 0: cprint("oncvpsp returned %s. Exiting" % retcode, color="red") return retcode if psgen.status != psgen.S_OK: cprint(f"psgen.status = {psgen.status} != psgen.S_OK", color="red") if psgen.parser.warnings: print(2 * "\n") print("List of WARNINGS:") for w in psgen.parser.warnings: print(w) if psgen.parser.errors: print(2 * "\n") print("List of ERRORS:") for e in psgen.parser.errors: print(e) # Transfer final output file. shutil.copy(psgen.stdout_path, out_path) # Parse the output file. onc_parser = OncvParser(out_path).scan() if not onc_parser.run_completed: cprint("oncvpsp output is not completed. Exiting", color="red") return 1 # Extract psp8 files from the oncvpsp output and write it to file. with open(psp8_path, "wt") as fh: fh.write(onc_parser.get_psp8_str()) # Write UPF2 file if available. upf_str = onc_parser.get_upf_str() if upf_str is not None: with open(psp8_path.replace(".psp8", ".upf"), "wt") as fh: fh.write(upf_str) else: cprint("UPF2 file has not been produced. Use `both` in input file!", color="red") pseudo = Pseudo.from_file(psp8_path) if pseudo is None: cprint("Cannot parse psp8 file: %s" % psp8_path, color="red") return 1 # Initialize and write djson file. # FIXME: This part can be removed when we migrate to the new lightweight testing # infrastructure with small json files. from pseudo_dojo.core.dojoreport import DojoReport report = DojoReport.empty_from_pseudo(pseudo, onc_parser.hints, devel=False) report.json_write() cli.customize_mpl(options) # Build the plotter plotter = onc_parser.get_plotter() plotter.expose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout, use_web=options.expose_web, verbose=options.verbose) return 0
[docs] def oncv_ghost(options) -> int: """ Scan directories for oncvpsp output files and build dataframe with ghost position """ #cli.customize_mpl(options) # Walk through the directory tree and find all .out files. root_dir = options.filepath out_paths = [] for dirpath, _, filenames in os.walk(root_dir): for filename in filenames: if filename.endswith(".out"): out_paths.append(os.path.join(dirpath, filename)) #print(out_paths) # Parse the output file. ierr = 0 rows = [] for out_path in out_paths: onc_parser = OncvParser(out_path).scan() if not onc_parser.run_completed: cprint("oncvpsp output is not completed. Exiting", color="red") ierr += 1 continue hints = onc_parser.hints rows.append(dict( out_path=out_path, z=onc_parser.z, num_warnings=len(onc_parser.warnings), num_errors=len(onc_parser.errors), min_ghost_empty_ha=onc_parser.min_ghost_empty_ha, #min_ghost_occ_ha=oncv_parser.min_ghost_occ_ha, #ppgen_hint_low=hints["low"]["ecut"], #ppgen_hint_normal=hints["normal"]["ecut"], ppgen_hint_high=hints["high"]["ecut"], )) import pandas as pd df = pd.DataFrame(rows) df = df.sort_values(by='z') from abipy.tools.printing import print_dataframe print_dataframe(df) return ierr
[docs] def oncv_gui(options): """ Start a panel web app to generate pseudopotentials. """ import panel as pn from abipy.panels.oncvpsp_gui import OncvGui from abipy.panels.core import abipanel, get_abinit_template_cls_kwds, AbipyParameterized # Load abipy/panel extensions and set the default template abipanel(panel_template=options.panel_template) if options.has_remote_server: print("Enforcing limitations on what the user can do on the abinit server") AbipyParameterized.has_remote_server = options.has_remote_server tmpl_cls, tmpl_kwds = get_abinit_template_cls_kwds() if options.verbose: print("Using default template:", tmpl_cls, "with kwds:\n", pformat(tmpl_kwds), "\n") def build(): gui = OncvGui.from_file(os.path.abspath(options.filepath), plotlyFlag=options.plotly) return tmpl_cls(main=gui.get_panel(), title="Oncvpsp GUI", **tmpl_kwds) # Call pn.serve to serve the multipage app. serve_kwargs = cli.get_pn_serve_kwargs(options) return pn.serve(build, **serve_kwargs)
[docs] def get_epilog() -> str: return """\ Usage example: oncv.py run H.in ==> Run oncvpsp input file (scalar relativistic mode). oncv.py run H_r.in ==> Run oncvpsp input file (relativistic mode). oncv.py plot H.out ==> Use matplotlib to plot oncvpsp results for pseudo H.psp8. oncv.py plot H.out -ew ==> Show matplotlib figures in browser. oncv.py gui H.in ==> Start a panel web app to generate pseudopotential. oncv.py print H.out ==> Parse oncvps output and print results to terminal. oncv.py compare H.out other_H.out ==> Compare multiple output files (supports -ew option as well) oncv.py gnuplot H.out ==> Use gnuplot to plot oncvpsp results for pseudo H.psp8. oncv.py notebook H.out ==> Generate jupyter notebook to plot oncvpsp results. """
[docs] def get_parser(with_epilog=False): def get_copts_parser(multi=False): # Parent parser implementing common options. p = argparse.ArgumentParser(add_help=False) p.add_argument('-v', '--verbose', default=0, action='count', # -vv --> verbose=2 help='Verbose, can be supplied multiple times to increase verbosity') p.add_argument('--loglevel', default="ERROR", type=str, help="set the loglevel. Possible values: CRITICAL, ERROR (default), WARNING, INFO, DEBUG") if multi: p.add_argument('filepaths', nargs="+", help="List of files to compare.") else: p.add_argument('filepath', default="", help="Path to the input/output file") return p copts_parser = get_copts_parser(multi=False) copts_parser_multi = get_copts_parser(multi=True) # Parent parser for commands supporting MplExposer. plot_parser = argparse.ArgumentParser(add_help=False) cli.add_expose_options_to_parser(plot_parser) # Build the main parser. parser = argparse.ArgumentParser(epilog=get_epilog(), formatter_class=argparse.RawDescriptionHelpFormatter) from abipy.core.release import __version__ parser.add_argument('-V', '--version', action='version', version=__version__) # Create the parsers for the sub-commands subparsers = parser.add_subparsers(dest='command', help='sub-command help', description="Valid subcommands") # Subparser for run command. p_run = subparsers.add_parser('run', parents=[copts_parser, plot_parser], help=oncv_run.__doc__) p_run.add_argument("--rel", default="from_file", help=("Relativistic treatment: `nor` for non-relativistic, " "`sr` for scalar-relativistic, `fr` for fully-relativistic. Default: `from_file` i.e. detected from file")) p_run.add_argument("--use-mgga", action='store_true', default=False, help="Produce mega-gga pseudo with oncvpspm.x") # Subparser for print command. p_print = subparsers.add_parser('print', parents=[copts_parser], help=oncv_print.__doc__) # Subparser for plot command. p_plot = subparsers.add_parser('plot', parents=[copts_parser, plot_parser], help=oncv_plot.__doc__) # Subparser for plot command. p_plot_pseudo = subparsers.add_parser('plot_pseudo', parents=[copts_parser, plot_parser], help=oncv_plot_pseudo.__doc__) # Subparser for compare command. p_compare = subparsers.add_parser("compare", parents=[copts_parser_multi, plot_parser], help=oncv_compare.__doc__) # Subparser for ghost command. p_ghost = subparsers.add_parser("ghost", parents=[copts_parser], help=oncv_ghost.__doc__) # notebook options. p_nb = subparsers.add_parser('notebook', parents=[copts_parser], help=oncv_notebook.__doc__) p_nb.add_argument('-nb', '--notebook', action='store_true', default=False, help="Open file in jupyter notebook") p_nb.add_argument('--classic-notebook', "-cnb", action='store_true', default=False, help="Use classic jupyter notebook instead of jupyterlab.") p_nb.add_argument('--no-browser', action='store_true', default=False, help=("Start the jupyter server to serve the notebook " "but don't open the notebook in the browser.\n" "Use this option to connect remotely from localhost to the machine running the kernel")) p_nb.add_argument('--foreground', action='store_true', default=False, help="Run jupyter notebook in the foreground.") parents = [copts_parser, cli.pn_serve_parser(), plot_parser] # Subparser for gui command. p_gui = subparsers.add_parser('gui', parents=parents, help=oncv_gui.__doc__) # Subparser for gnuplot command. p_gnuplot = subparsers.add_parser('gnuplot', parents=[copts_parser], help=oncv_gnuplot.__doc__) # Subparser for hints command. #p_hints = subparsers.add_parser('hints', parents=[copts_parser], help=oncv_hints.__doc__) #p_hints.add_argument("pseudo_paths", nargs="+", type=str, help="Pseudopotential path.") #p_hints.add_argument("--ecut", type=float, required=True, help="Cutoff energy in Ha.") #p_hints.add_argument("-rc", "--vloc-rcut-list", nargs="+", default=None, type=float, # help="List of cutoff radii for vloc in Bohr.") #cli.add_expose_options_to_parser(p_hints) return parser
[docs] def main(): def show_examples_and_exit(err_msg=None, error_code=1): """Display the usage of the script.""" sys.stderr.write(get_epilog()) if err_msg: sys.stderr.write("Fatal Error\n" + err_msg + "\n") sys.exit(error_code) parser = get_parser(with_epilog=True) # Parse command line. try: options = parser.parse_args() except Exception as exc: show_examples_and_exit(error_code=1) cli.set_loglevel(options.loglevel) # Use seaborn settings. if getattr(options, "seaborn", None): import seaborn as sns sns.set(context=options.seaborn, style='darkgrid', palette='deep', font='sans-serif', font_scale=1, color_codes=False, rc=None) # Dispatch return globals()["oncv_" + options.command](options)
if __name__ == "__main__": sys.exit(main())