Add abiopen FILE.json --panel

This commit is contained in:
gmatteo 2020-02-03 16:36:27 +01:00
parent 398ee0c219
commit 358d1387e7
12 changed files with 173 additions and 30 deletions

View File

@ -7,6 +7,7 @@ Release 0.8.0: xxxx-xx-xx
* Add examples and flows for effective mass calculations
* Add examples for quasi-harmonic calculations and post-processing tools
* Add support for JSON files (including MSONable format) to abiopen.py
Supports `--notebook`, `--panel` options such as `abiopen.py FILE.json --panel`
* Improved support for EPH calculations.
* Add `primitive` command to `abistruct.py` to get primitive structure from spglib

View File

@ -226,7 +226,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
new = ncfile.read_structure(cls=cls)
new.set_abi_spacegroup(AbinitSpaceGroup.from_ncreader(ncfile))
# Try to read indsym from file (added in 8.9.x)
# Try to read indsym table from file (added in 8.9.x)
indsym = ncfile.read_value("indsym", default=None)
if indsym is not None:
# Fortran --> C convention
@ -2065,7 +2065,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
nbv.new_code_cell("print(structure)"),
nbv.new_code_cell("print(structure.abi_string)"),
nbv.new_code_cell("structure"),
nbv.new_code_cell("print(structure.spglib_summary())"),
nbv.new_code_cell("print(structure.spget_summary())"),
nbv.new_code_cell("if structure.abi_spacegroup is not None: print(structure.abi_spacegroup)"),
nbv.new_code_cell("print(structure.hsym_kpoints)"),
nbv.new_code_cell("structure.plot_bz();"),

View File

@ -851,6 +851,18 @@ class ElectronBands(Has_Structure):
"""True if time-reversal symmetry is used in the BZ sampling."""
return has_timrev_from_kptopt(self.kptopt)
@lazy_property
def supports_fermi_surface(self):
"""
True if the kpoints used for the energies can be employed to visualize Fermi surface.
Fermi surface viewers require gamma-centered k-mesh.
"""
if self.kpoints.is_mpmesh:
mpdivs, shifts = self.kpoints.mpdivs_shifts
if shifts is not None and np.all(shifts == 0.0):
return True
return False
def kindex(self, kpoint):
"""
The index of the k-point in the internal list of k-points.

View File

@ -1542,6 +1542,13 @@ class FatBandsFile(AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, N
yield self.plot_pjdos_lview(show=False)
yield self.plot_pjdos_typeview(show=False)
def get_panel(self):
"""
Build panel with widgets to interact with the |FatbandsFile| either in a notebook or in panel app.
"""
from abipy.panels.fatbands import FatBandsFilePanel
return FatBandsFilePanel(self).get_panel()
def write_notebook(self, nbpath=None):
"""
Write a jupyter_ notebook to nbpath. If nbpath is None, a temporay file in the current
@ -1587,9 +1594,10 @@ class FatBandsFile(AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, N
if self.prtdos == 3 and self.ebands.kpoints.is_path:
nb.cells.extend([
nbv.new_markdown_cell("## L-DOSes with fatbands\n"
"(require `prtdos=3`, `fbnc` must contain a k-path, "
"`pjdosfile` is a `FATBANDS.nc` file with a BZ sampling)"),
nbv.new_markdown_cell(
"## L-DOSes with fatbands\n"
"(require `prtdos=3`, `fbnc` must contain a k-path, "
"`pjdosfile` is a `FATBANDS.nc` file with a BZ sampling)"),
nbv.new_code_cell("fbnc.plot_fatbands_with_pjdos(pjdosfile=None, ylims=ylims, view='type');"),
])

View File

@ -479,6 +479,7 @@ class ElectronBandsTest(AbipyTest):
with abilab.abiopen(abidata.ref_file("mgb2_kmesh181818_FATBANDS.nc")) as fbnc_kmesh:
ebands = fbnc_kmesh.ebands
str(ebands)
assert ebands.supports_fermi_surface
ebands.to_bxsf(self.get_tmpname(text=True))
# Test Ebands3d
@ -525,6 +526,7 @@ class ElectronBandsFromRestApi(AbipyTest):
new_fermie = r.ebands_kpath.set_fermie_to_vbm()
assert new_fermie == r.ebands_kpath.fermie
assert not r.ebands_kpath.supports_fermi_surface
edos = r.ebands_kmesh.get_edos()
new_fermie = r.ebands_kpath.set_fermie_from_edos(edos)

View File

@ -39,6 +39,9 @@ class TestElectronFatbands(AbipyTest):
if self.has_nbformat():
fbnc_kpath.write_notebook(nbpath=self.get_tmpname(text=True))
if self.has_panel():
assert hasattr(fbnc_kpath.get_panel(), "show")
fbnc_kmesh = FatBandsFile(abidata.ref_file("mgb2_kmesh181818_FATBANDS.nc"))
repr(fbnc_kmesh); str(fbnc_kmesh)
assert fbnc_kmesh.ebands.kpoints.is_ibz

View File

@ -48,8 +48,8 @@ class PanelWithElectronBands(AbipyParameterized): #, metaclass=abc.ABCMeta):
# - `fermie`: shift all eigenvalues to have zero energy at the Fermi energy (`self.fermie`).
# - Number e.g e0=0.5: shift all eigenvalues to have zero energy at 0.5 eV
# - None: Don't shift energies, equivalent to e0=0
set_fermie_to_vbm = pnw.Checkbox(name="Set Fermie to VBM")
#set_fermie_to_vbm
plot_ebands_btn = pnw.Button(name="Plot e-bands", button_type='primary')
# DOS plot.
@ -58,19 +58,25 @@ class PanelWithElectronBands(AbipyParameterized): #, metaclass=abc.ABCMeta):
edos_width = pnw.Spinner(name='e-DOS Gaussian broadening (eV)', value=0.2, step=0.05, start=1e-6, end=None)
plot_edos_btn = pnw.Button(name="Plot e-DOS", button_type='primary')
# Fermi surface plot.
fs_viewer = pnw.Select(name="FS viewer", options=["matplotlib", "xcrysden"])
plot_fermi_surface_btn = pnw.Button(name="Plot Fermi surface", button_type='primary')
#@abc.abstractproperty
#def ebands(self):
# """Returns the |ElectronBands| object."""
def get_plot_ebands_widgets(self):
"""Widgets to plot ebands."""
return pn.Column(self.with_gaps,
self.plot_ebands_btn)
return pn.Column(self.with_gaps, self.set_fermie_to_vbm, self.plot_ebands_btn)
@param.depends('plot_ebands_btn.clicks')
def on_plot_ebands_btn(self):
"""Button triggering ebands plot."""
if self.plot_ebands_btn.clicks == 0: return
if self.set_fermie_to_vbm.value:
self.ebands.set_fermie_to_vbm()
fig1 = self.ebands.plot(e0="fermie", ylims=None,
with_gaps=self.with_gaps.value, max_phfreq=None, fontsize=8, **self.fig_kwargs)
@ -89,9 +95,37 @@ class PanelWithElectronBands(AbipyParameterized): #, metaclass=abc.ABCMeta):
if self.plot_edos_btn.clicks == 0: return
edos = self.ebands.get_edos(method=self.edos_method.value, step=self.edos_step.value, width=self.edos_width.value)
fig = edos.plot(**self.fig_kwargs)
#print(edos)
return pn.Row(self._mp(fig), sizing_mode='scale_width')
def get_plot_fermi_surface_widgets(self):
"""Widgets to compute e-DOS."""
return pn.Column(self.fs_viewer, self.plot_fermi_surface_btn)
@param.depends('plot_fermi_surface_btn.clicks')
def on_plot_fermi_surface_btn(self):
if self.plot_fermi_surface_btn.clicks == 0: return
if hasattr(self, "_eb3d"):
eb3d = self._eb3d
else:
# Build ebands in full BZ.
eb3d = self._eb3d = self.ebands.get_ebands3d()
if self.fs_viewer.value == "matplotlib":
# Use matplotlib to plot isosurfaces corresponding to the Fermi level (default)
# Warning: requires skimage package, rendering could be slow.
fig = eb3d.plot_isosurfaces(e0="fermie", cmap=None, **self.fig_kwargs)
return pn.Row(self._mp(fig), sizing_mode='scale_width')
elif self.fs_viewer.value == "xcrysden":
# Alternatively, it's possible to export the data in xcrysden format
# and then use `xcrysden --bxsf mgb2.bxsf`
#eb3d.to_bxsf("mgb2.bxsf")
# If you have mayavi installed, try:
#eb3d.mvplot_isosurfaces()
else:
raise ValueError("Invalid choice: %s" % self.fs_viewer.value)
class BaseRobotPanel(AbipyParameterized):
"""pass"""

71
abipy/panels/fatbands.py Normal file
View File

@ -0,0 +1,71 @@
"""Panels for interacting with FATBANDS files."""
import param
import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from .core import PanelWithElectronBands, PanelWithEbandsRobot
class FatBandsFilePanel(PanelWithElectronBands):
"""
Panel with widgets to interact with a |FatBandsFile|.
"""
def __init__(self, ncfile, **params):
super().__init__(**params)
self.ncfile = ncfile
@property
def ebands(self):
"""|ElectronBands|."""
return self.ncfile.ebands
def get_panel(self):
"""Return tabs with widgets to interact with the DDB file."""
tabs = pn.Tabs(); app = tabs.append
app(("Summary", pn.Row(bkw.PreText(text=self.ncfile.to_string(verbose=self.verbose),
sizing_mode="scale_both"))))
app(("e-Bands", pn.Row(self.get_plot_ebands_widgets(), self.on_plot_ebands_btn)))
# Add DOS tab only if k-sampling.
if self.ncfile.ebands.kpoints.is_ibz:
app(("e-DOS", pn.Row(self.get_plot_edos_widgets(), self.on_plot_edos_btn)))
if self.ncfile.ebands.supports_fermi_surface:
# Fermi surface requires gamma-centered k-mesh
app(("Fermi Surface", pn.Row(self.get_plot_fermi_surface_widgets(), self.on_plot_fermi_surface_btn)))
return tabs
#class GsrRobotPanel(PanelWithEbandsRobot):
# """
# A Panel to interoperate with multiple GSR files.
# """
#
# gsr_dataframe_btn = pnw.Button(name="Compute", button_type='primary')
#
# def __init__(self, robot, **params):
# super().__init__(**params)
# self.robot = robot
#
# @param.depends("gsr_dataframe_btn.clicks")
# def on_gsr_dataframe_btn(self):
# if self.gsr_dataframe_btn.clicks == 0: return
# df = self.robot.get_dataframe(with_geo=True)
# return pn.Column(self._df(df), sizing_mode='stretch_width')
#
# def get_panel(self):
# """Return tabs with widgets to interact with the |GsrRobot|."""
# tabs = pn.Tabs(); app = tabs.append
# app(("Summary", pn.Row(bkw.PreText(text=self.robot.to_string(verbose=self.verbose),
# sizing_mode="scale_both"))))
# app(("e-Bands", pn.Row(self.get_ebands_plotter_widgets(), self.on_ebands_plotter_btn)))
#
# # Add e-DOS tab only if all ebands have k-sampling.
# if all(abifile.ebands.kpoints.is_ibz for abifile in self.robot.abifiles):
# app(("e-DOS", pn.Row(self.get_edos_plotter_widgets(), self.on_edos_plotter_btn)))
#
# app(("GSR-DataFrame", pn.Row(self.gsr_dataframe_btn, self.on_gsr_dataframe_btn)))
#
# return tabs

View File

@ -28,9 +28,14 @@ class GsrFilePanel(PanelWithElectronBands):
app(("e-Bands", pn.Row(self.get_plot_ebands_widgets(), self.on_plot_ebands_btn)))
# Add DOS tab only if k-sampling.
if self.gsr.ebands.kpoints.is_ibz:
kpoints = self.gsr.ebands.kpoints
if kpoints.is_ibz:
app(("e-DOS", pn.Row(self.get_plot_edos_widgets(), self.on_plot_edos_btn)))
if self.gsr.ebands.supports_fermi_surface:
# Fermi surface requires gamma-centered k-mesh
app(("Fermi Surface", pn.Row(self.get_plot_fermi_surface_widgets(), self.on_plot_fermi_surface_btn)))
return tabs

View File

@ -6,6 +6,7 @@ import bokeh.models.widgets as bkw
from abipy.panels.core import AbipyParameterized
pn.config.js_files = {
'$': 'https://code.jquery.com/jquery-3.4.1.slim.min.js',
"clipboard": "https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js",
@ -79,7 +80,7 @@ class StructurePanel(AbipyParameterized):
@param.depends("viewer_btn.clicks")
def view(self):
if self.viewer_btn.clicks == 0: return
return self.structure.nglview()
#return self.structure.nglview()
#import nglview as nv
#view = nv.demo(gui=False)
#return view
@ -96,12 +97,12 @@ class StructurePanel(AbipyParameterized):
spin_mode=self.label2mode[self.spin_mode.value],
smearing=None)
gs_inp.pop_vars(("charge", "chksymbreak"))
gs_inp.set_vars(ecut="?? # depends on pseudos", nband="?? # depends on pseudos")
if self.gs_type.value == "relax":
gs_inp.set_vars(optcell=2, ionmov=2, ecutsm=0.5, dilatmx=1.05)
gs_inp.set_mnemonics(False)
#return gs_inp._repr_html_()
return """
<button class="btn btn-primary btn-sm" data-clipboard-target="#foobar"> Copy to clipboard </button>
<div id="foobar"> %s </div>

View File

@ -87,7 +87,7 @@ Use `-v` to increase verbosity level (can be supplied multiple times e.g -vv).
JSON file are supported as well. In this case, abiopen.py tries to reconstruct python objects
assuming JSON document in MSONable format and then invokes ipython with the `data` object.
Use `-e` to print the JSON documenting without reconstructing python objects.
Use `-e` or `--notebook` or `--panel` to print the JSON dictionary without reconstructing python objects.
Table mapping file extension to AbiPy object:
@ -133,8 +133,6 @@ def get_parser(with_epilog=False):
help=("Set matplotlib interactive backend. "
"Possible values: GTKAgg, GTK3Agg, GTK, GTKCairo, GTK3Cairo, WXAgg, WX, TkAgg, Qt4Agg, Qt5Agg, macosx."
"See also: https://matplotlib.org/faq/usage_faq.html#what-is-a-backend."))
#parser.add_argument('--pylustrator', action='store_true', default=False,
# help="Style matplotlib plots with pylustrator. See https://pylustrator.readthedocs.io/en/latest/")
return parser
@ -178,11 +176,6 @@ def main():
sns.set(context=options.seaborn, style='darkgrid', palette='deep',
font='sans-serif', font_scale=1, color_codes=False, rc=None)
#if options.pylustrator:
# # Start pylustrator to style matplotlib plots
# import pylustrator
# pylustrator.start()
if not os.path.exists(options.filepath):
raise RuntimeError("%s: no such file" % options.filepath)
@ -272,18 +265,37 @@ Use `print(abifile)` to print the object.
def handle_json(options):
"""Handle JSON file."""
if not options.notebook:
if options.notebook:
# Visualize JSON document in jupyter
cmd = "jupyter-lab %s" % options.filepath
print("Executing:", cmd)
process = subprocess.Popen(cmd.split(), shell=False) #, stdout=fd, stderr=fd)
cprint("pid: %s" % str(process.pid), "yellow")
return 0
elif options.panel:
# Visualize JSON document in panel dashboard
import json
import panel as pn
with open(options.filepath, "rt") as fh:
d = json.load(fh)
json_pane = pn.pane.JSON(d, name='JSON', height=300, width=500)
app = pn.Row(json_pane.controls(jslink=True), json_pane)
app.show()
return 0
else:
if options.print:
# Print object to terminal.
# Print python object to terminal.
data = abilab.mjson_load(options.filepath)
pprint(data, indent=4)
return 0
elif options.expose:
# Pretty-print dict to terminal.
import json
with open(options.filepath, "rt") as fh:
data = json.load(fh)
pprint(data, indent=4)
return 0
data = abilab.mjson_load(options.filepath)
@ -294,13 +306,6 @@ def handle_json(options):
The object initialized from JSON (MSONable) is associated to the `data` python variable.
""")
else:
cmd = "jupyter-lab %s" % options.filepath
print("Executing:", cmd)
process = subprocess.Popen(cmd.split(), shell=False) #, stdout=fd, stderr=fd)
cprint("pid: %s" % str(process.pid), "yellow")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -110,6 +110,7 @@
.. |MdfFile| replace:: :class:`abipy.electrons.bse.MdfFile`
.. |DdbFile| replace:: :class:`abipy.dfpt.ddb.DdbFile`
.. |HistFile| replace:: :class:`abipy.dynamics.hist.HistFile`
.. |FatBandsFile| replace:: :class:`abipy.electrons.fatbands.FatBandsFile`
.. |DielectricTensorGenerator| replace:: :class:`abipy.dfpt.ddb.DielectricTensorGenerator`
.. |DdbRobot| replace:: :class:`abipy.dfpt.ddb.DdbRobot`
.. |AnaddbNcFile| replace:: :class:`abipy.dfpt.anaddb.AnaddbNcFile`