Merged trunk/develop

This commit is contained in:
Guillaume Brunin 2021-05-20 11:52:42 +02:00
commit 74b2f6e741
107 changed files with 7297 additions and 1841 deletions

3
.gitignore vendored
View File

@ -52,6 +52,9 @@ docs/flow_gallery
# Pycharm
.idea/
# VSC
.vscode/
# vim files
*.swp
*.swo

View File

@ -1,19 +1,19 @@
Release 0.9.1:
Release 0.9.1:
* abiopen, abiview and abicom now supports --plotly to produce plotly figures in the local browser
* Add "-ew", "--expose-web",
* abiopen, abiview and abicomp now supports --plotly to produce plotly figures in the local browser
and --chart-studio to push the figure to the chart studio service.
Note that, at present, only DDB files support plotly.
* AbinitInput set_kpath and make_ebands_input now support negative values of ndivsm that
* AbinitInput set_kpath and make_ebands_input now support negative values of ndivsm that
are interpreted as line_density following pymatgen conventions.
This option is the recommended one if the k-path contains two consecutive high symmetry k-points
This option is the recommended one if the k-path contains two consecutive high symmetry k-points
that are very close as ndivsm > 0 may produce a very large number of wavevectors.
* Preliminary support for plotly plots (phonons).
* AbinitInputParser now can parse strings in the input file and read structure is the `suctructre:abivars`
* AbinitInputParser now can parse strings in the input file and read structure is the `structure:abivars`
syntax in used.
Release 0.9.0:
Release 0.9.0:
* Require pymatgen >= 2019.12.22
* Integration with abinit 9.4.0
@ -24,13 +24,12 @@ Release 0.9.0:
* Use last version of apscheduler.
* Minor bug fixes
* New tools for Phonon and EPH calculations.
* Note that this is last AbiPy version supporting Abinit8.
* Note that this is the last AbiPy version supporting Abinit8.
AbiPy version 1.0 will start to take advantage of features and ameliorations introduced in Abinit9
We will also take the opportunity to refactor the code base so backward incompatibe changes in the API
are expected in the next major version.
Release 0.8.0:
Release 0.8.0:
* Add abicheck.py --create-config option to install predefined yaml configuration files
* Add support for NSCF calculations with meta-GGA.
@ -70,7 +69,7 @@ Release:0.3.0 2017-12-26
and ``flow-gallery`` with AbiPy flows are now automatically generated.
* Add Shankland-Koelling-Wood Fourier interpolation scheme.
Release 0.2.0 <2017-03-10>
Release 0.2.0 2017-03-10
This is the first official release in which we have reached a relatively stable API
and a well-defined interface with the netcdf files produced by Abinit.

View File

@ -1,6 +1,7 @@
"""
This module gathers the most important classes and helper functions used for scripting.
"""
import sys
import os
import collections
@ -178,6 +179,14 @@ def extcls_supporting_panel(as_table=True, **tabulate_kwargs):
return tabulate(items, headers=["Extension", "Class"], **tabulate_kwargs)
def abipanel(**kwargs):
"""
Activate panel extensions used by AbiPy. Return panel module.
"""
from abipy.panels.core import abipanel
return abipanel(**kwargs)
def abifile_subclass_from_filename(filename):
"""
Returns the appropriate class associated to the given filename.
@ -331,14 +340,24 @@ def mjson_write(d, filepath, **kwargs):
json.dump(d, fh, cls=MontyEncoder, **kwargs)
def software_stack():
def software_stack(as_dataframe=False):
"""
Import all the hard dependencies. Returns ordered dict: package --> string with version info.
Import all the hard dependencies and some optional packages.
Returns ordered dict: package --> string with version info or pandas dataframe if as_dataframe.
"""
import platform
system, node, release, version, machine, processor = platform.uname()
# These packages are required
import numpy, scipy, netCDF4, pymatgen, apscheduler, pydispatch, yaml
import numpy, scipy, netCDF4, pymatgen, apscheduler, pydispatch, yaml, plotly
from importlib import import_module
def get_version(pkg_name):
"""Return version of package from string."""
try:
mod = import_module(pkg_name)
return mod.__version__
except:
return None
try:
from pymatgen.core import __version__ as pmg_version
@ -355,17 +374,19 @@ def software_stack():
("apscheduler", apscheduler.version),
("pydispatch", pydispatch.__version__),
("yaml", yaml.__version__),
("boken", get_version("bokeh")),
("panel", get_version("panel")),
("plotly", get_version("plotly")),
("ase", get_version("ase")),
("phonopy", get_version("phonopy")),
("monty", get_version("monty")),
("pymatgen", pmg_version),
("abipy", __version__),
])
# Optional but strongly suggested.
#try:
# import matplotlib
# d["matplotlib"] = "%s (backend: %s)" % (matplotlib.__version__, matplotlib.get_backend())
#except ImportError:
# pass
return d
if not as_dataframe: return d
import pandas as pd
return pd.Series(data=d, name="version").to_frame().rename_axis("Package")
def abicheck(verbose=0):

View File

@ -1,6 +1,11 @@
To update the python modules using the more recent version available in the Abinit repository use vimdiff:
To update the python modules using the more recent version available in the Abinit repository
and use eg. vimdiff:
vimdiff $ABINIT_REPO/abimkdocs/variables_abinit.py variables_abinit.py
and patch the file manually!
to patch the file manually!
Do not change the initial part of the module since it's needed by AbiPy.
To automate the process, use:
invoke update-vars ABINIT_REPO/

View File

@ -18,7 +18,7 @@ Variable(
mnemonics="ACCURACY",
added_in_version="before_v9",
text=r"""
Allows to tune the accuracy of a calculation by setting automatically the
Allows to tune the accuracy of a ground-state or DFPT calculation [[optdriver]]=0 or 1, by setting automatically the
variables according to the following table:
accuracy | 1 | 2 | 3 | 4 | 5 | 6
@ -51,6 +51,10 @@ If the user wants to modify one of the input variable automatically tuned by *ac
they must put it in the input file. The other input variables automatically tuned
by *accuracy* will not be affected.
*accuracy* = 0 means that this input variable is deactivated.
For the other values of [[optdriver]], many of the above input variables have no meaning,
so the accuracy has to be tuned by the user (e.g. for GW calculations, perform convergence studies
with respect to [[ecuteps]] and other relevant input variables).
""",
),
@ -424,32 +428,32 @@ Control the size of the block in the LOBPCG algorithm.
!!! important
This keyword works only with [[paral_kgb]] = 1 and has to be either 1 or a multiple of 2.
Moreover [[nband]] / ([[npband]] $\times$ n) has to be integer.
Moreover [[nband]] / ([[npband]] $\times$ [[bandpp]]) has to be integer.
With [[npband]] = 1:
* 1 --> band-per-band algorithm
* n --> The minimization is performed using [[nband]] blocks of n bands.
* [[bandpp]]=1 --> band-per-band algorithm
* [[bandpp]]/=1 --> The minimization is performed using [[nband]]/[[bandpp]] blocks of [[bandpp]] bands.
With [[npband]] > 1:
* 1 --> The minimization is performed using [[nband]] / [[npband]] blocks of [[npband]] bands.
* n --> The minimization is performed using [[nband]] / ([[npband]] $\times$ n) blocks of [[npband]] $\times$ n bands.
* [[bandpp]]=1 --> The minimization is performed using [[nband]] / [[npband]] blocks of [[npband]] bands.
* [[bandpp]]/=1 --> The minimization is performed using [[nband]] / ([[npband]] $\times$ [[bandpp]]) blocks of [[npband]] $\times$ [[bandpp]] bands.
By minimizing a larger number of bands together in LOBPCG, we increase the
convergence of the residuals. The better minimization procedure (as concerns
the convergence, but not as concerns the speed) is generally performed by
using *bandpp* $\times$ [[npband]] = [[nband]].
using [[bandpp]] $\times$ [[npband]] = [[nband]].
When performing Gamma-only calculations ([[istwfk]] = 2), it is recommended to set *bandpp* = 2
When performing Gamma-only calculations ([[istwfk]] = 2), it is recommended to set [[bandpp]] = 2
(or a multiple of 2) as the time spent in FFTs is divided by two.
Also, the time required to apply the non-local part of the KS Hamiltonian can be significantly
reduced if [[bandpp]] > 1 is used in conjunction with [[use_gemm_nonlop]] = 1.
Note that increasing the value of [[bandpp] can have a significant impact on the computing time
Note that increasing the value of [[bandpp]] can have a significant impact (reduction) on the computing time
(especially if [[use_gemm_nonlop]] is used)
but keep in mind that the size of the workspace arrays will also increase so the calculation may go out-of-memory
if a too large [[bandpp] is used in systems if many atoms.
if a too large [[bandpp]] is used in systems if many atoms.
""",
),
@ -625,10 +629,10 @@ Variable(
* 1 --> the polarization will be kept in the same branch on each iteration.
At the end of the run, a file "POLSAVE" will be saved containing the reduced polarization in atomic units.
!!! note
!!! note
Make sure that "POLSAVE" is empty or it does not exist before the calculation, or else that
it specifies the desired polarization branch.
Make sure that "POLSAVE" is empty or it does not exist before the calculation, or else that
it specifies the desired polarization branch.
""",
),
@ -3496,7 +3500,7 @@ Variable(
abivarname="ecutsigx",
varset="gw",
vartype="real",
topics=['SelfEnergy_compulsory'],
topics=['SelfEnergy_basic'],
dimensions="scalar",
defaultval=0.0,
mnemonics="Energy CUT-off for SIGma eXchange",
@ -3510,6 +3514,8 @@ calculations, it is pointless to have [[ecutsigx]] bigger than 4*[[ecut]],
while for PAW calculations, the maximal useful value is [[pawecutdg]]. Thus,
if you do not care about CPU time, please use these values. If you want to
spare some CPU time, you might try to use a value between [[ecut]] and these upper limits.
[[ecutsigx]] is actually used to initialize the internal variable [[npwsigx]].
""",
),
@ -3556,7 +3562,7 @@ Variable(
abivarname="ecutwfn",
varset="gw",
vartype="real",
topics=['Susceptibility_compulsory', 'SelfEnergy_compulsory'],
topics=['Susceptibility_basic', 'SelfEnergy_basic'],
dimensions="scalar",
defaultval=ValueWithConditions({'[[optdriver]] in [3, 4]': '[[ecut]]', 'defaultval': 0.0}),
mnemonics="Energy CUT-off for WaveFunctioNs",
@ -4421,7 +4427,7 @@ In reciprocal space, this expression is evaluated by a convolution in which
the number of reciprocal lattice vectors employed to describe the
wavefunctions is given by [[ecutwfn]]. In the case of screening calculations,
the number of **G** vectors in the above expression is defined by [[ecuteps]],
while [[ecutsigx]] defined the number of **G** used in sigma calculations. To
while [[ecutsigx]] defines the number of **G** used in sigma calculations. To
improve the efficiency of the code, the oscillator matrix elements are
evaluated in real space through FFT techniques, and the [[fftgw]] input
variable is used to select the FFT mesh to be used.
@ -6213,7 +6219,7 @@ Variable(
abivarname="gwaclowrank",
varset="gw",
vartype="integer",
topics=['GW_basic', 'SelfEnergy_basic'],
topics=['GW_useful', 'SelfEnergy_useful'],
dimensions="scalar",
defaultval=0,
mnemonics="GW Analytic Continuation LOW RANK approximation",
@ -6715,7 +6721,7 @@ Variable(
abivarname="gwmem",
varset="gw",
vartype="integer",
topics=['Susceptibility_expert', 'SelfEnergy_expert', 'GW_expert'],
topics=['Susceptibility_expert', 'SelfEnergy_expert', 'GW_expert', 'TuningSpeedMem_expert'],
dimensions="scalar",
defaultval=11,
mnemonics="GW MEMory",
@ -6733,6 +6739,12 @@ Variable(
The default is [[gwmem]] = 11, which is the fastest, but also the most memory
consuming. When experiencing memory shortage, one should try [[gwmem]] = 0.
The first digit is only meaningful when performing sigma calculations.
!!! important
Note that reading the screening file the SCR file during the sigma run leads to a **significant increase of the IO**
with a subsequent slowdown. Use this option if you really need it and make sure the sysadmin won't complain
about an abnormal IO activity of your jobs.
""",
),
@ -6758,6 +6770,17 @@ In the present status of the code, only the parallelization over bands
([[gwpara]] = 2) allows one to reduce the memory allocated by each processor.
Using [[gwpara]] = 1, indeed, requires the same amount of memory as a sequential
run, irrespectively of the number of CPUs used.
In the screening calculation [[optdriver]]=3, with [[gwpara]]=2, the
code distributes the wavefunctions such that each processing unit owns the
FULL set of occupied bands while the empty states are DISTRIBUTED among the
nodes. Thus the parallelisation is over the unoccupied states.
The parallelism of the self-energy calculation [[optdriver]]=4,
with [[gwpara]]=2, is somehow different. It is over the entire set of bands,
and has different characteristics for the correlation calculation and for the exchange calculation..
The MPI computation of the correlation part is efficient when the number of processors divides [[nband]].
Optimal scaling in the exchange part is obtained only when each node possesses the full set of occupied states.
""",
),
@ -7580,9 +7603,10 @@ No meaning for RF calculations.
For instance, a server that wants to performs calculations with varying unit cells
should set [[optcell]] > 0 in the initial input.
Note that this feature is still under DEVELOPMENT and is mainly used to interface ABINIT
with the ASE optimization routines.
Examples will be provided when the feature is ready for production.
Note that, at present, this feature is mainly used to interface ABINIT
with the ASE optimization routines. Moreover the user is responsible for creating an input
file with tuned tolerances to prevent Abinit from exiting when internal convergence is reached.
See examples available in the [ASE documentation](https://wiki.fysik.dtu.dk/ase/dev/ase/calculators/socketio/socketio.html)
**Purpose:** Structural optimization driver by the server (MD runs are not yet supported)
**Cell optimization:** Yes (provide [[optcell]] > 0 in the initial input)
@ -8694,7 +8718,7 @@ See [[cite:Sun2011]] for the formulas.
* 207 --> XC_MGGA_X_BJ06 Becke & Johnson correction to Becke-Roussel 89 [[cite:Becke2006]]
!!! warning
This Vxc-only mGGA can only be used with a LDA correlation, typically Perdew-Wang 92 [[cite:Perdew1992a]].
This Vxc-only mGGA can only be used with a LDA correlation, typically Perdew-Wang 92 [[cite:Perdew1992a]], hence [[ixc]]=-12208 ..
* 208 --> XC_MGGA_X_TB09 Tran-blaha - correction to Becke & Johnson correction to Becke-Roussel 89 [[cite:Tran2009]]
@ -11078,10 +11102,13 @@ Variable(
added_in_version="before_v9",
text=r"""
Give the number of images (or replicas) of the system, for which the forces
and stresses might be computed independently, in the context of the string
method, the genetic algorithm, hyperdynamics or Path-Integral Molecular
Dynamics depending on the value of [[imgmov]]). Related input variables:
and stresses might be computed independently, in the context of string
or NEB method, genetic algorithm, hyperdynamics, Path-Integral Molecular
Dynamics, linear combination of images, pSIC, genetic algorithm, etc, depending on the value of [[imgmov]]). Related input variables:
[[dynimage]], [[npimage]], [[ntimimage]] and [[prtvolimg]].
If [[nimage]]>1, the default choice for printing many files is set to zero, and the user might
want to manually reestablish the printing, using, e.g. [[prtgsr]], [[prtwf]], [[prtebands]], [[prteig]], etc.
Images might differ by the position of atoms in the unit cell, their velocity,
as well as by their cell geometry. The following input variables might be used
to define the images:
@ -11882,7 +11909,7 @@ Variable(
text=r"""
[[npwsigx]] determines the cut-off energy of the planewave set used to
generate the exchange part of the self-energy operator.
It is an internal variable, determined from [[ecutsigx]].
It is an internal variable, determined from the largest of [[ecutsigx]] or [[ecutwfn]].
""",
),
@ -12675,18 +12702,22 @@ Variable(
varset="gstate",
vartype="real",
topics=['BandOcc_basic'],
dimensions=['[[nband]]', "[[mband]]", "[[nsppol]]"],
dimensions=['[[nband]]', '[[nsppol]]'],
commentdims="in case [[occopt]]=2, dimensions are ([[mband]],[[nkpt]],[[nsppol]])",
defaultval=MultipleValue(number=None, value=0),
mnemonics="OCCupation numbers",
characteristics=['[[EVOLVING]]'],
added_in_version="before_v9",
text=r"""
Gives occupation numbers for all bands in the problem. Needed if [[occopt]] == 0
or [[occopt]] == 2. Ignored otherwise. Also ignored when [[iscf]] = -2.
Typical band occupancy is either 2 or 0, but can be 1 for half-occupied band
or other choices in special circumstances.
or [[occopt]] == 2. Ignored otherwise (automatically computed). Also ignored when [[iscf]] = -2.
Typical band occupancy is either 2.0 or 0.0, but will usually be 1.0 or 0.0 for [[nsppol]]=2, or [[nspinor]]=2,
or half-occupied band, or other choices in special circumstances.
If [[occopt]] is not 2, then the occupancies must be the same for each k point.
If [[nsppol]]=1, the total number of arrays which must be provided is [[nband]], in order of increasing energy.
If [[nsppol]]=2, the total number of arrays which must be provided is [[nband]]*[[nsppol]],
first spin up, in order of increasing electronic eigenenergy, then spin down, in order of increasing electronic eigenenergy.
If [[occopt]] = 2, then the band occupancies must be provided explicitly for
each band, EACH k POINT, and EACH SPIN-POLARIZATION, in an array which runs
@ -12696,6 +12727,7 @@ point (spin up), then all bands at the second k point (spin up), etc, then all
k-points spin down.
The total number of array elements which must be provided is
( [[nband]](1)+[[nband]](2)+...+ [[nband]]([[nkpt]]) ) * [[nsppol]].
The occupation numbers evolve only for metallic occupations, that is, [[occopt]] 3.
When there are several images, [[occ]] might depend on the image number, see the description in [[nimage]].
@ -12716,7 +12748,7 @@ Controls how input parameters [[nband]], [[occ]], and [[wtk]] are handled.
Possible values are from 0 to 9.
For gapped materials (semiconductors, molecules, ...), [[occopt]]=1 is the favourite for most usages.
For metallic situations (also molecules with degenerate levels at Fermi energy), [[occopt]]=7 is the favourite for most usages,
and one need to pay attention to the input variable [[tsmear]].
and one needs moreover to control the input variable [[tsmear]].
Use [[occopt]]=9 for quasi-Fermi energy calculations of excited states in gapped materials.
* [[occopt]] = 0:
@ -12748,7 +12780,7 @@ the sum of [[nband]](ikpt) over all k points and spins. The k point weights
Metallic occupation of levels, using different occupation schemes (see below).
The corresponding thermal broadening, or cold smearing, is defined by the
input variable [[tsmear]] (see below: the variable xx is the energy in Ha,
divided by [[tsmear]])
divided by [[tsmear]]).
Like for [[occopt]] = 1, the variable [[occ]] is not read.
All k points have the same number of bands, [[nband]] is given as a single
number, read by the code.
@ -12757,31 +12789,35 @@ the code to add to 1. The combination of a broadening and a physical temperature
can be obtained by using both [[tsmear]] and [[tphysel]].
* [[occopt]] = 3:
Fermi-Dirac smearing (finite-temperature metal) Smeared delta function:
0.25/(cosh(xx/2.0)**2). For usual calculations, at zero temperature, do not use [[occopt]]=3,
Fermi-Dirac smearing (finite-temperature metal). Smeared delta function:
$\tilde{\delta}(x)=0.25 (\cosh(x/2.0))^{-2}$. For usual calculations, at zero temperature, do not use [[occopt]]=3,
but likely [[occopt]]=7. If you want to do a calculation at finite temperature, please also read the
information about [[tphysel]].
* [[occopt]] = 4:
"Cold smearing" of N. Marzari (see his thesis work), with a=-.5634
(minimization of the bump)
(minimization of the bump).
Smeared delta function:
exp(-xx 2 )/sqrt(pi) * (1.5+xx*(-a*1.5+xx*(-1.0+a*xx)))
$\tilde{\delta}(x)= (1.5+x(-1.5a+x(-1.0+ax))) \exp(-x^2)/\sqrt{\pi}$ .
Must be used with caution, see the note below.
* [[occopt]] = 5:
"Cold smearing" of N. Marzari (see his thesis work), with a=-.8165 (monotonic
function in the tail)
Same smeared delta function as [[occopt]] = 4, with different a.
Must be used with caution, see the note below.
* [[occopt]] = 6:
Smearing of Methfessel and Paxton [[cite:Methfessel1989]] with Hermite polynomial
of degree 2, corresponding to "Cold smearing" of N. Marzari with a=0 (so, same
smeared delta function as [[occopt]] = 4, with different a).
Must be used with caution, see the note below.
* [[occopt]] = 7:
Gaussian smearing, corresponding to the 0 order Hermite polynomial of
Gaussian smearing, corresponding to the 0-order Hermite polynomial of
Methfessel and Paxton.
Smeared delta function: 1.0*exp(-xx**2)/sqrt(pi)
Smeared delta function: $\tilde{\delta}(x)=\exp(-x^2)/\sqrt{\pi}$ .
Robust and quite efficient.
* [[occopt]] = 8:
Uniform smearing (the delta function is replaced by a constant function of
@ -12789,7 +12825,7 @@ value one over ]-1/2,1/2[ (with one-half value at the boundaries). Used for
testing purposes only.
* [[occopt]] = 9:
Fermi-Dirac occupation is enforced with two distinct quasi-Fermi levels: [[nqfd]] holes are forced in bands 1 to [[ivalence]] and [[nqfd]] electrons are forced in bands with index > [[ivalence]]. See details in [[cite:Paillard2019]]. At present, the number of holes and electrons should be the same. Note that occopt = 9 cannot be used with fixed magnetization calculation.
Fermi-Dirac occupation is enforced with two distinct quasi-Fermi levels: [[nqfd]] holes are forced in bands 1 to [[ivalence]] and [[nqfd]] electrons are forced in bands with index > [[ivalence]]. See details in [[cite:Paillard2019]]. At present, the number of holes and electrons should be the same. Note that [[occopt]] = 9 cannot be used with fixed magnetization calculation.
!!! note
@ -12995,7 +13031,7 @@ Variable(
([[kptopt]] == 3 or [[kptopt]] == 0) """,
added_in_version="before_v9",
text=r"""
Compute quantities related to orbital magnetization. The
Compute quantities related to orbital magnetic moment. The
implementation assumes an insulator, so no empty or partially
filled bands, and currently restricted to [[nspinor]] 1. Such
insulators have orbital magnetization zero, except in the presence
@ -13003,24 +13039,15 @@ Compute quantities related to orbital magnetization. The
is parallelized over k points only. The implementation follows the
theory outlined in [[cite:Gonze2011a]] extended to the PAW case;
see also [[cite:Ceresoli2006]]. The computed results are returned in the
standard output file, search for "Orbital magnetization" and "Chern number".
standard output file, search for "Orbital magnetic moment". This calculation requires
both the ground state and DDK wavefunctions, and is triggered at the end of a
DDK calculation.
* [[orbmag]] = 11: Compute orbital magnetization and Chern number (integral of the
Berry curvature over the Brillouin zone) using both GS and DDK wavefunctions. This is
the most robust method.
* [[orbmag]] = 1: Compute Chern number using discretized wavefunctions. This computation is
faster than the full [[orbmag]] calculation, and a nonzero value indicates a circulating
electronic current.
* [[orbmag]] = 2: Compute electronic orbital magnetization.
* [[orbmag]] = 3: Compute both Chern number and electronic orbital magnetization.
[[orbmag]] values 1--3 use an implementation based on a discretization of the wavefunction
derivatives, as in [[cite:Ceresoli2006]]. Using [[orbmag]] -1, -2, -3 delivers the
same computations as the corresponding 1, 2, 3 values, but based on an implementation
using a discretization of the density operator itself. Both methods should converge to
the same values but in our experience the wavefunction-based method converges faster. The
DDK method converges considerably faster than either of the above methods and is also robust
in case of only a single kpt.
* [[orbmag]] = 1: Compute orbital magnetization and integral of the
Berry curvature (Chern number) over the Brillouin zone.
* [[orbmag]] = 2: Same as [[orbmag]] 1 but also print out values of each term making up total
orbital magnetic moment.
* [[orbmag]] = 3: Same as [[orbmag]] 2 but print out values of each term for each band.
""",
),
@ -13524,6 +13551,10 @@ The following values are permitted for **pawovlp**:
- **pawovlp** < 0 --> overlap is always allowed
- **pawovlp** = 0 --> no overlap is allowed
- **pawovlp** > 0 and < 100 --> overlap is allowed only if it is less than **pawovlp** %
Note that ABINIT will not stop at the first time a too large overlap is identified, in case of [[ionmov]]/=0
or [[imgmov]]/=0, but only at the second time in the same dataset. Indeed, such trespassing might only be transient.
However, a second trespassing in the same dataset, or if both [[ionmov]]=0 and [[imgmov]]=0, will induce stop.
""",
),
@ -15325,7 +15356,7 @@ Variable(
vartype="integer",
topics=['printing_prgs'],
dimensions="scalar",
defaultval="prtgsr = 0",
defaultval=ValueWithConditions({'[[nimage]] > 1': 0, 'defaultval': 1}),
mnemonics="PRinT the GSR file",
added_in_version="before_v9",
text=r"""
@ -15516,8 +15547,10 @@ Variable(
text=r"""
Print out VASP-style POSCAR and FORCES files, for use with PHON or frophon
codes for frozen phonon calculations. See the associated script in
{% modal ../scripts/post_processing/phondisp2abi.py %} for further details on
interfacing with PHON, PHONOPY, etc...
{% dialog ../scripts/post_processing/phondisp2abi.py %}
for further details on interfacing with PHON, PHONOPY, etc...
""",
),
@ -15961,11 +15994,12 @@ will be the root output name, followed by _WFK. If [[nqpt]] = 1, the root name
will be followed by _WFQ. For response-function calculations, the root name
will be followed by _1WFx, where x is the number of the perturbation. The
dataset information will be added as well, if relevant.
No wavefunction output is provided by [[prtwf]] = 0.
If [[prtwf]] = 0, no wavefunction output is provided.
If [[prtwf]] = -1, the code writes the wavefunction file only if convergence is
not achieved in the self-consistent cycle.
If [[prtwf]] = 2, a file pwfn.data is produced, to be used as input for the
CASINO QMC code. See more explanation at the end of this section.
If [[prtwf]] = 3, the file that is created is nearly the same as with
@ -16913,7 +16947,7 @@ elements of the dynamical matrix, use different values of [[rfatpol]] and/or
[[rfdir]]. The name 'iatpol' is used for the part of the internal variable
ipert when it runs from 1 to [[natom]]. The internal variable ipert can also
assume values larger than [[natom]], denoting perturbations of electric field
or stress type (see [the DFPT help file](../guide/respfn)).
or stress type (see [the DFPT help file](/guide/respfn)).
""",
),
@ -20531,7 +20565,7 @@ The different possibilities are:
where LOBPCG does not scale anymore. It is not able to use preconditionning and therefore might converge slower than other algorithms.
By design, it will **not** converge the last bands: it is recommended to use slightly more bands than necessary.
For usage with [[tolwfr]], it is imperative to use [[nbdbuf]]. For more performance, try [[use_gemm_nonlop]].
For more information, see the [performance guide](../../theory/howto_chebfi.pdf) and the [[cite:Levitt2015]]. Status: experimental but usable.
For more information, see the [performance guide](/theory/howto_chebfi.pdf) and the [[cite:Levitt2015]]. Status: experimental but usable.
Questions and bug reports should be sent to antoine (dot) levitt (at) gmail.com.
""",
),
@ -20764,7 +20798,7 @@ Variable(
added_in_version="before_v9",
text=r"""
The modified Becke-Johnson exchange-correlation functional by
[[cite:Tran2009 | Tran and Blaha]] reads:
[[cite:Tran2009 | Tran and Blaha]] (acronym TB09, used when [[ixc]]=-12208, which needs [[usekden]]=1) reads:
$$ V_x(r) =
c V_x^{BR}(r) +
@ -20773,7 +20807,7 @@ c V_x^{BR}(r) +
where $\rho(r)$ is the electron density,
$t(r)$ is the kinetic-energy density, and
$ V_x^{BR}(r)$ is the Becke-Roussel potential.
$V_x^{BR}(r)$ is the Becke-Roussel potential.
In this equation the parameter $c$ can be evaluated at each SCF step according
to the following equation:
@ -21519,7 +21553,7 @@ allocated for the wavefunctions, especially when we have to sum over empty state
parallelize along this dimension. The parallelization over q-points seem to be more efficient than
the one over perturbations although it introduces some load imbalance because, due to memory reasons,
the code distributes the q-points in the IBZ (nqibz) instead of the q-points in the full BZ (nqbz).
Moreover non all the q-points in the IBZ contribute to the imaginary part of $\Sigma_nk$.
Moreover non all the q-points in the IBZ contribute to the imaginary part of $\Sigma_{nk}$.
The MPI parallelism over k-points and spins is supported with similar behaviour as in **eph_task** +4.
@ -21712,9 +21746,9 @@ to integrate the Frohlich divergence.
Possible values:
- = 0 --> Approximate oscillators with $ \delta_{b_1 b_2} $
- > 0 --> Use full expression with G-dependence
- < 0 --> Deactivate computation of oscillators.
- = 0 --> Approximate oscillators with $ \delta_{b_1 b_2} $
- > 0 --> Use full expression with G-dependence
- < 0 --> Deactivate computation of oscillators.
!!! important
@ -22098,9 +22132,9 @@ to go from $W(\rr,\RR)$ to $v1scf(\rr,\qq)$.
Possible values are:
0 --> Use unit super cell for $\RR$ space. All weights set to 1.
1 --> Use Wigner-Seitz super cell and atom-dependent weights (same algorithm as for the dynamical matrix).
Note that this option leads to more $\RR$-points with a non-negligible increase of the memory allocated.
0 --> Use unit super cell for $\RR$ space. All weights set to 1.
1 --> Use Wigner-Seitz super cell and atom-dependent weights (same algorithm as for the dynamical matrix).
Note that this option leads to more $\RR$-points with a non-negligible increase of the memory allocated.
!!! tip
@ -22266,8 +22300,8 @@ When performing structural relaxations, RMM-DIIS is activated after [[rmm_diis]]
once the first GS calculation is completed.
This means that using [[rmm_diis]] = 1 for a structural relaxation leads to:
- 4 SCF iterations with the CG/LOBPCG eigensolver followed by RMM-DIIS when are performing the **first GS calculation**.
- 1 SCF iterations with CG/LOBPCG followed by RMM-DIIS for all the subsequent relaxation steps.
- 4 SCF iterations with the CG/LOBPCG eigensolver followed by RMM-DIIS when are performing the **first GS calculation**.
- 1 SCF iterations with CG/LOBPCG followed by RMM-DIIS for all the subsequent relaxation steps.
A negative value [[rmm_diis]] (e.g. -3) can be used to bypass the initial CG/LOBPCG iterations
but this option should be used with extreme care and it is not recommended in general.
@ -22281,7 +22315,7 @@ However, the additional steps of the algorithm (subspace rotation and Cholesky o
present poor MPI-scalability hence this part will start to dominate the wall-time in systems with large [[nband]].
The algorithm can also be used for NSCF band structure calculations although one should not expect RMM-DIIS
to provide **high-energy** states of the same quality as the one obtain with other eigenvalue solvers.
to provide **high-energy** states of the same quality as the one obtained with other eigenvalue solvers.
Although RMM-DIIS can be used for computing band structures and electron DOS with [[iscf]] = -2, we do not recommend
using this solver to produce WFK files with many empty states as required by many-body calculations.
@ -22304,8 +22338,8 @@ for the Rayleigh-Ritz subspace rotation and this step is crucial for finding the
to the eigenvectors before starting the DIIS optimization.
For a given precision, RMM-DIIS usually requires more iterations than the other eigensolvers.
For performance reasons, one should avoid using tight tolerances, .
Something of the order of [[tolvrs] = 1e-8 or [[toldfe]] = 1e-10 to stop the SCF cycle should be fine.
For performance reasons, one should avoid using tight tolerances.
Something of the order of [[tolvrs]] = 1e-8 or [[toldfe]] = 1e-10 to stop the SCF cycle should be fine.
Avoid using ([[tolwfr]]) (criterion on the residuals) as converge criterion for SCF cycles
Use [[tolwfr]] only if you are using RMM-DIIS for NSCF band structure calculations (as this is the only converge criterion
available for NSCF calculations).
@ -22334,4 +22368,51 @@ In this case, one can use [[rmm_diis_savemem]] = 1 to activate a version of RMM-
""",
),
Variable(
abivarname="useextfpmd",
varset="gstate",
vartype="integer",
topics=['ExtFPMD_basic'],
dimensions="scalar",
defaultval=0,
mnemonics="USE EXTended FPMD model",
added_in_version="9.5.2",
text=r"""
Enables the calculation of contributions to the energy, entropy, stresses,
number of electrons and chemical potential using the extended first principle
molecular dynamics model for high temperature simulations.
* **useextfpmd** = 1 *(Recommanded)*, the energy shift factor will be evaluated
by making an integration of the trial potential over the real space and the
contributions will be computed with integrals over the band number.
* **useextfpmd** = 2, the energy shift factor will be evaluated by making
the average between the eigenvalues and the Fermi gas energy over the last
[[extfpmd_nbcut]] bands, and the contributions will be computed with integrals
over the band number.
* **useextfpmd** = 3, the energy shift factor will be evaluated by making the
average between the eigenvalues and the kinetic energies over the last
[[extfpmd_nbcut]] bands, and the contributions will be computed using the
density of states of the Fermi gas.
""",
),
Variable(
abivarname="extfpmd_nbcut",
varset="gstate",
vartype="integer",
topics=['ExtFPMD_basic'],
dimensions="scalar",
defaultval=25,
mnemonics="EXTended FPMD: Number of Bands at CUT",
added_in_version="9.5.2",
text=r"""
Specify the number of bands to use when averaging over last bands to get the
energy shift factor when [[useextfpmd]] = 2 or 3.
**extfpmd_nbcut** must be less than [[nband]].
""",
),
]

View File

@ -20,7 +20,7 @@ Variable(
characteristics=['[[ENERGY]]'],
added_in_version="before_v9",
text=r"""
Smearing width for the Eliashberg $\\alpha^2$F function (similar to a phonon DOS),
Smearing width for the Eliashberg $\alpha^2$F function (similar to a phonon DOS),
which is sampled on a finite q and k grid. The Dirac delta functions in energy
are replaced by Gaussians of width **a2fsmear** (by default in Hartree).
""",
@ -1612,6 +1612,7 @@ Variable(
text=r"""
Only for electron-phonon calculations. This input variable is used to
calculate the nesting function defined as:
$$
\chi_{nm}(q) = \sum_k\delta(\epsilon_{k,n}-\epsilon_F) \delta(\epsilon_{k+q,m}-\epsilon_F).
$$
@ -2378,4 +2379,21 @@ instead of the legacy mode based on the files file. Example:
""",
),
Variable(
abivarname="dos_maxmode@anaddb",
varset="anaddb",
vartype="integer",
topics=['PhononBands_useful'],
dimensions="scalar",
defaultval=0,
mnemonics="Phonon DOS MAX MODE included",
added_in_version="9.5",
text=r"""
This variable specifies the maximum phonon mode index (up to 3*natom)
included in the computation of the phonon DOS
Default is 0 i.e. all modes are included in the DOS.
""",
),
]

View File

@ -184,6 +184,24 @@ symmetry of the material. The directions of the requested components are specifi
""",
),
Variable(
abivarname="prtlincompmatrixelements@optic",
varset="optic",
vartype="integer",
topics=['Optic_basic'],
dimensions="scalar",
defaultval=0,
mnemonics="PRinT the LINear COMPonent of the dielectric tensor's MATRIX ELEMENTS",
added_in_version="v9.5",
text=r"""
If set to 1, the matrix elements, the renormalized (but unshifted) Kohn-Sham eigenvalues,
the occupations and the kpts weights are all printed out in the _OPTIC.nc file
generated by the optic script. The matrix elements are the ones used to build the linear
components of the dielectric tensor. Useful for post processing of matrix elements or
rebuild the linear components of the dielectric tensor in an external script.
""",
),
Variable(
abivarname="linel_comp@optic",
varset="optic",

View File

@ -665,7 +665,7 @@ def structure_from_abistruct_fmt(string):
"""
Parse geometrical information given in the structure:abivars format return Structure object
A typical input file in "structure:abivars" format looks like:
A typical input file in "structure:abivars" format looks like::
# MgB2 lattice structure.
natom 3

View File

@ -157,6 +157,12 @@ class AbstractInput(MutableMapping, metaclass=abc.ABCMeta):
kwargs.update(dict(*args))
for varname, varvalue in kwargs.items():
self[varname] = varvalue
# Just to make life easier to the user, we update some dimensions
# if only the "array" part is specified in input.
if "shiftk" in kwargs:
self["nshiftk"] = len(np.reshape(self["shiftk"], (-1, 3)))
return kwargs
def set_vars_ifnotin(self, *args, **kwargs):
@ -1213,6 +1219,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
Return a new input with the given variables.
Example:
new = input.new_with_vars(ecut=20, tsmear=0.04)
"""
# Avoid modifications in self.
@ -1386,6 +1393,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
as ndivsm > 0 may produce a very large number of wavevectors.
tolwfr: Tolerance on residuals for NSCF calculation.
nscf_nband: Number of bands for NSCF calculation. If None, use nband + nb_extra
nb_extra: Extra bandd to to be added to input nband if nscf_nband is None.
"""
nscf_input = self.deepcopy()
nscf_input.pop_vars(["ngkpt", "shiftk"])
@ -1451,7 +1459,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
return nscf_input
def make_dfpt_effmass_inputs(self, kpts, effmass_bands_f90, tolwfr=1e-20, iscf=-2):
def make_dfpt_effmass_inputs(self, kpts, effmass_bands_f90, ngfft=None, tolwfr=1e-20, iscf=-2):
"""
Return a |MultiDataset| object with 2 inputs for the calculation of effective masses with DFPT
The first input in a standard NSCF run, the second input computes the effective masses.
@ -1460,6 +1468,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
kpts: List of k-points in reduced coordinates where effective masses are wanted.
efmas_bands_f90: (nkpt, 2) array with band range for effmas computation.
WARNING: Assumes Fortran convention with indices starting from 1.
ngfft: FFT divisions (3 integers). Used to enforce the same FFT mesh in the NSCF run as the one used for GS.
tolwfr: Tolerance on residuals.
"""
multi = MultiDataset.replicate_input(input=self, ndtset=3)
@ -1469,7 +1478,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
kpts = np.reshape(kpts, (-1, 3))
nkpt = len(kpts)
# NSCF calculation (requires DEN)
multi[0].set_vars(tolwfr=tolwfr, kptopt=0, iscf=-2, nkpt=nkpt, kpt=kpts, prtwf=1)
multi[0].set_vars(tolwfr=tolwfr, kptopt=0, iscf=-2, nkpt=nkpt, kpt=kpts, prtwf=1, ngfft=ngfft)
# Response function calculation: d/dk (requires DEN and GS WFK)
multi[1].set_vars(
@ -1484,6 +1493,7 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
efmas_calc_dirs=1,
efmas_n_dirs=7,
efmas_dirs=np.reshape([1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0], (7, 3)),
ngfft=ngfft,
)
# Input variables for Frohlich model calculation (need DDB, WFK and EFMAS file)
@ -1909,32 +1919,60 @@ with the Abinit version you are using. Please contact the AbiPy developers.""" %
#new.add_phbbands_vars()
return new
def make_eph_isotc_input(self, ddb_ngqpt, eph_fsewin, eph_ngqpt_fine=None,
mixprec=1, boxcutmin=1.1):
"""
Return an |AbinitInput| to perform phonon-limited transport calculations.
This method is usually called with with the input associated to the NSCF run that produces
the WFK file used to start the EPH run so that we can directly inherit the k-mesh
#def make_eph_zpr_input(self, ddb_ngqpt, tmesh, eph_ngqpt_fine=None,,
# mixprec=1, boxcutmin=1.1):
# """
# Return an |AbinitInput| to perform phonon-limited transport calculations.
# This method is usually called with with the input associated to the NSCF run that produces
# the WFK file used to start the EPH run so that we can directly inherit the k-mesh
Args:
ddb_ngqpt: the coarse qpt grid used to compute the DDB and DVDB files in the phonon_work.
eph_fsewin:
eph_ngqpt_fine: the fine qpt grid used for the Fourier interpolation.
boxcutmin: For the last task only, 1.1 is often used to decrease memory and is faster over the Abinit default of 2.
mixprec: For the last task only, 1 is often used to make the EPH calculation faster. Note that Abinit default is 0.
"""
eph_ngqpt_fine = self.get("ngkpt") if eph_ngqpt_fine is None else eph_ngqpt_fine
new = self.new_with_vars(
optdriver=7, # Enter EPH driver.
eph_task=-4, # Compute imag part of Phi phonon self-energy due to to electrons.
ddb_ngqpt=ddb_ngqpt, # Ab-initio coarse q-mesh used to produce the DDB/DVDB files.
eph_ngqpt_fine=eph_ngqpt_fine, # Interpolate DFPT potentials on this denser q-mesh.
eph_fsewin=eph_fsewin,
mixprec=mixprec,
boxcutmin=boxcutmin,
)
#new.add_phbbands_vars()
return new
# Args:
# ddb_ngqpt: the coarse qpt grid used to compute the DDB and DVDB files in the phonon_work.
# eph_ngqpt_fine: the fine qpt grid used for the Fourier interpolation.
# sigma_erange: Energy window for k-states (see Abinit variable)
# tmesh: The mesh of temperatures (in Kelvin)
# boxcutmin: For the last task only, 1.1 is often used to decrease memory and is faster over the Abinit default of 2.
# mixprec: For the last task only, 1 is often used to make the EPH calculation faster. Note that Abinit default is 0.
# """
# eph_ngqpt_fine = self.get("ngkpt") if eph_ngqpt_fine is None else eph_ngqpt_fine
# new = self.new_with_vars(
# optdriver=7, # Enter EPH driver.
# eph_task=+4, # Compute real and imag part of sigma_eph.
# ddb_ngqpt=ddb_ngqpt, # Ab-initio coarse q-mesh used to produce the DDB/DVDB files.
# eph_ngqpt_fine=eph_ngqpt_fine, # Interpolate DFPT potentials on this denser q-mesh.
# tmesh=tmesh,
# mixprec=mixprec,
# boxcutmin=boxcutmin,
# )
# #new.add_phbbands_vars()
# return ne
#def make_eph_isotc_input(self, ddb_ngqpt, eph_fsewin, eph_ngqpt_fine=None,,
# mixprec=1, boxcutmin=1.1):
# """
# Return an |AbinitInput| to perform phonon-limited transport calculations.
# This method is usually called with with the input associated to the NSCF run that produces
# the WFK file used to start the EPH run so that we can directly inherit the k-mesh
# Args:
# ddb_ngqpt: the coarse qpt grid used to compute the DDB and DVDB files in the phonon_work.
# eph_fsewin:
# eph_ngqpt_fine: the fine qpt grid used for the Fourier interpolation.
# boxcutmin: For the last task only, 1.1 is often used to decrease memory and is faster over the Abinit default of 2.
# mixprec: For the last task only, 1 is often used to make the EPH calculation faster. Note that Abinit default is 0.
# """
# eph_ngqpt_fine = self.get("ngkpt") if eph_ngqpt_fine is None else eph_ngqpt_fine
# new = self.new_with_vars(
# optdriver=7, # Enter EPH driver.
# eph_task=-4, # Compute imag part of Phi phonon self-energy due to to electrons.
# ddb_ngqpt=ddb_ngqpt, # Ab-initio coarse q-mesh used to produce the DDB/DVDB files.
# eph_ngqpt_fine=eph_ngqpt_fine, # Interpolate DFPT potentials on this denser q-mesh.
# eph_fsewin=eph_fsewin,
# mixprec=mixprec,
# boxcutmin=boxcutmin,
# )
# #new.add_phbbands_vars()
# return new
def abivalidate(self, workdir=None, manager=None):
"""

View File

@ -148,6 +148,9 @@ class TestAbinitInput(AbipyTest):
assert popped["npband"] == 2 and "npband" not in new_inp
assert popped["npfft"] == 3 and "npfft" not in new_inp
new_inp.set_vars(shiftk=[0, 0, 0, 0.5, 0, 0, 0, 0, 0.5])
assert new_inp["nshiftk"] == 3
def test_input_errors(self):
"""Testing typical AbinitInput Error"""
si_structure = abilab.Structure.from_file(abidata.cif_file("si.cif"))

View File

@ -109,7 +109,7 @@ def phunit_tag(units, unicode=False):
return s
def wlabel_from_units(units):
def wlabel_from_units(units, unicode=False):
"""
Return latex string for phonon frequencies in ``units``.
"""
@ -119,10 +119,15 @@ def wlabel_from_units(units):
'thz': r'Frequency (Thz)',
}
try:
return d[units.lower().strip()]
s = d[units.lower().strip()]
except KeyError:
raise KeyError('Value for units `{}` unknown\nPossible values are:\n {}'.format(units, list(d.keys())))
if unicode:
s = s.replace('$^{-1}$', '⁻¹')
return s
def phdos_label_from_units(units, unicode=False):
"""

View File

@ -546,11 +546,7 @@ class Function1D(object):
if exchange_xy:
xx, yy = yy, xx
if ply_row == 1 and ply_col == 1:
fig.add_trace(go.Scatter(x=xx, y=yy, mode="lines", showlegend=showlegend, *args, **kwargs))
else:
fig.add_trace(go.Scatter(x=xx, y=yy, mode="lines", showlegend=showlegend, *args, **kwargs),
fig.add_trace(go.Scatter(x=xx, y=yy, mode="lines", showlegend=showlegend, *args, **kwargs),
row=ply_row, col=ply_col)
@add_fig_kwargs

View File

@ -13,8 +13,7 @@ from monty.collections import AttrDict, dict2namedtuple
from monty.functools import lazy_property
from monty.string import marquee
from pymatgen.core.lattice import Lattice
from pymatgen.util.serialization import pmg_serialize
from pymatgen.util.serialization import SlotPickleMixin
from pymatgen.util.serialization import pmg_serialize, SlotPickleMixin
from abipy.iotools import ETSF_Reader
from abipy.tools.derivatives import finite_diff
from abipy.tools.numtools import add_periodic_replicas, is_diagonal
@ -1059,6 +1058,34 @@ class KpointList(collections.abc.Sequence):
err_msg += "%s\n%s" % (self.__class__, self.to_string(verbose=0))
raise ValueError(err_msg)
def get_highsym_datataframe(self, with_cart_coords=False):
"""
Return pandas Dataframe with the names of the high-symmetry k-points
and the reduced coordinates.
Args:
with_cart_coords: True to add extra column with the Cartesian coordinates.
Return: |pandas-DataFrame|
"""
replace = {
r"$\Gamma$": "Γ",
}
import pandas as pd
rows, index = [], []
for ik, kpt in enumerate(self):
if kpt.name is None: continue
d = dict(name=replace.get(kpt.name, kpt.name), frac_coords=kpt.frac_coords)
if with_cart_coords: d["cart_coords"] = kpt.cart_coords
rows.append(d)
index.append(ik)
df = pd.DataFrame(rows, index=index)
df.index.name = "Idx"
return df
def remove_duplicated(self):
"""
Remove duplicated k-points from self. Returns new :class:`KpointList` instance.
@ -1109,6 +1136,19 @@ class KpointList(collections.abc.Sequence):
return plot_brillouin_zone(self.reciprocal_lattice, kpoints=self.frac_coords,
ax=ax, fold=fold, **kwargs)
def plotly(self, fig=None, **kwargs):
"""Plot k-points with plotly."""
from abipy.tools.plotting import plotly_wigner_seitz, plotly_brillouin_zone
fold = False
if self.is_path:
labels = {k.name: k.frac_coords for k in self if k.name}
frac_coords_lines = [self.frac_coords[line] for line in self.lines]
return plotly_brillouin_zone(self.reciprocal_lattice, lines=frac_coords_lines, labels=labels,
fig=fig, fold=fold, **kwargs)
else:
return plotly_brillouin_zone(self.reciprocal_lattice, kpoints=self.frac_coords,
fig=fig, fold=fold, **kwargs)
def get_k2kqg_map(self, qpt, atol_kdiff=None):
"""
Compute mapping k_index --> (k + q)_index, g0
@ -1190,15 +1230,21 @@ class Kpath(KpointList):
@classmethod
def from_vertices_and_names(cls, structure, vertices_names, line_density=20):
"""
Generate normalized K-path from a list of vertices and the corresponding labels.
Generate normalized k-path from a list of vertices and the corresponding labels.
Args:
structure: |Structure| object.
vertices_names: List of tuple, each tuple is of the form (kfrac_coords, kname) where
kfrac_coords are the reduced coordinates of the k-point and kname is a string with the name of
the k-point. Each point represents a vertex of the k-path.
line_density: Number of points used to sample the smallest segment of the path
line_density: Number of points used to sample the smallest segment of the path.
If 0, use list of k-points given in vertices_names
"""
if line_density == 0:
frac_coords = [vn[0] for vn in vertices_names]
knames = [vn[1] for vn in vertices_names]
return cls(structure.lattice.reciprocal_lattice, frac_coords=frac_coords, weights=None, names=knames)
gmet = structure.lattice.reciprocal_lattice.metric_tensor
vnames = [str(vn[1]) for vn in vertices_names]
vertices = np.array([vn[0] for vn in vertices_names], dtype=float)
@ -1230,8 +1276,7 @@ class Kpath(KpointList):
knames.append(vnames[-1])
frac_coords.append(vertices[-1])
return cls(structure.lattice.reciprocal_lattice, frac_coords=frac_coords,
weights=None, names=knames)
return cls(structure.lattice.reciprocal_lattice, frac_coords=frac_coords, weights=None, names=knames)
def __str__(self):
return self.to_string()

View File

@ -131,13 +131,26 @@ class AbinitNcFile(BaseFile):
according to the ETSF-IO specifications (when available).
An AbinitNcFile has a netcdf reader to read data from file and build objects.
"""
@classmethod
def from_binary_string(cls, bstring):
"""
Build object from a binary string with the netcdf data.
Useful for implementing GUIs in which widgets returns binary data.
"""
workdir = tempfile.mkdtemp()
fd, tmp_path = tempfile.mkstemp(suffix=".nc")
with open(tmp_path, "wb") as fh:
fh.write(bstring)
return cls.from_file(tmp_path)
def ncdump(self, *nc_args, **nc_kwargs):
"""Returns a string with the output of ncdump."""
return NcDumper(*nc_args, **nc_kwargs).dump(self.filepath)
@lazy_property
def abinit_version(self):
"""String with abinit version: three digits separated by comma."""
"""String with the abinit version: three digits separated by comma."""
return self.reader.rootgrp.getncattr("abinit_version")
@abc.abstractproperty
@ -147,8 +160,21 @@ class AbinitNcFile(BaseFile):
Used to construct |pandas-DataFrames|.
"""
#def get_abinit_input(self):
# input_string = self.rootgrp.get_varname_set("input_string")
def get_dims_dataframe(self, path="/"):
"""
Return: |pandas-Dataframe| with the dimensions defined in the `path` group.
"""
grp = self.reader.rootgrp if path == "/" else self.path2group[path]
d = {k: len(v) for k, v in grp.dimensions.items()}
# Since this is a Series but we want a dataframe to faciliate interoperability.
# we have to call init with additional kwargs.
import pandas as pd
return pd.DataFrame.from_dict(d, orient='index', columns=['value'])
#def get_abinit_input_str(self, path="/"):
# group = self.reader.rootgrp if path == "/" else self.path2group[path]
# input_string = group.get_varname_set("input_string")
# return input_string
# from abipy.abio.inputs import AbinitInput
# return AbinitInput(structure, pseudos, pseudo_dir=None, abi_kwargs=None)
@ -281,6 +307,10 @@ class Has_Structure(metaclass=abc.ABCMeta):
"""*Generates* a predefined list of matplotlib figures with minimal input from the user."""
yield self.structure.plot(show=False)
def yield_structure_plotly_figs(self, **kwargs):
"""*Generates* a predefined list of plotly figures with minimal input from the user."""
yield self.structure.plotly(show=False)
class Has_ElectronBands(metaclass=abc.ABCMeta):
"""Mixin class for |AbinitNcFile| containing electron data."""
@ -366,14 +396,46 @@ class Has_ElectronBands(metaclass=abc.ABCMeta):
yield self.ebands.plot_with_edos(edos, with_gaps=with_gaps, show=False)
yield edos.plot(show=False)
def expose_ebands(self, slide_mode=False, slide_timeout=None, **kwargs):
def yield_ebands_plotly_figs(self, **kwargs):
"""*Generates* a predefined list of plotly figures with minimal input from the user."""
with_gaps = not self.ebands.has_metallic_scheme
if self.ebands.kpoints.is_path:
yield self.ebands.plotly(with_gaps=with_gaps, show=False)
yield self.ebands.kpoints.plotly(show=False)
yield self.ebands.kpoints.plot(show=False)
else:
edos = self.ebands.get_edos()
# TODO
#yield self.ebands.plotly_with_edos(edos, with_gaps=with_gaps, show=False)
yield edos.plotly(show=False)
def expose_ebands(self, slide_mode=False, slide_timeout=None, expose_web=False, **kwargs):
"""
Shows a predefined list of matplotlib figures for electron bands with minimal input from the user.
"""
from abipy.tools.plotting import MplExpose
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
from abipy.tools.plotting import MplExpose, PanelExpose
if expose_web:
e = PanelExpose(title=f"e-Bands of {self.structure.formula}")
else:
e = MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1)
with e:
e(self.yield_ebands_figs(**kwargs))
#def plotly_expose_ebands(self, **kwargs):
# """
# This function *generates* a predefined list of plotly figures with minimal input from the user.
# """
# chart_studio = kwargs.pop("chart_studio", None)
# verbose = kwargs.pop("verbose", 0)
# kwargs.update(dict(
# renderer="chart_studio" if chart_studio else None,
# title=f"Band structure of {self.ebands.structure.formula}",
# with_gaps = not self.ebands.has_metallic_scheme,
# ))
# self.ebands.plotly(**kwargs)
class Has_PhononBands(metaclass=abc.ABCMeta):
"""
@ -409,6 +471,16 @@ class Has_PhononBands(metaclass=abc.ABCMeta):
yield self.phbands.plot(units=units, show=False)
yield self.phbands.plot_colored_matched(units=units, show=False)
def yield_phbands_plotly_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
Used in abiview.py to get a quick look at the results.
"""
units = kwargs.get("units", "mev")
yield self.phbands.qpoints.plotly(show=False)
yield self.phbands.plotly(units=units, show=False)
yield self.phbands.plot_colored_matched(units=units, show=False)
def expose_phbands(self, slide_mode=False, slide_timeout=None, **kwargs):
"""
Shows a predefined list of matplotlib figures for phonon bands with minimal input from the user.
@ -470,11 +542,17 @@ def get_filestat(filepath):
])
class NotebookWriter(metaclass=abc.ABCMeta):
"""
Mixin class for objects that are able to generate jupyter_ notebooks.
Subclasses must provide a concrete implementation of `write_notebook`.
"""
class HasNotebookTools(object):
def has_panel(self):
"""
Return panel module (that evaluates to True) if panel is installed else False.
"""
try:
import panel as pn
return pn
except ImportError:
return False
def make_and_open_notebook(self, nbpath=None, foreground=False,
classic_notebook=False, no_browser=False): # pragma: no cover
@ -635,6 +713,28 @@ abilab.enable_notebook(with_seaborn=True)
return nbformat, nbv, nb
@staticmethod
def _write_nb_nbpath(nb, nbpath):
"""
This method must be called at the end of ``write_notebook``.
nb is the jupyter notebook and nbpath the argument passed to ``write_notebook``.
"""
import io, os, tempfile
if nbpath is None:
_, 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)
return nbpath
class NotebookWriter(HasNotebookTools, metaclass=abc.ABCMeta):
"""
Mixin class for objects that are able to generate jupyter_ notebooks.
Subclasses must provide a concrete implementation of `write_notebook`.
"""
@abc.abstractmethod
def write_notebook(self, nbpath=None):
"""
@ -658,22 +758,6 @@ abilab.enable_notebook(with_seaborn=True)
return self._write_nb_nbpath(nb, nbpath)
"""
@staticmethod
def _write_nb_nbpath(nb, nbpath):
"""
This method must be called at the end of ``write_notebook``.
nb is the jupyter notebook and nbpath the argument passed to ``write_notebook``.
"""
import io, os, tempfile
if nbpath is None:
_, 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)
return nbpath
@classmethod
def pickle_load(cls, filepath):
"""
@ -705,13 +789,94 @@ abilab.enable_notebook(with_seaborn=True)
Used in abiview.py to get a quick look at the results.
"""
def expose(self, slide_mode=False, slide_timeout=None, **kwargs):
#@abc.abstractmethod
#def yield_plotly_figs(self, **kwargs): # pragma: no cover
# """
# This function *generates* a predefined list of matplotlib figures with minimal input from the user.
# Used in abiview.py to get a quick look at the results.
# """
def _get_panel_and_template(self):
# Create panel template with matplotlib figures and show them in the browser.
import panel as pn
pn.config.sizing_mode = 'stretch_width'
from abipy.panels.core import get_template_cls_from_name
cls = get_template_cls_from_name("FastGridTemplate")
title = self.__class__.__name__
if hasattr(self, "structure"): title = f"{title} <small>({self.structure.formula})</small>"
template = cls(
title=title,
header_background="#ff8c00 ", # Dark orange
)
return pn, template
def expose(self, slide_mode=False, slide_timeout=None, use_web=False, **kwargs):
"""
Shows a predefined list of matplotlib figures with minimal input from the user.
Relies on the ``yield_fig``s methods implemented by the subclass to generate matplotlib figures.
Args:
use_web: True to show all figures inside a panel template executed in the local browser.
False to show figures in different GUIs
"""
from abipy.tools.plotting import MplExpose
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
e(self.yield_figs(**kwargs))
if not use_web:
# Produce all matplotlib versions and show them with the X-server.
from abipy.tools.plotting import MplExpose
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
e(self.yield_figs(**kwargs))
else:
# Create panel template with matplotlib figures and show them in the browser.
pn, template = self._get_panel_and_template()
pn.config.sizing_mode = 'stretch_width'
from abipy.panels.core import mpl
for i, fig in enumerate(self.yield_figs()):
row, col = divmod(i, 2)
p = mpl(fig, with_divider=False)
if hasattr(template.main, "append"):
template.main.append(p)
else:
# Assume .main area acts like a GridSpec
row_slice = slice(3 * row, 3 * (row + 1))
if col == 0: template.main[row_slice, :6] = p
if col == 1: template.main[row_slice, 6:] = p
return template.show()
def plotly_expose(self, **kwargs): # chart_studio=False, verbose=0,
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
Relies on yield_plotly_figs implemented by the subclass to generate the figures.
"""
print("in plotly expose")
pn, template = self._get_panel_and_template()
pn.config.sizing_mode = 'stretch_width'
from abipy.panels.core import mpl, ply
# Insert figure in template.main.
from abipy.tools.plotting import is_mpl_figure, is_plotly_figure
for i, fig in enumerate(self.yield_plotly_figs()):
row, col = divmod(i, 2)
# Handle both matplotlib and plotly figures since we dont' support plotly everywhere.
if is_plotly_figure(fig):
p = ply(fig, with_divider=False)
elif is_mpl_figure(fig):
p = mpl(fig, with_divider=False)
else:
raise TypeError(f"Don't know how to handle type: `{type(fig)}`")
if hasattr(template.main, "append"):
template.main.append(p)
else:
# Assume .main area acts like a panel GridSpec
row_slice = slice(3 * row, 3 * (row + 1))
if col == 0: template.main[row_slice, :6] = p
if col == 1: template.main[row_slice, 6:] = p
return template.show()
class Has_Header(object):

View File

@ -20,7 +20,7 @@ from pymatgen.core.structure import Structure as pmg_Structure
from pymatgen.core.sites import PeriodicSite
from pymatgen.core.lattice import Lattice
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt, add_plotly_fig_kwargs
from abipy.flowtk import PseudoTable
from abipy.core.mixins import NotebookWriter
from abipy.core.symmetries import AbinitSpaceGroup
@ -660,10 +660,10 @@ class Structure(pmg_Structure, NotebookWriter):
return("\n".join(lines))
def get_panel(self):
def get_panel(self, **kwargs):
"""Build panel with widgets to interact with the structure either in a notebook or in a bokeh app"""
from abipy.panels.structure import StructurePanel
return StructurePanel(self).get_panel()
return StructurePanel(self).get_panel(**kwargs)
def get_conventional_standard_structure(self, international_monoclinic=True,
symprec=1e-3, angle_tolerance=5):
@ -1307,6 +1307,35 @@ class Structure(pmg_Structure, NotebookWriter):
return od
def get_symb2coords_dataframe(self, with_cart_coords=False):
"""
Return dictionary mapping element symbol to DataFrame with atomic positions
in cartesian coordinates.
Args:
with_cart_coords: True if Cartesian coordinates should be added as well.
"""
from collections import defaultdict
if with_cart_coords:
group = {symb: {"site_idx": [], "frac_coords": [], "cart_coords": []} for symb in self.symbol_set}
else:
group = {symb: {"site_idx": [], "frac_coords": []} for symb in self.symbol_set}
for idx, site in enumerate(self):
symb = site.specie.symbol
group[symb]["site_idx"].append(idx)
group[symb]["frac_coords"].append(site.frac_coords)
if with_cart_coords:
group[symb]["cart_coords"].append(site.coords)
import pandas as pd
out = {symb: pd.DataFrame.from_dict(d) for symb, d in group.items()}
# Use site_idx and new index.
for df in out.values():
df.set_index("site_idx", inplace=True)
return out
@add_fig_kwargs
def plot(self, **kwargs):
"""
@ -1316,6 +1345,15 @@ class Structure(pmg_Structure, NotebookWriter):
from abipy.tools.plotting import plot_structure
return plot_structure(self, **kwargs)
@add_plotly_fig_kwargs
def plotly(self, **kwargs):
"""
Plot structure in 3D with plotly. Return plotly Figure
See plot_structure for kwargs
"""
from abipy.tools.plotting import plotly_structure
return plotly_structure(self, **kwargs)
@add_fig_kwargs
def plot_bz(self, ax=None, pmg_path=True, with_labels=True, **kwargs):
"""
@ -1335,6 +1373,25 @@ class Structure(pmg_Structure, NotebookWriter):
else:
return plot_brillouin_zone(self.reciprocal_lattice, ax=ax, labels=labels, show=False, **kwargs)
@add_plotly_fig_kwargs
def plotly_bz(self, fig=None, pmg_path=True, with_labels=True, **kwargs):
"""
Use matplotlib to plot the symmetry line path in the Brillouin Zone.
Args:
ax: matplotlib :class:`Axes` or None if a new figure should be created.
pmg_path (bool): True if the default path used in pymatgen should be show.
with_labels (bool): True to plot k-point labels.
Returns: |matplotlib-Figure|.
"""
from abipy.tools.plotting import plotly_brillouin_zone_from_kpath, plotly_brillouin_zone
labels = None if not with_labels else self.hsym_kpath.kpath["kpoints"]
if pmg_path:
return plotly_brillouin_zone_from_kpath(self.hsym_kpath, fig=fig, show=False, **kwargs)
else:
return plotly_brillouin_zone(self.reciprocal_lattice, fig=fig, labels=labels, show=False, **kwargs)
@add_fig_kwargs
def plot_xrd(self, wavelength="CuKa", symprec=0, debye_waller_factors=None,
two_theta_range=(0, 90), annotate_peaks=True, ax=None, **kwargs):
@ -1534,17 +1591,17 @@ class Structure(pmg_Structure, NotebookWriter):
raise ImportError("jupyter_jsmol is not installed. See https://github.com/fekad/jupyter-jsmol")
cif_str = self.write_cif_with_spglib_symms(None, symprec=symprec, ret_string=True)
print("cif_str:\n", cif_str)
#print("cif_str:\n", cif_str)
#return JsmolView.from_str(cif_str)
from IPython.display import display, HTML
#from IPython.display import display, HTML
# FIXME TEMPORARY HACK TO LOAD JSMOL.js
# See discussion at
# https://stackoverflow.com/questions/16852885/ipython-adding-javascript-scripts-to-ipython-notebook
display(HTML('<script type="text/javascript" src="/nbextensions/jupyter-jsmol/jsmol/JSmol.min.js"></script>'))
#display(HTML('<script type="text/javascript" src="/nbextensions/jupyter-jsmol/jsmol/JSmol.min.js"></script>'))
jsmol = JsmolView(color='white')
display(jsmol)
#display(jsmol)
cmd = 'load inline "%s" {1 1 1}' % cif_str
if verbose: print("executing cmd:", cmd)
jsmol.script(cmd)

View File

@ -77,6 +77,7 @@ class TestHelperFunctions(AbipyTest):
[0.4, 0.0, 0.0 ],
[0.5, 0.0, 0.0 ]])
class TestKpoint(AbipyTest):
"""Unit tests for Kpoint object."""
@ -283,6 +284,12 @@ class TestKpath(AbipyTest):
#assert kpath.ksampling.kptopt == 1
#self.assert_equal(kpath.ksampling.mpdivs, [4, 4, 4])
df = kpath.get_highsym_datataframe(with_cart_coords=True)
#print(df)
assert "G" in df["name"].values
assert "cart_coords" in df
self.assert_equal(df["frac_coords"][0], [0.0, 0.0, 0.0])
assert Kpoint.from_name_and_structure("Gamma", structure) == kpath[0]
assert len(kpath.ds) == len(kpath) - 1

View File

@ -58,6 +58,13 @@ class TestStructure(AbipyTest):
self.assert_equal(kfrac_coords,
([[0. , 0. , 0. ], [0.5, 0. , 0.5], [0.5, 0.5, 0.5], [0. , 0. , 0. ]]))
d = si.get_symb2coords_dataframe(with_cart_coords=True)
assert "Si" in d
df = d["Si"]
assert "frac_coords" in df and len(df.frac_coords) == 2
for i in range(2):
self.assert_equal(si.frac_coords[i], df.frac_coords.values[i])
si_wfk = Structure.as_structure(abidata.ref_file("si_scf_WFK.nc"))
assert si_wfk.formula == "Si2"
si_wfk.print_neighbors(radius=2.5)

View File

@ -33,14 +33,28 @@ from abipy.dfpt.ifc import InteratomicForceConstants
from abipy.dfpt.elastic import ElasticData
from abipy.dfpt.raman import Raman
from abipy.core.abinit_units import phfactor_ev2units, phunit_tag
from abipy.tools.plotting import (add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt,
plotlyfigs_to_browser, push_to_chart_studio)
from abipy.tools.plotting import (add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt, get_figs_plotly, get_fig_plotly,
add_plotly_fig_kwargs, PlotlyRowColDesc, plotlyfigs_to_browser, push_to_chart_studio)
from abipy.tools import duck
from abipy.tools.iotools import ExitStackWithFiles
from abipy.tools.tensors import DielectricTensor, ZstarTensor, Stress
from abipy.abio.robots import Robot
SUBSCRIPT_UNICODE = {
"0": "",
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": "",
"7": "",
"8": "",
"9": "",
}
class DdbError(Exception):
"""Error class raised by DDB."""
@ -909,6 +923,14 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
yield self.qpoints.plot(show=False)
yield self.structure.plot_bz(show=False)
def yield_plotly_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
"""
yield self.structure.plotly(show=False)
yield self.qpoints.plotly(show=False)
yield self.structure.plotly_bz(show=False)
def anaget_phmodes_at_qpoint(self, qpoint=None, asr=2, chneut=1, dipdip=1, workdir=None, mpi_procs=1,
manager=None, verbose=0, lo_to_splitting=False, spell_check=True,
directions=None, anaddb_kwargs=None, return_input=False):
@ -1422,11 +1444,13 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
label = "asr: %d, chneut: %d, dipdip: %d, dipquad: %d, quadquad: %d " % (
asr, dipdip, chneut, conf["dipquad"], conf["quadquad"])
if phdos_file is not None:
phbands_plotter.add_phbands(label, phbst_file.phbands, phdos=phdos_file.phdos)
phdos_file.close()
else:
phbands_plotter.add_phbands(label, phbst_file.phbands)
phbst_file.close()
return phbands_plotter
@ -1544,6 +1568,7 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
from abipy.dfpt.converters import abinit_to_phonopy
anaddbnc_path = task.outpath_from_ext("anaddb.nc")
anaddbnc = AnaddbNcFile(anaddbnc_path)
phon = abinit_to_phonopy(anaddbnc=anaddbnc, supercell_matrix=supercell_matrix,
symmetrize_tensors=symmetrize_tensors, output_dir_path=output_dir_path,
prefix_outfiles=prefix_outfiles, symprec=symprec, set_masses=set_masses)
@ -1847,6 +1872,7 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
def remove_block(self, dord, qpt=None, qpt3=None):
"""
Removes one block from the list of blocks in the ddb
Args:
dord: the order of the perturbation (from 0 to 3).
qpt: the fractional coordinates of the q point of the block to be
@ -1909,10 +1935,12 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
self.insert_block(block_data, replace=replace)
def get_panel(self):
"""Build panel with widgets to interact with the |DdbFile| either in a notebook or in panel app."""
def get_panel(self, **kwargs):
"""
Build panel with widgets to interact with the |DdbFile| either in a notebook or in a bokeh app.
"""
from abipy.panels.ddb import DdbFilePanel
return DdbFilePanel(self).get_panel()
return DdbFilePanel(self).get_panel(**kwargs)
def write_notebook(self, nbpath=None):
"""
@ -1920,11 +1948,23 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
working directory is created. Return path to the notebook.
"""
nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None)
first_char = "" if self.has_panel() else "#"
nb.cells.extend([
nbv.new_code_cell("ddb = abilab.abiopen('%s')" % self.filepath),
nbv.new_code_cell("units = 'eV'\nprint(ddb)"),
nbv.new_code_cell("# display(ddb.header)"),
# Add panel GUI but comment the python code if panel is not available.
nbv.new_markdown_cell("## Panel dashboard"),
nbv.new_code_cell(f"""\
# Execute this cell to display the panel GUI (requires panel package).
# To display the dashboard inside the browser use `abiopen.py FILE --panel`.
{first_char}abilab.abipanel()
{first_char}ddb.get_panel()
"""),
nbv.new_markdown_cell("## Invoke `anaddb` to compute bands and dos"),
nbv.new_code_cell("""\
bstfile, phdosfile = ddb.anaget_phbst_and_phdos_files(nqsmall=10, ndivsm=20,
@ -2289,7 +2329,7 @@ class DielectricTensorGenerator(Has_Structure):
def plot(self, w_min=0, w_max=None, gamma_ev=1e-4, num=500, component='diag', reim="reim", units='eV',
with_phfreqs=True, ax=None, fontsize=12, **kwargs):
"""
Plots the selected components of the dielectric tensor as a function of frequency.
Plots the selected components of the dielectric tensor as a function of frequency with matplotlib.
Args:
w_min: minimum frequency in units `units`.
@ -2325,8 +2365,8 @@ class DielectricTensorGenerator(Has_Structure):
if 'linewidth' not in kwargs:
kwargs['linewidth'] = 2
ax.set_xlabel('Frequency {}'.format(phunit_tag(units)))
ax.set_ylabel(r'$\epsilon(\omega)$')
ax.set_xlabel('Frequency {}'.format(phunit_tag(units)), fontsize=fontsize)
ax.set_ylabel(r'$\epsilon(\omega)$', fontsize=fontsize)
ax.grid(True)
reimfs = []
@ -2349,7 +2389,7 @@ class DielectricTensorGenerator(Has_Structure):
label = reims % r'$\epsilon_{%d%d}$' % (i, j)
ax.plot(wmesh, reimf(t[:, i, j]), label=label, **kwargs)
elif component == 'diag_av':
label = r'$Average\, %s\epsilon_{ii}$' % reims
label = r'Average %s' % (reims % r'$\epsilon_{ii}$')
ax.plot(wmesh, np.trace(reimf(t), axis1=1, axis2=2)/3, label=label, **kwargs)
else:
raise ValueError('Unkwnown component {}'.format(component))
@ -2360,6 +2400,89 @@ class DielectricTensorGenerator(Has_Structure):
return fig
@add_plotly_fig_kwargs
def plotly(self, w_min=0, w_max=None, gamma_ev=1e-4, num=500, component='diag', reim="reim", units='eV',
with_phfreqs=True, fig=None, rcd=None, fontsize=16, **kwargs):
"""
Plots the selected components of the dielectric tensor as a function of frequency with plotly.
Args:
w_min: minimum frequency in units `units`.
w_max: maximum frequency. If None it will be set to the value of the maximum frequency + 5*gamma_ev.
gamma_ev: Phonon damping factor in eV (full width). Poles are shifted by phfreq * gamma_ev.
Accept scalar or [nfreq] array.
num: number of values of the frequencies between w_min and w_max.
component: determine which components of the tensor will be displayed. Can be a list/tuple of two
elements, indicating the indices [i, j] of the desired component or a string among::
* 'diag_av' to plot the average of the components on the diagonal
* 'diag' to plot the elements on diagonal
* 'all' to plot all the components in the upper triangle.
* 'offdiag' to plot the off-diagonal components in the upper triangle.
reim: a string with "re" will plot the real part, with "im" selects the imaginary part.
units: string specifying the units used for phonon frequencies. Possible values in
("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
with_phfreqs: True to show phonon frequencies with dots.
fig: |plotly.graph_objects.Figure| or None if a new figure should be created.
rcd: PlotlyRowColDesc object used when fig is not None to specify the (row, col) of the subplot in the grid.
fontsize: Legend and label fontsize.
Return: |plotly.graph_objects.Figure|
"""
wmesh = self._get_wmesh(gamma_ev, num, units, w_min, w_max)
t = np.zeros((num, 3, 3), dtype=complex)
for i, w in enumerate(wmesh):
t[i] = self.tensor_at_frequency(w, units=units, gamma_ev=gamma_ev)
if fig is None:
fig, _ = get_fig_plotly()
rcd = PlotlyRowColDesc.from_object(rcd)
iax, ply_row, ply_col = rcd.iax, rcd.ply_row, rcd.ply_col
xaxis = 'xaxis%u' % iax
yaxis = 'yaxis%u' % iax
fig.layout[xaxis].title = dict(text='Frequency {}'.format(phunit_tag(units, unicode=True)), font_size=fontsize)
fig.layout[yaxis].title = dict(text='ε(ω)', font_size=fontsize)
if 'line_width' not in kwargs:
kwargs['line_width'] = 2
reimfs = []
if 're' in reim: reimfs.append((np.real, "Re{%s}"))
if 'im' in reim: reimfs.append((np.imag, "Im{%s}"))
for reimf, reims in reimfs:
if isinstance(component, (list, tuple)):
label = reims % r'ε%s%s' % (SUBSCRIPT_UNICODE[str(component[0])],SUBSCRIPT_UNICODE[str(component[1])])
fig.add_scatter(x=wmesh, y=reimf(t[:,component[0], component[1]]), mode='lines', showlegend=True,
name=label, row=ply_row, col=ply_col, **kwargs)
elif component == 'diag':
for i in range(3):
s = SUBSCRIPT_UNICODE[str(i)]
label = reims % r'ε%s%s' % (s, s)
fig.add_scatter(x=wmesh, y=reimf(t[:, i, i]), mode='lines', name=label, row=ply_row, col=ply_col, **kwargs)
elif component in ('all', "offdiag"):
for i in range(3):
for j in range(3):
if component == "all" and i > j: continue
if component == "offdiag" and i >= j: continue
label = reims % r'ε%s%s' % (SUBSCRIPT_UNICODE[str(i)], SUBSCRIPT_UNICODE[str(j)])
fig.add_scatter(x=wmesh, y=reimf(t[:, i, j]), mode='lines', name=label, row=ply_row,
col=ply_col, **kwargs)
elif component == 'diag_av':
label = r'Average %s' % (reims % r'εᵢᵢ')
fig.add_scatter(x=wmesh, y=np.trace(reimf(t), axis1=1, axis2=2)/3, mode='lines', name=label,
row=ply_row, col=ply_col, **kwargs)
else:
raise ValueError('Unkwnown component {}'.format(component))
self._add_phfreqs_plotly(fig, rcd, units, with_phfreqs)
fig.layout.legend.font.size = fontsize
return fig
def _get_wmesh(self, gamma_ev, num, units, w_min, w_max):
"""
Helper function to get the wmesh for the plots.
@ -2500,6 +2623,22 @@ class DielectricTensorGenerator(Has_Structure):
wvals = self.phfreqs[3:] * phfactor_ev2units(units)
ax.scatter(wvals, np.zeros_like(wvals), s=30, marker="o", c="blue")
def _add_phfreqs_plotly(self, fig, rcd, units, with_phfreqs):
"""
Helper functions to add the phonon frequencies to the plotly fig.
Args:
fig: |plotly.graph_objects.Figure|
rcd: PlotlyRowColDesc object used when fig is not None to specify the (row, col) of the subplot in the grid.
units: string specifying the units used for phonon frequencies. Possible values in
("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
with_phfreqs: True to show phonon frequencies with dots.
"""
# Add points showing phonon energies.
if with_phfreqs:
wvals = self.phfreqs[3:] * phfactor_ev2units(units)
fig.add_scatter(x=wvals, y=np.zeros_like(wvals), mode='markers', marker=dict(color='blue', size=10),
name='', row=rcd.ply_row, col=rcd.ply_col, showlegend=False)
def reflectivity(self, qdir, w, gamma_ev=1e-4, units='eV'):
"""
Calculates the reflectivity from the dielectric tensor along the specified direction
@ -3009,16 +3148,16 @@ class DdbRobot(Robot):
if all(ddb.has_at_least_one_atomic_perturbation() for ddb in self.abifiles):
print("Invoking anaddb through anaget_phonon_plotters...")
r = self.anaget_phonon_plotters()
#for fig in r.phbands_plotter.yield_figs(): yield fig
#for fig in r.phdos_plotter.yield_figs(): yield fig
#for fig in r.phbands_plotter.yield_plotly_figs(): yield fig
#for fig in r.phdos_plotter.yield_plotly_figs(): yield fig
f(r.phbands_plotter.combiplotly(show=False))
push_to_chart_studio(figs) if chart_studio else plotlyfigs_to_browser(figs)
def get_panel(self):
def get_panel(self, **kwargs):
"""Return a panel object that allows the user to compare the results with a web-based interface."""
from abipy.panels.ddb import DdbRobotPanel
return DdbRobotPanel(self).get_panel()
return DdbRobotPanel(self).get_panel(**kwargs)
def write_notebook(self, nbpath=None):
"""
@ -3053,20 +3192,20 @@ class DdbRobot(Robot):
def get_2nd_ord_block_string(qpt, data):
"""
Helper function providing the lines required in a DDB file for a given
q point and second order derivatives.
q-point and second order derivatives.
Args:
qpt: the fractional coordinates of the q point.
data: a dictionary of the form {qpt: {(idir1, ipert1, idir2, ipert2): complex value}}
with the data that should be given in the string.
Returns:
list of str: the lines that can be added to the DDB file.
Returns: list of str: the lines that can be added to the DDB file.
"""
lines = []
lines.append(f" 2nd derivatives (non-stat.) - # elements :{len(data):8}")
lines.append(" qpt{:16.8E}{:16.8E}{:16.8E} 1.0".format(*qpt))
l_format = "{:4d}" * 4 + " {:22.14E}" * 2
for p, v in data.items():
lines.append(l_format.format(*p, v.real, v.imag))

View File

@ -26,8 +26,8 @@ from abipy.iotools import ETSF_Reader
from abipy.tools import duck
from abipy.tools.numtools import gaussian, sort_and_groupby
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, set_axlims, get_axarray_fig_plt, set_visible,\
set_ax_xylabels, get_figs_plotly, get_fig_plotly, add_plotly_fig_kwargs,\
plotlyfigs_to_browser, push_to_chart_studio, PlotlyRowColDesc, plotly_klabels
set_ax_xylabels, get_figs_plotly, get_fig_plotly, add_plotly_fig_kwargs, plotlyfigs_to_browser,\
push_to_chart_studio, PlotlyRowColDesc, plotly_klabels, plotly_set_xylabels, plotly_set_lims
from .phtk import match_eigenvectors, get_dyn_mat_eigenvec, open_file_phononwebsite, NonAnalyticalPh
__all__ = [
@ -1015,7 +1015,7 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
# Handle conversion factor.
if units:
fig.layout['yaxis%u' % iax].title.text = abu.wlabel_from_units(units).replace('$^{-1}$', '⁻¹')
fig.layout['yaxis%u' % iax].title.text = abu.wlabel_from_units(units, unicode=True)
fig.layout[xaxis].title.text = "Wave Vector"
@ -1085,7 +1085,7 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
@add_plotly_fig_kwargs
def plotly(self, units="eV", qlabels=None, branch_range=None, match_bands=False, temp=None,
fig=None, rcd=None, fontsize=16, **kwargs):
fig=None, rcd=None, fontsize=12, **kwargs):
r"""
Plot the phonon band structure with plotly.
@ -1108,7 +1108,7 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
branch_range = range(self.num_branches) if branch_range is None else \
range(branch_range[0], branch_range[1], 1)
fig, go = get_fig_plotly(fig=fig)
fig, _ = get_fig_plotly(fig=fig)
# Decorate the axis (e.g. add ticks and labels).
rcd = PlotlyRowColDesc.from_object(rcd)
@ -1124,9 +1124,8 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
# Scatter plot with Bose-Einstein occupation factors for T = temp
factor = abu.phfactor_ev2units(units)
if temp < 1: temp = 1
# this will be covered if the title is set by the user
fig.layout.title.text = "T = %.1f K" % temp
fig.layout.title.font.size = fontsize
fig.layout.annotations=[dict(text="T = %.1f K" % temp, font_size=fontsize, x=0.5, xref='paper',
xanchor='center', y=1, yref='paper', yanchor='bottom' ,showarrow=False)]
xs = np.arange(self.num_qpoints)
for nu in self.branches:
ws = self.phfreqs[:, nu]
@ -1136,9 +1135,9 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
occ = 1.0 / (np.exp(wkt) - 1.0)
s = np.where(occ < 0.3, occ, 0.3) * 50
#print("rcd", rcd)
fig.add_trace(go.Scatter(x=xs, y=ws * factor, mode='markers',
marker=dict(color=occ, colorscale='jet', size=s, opacity=0.6, line_width=0),
showlegend=False), row=rcd.ply_row, col=rcd.ply_col)
fig.add_scatter(x=xs, y=ws * factor, mode='markers', row=rcd.ply_row, col=rcd.ply_col, showlegend=False,
marker=dict(color='blue', size=s, opacity=0.6, line_width=0), name='')
# marker=dict(color=occ, colorscale='jet', size=s, opacity=0.6, line_width=0),
return fig
def plot_ax(self, ax, branch, units='eV', match_bands=False, **kwargs):
@ -1173,13 +1172,13 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
return lines
def plotly_traces(self, fig, branch, rcd=None, units='eV', name='', match_bands=False,
showlengend=False, **kwargs):
showlegend=False, **kwargs):
"""
Plots the frequencies for the given branches indices as a function of the q-index on figure ``fig`` .
If ``fig`` has subplots, ``rcd`` is used to add traces on these subplots.
If ``branch`` is None, all phonon branches are plotted.
kwargs: Passed to fig.add_scatter method.
"""
import plotly.graph_objects as go
linecolor = kwargs.pop("color", "black")
linewidth = kwargs.pop("linewidth", 2.0)
@ -1203,17 +1202,11 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
pf = pf * factor
xx = list(range(first_xx, first_xx + len(pf)))
for branch in branch_range:
if ply_row == 1 and ply_col == 1:
fig.add_trace(
go.Scatter(x=xx, y=pf[:, branch], mode='lines', name=name, legendgroup=name, showlegend=False,
line=dict(color=linecolor, width=linewidth), **kwargs))
else:
fig.add_trace(
go.Scatter(x=xx, y=pf[:, branch], mode='lines', name=name, legendgroup=name, showlegend=False,
line=dict(color=linecolor, width=linewidth), **kwargs), row=ply_row, col=ply_col)
fig.add_scatter(x=xx, y=pf[:, branch], mode='lines', name=name, legendgroup=name, showlegend=False,
line=dict(color=linecolor, width=linewidth), **kwargs, row=ply_row, col=ply_col)
first_xx = xx[-1]
if showlengend:
if showlegend:
fig.data[-1].showlegend = True
@add_fig_kwargs
@ -1277,7 +1270,7 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
use_becs=True, colormap="jet", fontsize=12, **kwargs):
r"""
Plot the phonon band structure with colored lines. The color of the lines indicates
the degree to which the mode is longitudinal:
the degree to which the mode is longitudinal.
Red corresponds to longitudinal modes and black to purely transverse modes.
Args:
@ -1787,7 +1780,7 @@ See also <https://forum.abinit.org/viewtopic.php?f=10&t=545>
else:
rcd_phdos = PlotlyRowColDesc(0, 1, 1, 2)
phdos.plotly_dos_idos(fig, rcd=rcd_phdos, what="d", units=units, exchange_xy=True, **kwargs)
phdos.plotly_dos_idos(fig, rcd=rcd_phdos, what="d", units=units, exchange_xy=True, showlegend=False, **kwargs)
return fig
@ -2806,12 +2799,18 @@ class PhbstFile(AbinitNcFile, Has_Structure, Has_PhononBands, NotebookWriter):
"""
return self.yield_phbands_figs(**kwargs)
def plotly_expose(self, chart_studio=False, verbose=0, **kwargs):
def yield_plotly_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
"""
renderer = "chart_studio" if chart_studio else None
self.phbands.plotly(renderer=renderer)
return self.yield_phbands_plotly_figs(**kwargs)
#def plotly_expose(self, chart_studio=False, verbose=0, **kwargs):
# """
# This function *generates* a predefined list of plotly figures with minimal input from the user.
# """
# renderer = "chart_studio" if chart_studio else None
# self.phbands.plotly(renderer=renderer)
def write_notebook(self, nbpath=None):
"""
@ -2967,7 +2966,7 @@ class PhononDos(Function1D):
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
Case-insensitive.
rcd: PlotlyRowColDesc object used when fig is not None to specify the (row, col) of the subplot in the grid.
kwargs: Options passed to plotly.graph_objects Scatter method.
kwargs: Passed to fig.add_scatter method.
"""
opts = [c.lower() for c in what]
@ -3194,7 +3193,7 @@ class PhononDos(Function1D):
ax.set_ylabel(_THERMO_YLABELS[qname][units], fontsize=fontsize)
#ax.legend(loc="best", fontsize=fontsize, shadow=True)
if irow != nrows:
if irow != nrows-1:
set_visible(ax, False, "xlabel")
return fig
@ -3229,7 +3228,7 @@ class PhononDos(Function1D):
ncols = 2
nrows = num_plots // ncols + num_plots % ncols
fig, go = get_figs_plotly(nrows=nrows, ncols=ncols, subplot_titles=quantities, sharex=True, sharey=False)
fig, _ = get_figs_plotly(nrows=nrows, ncols=ncols, subplot_titles=quantities, sharex=True, sharey=False)
for iq, qname in enumerate(quantities):
irow, icol = divmod(iq, ncols)
@ -3238,11 +3237,13 @@ class PhononDos(Function1D):
ys = f1d.values
if formula_units is not None: ys /= formula_units
if units == "Jmol": ys = ys * abu.e_Cb * abu.Avogadro
fig.add_trace(go.Scatter(x=f1d.mesh, y=ys, mode="lines", name=qname), row=irow + 1, col=icol + 1)
fig.add_scatter(x=f1d.mesh, y=ys, mode="lines", name=qname, row=irow + 1, col=icol + 1)
fig.layout.annotations[iq].font.size = fontsize
iax = iq + 1
fig.layout['yaxis%u' % iax].title = {'text': _PLOTLY_THERMO_YLABELS[qname][units], 'font_size': fontsize}
fig.layout['xaxis%u' % iax].title = {'text': 'T (K)', 'font_size': fontsize}
if irow == nrows-1:
fig.layout['xaxis%u' % iax].title = {'text': 'T (K)', 'font_size': fontsize}
return fig
@ -3445,7 +3446,7 @@ class PhdosFile(AbinitNcFile, Has_Structure, NotebookWriter):
def plot_pjdos_type(self, units="eV", stacked=True, colormap="jet", alpha=0.7, exchange_xy=False,
ax=None, xlims=None, ylims=None, fontsize=12, **kwargs):
"""
Plot type-projected phonon DOS.
Plot type-projected phonon DOS with matplotlib.
Args:
ax: |matplotlib-Axes| or None if a new figure should be created.
@ -3508,6 +3509,64 @@ class PhdosFile(AbinitNcFile, Has_Structure, NotebookWriter):
return fig
@add_plotly_fig_kwargs
def plotly_pjdos_type(self, units="eV", stacked=True, exchange_xy=False,
fig=None, xlims=None, ylims=None, fontsize=12, **kwargs):
"""
Plot type-projected phonon DOS with plotly.
Args:
fig: plotly figure or None if a new figure should be created.
stacked: True if DOS partial contributions should be stacked on top of each other.
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
Case-insensitive.
exchange_xy: True to exchange x-y axis.
xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)``.
ylims: Set the data limits for the y-axis. Accept tuple e.g. ``(left, right)``.
fontsize: legend and title fontsize.
Returns: |plotly.graph_objects.Figure|
"""
lw = kwargs.pop("lw", 2)
factor = abu.phfactor_ev2units(units)
fig, _ = get_fig_plotly(fig=fig)
plotly_set_lims(fig, xlims, "x")
plotly_set_lims(fig, ylims, "y")
xlabel = 'Frequency %s' % abu.phunit_tag(units, unicode=True)
ylabel = 'PJDOS %s' % abu.phdos_label_from_units(units, unicode=True)
plotly_set_xylabels(fig, xlabel, ylabel, exchange_xy)
# Type projected DOSes.
cumulative = np.zeros(len(self.wmesh))
for i, (symbol, pjdos) in enumerate(self.pjdos_symbol.items()):
x, y = pjdos.mesh * factor, pjdos.values / factor
if exchange_xy: x, y = y, x
if not stacked:
fig.add_scatter(x=x, y=y, mode='lines', name=symbol, line=dict(width=lw))
else:
if not exchange_xy:
fig.add_scatter(x=x, y=cumulative + y, mode='lines', name=symbol,
line=dict(width=lw), fill='tonextx')
cumulative += y
else:
fig.add_scatter(x=cumulative + x, y=y, mode='lines', name=symbol,
line=dict(width=lw), fill='tonexty')
cumulative += x
# Total PHDOS
x, y = self.phdos.mesh * factor, self.phdos.values / factor
if exchange_xy: x, y = y, x
fig.add_scatter(x=x, y=y, mode='lines', line=dict(width=lw, color='black'), name="Total PHDOS")
fig.layout.legend.font.size = fontsize
fig.layout.title.font.size = fontsize
return fig
@add_fig_kwargs
def plot_pjdos_cartdirs_type(self, units="eV", stacked=True, colormap="jet", alpha=0.7,
xlims=None, ylims=None, ax_list=None, fontsize=8, **kwargs):
@ -3665,10 +3724,22 @@ class PhdosFile(AbinitNcFile, Has_Structure, NotebookWriter):
yield msqd_dos.plot(units=units, show=False)
yield msqd_dos.plot_tensor(show=False)
def plotly_expose(self, chart_studio=False, units="meV", verbose=0, **kwargs):
renderer = "chart_studio" if chart_studio else None
#self.phdos.plotly_dos_idos(units=units, renderer=renderer, show=False)
self.phdos.plotly(units=units, renderer=renderer, show=True)
def yield_plotly_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
Used in abiview.py to get a quick look at the results.
"""
units = kwargs.get("units", "mev")
yield self.phdos.plotly(units=units, show=False)
yield self.plotly_pjdos_type(units=units, show=False)
# Old formats do not have MSQDOS arrays.
#try:
# msqd_dos = self.msqd_dos
#except Exception:
# msqd_dos = None
#if msqd_dos is not None:
# yield msqd_dos.plot(units=units, show=False)
# yield msqd_dos.plot_tensor(show=False)
def write_notebook(self, nbpath=None):
"""
@ -3881,7 +3952,7 @@ class PhononBandsPlotter(NotebookWriter):
for i, (label, phbands) in enumerate(self.phbands_dict.items()):
app("[%d] %s --> %s" % (i, label, func(phbands)))
if self.phdoses_dict:
if self.phdoses_dict and verbose:
for i, (label, phdos) in enumerate(self.phdoses_dict.items()):
app("[%d] %s --> %s" % (i, label, func(phdos)))
@ -4055,8 +4126,7 @@ class PhononBandsPlotter(NotebookWriter):
Case-insensitive.
qlabels: dictionary whose keys are tuples with the reduced coordinates of the k-points.
The values are the labels e.g. ``klabels = {(0.0,0.0,0.0): "$\Gamma$", (0.5,0,0): "L"}``.
ylims: Set the data limits for the y-axis. Accept tuple e.g. ``(left, right)``
or scalar e.g. ``left``. If left (right) is None, default values are used
ylims: Set the data limits for the y-axis. Accept tuple e.g. ``(left, right)``.
width_ratios: Ratio between the width of the phonon bands plots and the DOS plots.
Used if plotter has DOSes.
fontsize: fontsize for titles and legend.
@ -4066,24 +4136,13 @@ class PhononBandsPlotter(NotebookWriter):
"""
if self.phdoses_dict:
nrows, ncols = (1, 2)
fig, go = get_figs_plotly(nrows=nrows, ncols=ncols, subplot_titles=[], sharex=False, sharey=True,
fig, _ = get_figs_plotly(nrows=nrows, ncols=ncols, subplot_titles=[], sharex=False, sharey=True,
horizontal_spacing=0.03, column_widths=width_ratios)
else:
nrows, ncols = (1, 1)
fig, go = get_fig_plotly()
fig, _ = get_fig_plotly()
if ylims is not None:
try:
len_lims = len(ylims)
except TypeError:
# Assume Scalar
raise NotImplementedError()
if len_lims is not None:
if len(ylims) == 2:
fig.layout.yaxis.range = ylims
elif len(ylims) == 1:
raise NotImplementedError()
plotly_set_lims(fig, ylims, 'y')
# Plot phonon bands.
my_kwargs, opts_label = kwargs.copy(), {}
@ -4104,7 +4163,7 @@ class PhononBandsPlotter(NotebookWriter):
if os.path.isfile(label): label = os.path.relpath(label)
rcd = PlotlyRowColDesc(0, 0, nrows, ncols)
phbands.plotly_traces(fig, branch=None, rcd=rcd, units=units, name=label, showlengend=True, **my_kwargs)
phbands.plotly_traces(fig, branch=None, rcd=rcd, units=units, name=label, showlegend=True, **my_kwargs)
# Set ticks and labels, legends.
if i == 0:
@ -4177,7 +4236,7 @@ class PhononBandsPlotter(NotebookWriter):
with_dos: True to plot phonon DOS (if available).
fontsize: legend and title fontsize.
Returns: |matplotlib-Figure|
Returns: |plotly.graph_objects.Figure|
"""
titles = list(self._bands_dict.keys())
phb_objects = list(self._bands_dict.values())
@ -4198,15 +4257,16 @@ class PhononBandsPlotter(NotebookWriter):
raise NotImplementedError("")
for i, (phbands, phdos) in enumerate(zip(phb_objects, phdos_objects)):
row, col = divmod(i, ncols)
#rcd_phdos = PlotlyRowColDesc(row, col, nrows, ncols)
#rcd_phbands = PlotlyRowColDesc(row, col, nrows, ncols)
phbands.plotly_with_phdos(phdos, fig=fig, rcd_phbands=None, rcd_phdos=None,
rcd_phdos = PlotlyRowColDesc(row, col, nrows, ncols)
rcd_phbands = PlotlyRowColDesc(row, col, nrows, ncols)
phbands.plotly_with_phdos(phdos, fig=fig, rcd_phbands=rcd_phbands, rcd_phdos=rcd_phdos,
units=units, fontsize=fontsize,
width_ratios=(2, 1), show=False)
else:
for i, phbands in enumerate(phb_objects):
row, col = divmod(i, ncols)
rcd = PlotlyRowColDesc(row, col, nrows, ncols)
phbands.plotly(fig=fig, rcd=rcd, show=False)
phbands.plotly(fig=fig, rcd=rcd, units=units, fontsize=fontsize, show=False)
return fig
@ -4644,16 +4704,15 @@ class PhononDosPlotter(NotebookWriter):
fig: plotly figure or None if a new figure should be created.
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
Case-insensitive.
xlims: Set the data limits for the x-axis. Accept tuple e.g. `(left, right)`
or scalar e.g. `left`. If left (right) is None, default values are used
ylims: y-axis limits.
xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)``.
ylims: Set the data limits for the y-axis. Accept tuple e.g. ``(left, right)``.
fontsize: Legend and title fontsize.
Returns: |plotly.graph_objects.Figure|
"""
fig, _ = get_fig_plotly(fig=fig)
#set_axlims(ax, xlims, "x")
#set_axlims(ax, ylims, "y")
plotly_set_lims(fig, xlims, "x")
plotly_set_lims(fig, ylims, "y")
fig.layout['xaxis1'].title = {'text': 'Energy %s' % abu.phunit_tag(units, unicode=True)}
fig.layout['yaxis1'].title = {"text": 'DOS %s' % abu.phdos_label_from_units(units, unicode=True)}

View File

@ -9,7 +9,8 @@ from abipy.core.mixins import Has_Structure, NotebookWriter
from abipy.dfpt.ddb import DdbFile
from abipy.dfpt.phonons import PhononBands, get_dyn_mat_eigenvec, match_eigenvectors
from abipy.abio.inputs import AnaddbInput
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, set_visible
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, set_visible, get_fig_plotly, get_figs_plotly, \
add_plotly_fig_kwargs, plotlyfigs_to_browser, push_to_chart_studio, PlotlyRowColDesc
from pymatgen.core.units import bohr_to_angstrom, eV_to_Ha
@ -284,14 +285,14 @@ class SoundVelocity(Has_Structure, NotebookWriter):
@add_fig_kwargs
def plot_fit_freqs_dir(self, idir, ax=None, units="eV", fontsize=8, **kwargs):
"""
Plots the phonon frequencies, if available, along the specified direction.
Plots the phonon frequencies with matplotlib, if available, along the specified direction.
The line representing the fitted value will be shown as well.
Args:
idir: index of the direction.
ax: |matplotlib-Axes| or None if a new figure should be created.
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
fontsize: fontsize for legends and titles
fontsize: fontsize for subtitles
Returns: |matplotlib-Figure|
"""
@ -323,16 +324,74 @@ class SoundVelocity(Has_Structure, NotebookWriter):
return fig
@add_plotly_fig_kwargs
def plotly_fit_freqs_dir(self, idir, fig=None, rcd=None, units="eV", fontsize=12, **kwargs):
"""
Plots the phonon frequencies with plotly, if available, along the specified direction.
The line representing the fitted value will be shown as well.
Args:
idir: index of the direction.
fig: |plotly.graph_objects.Figure|
rcd: PlotlyRowColDesc object used to specify the (row, col) of the subplot in the grid.
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
fontsize: fontsize for subtitles
Returns: |plotly.graph_objects.Figure|
"""
if self.phfreqs is None or self.qpts is None:
raise ValueError("The plot requires phonon frequencies and qpoints.")
title = "[{:.3f}, {:.3f}, {:.3f}]".format(*self.directions[idir])
if self.labels:
title += " - {}".format(self.labels[idir])
rcd = PlotlyRowColDesc.from_object(rcd)
ply_row, ply_col = rcd.ply_row, rcd.ply_col
xaxis = 'xaxis%u' % rcd.iax
yaxis = 'yaxis%u' % rcd.iax
if fig is None:
fig, _ = get_fig_plotly()
fig.layout=dict(annotations=[dict(text=title, font_size=fontsize, x=0.5, xref='paper', xanchor='center',
y=1, yref='paper', yanchor='bottom' ,showarrow=False)],
yaxis_title_text=abu.wlabel_from_units(units, unicode=True),
xaxis_title_text= "Wave Vector")
else:
fig.layout.annotations[idir].text = title
fig.layout.annotations[idir].font.size = fontsize
if idir == self.n_directions - 1:
fig.layout[xaxis].title.text = "Wave Vector"
if idir == 0:
fig.layout[yaxis].title.text = abu.wlabel_from_units(units, unicode=True)
fig.layout[xaxis].rangemode = 'tozero'
fig.layout[yaxis].rangemode = 'tozero'
rlatt = self.structure.lattice.reciprocal_lattice
freqs = self.phfreqs[idir]
qpt_cart_coords = np.array([np.linalg.norm(rlatt.get_cartesian_coords(c)) for c in self.qpts[idir]])
slope = self.sound_velocities[idir] / abu.velocity_at_to_si * bohr_to_angstrom / eV_to_Ha
units_factor = abu.phfactor_ev2units(units)
for i, c in enumerate(["red", "blue", "green"]):
fig.add_scatter(x=qpt_cart_coords, y=slope[i] * qpt_cart_coords * units_factor, line_color=c,
name='', showlegend=False, mode='lines', row=ply_row, col=ply_col)
fig.add_scatter(x=qpt_cart_coords, y=freqs[i] * units_factor, marker=dict(symbol=4, size=8, color=c),
name='', showlegend=False, mode='markers', row=ply_row, col=ply_col)
return fig
@add_fig_kwargs
def plot(self, units="eV", fontsize=8, **kwargs):
"""
Plots the phonon frequencies, if available, along all the directions.
Plots the phonon frequencies with matplotlib, if available, along all the directions.
The lines representing the fitted values will be shown as well.
Args:
ax: |matplotlib-Axes| or None if a new figure should be created.
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
fontsize: fontsize for legends and titles
fontsize: fontsize for subtitles
Returns: |matplotlib-Figure|
"""
@ -352,6 +411,29 @@ class SoundVelocity(Has_Structure, NotebookWriter):
return fig
@add_plotly_fig_kwargs
def plotly(self, units="eV", fontsize=12, **kwargs):
"""
Plots the phonon frequencies with plotly, if available, along all the directions.
The lines representing the fitted values will be shown as well.
Args:
units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
fontsize: fontsize for subtitles
Returns: |plotly.graph_objects.Figure|
"""
nrows, ncols = math.ceil(self.n_directions / 2), 2
fig, _ = get_figs_plotly(nrows=nrows, ncols=ncols, subplot_titles=list(range(1, self.n_directions+1)),
horizontal_spacing=0.05)
for i in range(self.n_directions):
rcd = PlotlyRowColDesc(i//2, i%2, nrows, ncols)
self.plotly_fit_freqs_dir(i, fig, rcd, units=units, fontsize=fontsize, show=False)
return fig
def yield_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of matplotlib figures with minimal input from the user.

View File

@ -29,7 +29,6 @@ class HistFile(AbinitNcFile, NotebookWriter):
with HistFile("foo_HIST") as hist:
hist.plot()
.. rubric:: Inheritance Diagram
.. inheritance-diagram:: HistFile
"""
@ -196,6 +195,7 @@ class HistFile(AbinitNcFile, NotebookWriter):
"""
if filepath is not None and os.path.exists(filepath) and not overwrite:
raise RuntimeError("Cannot overwrite pre-existing file `%s`" % filepath)
if filepath is None:
import tempfile
fd, filepath = tempfile.mkstemp(text=True, suffix="_XDATCAR")
@ -400,7 +400,6 @@ class HistFile(AbinitNcFile, NotebookWriter):
ax_list, fig, plt = get_axarray_fig_plt(ax_list, nrows=nrows, ncols=ncols,
sharex=True, sharey=False, squeeze=False)
ax_list = ax_list.ravel()
assert len(ax_list) == len(what_list)
# don't show the last ax if nplots is odd.
if nplots % ncols != 0: ax_list[-1].axis("off")
@ -443,6 +442,14 @@ class HistFile(AbinitNcFile, NotebookWriter):
yield self.plot(show=False)
yield self.plot_energies(show=False)
#def yield_plotly_figs(self, **kwargs): # pragma: no cover
# """
# This function *generates* a predefined list of matplotlib figures with minimal input from the user.
# """
# yield self.plotly(show=False)
# yield self.plotly_energies(show=False)
def mvplot_trajectories(self, colormap="hot", sampling=1, figure=None, show=True,
with_forces=True, **kwargs): # pragma: no cover
"""
@ -515,10 +522,12 @@ class HistFile(AbinitNcFile, NotebookWriter):
anim()
def get_panel(self):
"""Build panel with widgets to interact with the |HistFile| either in a notebook or in panel app."""
def get_panel(self, **kwargs):
"""
Build panel with widgets to interact with the |HistFile| either in a notebook or in panel app.
"""
from abipy.panels.hist import HistFilePanel
return HistFilePanel(self).get_panel()
return HistFilePanel(self).get_panel(**kwargs)
def write_notebook(self, nbpath=None):
"""

View File

@ -30,7 +30,22 @@ from abipy.iotools import ETSF_Reader
from abipy.tools import duck
from abipy.tools.numtools import gaussian
from abipy.tools.plotting import (set_axlims, add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt,
get_ax3d_fig_plt, rotate_ticklabels, set_visible, plot_unit_cell, set_ax_xylabels)
get_ax3d_fig_plt, rotate_ticklabels, set_visible, plot_unit_cell, set_ax_xylabels, get_figs_plotly,
get_fig_plotly, add_plotly_fig_kwargs, PlotlyRowColDesc, plotly_klabels, plotly_set_lims)
SUBSCRIPT_UNICODE = {
"0": "",
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": "",
"7": "",
"8": "",
"9": "",
}
__all__ = [
@ -328,6 +343,18 @@ class ElectronBands(Has_Structure):
assert new.__class__ == cls
return new
@classmethod
def from_binary_string(cls, bstring):
"""
Build object from a binary string with the netcdf data.
Useful for implementing GUIs in which widgets returns binary data.
"""
workdir = tempfile.mkdtemp()
fd, tmp_path = tempfile.mkstemp(suffix=".nc")
with open(tmp_path, "wb") as fh:
fh.write(bstring)
return cls.from_file(tmp_path)
@classmethod
def from_dict(cls, d):
"""Reconstruct object from the dictionary in MSONable format produced by as_dict."""
@ -569,6 +596,16 @@ class ElectronBands(Has_Structure):
__radd__ = __add__
def get_plotter_with(self, self_key, other_key, other_ebands):
"""
Build and |ElectronBandsPlotter| from self and other, use self_key and other_key as keywords
"""
plotter = ElectronBandsPlotter()
plotter.add_ebands(self_key, self)
plotter.add_ebands(other_key, other_ebands)
return plotter
# Handy variables used to loop
@property
def spins(self):
@ -1355,29 +1392,39 @@ class ElectronBands(Has_Structure):
return dirgaps
def get_gaps_string(self, with_latex=True):
def get_gaps_string(self, with_latex=True, unicode=False):
"""
Return string with info about fundamental and direct gap (if not metallic scheme)
Args:
with_latex: True to get latex symbols for the gap names else text.
with_latex: True to get latex symbols for the gap names and formula else text.
unicode: True to get unicode symbols for the formula else text.
"""
enough_bands = (self.mband > self.nspinor * self.nelect // 2)
dg_name, fg_name = "direct gap", "fundamental gap"
if with_latex:
dg_name, fg_name = "$E^{dir}_{gap}$", "$E^{fund}_{gap}$"
formula = self.structure.latex_formula
else:
dg_name, fg_name = "direct gap", "fundamental gap"
formula = self.structure.formula
if unicode:
import re
numl=re.findall(r'\d', formula)
for s in numl:
formula = formula.replace(s,SUBSCRIPT_UNICODE[s])
if enough_bands and not self.has_metallic_scheme:
if self.nsppol == 1:
s = "%s: %s = %.2f, %s = %.2f (eV)" % (
self.structure.latex_formula,
formula,
dg_name, self.direct_gaps[0].energy,
fg_name, self.fundamental_gaps[0].energy)
else:
dgs = [t.energy for t in self.direct_gaps]
fgs = [t.energy for t in self.fundamental_gaps]
s = "%s: %s = %.2f (%.2f), %s = %.2f (%.2f) (eV)" % (
self.structure.latex_formula,
formula,
dg_name, dgs[0], dgs[1],
fg_name, fgs[0], fgs[1])
else:
@ -1412,7 +1459,11 @@ class ElectronBands(Has_Structure):
k0_list.append(k)
effmass_bands_f90.append(v)
# Set small values to zero.
k0_list = np.reshape(k0_list, (-1, 3))
k0_list = np.where(np.abs(k0_list) > 1e-12, k0_list, 0.0)
effmass_bands_f90 = np.reshape(effmass_bands_f90, (-1, 2))
#print("k0_list:\n", k0_list, "\neffmass_bands_f90:\n", effmass_bands_f90)
@ -2001,6 +2052,145 @@ class ElectronBands(Has_Structure):
return fig
@add_plotly_fig_kwargs
def plotly(self, spin=None, band_range=None, klabels=None, e0="fermie", fig=None, ylims=None,
points=None, with_gaps=False, max_phfreq=None, fontsize=12, **kwargs):
r"""
Plot the electronic band structure with plotly.
Args:
spin: Spin index. None to plot both spins.
band_range: Tuple specifying the minimum and maximum band to plot (default: all bands are plotted)
klabels: dictionary whose keys are tuple with the reduced
coordinates of the k-points. The values are the labels. e.g.
``klabels = {(0.0,0.0,0.0): "$\Gamma$", (0.5,0,0):"L"}``.
e0: Option used to define the zero of energy in the band structure plot. Possible values:
- ``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
fig: plotly figure or None if a new figure should be created.
ylims: Set the data limits for the y-axis. Accept tuple e.g. ``(left, right)``
points: Marker object with the position and the size of the marker.
Used for plotting purpose e.g. QP energies, energy derivatives.
with_gaps: True to add markers and arrows showing the fundamental and the direct gap.
IMPORTANT: If the gaps are now showed correctly in a non-magnetic semiconductor,
call `ebands.set_fermie_to_vbm()` to align the Fermi level at the top of the valence
bands before executing `ebands.plot().
The Fermi energy stored in the object, indeed, comes from the GS calculation
that produced the DEN file. If the k-mesh used for the GS and the CBM is e.g. at Gamma,
the Fermi energy will be underestimated and a manual aligment is needed.
max_phfreq: Max phonon frequency in eV to activate scatterplot showing
possible phonon absorption/emission processes based on energy-conservation alone.
All final states whose energy is within +- max_phfreq of the initial state are included.
By default, the four electronic states defining the fundamental and the direct gaps
are considered as initial state (not available for metals).
fontsize: fontsize for legends and titles
kwargs: Passed to go.Scatter
Returns: |plotly.graph_objects.Figure|
"""
# Select spins
spin_list = self.spins if spin is None else [spin]
# Select the band range.
if band_range is None:
band_list = list(range(self.mband))
else:
band_list = list(range(band_range[0], band_range[1], 1))
e0 = self.get_e0(e0)
fig, go = get_fig_plotly(fig=fig)
# Decorate the axis (e.g add ticks and labels).
self.decorate_plotly(fig, klabels=klabels)
plotly_set_lims(fig, ylims, "y")
# Plot the band energies.
for spin in spin_list:
lw = kwargs.pop("lw", 2.0)
line_opts = {"color": "black", "width": lw} if spin == 0 else {"color": "red", "width": lw}
for ib, band in enumerate(band_list):
if ib != 0: kwargs.pop("label", None)
self.plotly_traces(fig, e0, spin=spin, band=band, line_opts=line_opts, **kwargs)
if points is not None:
fig.add_trace(go.Scatter(x=points.x, y=np.array(points.y) - e0, mode='markers', showlegend=False,
marker=dict(color='blue', size=np.abs(points.s), opacity=0.6, line_width=0)))
if with_gaps and (self.mband > self.nspinor * self.nelect // 2):
# Show fundamental and direct gaps for each spin.
from plotly.figure_factory import create_quiver
for spin in self.spins:
f_gap = self.fundamental_gaps[spin]
d_gap = self.direct_gaps[spin]
# Need arrows only if fundamental and direct gaps for this spin are different.
need_arrows = f_gap != d_gap
arrow_opts = {"color": "gray"} if spin == 0 else {"color": "orange"}
scatter_opts = {"color": "blue"} if spin == 0 else {"color": "green"}
scatter_opts.update(opacity=0.9, size=12, line_width=2)
# Fundamental gap.
mgap = -1
for ik1, ik2 in f_gap.all_kinds:
posA = (ik1, f_gap.in_state.eig - e0)
posB = (ik2, f_gap.out_state.eig - e0)
mgap = max(mgap, posA[1], posB[1])
fig.add_trace(go.Scatter(x=[posA[0], posB[0]], y=[posA[1], posB[1]], mode='markers', name='',
showlegend=False, marker=scatter_opts))
if need_arrows:
figcq = create_quiver(x=[posA[0]], y=[posA[1]], u=[posB[0]-posA[0]], v=[posB[1]-posA[1]],
name='', scale=1, arrow_scale=0.2, showlegend=False, hoverinfo='none',
marker=arrow_opts, line=dict(width=2))
fig.add_trace(figcq.data[-1])
if d_gap != f_gap:
# Direct gap.
for ik1, ik2 in d_gap.all_kinds:
posA = (ik1, d_gap.in_state.eig - e0)
posB = (ik2, d_gap.out_state.eig - e0)
mgap = max(mgap, posA[1], posB[1])
fig.add_trace(go.Scatter(x=[posA[0],posB[0]], y=[posA[1],posB[1]], mode='markers', name='',
showlegend=False, marker=scatter_opts))
if need_arrows:
figcq = create_quiver(x=[posA[0]], y=[posA[1]], u=[posB[0]-posA[0]], v=[posB[1]-posA[1]],
name='', scale=1, arrow_scale=0.2, showlegend=False, hoverinfo='none',
marker=arrow_opts, line=dict(width=2))
fig.add_trace(figcq.data[-1])
# Try to set nice limits if not given by user.
if ylims is None:
plotly_set_lims(fig, (-mgap - 5, +mgap + 5), "y")
gaps_string = self.get_gaps_string(with_latex=False, unicode=True)
if gaps_string:
fig.layout.title = dict(text=gaps_string, font=dict(size=fontsize))
if max_phfreq is not None and (self.mband > self.nspinor * self.nelect // 2):
# Add markers showing phonon absorption/emission processes.
for spin in self.spins:
#scatter_opts = {"color": "steelblue"} if spin == 0 else {"color": "teal"}
scatter_opts = dict(opacity=0.4, size=8)
items = (["fundamental_gaps", "direct_gaps"], ["in_state", "out_state"])
items = list(enumerate(itertools.product(*items)))
for i, (gap_name, state_name) in items:
# Use getattr to extract gaps, equivalent to:
# gap = self.fundamental_gaps[spin]
# e_start = gap.out_state.eig
gap = getattr(self, gap_name)[spin]
e_start = getattr(gap, state_name).eig
scatter_opts["color"] = i/len(items)
scatter_opts["colorscale"] = "dense" if spin == 0 else "Burgyl"
for band in range(self.mband):
eks = self.eigens[spin, :, band]
where = np.where(np.abs(e_start - eks) <= max_phfreq)[0]
if not np.any(where): continue
fig.add_trace(go.Scatter(x=where, y=eks[where] - e0, mode='markers',
marker=scatter_opts, showlegend=False))
return fig
@add_fig_kwargs
def plot_scatter3d(self, band, spin=0, e0="fermie", colormap="jet", ax=None, **kwargs):
r"""
@ -2013,7 +2203,7 @@ class ElectronBands(Has_Structure):
e0: Option used to define the zero of energy in the band structure plot. Possible values:
- ``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``
- None: Don't shift energies, equivalent to ``e0 = 0``.
colormap: Have a look at the colormaps here and decide which one you like:
<http://matplotlib.sourceforge.net/examples/pylab_examples/show_colormaps.html>
ax: matplotlib :class:`Axes3D` or None if a new figure should be created.
@ -2071,6 +2261,31 @@ class ElectronBands(Has_Structure):
#print("ticks", len(ticks), ticks)
ax.set_xlim(ticks[0], ticks[-1])
def decorate_plotly(self, fig, **kwargs):
"""
Add q-labels and unit name to figure ``fig``.
Use units="" to add k-labels without unit name.
Args:
klabels:
klabel_size:
iax: An int, use iax=n to decorate the nth axis when the fig has subplots.
"""
iax = kwargs.pop("iax", 1)
xaxis = 'xaxis%u' % iax
fig.layout[xaxis].title.text = "Wave Vector"
fig.layout['yaxis%u' % iax].title.text = "Energy (eV)"
# Set ticks and labels.
klabels = kwargs.pop("klabels", None)
ticks, labels = self._make_ticks_and_labels(klabels)
if ticks:
labels = plotly_klabels(labels)
fig.layout[xaxis].tickvals = ticks
fig.layout[xaxis].ticktext = labels
fig.layout[xaxis].tickfont.size = kwargs.pop("klabel_size", 16)
fig.layout[xaxis].range = (ticks[0], ticks[-1])
def get_e0(self, e0):
"""
e0: Option used to define the zero of energy in the band structure plot. Possible values:
@ -2132,6 +2347,50 @@ class ElectronBands(Has_Structure):
return lines
def plotly_traces(self, fig, e0, spin=None, band=None, showlegend=False, line_opts=None, **kwargs):
"""
Helper function to plot the energies for (spin, band) on figure ``fig``.
Args:
fig: |plotly.graph_objects.Figure|.
e0: Option used to define the zero of energy in the band structure plot.
spin: Spin index. If None, all spins are plotted.
band: Band index, If None, all bands are plotted.
showlegend: Determines whether or not an item corresponding to this trace is shown in the legend.
kwargs: Passed to go.Scatter
"""
import plotly.graph_objects as go
spin_range = range(self.nsppol) if spin is None else [spin]
band_range = range(self.mband) if band is None else [band]
label = kwargs.pop("label", '')
# Handle linewidths
with_linewidths = kwargs.pop("with_linewidths", True) and self.has_linewidths
if with_linewidths:
lw_opts = kwargs.pop("lw_opts", dict(opacity=0.6))
lw_fact = lw_opts.pop("fact", 2.0)
xx = np.arange(self.nkpt)
e0 = self.get_e0(e0)
for spin in spin_range:
for band in band_range:
yy = self.eigens[spin, :, band] - e0
# Set label only at the first iteration
fig.add_trace(go.Scatter(x=xx, y=yy, mode="lines", name=label, showlegend=showlegend, line=line_opts, **kwargs))
label = ''
showlegend=False
if with_linewidths:
w = self.linewidths[spin, :, band] * lw_fact / 2
lw_color = lines[-1].get_color()
raise NotImplementedError
# solution 1: Use scater points to fill
# solution 2: add two traces and fill the area between
# color problem
#!! ax.fill_between(xx, yy - w, yy + w, facecolor=lw_color, **lw_opts)
#, alpha=self.alpha, facecolor=self.l2color[l])
def _make_ticks_and_labels(self, klabels):
"""Return ticks and labels from the mapping qlabels."""
if klabels is not None:
@ -2481,7 +2740,7 @@ class ElectronBands(Has_Structure):
Args:
lpratio: Ratio between the number of star functions and the number of ab-initio k-points.
The default should be OK in many systems, larger values may be required for accurate derivatives.
knames: List of strings with the k-point labels for the k-path. Has precedence over vertices_names.
knames: List of strings with the k-point labels for the k-path. Has precedence over ``vertices_names``.
vertices_names: Used to specify the k-path for the interpolated band structure
It's a list of tuple, each tuple is of the form (kfrac_coords, kname) where
kfrac_coords are the reduced coordinates of the k-point and kname is a string with the name of
@ -2489,6 +2748,7 @@ class ElectronBands(Has_Structure):
the density of the sampling. If None, the k-path is automatically generated according
to the point group of the system.
line_density: Number of points in the smallest segment of the k-path.
If 0, use list of k-points given in vertices_names
kmesh: Used to activate the interpolation on the homogeneous mesh for DOS (uses spglib_ API).
kmesh is given by three integers and specifies mesh numbers along reciprocal primitive axis.
is_shift: three integers (spglib_ API). When is_shift is not None, the kmesh is shifted along
@ -2518,8 +2778,7 @@ class ElectronBands(Has_Structure):
# Build interpolator.
from abipy.core.skw import SkwInterpolator
cell = (self.structure.lattice.matrix, self.structure.frac_coords,
self.structure.atomic_numbers)
cell = (self.structure.lattice.matrix, self.structure.frac_coords, self.structure.atomic_numbers)
skw = SkwInterpolator(lpratio, self.kpoints.frac_coords, self.eigens[:,:,bstart:bstop], self.fermie, self.nelect,
cell, fm_symrel, self.has_timrev,
@ -2542,7 +2801,7 @@ class ElectronBands(Has_Structure):
self.nelect, self.nspinor, self.nspden, smearing=self.smearing)
ebands_kmesh = None
if kmesh is not None:
# Get kpts and weights in IBZ.
# Get kpts and weights in the IBZ.
kdos = Ktables(self.structure, kmesh, is_shift, self.has_timrev)
eigens_kmesh = skw.interp_kpts(kdos.ibz).eigens
@ -2747,6 +3006,13 @@ class ElectronBandsPlotter(NotebookWriter):
for mname in ("gridplot", "boxplot"):
yield getattr(self, mname)(show=False)
#def yield_plotly_figs(self, **kwargs): # pragma: no cover
# """
# This function *generates* a predefined list of matplotlib figures with minimal input from the user.
# """
# for mname in ("gridplot", "boxplot"):
# yield getattr(self, mname)(show=False)
@add_fig_kwargs
def combiplot(self, e0="fermie", ylims=None, width_ratios=(2, 1), fontsize=8,
linestyle_dict=None, **kwargs):
@ -2756,7 +3022,6 @@ class ElectronBandsPlotter(NotebookWriter):
Args:
e0: Option used to define the zero of energy in the band structure plot. Possible values::
- `fermie`: shift all eigenvalues to have zero energy at the Fermi energy (ebands.fermie)
Note that, by default, the Fermi energy is taken from the band structure object
i.e. the Fermi energy computed at the end of the SCF file that produced the density.
@ -2767,7 +3032,6 @@ class ElectronBandsPlotter(NotebookWriter):
Available only if plotter contains dos objects.
- 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
ylims: Set the data limits for the y-axis. Accept tuple e.g. `(left, right)`
or scalar e.g. `left`. If left (right) is None, default values are used
width_ratios: Defines the ratio between the band structure plot and the dos plot.
@ -2842,11 +3106,8 @@ class ElectronBandsPlotter(NotebookWriter):
return fig
def plot(self, *args, **kwargs):
"""An alias for combiplot."""
if "align" in kwargs or "xlim" in kwargs or "ylim" in kwargs:
raise ValueError("align|xlim|ylim options are not supported anymore.")
return self.combiplot(*args, **kwargs)
# An alias for combiplot.
plot = combiplot
@add_fig_kwargs
def gridplot(self, e0="fermie", with_dos=True, with_gaps=False, max_phfreq=None,

View File

@ -231,19 +231,26 @@ class GsrFile(AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, Notebo
return ComputedEntry(self.structure.composition, self.energy,
parameters=parameters, data=data)
def get_panel(self):
def get_panel(self, **kwargs):
"""
Build panel with widgets to interact with the |GsrFile| either in a notebook or in panel app.
"""
from abipy.panels.gsr import GsrFilePanel
return GsrFilePanel(self).get_panel()
return GsrFilePanel(self).get_panel(**kwargs)
def yield_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of matplotlib figures with minimal input from the user.
"""
for fig in self.yield_structure_figs(**kwargs): yield fig
for fig in self.yield_ebands_figs(**kwargs): yield fig
for fig in self.yield_structure_figs(**kwargs): yield fig
def yield_plotly_figs(self, **kwargs): # pragma: no cover
"""
This function *generates* a predefined list of plotly figures with minimal input from the user.
"""
for fig in self.yield_ebands_plotly_figs(**kwargs): yield fig
for fig in self.yield_structure_plotly_figs(**kwargs): yield fig
def write_notebook(self, nbpath=None):
"""
@ -251,10 +258,21 @@ class GsrFile(AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, Notebo
working directory is created. Return path to the notebook.
"""
nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None)
first_char = "" if self.has_panel() else "#"
nb.cells.extend([
nbv.new_code_cell("gsr = abilab.abiopen('%s')" % self.filepath),
nbv.new_code_cell("print(gsr)"),
# Add panel GUI but comment the python code if panel is not available.
nbv.new_markdown_cell("## Panel dashboard"),
nbv.new_code_cell(f"""\
# Execute this cell to display the panel GUI (requires panel package).
# To display the dashboard inside the browser use `abiopen.py FILE --panel`.
{first_char}abilab.abipanel()
{first_char}gsr.get_panel()
"""),
nbv.new_code_cell("gsr.ebands.plot();"),
nbv.new_code_cell("gsr.ebands.kpoints.plot();"),
nbv.new_code_cell("# gsr.ebands.plot_transitions(omega_ev=3.0, qpt=(0, 0, 0), atol_ev=0.1);"),
@ -505,7 +523,7 @@ class GsrRobot(Robot, RobotWithEbands):
def get_energyterms_dataframe(self, iref=None):
"""
Build and return with the different contributions to the total energy in eV
Build and return dataframe with the different contributions to the total energy in eV
Args:
iref: Index of the abifile used as reference: the energies of the
@ -584,7 +602,7 @@ class GsrRobot(Robot, RobotWithEbands):
Returns: |matplotlib-Figure|
Example:
Example::
robot.plot_gsr_convergence(sortby="nkpt", hue="tsmear")
"""
@ -606,12 +624,12 @@ class GsrRobot(Robot, RobotWithEbands):
yield self.plot_gsr_convergence(show=False)
for fig in self.get_ebands_plotter().yield_figs(): yield fig
def get_panel(self):
def get_panel(self, **kwargs):
"""
Build panel with widgets to interact with the |GsrRobot| either in a notebook or in panel app.
"""
from abipy.panels.gsr import GsrRobotPanel
return GsrRobotPanel(self).get_panel()
return GsrRobotPanel(self).get_panel(**kwargs)
def write_notebook(self, nbpath=None):
"""

View File

@ -1556,9 +1556,6 @@ class SigresReader(ETSF_Reader):
# Self-consistent case
self._en_qp_diago = self.read_value("en_qp_diago")
# <KS|QPState>
self._eigvec_qp = self.read_value("eigvec_qp", cmode="c")
#self._mlda_to_qp
#def is_selfconsistent(self, mode):
@ -1717,10 +1714,13 @@ class SigresReader(ETSF_Reader):
If band is None, <KS_b|QP_{b'}> is returned.
"""
ik = self.kpt2fileindex(kpoint)
# <KS|QPState>
# TODO
eigvec_qp = self.read_value("eigvec_qp", cmode="c")
if band is not None:
return self._eigvec_qp[spin, ik, :, band]
return eigvec_qp[spin, ik, :, band]
else:
return self._eigvec_qp[spin, ik, :, :]
return eigvec_qp[spin, ik, :, :]
def read_params(self):
"""
@ -1877,6 +1877,79 @@ class SigresRobot(Robot, RobotWithEbands):
row_names = row_names if not abspath else self._to_relpaths(row_names)
return pd.DataFrame(rows, index=row_names, columns=list(rows[0].keys()))
def get_fit_gaps_vs_ecuteps(self, spin, kpoint, plot_qpmks=True, slice_data=None, fontsize=12):
"""
Fit QP direct gaps as a function of ecuteps using Eq. 16 of http://dx.doi.org/10.1063/1.4900447
to extrapolate results for ecutsp --> +oo.
Args:
spin: Spin index (0 or 1)
kpoint: K-point in self-energy. Accepts |Kpoint|, vector or index.
plot_qpmks: If False, plot QP_gap, KS_gap else (QP_gap - KS_gap)
slice_data: Python slice object. Used to downsample data points.
None to use all files of the SigResRobot.
fontsize: legend and label fontsize.
Return: TODO
"""
# Make sure that nsppol, sigma_kpoints are consistent.
self._check_dims_and_params()
# Get dimensions and index of the k-point in the sigma_nk array.
nc0 = self.abifiles[0]
nsppol, sigma_kpoints = nc0.nsppol, nc0.sigma_kpoints
ik = nc0.reader.gwkpt2seqindex(kpoint)
kgw = nc0.sigma_kpoints[ik]
# Order files by ecuteps
labels, ncfiles, params = self.sortby("ecuteps", unpack=True)
ecuteps_vals = np.array(params)
# Get QP and KS gaps ordered by ecuteps_vals.
qp_gaps, ks_gaps = map(np.array, zip(*[ncfile.get_qpgap(spin, kgw, with_ksgap=True)
for ncfile in ncfiles]))
ydata = qp_gaps if not plot_qpmks else qp_gaps - ks_gaps
# Fig results as a function of ecuteps
from scipy.optimize import curve_fit
def func(x, a, b, c):
return a * x**(-1.5) + b * x**(-2.5) + c
if slice_data is not None:
# Allow user to select a subset of data points via python slice
ecuteps_vals = ecuteps_vals[slice_data]
ydata = ydata[slice_data]
popt, pcov = curve_fit(func, ecuteps_vals, ydata)
ax, fig, plt = get_ax_fig_plt(ax=None)
ax.plot(ecuteps_vals, ydata, 'ro', label='ab-initio data')
min_ecuteps, max_ecuteps = ecuteps_vals.min(), ecuteps_vals.max() + 20
xs = np.linspace(min_ecuteps, max_ecuteps, num=50)
# Change label depending on plot_qpmks
what = r"\Delta E_g" if plot_qpmks else r"E_g"
ax.plot(xs, func(xs, *popt), 'b-',
label=f'fit: $B_3$=%5.3f, $B_5$=%5.3f, ${what} (\infty)$=%5.3f' % tuple(popt))
ax.hlines(popt[-1], min_ecuteps, max_ecuteps, color="k")
ax.legend(loc="best", fontsize=fontsize, shadow=True)
ax.grid(True)
ax.set_xlabel('ecuteps (Ha)')
ax.set_ylabel(f'${what}$ (eV)')
#ax.set_ylabel('$\Delta E(E_c^{\chi})$ (eV)')
#ax.title(r'$\Delta E(E_c^{\chi}) = \Delta E_g (\infty) + B_3 * E_c^{\chi (-3/2)} + B_5* E_c^{\chi (-5/2)} $')
#if show:
plt.show()
return dict2namedtuple(
fig=fig,
func=func,
ecuteps_vals=ecuteps_vals,
ydata=ydata,
popt=popt,
pcov=pcov,
)
# An alias to have a common API for robots.
get_dataframe = get_qpgaps_dataframe
@ -1906,6 +1979,7 @@ class SigresRobot(Robot, RobotWithEbands):
nc0 = self.abifiles[0]
nsppol, sigma_kpoints = nc0.nsppol, nc0.sigma_kpoints
# Build grid with (nkpt, 1) plots.
ncols, nrows = 1, len(sigma_kpoints)
ax_list, fig, plt = get_axarray_fig_plt(None, nrows=nrows, ncols=ncols,

View File

@ -451,6 +451,10 @@ class ElectronBandsTest(AbipyTest):
# Export it in BXSF format.
r.ebands_kmesh.to_bxsf(self.get_tmpname(text=True))
# This just to call interpolate with line_density 0
r = si_ebands_kmesh.interpolate(lpratio=5, vertices_names=vertices_names, line_density=0,
verbose=1)
def test_derivatives(self):
"""Testing computation of effective masses."""
ebands = ElectronBands.from_file(abidata.ref_file("si_nscf_GSR.nc"))
@ -593,6 +597,11 @@ class ElectronDosPlotterTest(AbipyTest):
"""Testing ElelectronDosPlotter API."""
gsr_path = abidata.ref_file("si_scf_GSR.nc")
gs_bands = ElectronBands.from_file(gsr_path)
with open(gsr_path, "rb") as fh:
same_gsr_bands = ElectronBands.from_binary_string(fh.read())
assert same_gsr_bands.structure == gs_bands.structure
si_edos = gs_bands.get_edos()
plotter = ElectronDosPlotter()

View File

@ -67,8 +67,15 @@ class GSRFileTestCase(AbipyTest):
def test_gsr_silicon(self):
"""spin unpolarized GSR file"""
filepath = abidata.ref_file("si_scf_GSR.nc")
with GsrFile(abidata.ref_file("si_scf_GSR.nc")) as gsr:
# Init GSR from binary string.
with open(filepath, "rb") as fh:
same_gsr = GsrFile.from_binary_string(fh.read())
same_structure = same_gsr.structure
same_gsr.close()
with GsrFile(filepath) as gsr:
assert gsr.basename == "si_scf_GSR.nc"
assert gsr.relpath == os.path.relpath(abidata.ref_file("si_scf_GSR.nc"))
assert gsr.filetype
@ -86,6 +93,8 @@ class GSRFileTestCase(AbipyTest):
self.assert_almost_equal(gsr.energy.to("Ha"), -8.86527676798556)
self.assert_almost_equal(gsr.energy_per_atom * len(gsr.structure), gsr.energy)
assert gsr.structure == same_structure
assert gsr.params["nband"] == 8
assert gsr.params["nkpt"] == 29

View File

@ -184,7 +184,7 @@ class WrNcFile(AbinitNcFile, Has_Structure, NotebookWriter):
@add_fig_kwargs
def plot_maxw(self, scale="semilogy", ax=None, fontsize=8, **kwargs):
"""
Plot the decay of max_{r,idir,ipert} |W(R,r,idir,ipert)|
Plot the decay of max_{r,idir,ipert} `|W(R,r,idir,ipert)|`
for the long-range and the short-range part.
Args:

View File

@ -55,7 +55,7 @@ def main():
root = os.path.join(os.path.dirname(__file__), "plot")
scripts = []
for fname in os.listdir(root):
if fname.endswith(".py") and fname.startswith("plot_"):
if fname.endswith(".py") and fname.startswith("plot"):
scripts.append(os.path.join(root, fname))
# Run scripts according to mode.

View File

@ -9,7 +9,7 @@ Run the scripts to generate the directory with the flow, then use the :ref:`abir
Alternatively, one can use the ``-s`` option to generate the flow and run it immediately with the scheduler.
Use ``--help`` for further information on the available options.
Note that the figures can only show the initial configuration of the Flow.
Note that the figures can only show the **initial configuration of the Flow**.
Additional Works generated at runtime won't be displayed.
To visualize the entire Flow, you need to run the script and then use::
@ -21,5 +21,5 @@ where `FLOWDIR` is the directory of the Flow.
The following examples show how to use python and the AbiPy API to generate and run
Abinit calculations in a semi-automatic way.
These examples are not supposed to produce physically meaningful results
These examples **are not supposed to produce physically meaningful results**
as input parameters are usually underconverged.

View File

@ -0,0 +1,196 @@
#!/usr/bin/env python
import sys
import os
import abipy.abilab as abilab
import abipy.data as abidata
from abipy import flowtk
#def make_scf_input(scf_ngkpt, paral_kgb=0):
# """
# This function constructs the input file for the GS calculation:
# on a scf_ngkpt Gamma-centered k-mesh.
# """
# structure = abilab.Structure.from_abivars(
# acell=[7.7030079150, 7.7030079150, 7.7030079150],
# rprim=[0.0000000000, 0.5000000000, 0.5000000000,
# 0.5000000000, 0.0000000000, 0.5000000000,
# 0.5000000000, 0.5000000000, 0.0000000000],
# natom=2,
# ntypat=2,
# typat=[1, 2],
# znucl=[3, 9],
# xred=[0.0000000000, 0.0000000000, 0.0000000000,
# 0.5000000000, 0.5000000000, 0.5000000000]
# )
#
# pseudos = ["03-Li.LDA.TM.pspnc", "09-F.LDA.TM.pspnc"]
#
# gs_inp = abilab.AbinitInput(structure, pseudos=pseudos)
#
# gs_inp.set_vars(
# ecut=50.0,
# ngkpt=scf_ngkpt,
# shiftk=[0, 0, 0],
# nband=10,
# paral_kgb=paral_kgb,
# tolvrs=1.0e-10,
# prtpot=1, # This is required for the Sternheimer method in the EPH part.
# )
#
# return gs_inp
def make_scf_input((scf_ngkpt, paral_kgb=0):
"""
This function constructs the input file for the GS calculation:
"""
# Initialize MgO structure from abinit variables.
structure = abilab.Structure.from_abivars(
acell=3 * [4.252718 * abilab.units.ang_to_bohr],
rprim=[0.0000000000, 0.5000000000, 0.5000000000,
0.5000000000, 0.0000000000, 0.5000000000,
0.5000000000, 0.5000000000, 0.0000000000],
natom=2,
ntypat=2,
typat=[1, 2],
znucl=[12, 8],
xred=[0.0000000000, 0.0000000000, 0.0000000000,
0.5000000000, 0.5000000000, 0.5000000000]
)
# Input for GS part.
# NC pseudos assumed in currect working directory.
#pseudos = ["Mg-sp-gw.psp8", "O.psp8"]
pseudos = abidata.pseudos("12mg.pspnc", "O.psp8")
gs_inp = abilab.AbinitInput(structure, pseudos=pseudos)
gs_inp.set_vars(
nband=12,
paral_kgb=paral_kgb,
ecut=35.0, # Too low. Should be ~50
ngkpt=scf_ngkpt, # Too coarse
nshiftk=1, # Gamma-centered mesh. Important to have the CBM/VBM!
shiftk=[0, 0, 0],
tolvrs=1.0e-10,
diemac=9.0,
nstep=150,
nbdbuf=4,
prtpot=1, # Print potential for Sternheimer
iomode=3, # Produce output files in netcdf format.
)
return gs_inp
def build_flow(options):
"""
Create a `Flow` for ZPR calculations.
"""
# Working directory (default is the name of the script with '.py' removed and "run_" replaced by "flow_")
if not options.workdir:
options.workdir = os.path.basename(__file__).replace(".py", "").replace("run_", "flow_")
flow = flowtk.Flow(workdir=options.workdir)
# Build input for the GS calculation and register the first GS ScfTask.
gs_inp = make_scf_input(scf_ngkpt=[4, 4, 4])
work = flow.register_scf_task(gs_inp)
scf_task = work[0]
# Build input for NSCF calculation along k-path (automatically selected by AbiPy)
nscf_kpath_inp = gs_inp.make_ebands_input()
work.register_nscf_task(nscf_kpath_inp, deps={scf_task: "DEN"})
# This is the ab-initio q-mesh that should be COMMENSURATE with scf_ngkpt
ddb_ngqpt = [2, 2, 2]
# Now we build the NSCF tasks with different k-meshes and empty states.
# Each NSCF task generates one of the WFK files used to build the EPH self-energy.
# These WFK files are then used to erform convergece tests with respect to
# the interpolated fine q-mesh.
# NOTE that in the EPH we are gonna use k_mesh = q_mesh
ngkpt_fine_list = [
[8, 8, 8],
#[12, 12, 12],
#[32, 32, 32],
]
nscf_empty_states_tasks = []
for ngkpt_fine in ngkpt_fine_list:
nscf_empty_kmesh_inp = gs_inp.new_with_vars(
ngkpt=ngkpt_fine,
#nband=630, # Too low. ~300
#nbdbuf=30, # Reduces considerably the time needed to converge empty states!
nband=40, # Too low. ~300
nbdbuf=5, # Reduces considerably the time needed to converge empty states!
tolwfr=1e-18,
iscf=-2,
)
t = work.register_nscf_task(nscf_empty_kmesh_inp, deps={scf_task: "DEN"})
nscf_empty_states_tasks.append(t)
# Create work for phonon calculation on the coarse ddb_ngqpt q-mesh.
# Electric field and Born effective charges are computed.
ph_work = flowtk.PhononWfkqWork.from_scf_task(scf_task, ngqpt=ddb_ngqpt, with_becs=True)
#for task in ph_work:
# task.input.set_vars(prtwf=-1)
flow.register_work(ph_work)
# Build template for e-ph self-energy calculation (real + imag part)
# The k-points must be in the WFK file
eph_template = gs_inp.new_with_vars(
optdriver=7, # Enter EPH driver.
eph_task=4, # Activate computation of EPH self-energy.
ddb_ngqpt=ddb_ngqpt, # Ab-initio q-mesh used to produce the DDB file.
#nkptgw=2,
#kptgw=[0, 0, 0,
# 0.5, 5, 0],
#bdgw=[1, 8, 1, 8],
#gw_qprange=1,
tmesh=[0, 200, 1], # (start, step, num)
zcut="0.01 eV",
mixprec=1,
boxcutmin=1.1,
)
# Set q-path for Fourier interpolation of phonons.
eph_template.set_qpath(10)
# Set q-mesh for phonons DOS.
eph_template.set_phdos_qmesh(nqsmall=16, method="tetra")
# Now we use the EPH template to perform a convergence study in which
# we change the q-mesh used to integrate the self-energy and the number of bands.
# The code will activate the Fourier interpolation of the DFPT potentials
# if eph_ngqpt_fine != ddb_ngqpt
# Create empty work to contain EPH tasks with this value of eph_ngqpt_fine
eph_work = flow.new_work()
for ngkpt_fine, nscf_task in zip(ngkpt_fine_list, nscf_empty_states_tasks):
new_inp = eph_template.new_with_vars(
ngkpt=ngkpt_fine,
eph_ngqpt_fine=eph_ngqpt_fine
) #, nband=nband)
# The EPH code requires the GS WFK, the DDB file with all perturbations
# and the DVDB file with the DFPT potentials (already merged by ph_work)
deps = {nscf_task: "WFK", ph_work: ["DDB", "DVDB"]}
eph_work.register_eph_task(new_inp, deps=deps)
#flow.allocate()
return flow
@flowtk.flow_main
def main(options):
"""
This is our main function that will be invoked by the script.
flow_main is a decorator implementing the command line interface.
Command line args are stored in `options`.
"""
return build_flow(options)
if __name__ == "__main__":
sys.exit(main())

View File

@ -6,7 +6,7 @@ Flow for Born effective charges and dielectric tensors with DFPT
This example shows how to compute the Born effective charges and
the dielectric tensors (e0, einf) of AlAs with AbiPy flows.
We perform multiple calculations by varying the number of k-points
to analyze the convergence of the results wrt nkpt
in order to analyze the convergence of the results wrt nkpt
"""
import sys
import os
@ -15,7 +15,6 @@ import abipy.data as abidata
from abipy import flowtk
def make_scf_input(ngkpt, paral_kgb=0):
"""
This function constructs the input file for the GS calculation for a given IBZ sampling.
@ -62,7 +61,7 @@ def build_flow(options):
flow = flowtk.Flow(workdir=options.workdir)
for ngkpt in [(2, 2, 2), (4, 4, 4), (8, 8, 8)]:
# Build input for GS calculation
# Build input for GS calculation with different k-meshes
scf_input = make_scf_input(ngkpt=ngkpt)
flow.register_scf_task(scf_input, append=True)

View File

@ -1,10 +1,9 @@
#!/usr/bin/env python
r"""
Effective masses with finite difference
=======================================
Conductivity in metals
======================
Flow to compute effective masses with finite difference method.
Derivatives are computed along lines in k-space.
Flow to compute conductivity in metals
"""
import os

View File

@ -6,7 +6,8 @@ Effective masses with DFPT
Flow to compute effective masses with DFPT.
Two options are available:
- EffMassDFPTWork --> Run DFPT calculation directly assuming the location of the band edges is already known.
- EffMassDFPTWork --> Run DFPT calculation directly assuming the location
of the band edges is already known.
- EffMassAutoDFPTWork --> Run NSCF calculation to find band edges, then use DFPT.
"""
@ -24,7 +25,7 @@ def make_scf_input(usepaw=0, nspinor=1):
else:
pseudos = abidata.pseudos("Si_r.psp8") if usepaw == 0 else abidata.pseudos("Si.GGA_PBE-JTH-paw.xml")
# https://docs.abinit.org/tests/v7/Input/t82.in
# See https://docs.abinit.org/tests/v7/Input/t82.in
structure = dict(
ntypat=1,
natom=2,
@ -76,7 +77,8 @@ def build_flow(options):
# effmass_bands_f90 defines the band range for each k in k0_list
# Here we are interested in the effective masses at the Gamma point for the valence bands
effmass_bands_f90 = [1, 4] if scf_input["nspinor"] == 1 else [1, 8]
work = EffMassDFPTWork.from_scf_input(scf_input, k0_list=(0, 0, 0), effmass_bands_f90=effmass_bands_f90)
work = EffMassDFPTWork.from_scf_input(scf_input, k0_list=(0, 0, 0),
effmass_bands_f90=effmass_bands_f90)
flow.register_work(work)
# or use this Work to detect band edges automatically but increase ndivsm and decrease tolwfr!

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
r"""
Flow for Equation of State
Flow for equation of state
==========================
Flow to compute the equation of state by fitting E(V) at T = 0.

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
r"""
Estimate the ZPR at band edges with the generalized Frohlich model
==================================================================
Estimate the ZPR at the band edges with the generalized Frohlich model
======================================================================
Flow to estimate the zero-point renormalization at the band edges
using the generalized Frohlich model. The flow uses DFPT to compute

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
r"""
Estimate the ZPR of band edges with generalized Frohlich mode
=============================================================
Estimate the ZPR of band edges with generalized Frohlich model
==============================================================
Flow to estimate the zero-point renormalization at the band edges
using the generalized Frohlich model. The flow uses DFPT to compute

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
r"""
Band structure with/without spin-orbit
======================================
Band structure with/without SOC
================================
This example shows how to compute the band structure of GaAs with and without spin-orbit term.
We essentially build two BandStructureWork inside a loop over nspinor in [1, 2]

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
r"""
Flow for Non-linear optic with DFPT
Flow for non-linear optic with DFPT
===================================
Flow to compute non-linear optical properties with DFPT (static limit).
@ -78,8 +78,6 @@ def main(options):
flow_main is a decorator implementing the command line interface.
Command line args are stored in `options`.
"""
# Temporarily disabled in v8.8.2
#return 0
return build_flow(options)

View File

@ -3,8 +3,10 @@ r"""
Phonopy + Abinit Flow
=====================
This example shows how to compute phonon frequencies with phonopy (supercells and finite-difference method).
This approach could be useful to obtain vibrational properties with XC functionals for which DFPT is not yet implemented.
This example shows how to compute phonon frequencies with phonopy
(supercells and finite-difference method).
This approach could be useful to obtain vibrational properties with XC functionals
for which DFPT is not yet implemented.
.. warning:

View File

@ -3,16 +3,29 @@
AbiPy Gallery
=============
There are a variety of ways to use the AbiPy post-processing tools,
and most of them are illustrated in the examples in this directory.
Remember that one can also generate a jupyter notebook directly from the command line with
the :ref:`abiopen.py` script and the command::
abiopen.py FILE -nb
issue
There are a variety of ways to use the AbiPy post-processing tools,
and some of them are illustrated in the examples in this directory.
These examples represent an excellent starting point if you need to implement
a customized script to solve your particular problem.
Keep in mind, however, that simple visualization tasks can be easily
automated by just issuing in the terminal::
abiopen.py FILE --expose
to generate plots automatically or use one of the options of :ref:`abiview.py` to plot the results automatically.
to generate a predefined list of **matplotlib** plots.
To activate the plotly version use:
.. code-block:: shell
abiopen.py FILE --plotly
although at the time of writing not all the files support this protocol.
Note also that one can generate jupyter-lab notebooks directly from the command line with
abiopen.py_ and the command::
abiopen.py FILE -nb
Add `--classic-notebook` if you prefer classic jupyter notebooks.
Finally, use one of the options of the abiview.py_ script to plot the results automatically.

View File

@ -4,28 +4,29 @@ Eliashberg function
===================
This example shows how to plot the Eliashberg function a2F(w)
and the e-ph coupling strenght in metals.
and the total e-ph coupling strenght in metals.
"""
from abipy import abilab
import abipy.data as abidata
phdos_path = abidata.ref_file("al_161616q_PHDOS.nc")
with abilab.abiopen(abidata.ref_file("al_888k_161616q_A2F.nc")) as ncfile:
print(ncfile)
#ncfile.phbands.plot()
#ncfile.a2f_qintp.plot()
#with_lambda = False
#fig = ncfile.a2f_qcoarse.plot_nuterms(with_lambda=with_lambda, show=False)
#ncfile.a2f_qintp.plot_nuterms(axmat=fig.axes, with_lambda=with_lambda)
ncfile = abilab.abiopen(abidata.ref_file("al_888k_161616q_A2F.nc"))
print(ncfile)
#ncfile.plot()
ncfile.plot_with_a2f(phdos=phdos_path)
#ncfile.phbands.plot()
#ncfile.a2f_qintp.plot()
#with_lambda = False
#fig = ncfile.a2f_qcoarse.plot_nuterms(with_lambda=with_lambda, show=False)
#ncfile.a2f_qintp.plot_nuterms(axmat=fig.axes, with_lambda=with_lambda)
ncfile.plot_eph_strength(what="gamma")
#ncfile.plot_eph_strength(what="lambda")
#ncfile.plot()
ncfile.plot_with_a2f(phdos=phdos_path)
ncfile.plot_a2f_interpol()
ncfile.plot_eph_strength(what="gamma")
#ncfile.plot_eph_strength(what="lambda")
# Grid with 3 plots (a2F, F, a2F) with F taken from PHDOS file.
#ncfile.a2f_qintp.plot_a2(phdos_path)
ncfile.plot_a2f_interpol()
# Grid with 3 plots (a2F, F, a2F) with F taken from PHDOS file.
#ncfile.a2f_qintp.plot_a2(phdos_path)

View File

@ -15,8 +15,10 @@ wfk_file = abiopen(abidata.ref_file("si_scf_WFK.nc"))
# Extract the crystalline structure.
structure = wfk_file.structure
# Visualize the BZ.
#%%
# To visualize the BZ with matplotlib, use:
structure.plot_bz()
# Close the wfk file
#%%
# Remember to close the wfk file with:
wfk_file.close()

View File

@ -1,25 +1,61 @@
#!/usr/bin/env python
r"""
Phonon Bands with/without ASR
=============================
Phonon bands with/without the ASR
=================================
This example shows how to plot a phonon band structure
with and without imposing the acoustic sum rule at the Gamma point.
with and without enforcing the acoustic sum rule (ASR).
Both matplotlib and plotly are supported.
.. important::
Note that a **manager.yml** configuration file and an abinit installation are required
to run this script as AbiPy needs to invoke anaddb to compute phonons from the DDB file.
"""
#%%
# Open the DDB file with:
from abipy import abilab
import abipy.data as abidata
# Open DDB file
filepath = abidata.ref_file("mp-1009129-9x9x10q_ebecs_DDB")
ddb = abilab.abiopen(filepath)
# This method computes the phonon bands and DOS by calling anaddb
# with different values of asr and returns a PhononBandsPlotter object.
plotter = ddb.anacompare_asr(asr_list=(0, 2))
#%%
# The ``ddb.anacompare_asr`` method computes the phonon bands and the DOS by calling anaddb
# with different values of asr and returns a PhononBandsPlotter object:
# To make the computation faster, we use the **advanced** options dipdip -1.
# This option should produce results similar to dipdip 1 yet make sure to test
# the effect of this variable before using it in production.
plotter = ddb.anacompare_asr(asr_list=(0, 2), dipdip=-1)
print(plotter)
#%%
# To plot the bands on the same figure with matplotlib, use:
plotter.combiplot()
# Set nqsmall to 0 to disable DOS computation.
plotter = ddb.anacompare_asr(asr_list=(0, 2), nqsmall=0, ndivsm=10)
#%%
# For the plotly version, use:
plotter.combiplotly()
#%%
# To disable the DOS computation, set ``nqsmall` to 0:
plotter = ddb.anacompare_asr(asr_list=(0, 2), nqsmall=0, ndivsm=10, dipdip=-1)
#%%
# To plot the bands on different subplots with matplotlib, use:
plotter.gridplot()
#%%
# For the plotly version, use:
plotter.gridplotly()
#%%
# Finally, remember to close the file with:
ddb.close()

View File

@ -15,8 +15,9 @@ ncfile = abiopen(abidata.ref_file("si_DEN.nc"))
# The DEN file has a `Density`, a `Structure` and an `ElectronBands` object
print(ncfile.structure)
#%%
# To plot the KS eigenvalues.
#ncfile.ebands.plot()
ncfile.ebands.plot()
density = ncfile.density
print(density)
@ -24,14 +25,17 @@ print(density)
# To visualize the total charge wih vesta
#visu = density.visualize("vesta"); visu()
#%%
# To plot the density along the line connecting
# the first and the second in the structure:
density.plot_line(point1=0, point2=1)
#%%
# alternatively, one can define the line in terms of two points
# in fractional coordinates:
density.plot_line(point1=[0, 0, 0], point2=[2.25, 2.25, 2.25], num=300)
#%%
# To plot the density along the lines connect the firt atom in the structure
# and all the neighbors within a sphere of radius 3 Angstrom:
density.plot_line_neighbors(site_index=0, radius=3)

View File

@ -10,21 +10,37 @@ produced at the end of the GS run.
from abipy.abilab import abiopen
import abipy.data as abidata
#%%
# Here we use one of the GSR files shipped with abipy.
# Replace filename with the path to your GSR file or your WFK file.
filename = abidata.ref_file("si_nscf_GSR.nc")
#%%
# Open the GSR file and extract the band structure.
# (alternatively one can use the shell and `abiopen.py OUT_GSR.nc -nb`
# to open the file in a jupyter notebook.
with abiopen(filename) as ncfile:
ebands = ncfile.ebands
# Plot the band energies. Note that the labels for the k-points
# are found automatically in an internal database.
# Show fundamental and direct gaps.
#ebands.plot(with_gaps="fd", title="Silicon band structure")
#%%
# Plot the band energies with matplotlib.
# Note that the labels for the k-points are found automatically in an internal database.
# Use `with_gaps` to show fundamental and direct gaps.
ebands.plot(with_gaps=True, title="Silicon band structure")
# Plot the BZ and the k-point path.
#%%
# For the plotly version, use:
ebands.plotly(with_gaps=True)
# .. warning:
#
# Note that, for the time being, ``with_gaps`` is incompatible with the ``title`` argument.
#%%
# Plot the BZ and the k-point path with matplotlib
ebands.kpoints.plot()

View File

@ -14,7 +14,8 @@ import abipy.data as abidata
with abiopen(abidata.ref_file("si_scf_GSR.nc")) as gsr:
ebands = gsr.ebands
#%%
import matplotlib.pyplot as plt
# `swarm=True` to show the datapoints on top of the boxes
# Use `swarm=True` to show the datapoints on top of the boxes
ebands.boxplot(swarm=True)
plt.show()

View File

@ -19,10 +19,12 @@ with abiopen(abidata.ref_file("si_nscf_GSR.nc")) as nscf_file:
with abiopen(abidata.ref_file("si_scf_GSR.nc")) as gs_file:
gs_ebands = gs_file.ebands
#%%
# Compute the DOS with the Gaussian method (use default values for
# the broadening and the step of the linear mesh.
edos = gs_ebands.get_edos()
#%%
# Plot bands and DOS.
nscf_ebands.plot_with_edos(edos, e0=None, with_gaps=True)

View File

@ -22,17 +22,22 @@ plotter.add_ebands("k-path", ref_file("si_nscf_GSR.nc"))
frame = plotter.get_ebands_frame()
print(frame)
plotter.gridplot(with_gaps=True)
#plotter.animate()
#%%
# To create a grid plot use:
plotter.gridplot(with_gaps=True)
#%%
# To plot a grid with band structures + DOS, use the optional argument `edos_objects`
# The first subplot gets the band dispersion from eb_objects[0] and the DOS from edos_objects[0]
# edos_kwargs is an optional dictionary passed to `get_dos` to compute the DOS.
#
eb_objects = 2 * [ref_file("si_nscf_GSR.nc")]
edos_objects = 2 * [ref_file("si_scf_GSR.nc")]
# sphinx_gallery_thumbnail_number = 2
plotter = ElectronBandsPlotter()
plotter.add_ebands("Si", ref_file("si_nscf_GSR.nc"), edos=ref_file("si_scf_GSR.nc"))
plotter.add_ebands("Same data", ref_file("si_nscf_GSR.nc"), edos=ref_file("si_scf_GSR.nc"))
# sphinx_gallery_thumbnail_number = 2
plotter.gridplot()

View File

@ -4,15 +4,14 @@ Gruneisen parameters
====================
This example shows how to analyze the Gruneisen parameters
computed by anaddb via finite difference. See also v8/Input/t45.in
computed by anaddb via finite difference.
See also v8/Input/t45.in
"""
from __future__ import print_function, division
import abipy.data as abidata
from abipy import abilab
# Open the file with abiopen
# (alternatively one can use the shell and `abiopen.py OUT_GRUNS.nc -nb`
# Alternatively one can use the shell and `abiopen.py OUT_GRUNS.nc -nb`
# to open the file in a jupyter notebook.
ncfile = abilab.abiopen(abidata.ref_file("mg2si_GRUNS.nc"))
@ -25,7 +24,8 @@ ncfile.plot_phbands_with_gruns(title="Phonon bands with markers proportional to
ncfile.plot_gruns_bs(title="Gruneisen along high-symmetry path.")
ncfile.plot_phbands_with_gruns(fill_with="gruns_fd", title="Gruneisen parameters with finite differences.", with_doses=None)
ncfile.plot_phbands_with_gruns(fill_with="gruns_fd",
title="Gruneisen parameters with finite differences.", with_doses=None)
ncfile.plot_gruns_scatter(units='cm-1',title="Scatter plot with Gruneisen parameters")

View File

@ -1,33 +1,54 @@
#!/usr/bin/env python
r"""
Phonon Band structures
======================
Phonon band structures with LO-TO
=================================
This example shows how to plot the phonon band structure of AlAs.
without the LO-TO splitting.
See tutorial/lesson_rf2.html
"""
#%%
# Open the PHBST file produced by anaddb and get the phonon bands.
# Note that the treatment of the LO-TO splitting for q--> 0 requires
# additional steps. See `plot_phonons_lo_to.py`.
from abipy.abilab import abiopen
import abipy.data as abidata
# Open the PHBST file produced by anaddb and get the phonon bands.
with abiopen(abidata.ref_file("trf2_5.out_PHBST.nc")) as ncfile:
phbands = ncfile.phbands
#%%
# Read the Phonon DOS from the netcd file produced by anaddb (prtdos 2)
with abiopen(abidata.ref_file("trf2_5.out_PHDOS.nc")) as ncfile:
phdos = ncfile.phdos
# plot phonon bands and DOS with LO-TO.
# `plot_phonons_lo_to.py` shows how to treat the LO-TO splitting.
#%%
# Plot phonon bands and DOS with matplotib:
phbands.plot(title="AlAs Phonon bands and DOS in eV")
# plot phonon bands with DOS.
#%%
# For the plotly version use:
phbands.plotly(title="AlAs Phonon bands and DOS in eV")
#%%
# To plot phonon bands and phonon DOS with matplotlib use:
phbands.plot_with_phdos(phdos, units="cm-1",
title="AlAs Phonon bands + DOS in cm-1")
# plot phonon DOS.
phdos.plot(units="cm-1", title="Phonon DOS and IDOS in cm-1")
#%%
# For the plotly version use:
phbands.plotly_with_phdos(phdos, units="cm-1",
title="AlAs Phonon bands + DOS in cm-1")
#%%
# Plot the phonon band structure with different color for each line (matplotlib version).
# Plot the phonon band structure with different color for each line.
phbands.plot_colored_matched(units="cm-1",
title="AlAs with different color for each line.")

View File

@ -12,23 +12,40 @@ We use two files produced by anaddb:
See also tutorial/lesson_rf2.html
"""
#%%
# We start by defining a list with the paths to the PHBST.nc files
# In this case, for simplicity, we use the same file but we must
# use different labels when adding them to the plotter with the add_phbands method.
from abipy import abilab
import abipy.data as abidata
# To plot a grid with two band structures:
phbst_paths = 2 * [abidata.ref_file("trf2_5.out_PHBST.nc")]
plotter = abilab.PhononBandsPlotter()
plotter.add_phbands("AlAs", phbst_paths[0])
plotter.add_phbands("Same AlAs", phbst_paths[1])
#plotter.combiplot(title="CombiPlot in eV")
#%%
# At this point, we can use the plotter methods to plot the data:
# To produce a grid plot:
plotter.gridplot(units="eV", title="GridPlot in eV")
#%%
# At this point, we can use the plotter methods to plot the data:
plotter.boxplot(units="cm-1", title="BoxPlot in cm-1")
plotter.combiboxplot(units="Ha", title="CombiboxPlot in Ha")
#plotter.combiplot(title="CombiPlot in eV")
#%%
# To plot a grid with band structures + DOS, use the optional argument `phdos` of add_phbands
# The first subplot gets the band dispersion from phbst_paths[0] and the dos from phdos_paths[0]
phbst_paths = 3 * [abidata.ref_file("trf2_5.out_PHBST.nc")]
phdos_paths = 3 * [abidata.ref_file("trf2_5.out_PHDOS.nc")]
@ -37,5 +54,6 @@ plotter.add_phbands("AlAs phbands + DOS", phbst_paths[0], phdos=phdos_paths[0])
plotter.add_phbands("Same-data", phbst_paths[1], phdos=phdos_paths[1])
plotter.add_phbands("Same-data2", phbst_paths[2], phdos=phdos_paths[2])
#plotter.combiplot(title="Bands + DOS in eV with combiplot")
plotter.gridplot(units="cm-1", tight_layout=True, title="Bands + DOS in cm-1 with gridplot")
#plotter.combiplot(title="Bands + DOS in eV with combiplot")

View File

@ -1,19 +1,24 @@
#!/usr/bin/env python
r"""
Multiple phonon bands
=========================
Multiple phonon bands with DDB robot
====================================
This example shows how to plot several phonon band structures on a grid.
We use two files produced by anaddb:
.. important::
trf2_5.out_PHBST.nc: phonon frequencies on a q-path in the BZ (used to plot the band dispersion)
trf2_5.out_PHDOS.nc: phonon DOS compute with anaddb.
Note that a **manager.yml** file and an abinit installation are required
to run this script as AbiPy needs to invoke anaddb to compute phonons from the DDB file.
"""
#%%
# We start by defining a list of DDB files:
# obtained with the same structure but different k-mesh and tmear:
from abipy import abilab
import abipy.data as abidata
import os
paths = [
#"mgb2_444k_0.01tsmear_DDB",
#"mgb2_444k_0.02tsmear_DDB",
@ -28,18 +33,37 @@ paths = [
paths = [os.path.join(abidata.dirpath, "refs", "mgb2_phonons_nkpt_tsmear", f) for f in paths]
robot = abilab.DdbRobot()
for i, path in enumerate(paths):
robot.add_file(path, path)
#%%
# Now we initialize the DdbRobot from this list of paths:
robot = abilab.DdbRobot.from_files(paths)
print(robot.keys())
#%%
# Now we change the keys associated to the different files
# by defining a function that computes the new label from the
# info reported in the ddb object.
# These lables are then used to generate the legend in the matplotlib plot.
robot.remap_labels(lambda ddb: "nkpt: %s, tsmear: %.2f" % (ddb.header["nkpt"], ddb.header["tsmear"]))
print(robot.keys())
#%%
# Invoke anaddb to build a PhononBands plotter.
# We use a small q-mesh for the ph-DOS to speedup the computation:
r = robot.anaget_phonon_plotters(nqsmall=2)
#%%
# To group the results by tsmear use:
r.phbands_plotter.gridplot_with_hue("tsmear")
#%%
# If Phonon DOSes are wanted, use:
r.phbands_plotter.gridplot_with_hue("tsmear", with_dos=True)
#r.phbands_plotter.gridplot_with_hue("nkpt")
#r.phbands_plotter.gridplot()
robot.close()

View File

@ -3,23 +3,41 @@ r"""
Projected phonon DOS
====================
This example shows how to plot the projected phonon DOS of AlAs.
This example shows how to plot the projected phonon DOS (PJDOS) of AlAs.
See tutorial/lesson_rf2.html
"""
from abipy.abilab import abiopen
import abipy.data as abidata
#%%
# Read the Phonon DOS from the netcdf file produced by anaddb with prtdos 2
# (alternatively one can use the shell and `abiopen.py OUT_PHDOS.nc -nb`
# to open the file in a jupyter notebook.
with abiopen(abidata.ref_file("trf2_5.out_PHDOS.nc")) as phdos_file:
# Plot PJDOS.
phdos_file.plot_pjdos_type(units="cm-1", title="AlAs type-projected phonon DOS")
from abipy.abilab import abiopen
import abipy.data as abidata
# To have the projection along the cartesian directions (summed over atomic types)
phdos_file.plot_pjdos_cartdirs_type(units="Thz", stacked=True,
title="Type-projected ph-DOS decomposed along the three Cartesian directions.")
phdos_file = abiopen(abidata.ref_file("trf2_5.out_PHDOS.nc"))
# To plot the PJDOS for all the inequivalent sites.
phdos_file.plot_pjdos_cartdirs_site(view="inequivalent", stacked=True)
#%%
# To plot the PJDOS with matplotlib, use:
phdos_file.plot_pjdos_type(units="cm-1", title="AlAs type-projected phonon DOS")
#%%
# For the plotly version, use:
phdos_file.plotly_pjdos_type(units="cm-1", title="AlAs type-projected phonon DOS")
#%%
# To have the projection along the cartesian directions (summed over atomic types), use
phdos_file.plot_pjdos_cartdirs_type(units="Thz", stacked=True,
title="Type-projected ph-DOS decomposed along the three Cartesian directions.")
#%%
# To plot the PJDOS for all the inequivalent sites, use:
phdos_file.plot_pjdos_cartdirs_site(view="inequivalent", stacked=True)
#%%
# Remember to close the file
phdos_file.close()

View File

@ -30,7 +30,9 @@ gamma_ev = 1e-3
tgen.plot_all(gamma_ev=gamma_ev, title="Diagonal and off-diagonal components")
tgen.plot(component="diag", reim="re", gamma_ev=gamma_ev, title="Real part, diagonal components")
tgen.plotly(component="diag", reim="re", gamma_ev=gamma_ev, title="Real part, diagonal components")
tgen.plot(component="diag", reim="im", gamma_ev=gamma_ev, title="Imaginary part, diagonal components")
tgen.plotly(component="diag", reim="im", gamma_ev=gamma_ev, title="Imaginary part, diagonal components")
ddb.close()

View File

@ -1,28 +1,40 @@
#!/usr/bin/env python
r"""
Phonon Bands with LO-TO
=======================
Phonon bands with LO-TO from PHBST.nc
=====================================
This example shows how to plot the phonon band structure of AlAs
including the LO-TO splitting. See tutorial/lesson_rf2.html
including the LO-TO splitting.
These resultas tutorial/lesson_rf2.html
"""
from abipy.abilab import abiopen
import abipy.data as abidata
#%%
# Open PHBST file produced by anaddb and extract the phonon bands object.
# (alternatively one can use the shell and `abiopen.py OUT_PHBST.nc -nb`
# to open the file in a jupyter notebook.
from abipy.abilab import abiopen
import abipy.data as abidata
with abiopen(abidata.ref_file("ZnSe_hex_886.out_PHBST.nc")) as ncfile:
phbands = ncfile.phbands
#%%
# Phonon frequencies with non analytical contributions, if calculated, are saved
# in the anaddb.nc file produced by anaddb. The results should be fetched from there
# and added to the phonon bands.
phbands.read_non_anal_from_file(abidata.ref_file("ZnSe_hex_886.anaddb.nc"))
# Notice that all the directions starting from or arriving at gamma that are used
# in the path should explicitely calculated, even if the values are the same.
phbands.read_non_anal_from_file(abidata.ref_file("ZnSe_hex_886.anaddb.nc"))
#%%
# Plot the phonon frequencies. Note that the labels for the q-points
# are found automatically by searching in an internal database.
phbands.plot(title="ZnSe with LO-TO splitting")
#%%
# For the plotly version, use:
phbands.plotly(title="ZnSe with LO-TO splitting")

View File

@ -1,11 +1,12 @@
#!/usr/bin/env python
r"""
Projected phonon DOS
====================
Debye-Waller and generalized phonon DOS
=======================================
This example shows how to plot the generalized phonon DOS with the mean square
displacement tensor in cartesian coords and how to calculate Debye Waller factors
as a function of temperature.
See :cite:`Lee1995` for the further details about the internal implementation and
:cite:`Trueblood1996` for the different conventions used by crystallographers.
"""

View File

@ -6,8 +6,6 @@ Thermodinamic properties
This example shows how to compute and plot thermodinamic properties within
the harmonic approximation using the phonon DOS produced by anaddb.
"""
from __future__ import print_function
from abipy.abilab import abiopen
import abipy.data as abidata
@ -20,14 +18,22 @@ print(ncfile.structure)
zpe = phdos.zero_point_energy
print("Zero point energy:", zpe, zpe.to("J"), zpe.to("Ha"))
#%%
# Compute free energy from 2 to 300 K (20 points)
# By default, energies are is eV and thermodynamic quantities are given
# on a per-unit-cell basis.
f = phdos.get_free_energy(tstart=2, tstop=300, num=20)
#f.plot()
#%%
# Plot U, F, S, Cv as a function of T.
# Use J/mol units, results are divided by formula_units.
phdos.plot_harmonic_thermo(units="Jmol", formula_units=1)
#%%
# Plotly version:
phdos.plotly_harmonic_thermo(units="Jmol", formula_units=1)
#%%
# Remember to close the file:
ncfile.close()

View File

@ -27,3 +27,4 @@ abilab.print_dataframe(df)
# Plot fit
sv.plot()
sv.plotly()

View File

@ -8,13 +8,18 @@ This example shows how to display the unit cell with matplotlib.
from abipy.abilab import abiopen
import abipy.data as abidata
# Extract structure from netcdf file.
#%%
# Extract structure from the netcdf file:
with abiopen(abidata.ref_file("sio2_kpath_GSR.nc")) as gsr:
structure = gsr.structure
# Visualize sites structure.
#%%
# To visualize the structure with matplotlib, use:
structure.plot(color_scheme="Jmol")
# Wrap sites into first unit cell.
#%%
# To wrap sites into first unit cell, use:
# sphinx_gallery_thumbnail_number = 2
structure.plot(to_unit_cell=True)

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
r"""
Phonon Band structures (Plotly version)
=======================================
This example shows how to plot the phonon band structure of AlAs with plotly.
See tutorial/lesson_rf2.html
"""
from abipy.abilab import abiopen
import abipy.data as abidata
# Open the PHBST file produced by anaddb and get the phonon bands.
with abiopen(abidata.ref_file("trf2_5.out_PHBST.nc")) as ncfile:
phbands = ncfile.phbands
# Read the Phonon DOS from the netcd file produced by anaddb (prtdos 2)
with abiopen(abidata.ref_file("trf2_5.out_PHDOS.nc")) as ncfile:
phdos = ncfile.phdos
#%%
# plot phonon bands and DOS.
# `plot_phonons_lo_to.py` shows how to treat the LO-TO splitting.
phbands.plotly(title="AlAs Phonon bands and DOS in eV")
#%%
# plot phonon bands with DOS.
phbands.plotly_with_phdos(phdos, units="cm-1", title="AlAs Phonon bands + DOS in cm-1")
#%%
# plot phonon DOS.
phdos.plotly(units="cm-1", title="Phonon DOS and IDOS in cm-1")
# Plot the phonon band structure with different color for each line.
#phbands.plot_colored_matched(units="cm-1",
# title="AlAs with different color for each line.")
# sphinx_gallery_thumbnail_path = '_static/plotly_logo.png'

View File

@ -125,6 +125,7 @@ class NscfDdksWork(Work):
def from_scf_task(cls, scf_task, ddk_ngkpt, ddk_shiftk, ddk_nband, manager=None):
"""
Build NscfDdksWork from a scf_task.
Args:
scf_task: GS task. Must produce the DEN file required for the NSCF run.
ddk_ngkpt: k-mesh used for the NSCF run and the non self-consistent DDK tasks.

View File

@ -85,7 +85,7 @@ class EffMassDFPTWork(Work):
"""
@classmethod
def from_scf_input(cls, scf_input, k0_list, effmass_bands_f90, den_node=None, manager=None):
def from_scf_input(cls, scf_input, k0_list, effmass_bands_f90, ngfft=None, den_node=None, manager=None):
"""
Build the Work from an |AbinitInput| representing a GS-SCF calculation.
@ -94,12 +94,13 @@ class EffMassDFPTWork(Work):
k0_list: List with the reduced coordinates of the k-points where effective masses are wanted.
effmass_bands_f90: (nkpt, 2) array with band range for effmas computation.
WARNING: Assumes Fortran convention with indices starting from 1.
ngfft: FFT divisions (3 integers). Used to enforce the same FFT mesh in the NSCF run as the one used for GS.
den_node: Path to the DEN file or Task object producing a DEN file.
Can be used to avoid the initial SCF calculation if a DEN file is already available.
If None, a GS calculation is performed.
manager: |TaskManager| instance. Use default if None.
"""
multi = scf_input.make_dfpt_effmass_inputs(k0_list, effmass_bands_f90)
multi = scf_input.make_dfpt_effmass_inputs(k0_list, effmass_bands_f90, ngfft=ngfft)
nscf_input, effmass_input = multi[0], multi[1]
new = cls(manager=manager)
@ -177,10 +178,12 @@ class EffMassAutoDFPTWork(Work):
ebands.set_fermie_to_vbm()
# Find k0_list and effmass_bands_f90
k0_list, effmass_bands_f90 = ebands.get_kpoints_and_band_range_for_edges()
den_ngfft = gsr.reader.read_ngfft3()
# Create the work for effective mass computation with DFPT and add it to the flow.
# Keep a reference in generated_effmass_dfpt_work.
work = EffMassDFPTWork.from_scf_input(self.scf_input, k0_list, effmass_bands_f90, den_node=self.den_node)
work = EffMassDFPTWork.from_scf_input(self.scf_input, k0_list, effmass_bands_f90,
ngfft=den_ngfft, den_node=self.den_node)
self.generated_effmass_dfpt_work = work
self.flow.register_work(work)

View File

@ -347,10 +347,10 @@ class Flow(Node, NodeContainer, MSONable):
flow = pmg_pickle_load(strio)
return flow
def get_panel(self):
def get_panel(self, **kwargs):
"""Build panel with widgets to interact with the |Flow| either in a notebook or in panel app."""
from abipy.panels.flows import FlowPanel
return FlowPanel(self).get_panel()
return FlowPanel(self).get_panel(**kwargs)
def __len__(self):
return len(self.works)
@ -2008,9 +2008,28 @@ Use the `abirun.py FLOWDIR history` command to print the log files of the differ
return work
def new_work(self, deps=None, manager=None, workdir=None):
"""
Helper function to add a new empty |Work| and add it to the internal list.
Client code is responsible for filling the new work.
Args:
deps: List of :class:`Dependency` objects specifying the dependency of this node.
An empy list of deps implies that this node has no dependencies.
manager: The |TaskManager| responsible for the submission of the task.
If manager is None, we use the `TaskManager` specified during the creation of the work.
workdir: The name of the directory used for the |Work|.
Returns:
The registered |Work|.
"""
work = Work()
return self.register_work(work, deps=deps, manager=manager, workdir=workdir)
def register_work(self, work, deps=None, manager=None, workdir=None):
"""
Register a new |Work| and add it to the internal list, taking into account possible dependencies.
Register a new |Work| and add it to the internal list, taking into account
possible dependencies.
Args:
work: |Work| object.
@ -2392,7 +2411,7 @@ Use the `abirun.py FLOWDIR history` command to print the log files of the differ
def single_shot(self, check_status=True, **kwargs):
"""
Use :class:`PyLauncher` to submits one task.
Use :class:`PyLauncher` to submit one task.
kwargs contains the options passed to the launcher.
Return: Number of tasks submitted.

View File

@ -968,13 +968,15 @@ class Node(metaclass=abc.ABCMeta):
def write_json_in_outdir(self, filename, data):
"""
Write data to json file of basename filename inside the outdir directory of the node.
Support MSONable objects.
Support MSONable objects. Return path of json file.
"""
from monty.json import jsanitize
data = jsanitize(data, strict=False)
path = self.outdir.path_in(filename)
json_pretty_dump(data, path)
return path
##########################
### Abstract protocol ####
##########################

View File

@ -1018,7 +1018,7 @@ limits:
# stderr is redirected to mods.err file.
# module load 2>> mods.err
se.add_comment("Load Modules")
se.add_line("module purge")
se.add_line("module --force purge")
se.load_modules(self.modules)
se.add_emptyline()

View File

@ -1213,9 +1213,12 @@ class AbinitBuild(object):
# Temporary hack for abinit v9
return True
print("self.info", self.info)
# Parse info.
# flavor options were used in Abinit v8
for line in self.info.splitlines():
print(line)
if "Version" in line: self.version = line.split()[-1]
if "TRIO flavor" in line:
self.has_netcdf = "netcdf" in line
@ -1240,7 +1243,7 @@ class AbinitBuild(object):
# Temporary hack for abinit v9
#from abipy.core.testing import cmp_version
#if cmp_version(self.version, "9.0.0", op=">="):
# self.has_netcdf = True
self.has_netcdf = True
def __str__(self):
lines = []

View File

@ -302,13 +302,17 @@ class FlowTest(FlowUnitTest):
hello_flow = flowtk.Flow(workdir=self.mkdtemp())
hello_flow.register_scf_task(scf_input, append=True)
hello_flow.register_nscf_task(nscf_input, deps={hello_flow[0][0]: "DEN"}, append=True)
#flow[0].get_graphviz_dirtree()
#abilab.print_doc(flowtk.PhononWork)
hello_flow = flowtk.Flow(workdir=self.mkdtemp())
hello_flow.register_scf_task(scf_input, append=True)
assert len(hello_flow) == 1
hello_flow.register_nscf_task(nscf_input, deps={hello_flow[0][0]: "DEN"}, append=False)
assert len(hello_flow) == 2
empty_work = hello_flow.new_work()
assert len(empty_work) == 0
assert len(hello_flow) == 3

View File

@ -461,9 +461,10 @@ class NodeContainer(metaclass=abc.ABCMeta):
"""Register an electron-phonon task."""
kwargs["task_class"] = EphTask
eph_inp = args[0]
seq_manager = TaskManager.from_user_config().new_with_fixed_mpi_omp(1, 1)
if eph_inp.get("eph_frohlichm", 0) != 0 or abs(eph_inp.get("eph_task", 0)) == 15:
# FIXME: Hack to run task in sequential if calculation does not support MPI with nprocs > 1.
# FIXME: Hack to run task in sequential since this calculation does
# not support MPI with nprocs > 1.
seq_manager = TaskManager.from_user_config().new_with_fixed_mpi_omp(1, 1)
kwargs.update({"manager": seq_manager})
if eph_inp.get("eph_task",0) == -4:
@ -1115,7 +1116,7 @@ class RelaxWork(Work):
class G0W0Work(Work):
"""
Work for general G0W0 calculations.
Work for generic G0W0 calculations.
All input can be either single inputs or lists of inputs
.. rubric:: Inheritance Diagram
@ -1484,8 +1485,8 @@ class PhononWork(Work, MergeDdb):
This work consists of nirred Phonon tasks where nirred is
the number of irreducible atomic perturbations for a given set of q-points.
It provides the callback method (on_all_ok) that calls mrgddb (mrgdv) to merge
all the partial DDB (POT) files produced. The two files are available in the
output directory of the Work.
all the partial DDB (POT) files produced.
The two files are available in the output directory of the Work.
.. rubric:: Inheritance Diagram
.. inheritance-diagram:: PhononWork
@ -1493,7 +1494,7 @@ class PhononWork(Work, MergeDdb):
@classmethod
def from_scf_task(cls, scf_task, qpoints, is_ngqpt=False, tolerance=None, with_becs=False,
ddk_tolerance=None, manager=None):
ddk_tolerance=None, prtwf=-1, manager=None):
"""
Construct a `PhononWork` from a |ScfTask| object.
The input file for phonons is automatically generated from the input of the ScfTask.
@ -1509,6 +1510,11 @@ class PhononWork(Work, MergeDdb):
with_becs: Activate calculation of Electric field and Born effective charges.
ddk_tolerance: dict {"varname": value} with the tolerance used in the DDK run if with_becs.
None to use AbiPy default.
prtwf: Controls the output of the first-order WFK.
By default we set it to -1 when q != 0 so that AbiPy is still able
to restart the DFPT task if the calculation is not converged (worst case scenario)
but we avoid the output of the 1-st WFK if the calculation converged successfully.
Non-linear DFT applications should not be affected since they assume q == 0.
manager: |TaskManager| object.
"""
if not isinstance(scf_task, ScfTask):
@ -1523,16 +1529,19 @@ class PhononWork(Work, MergeDdb):
new.add_becs_from_scf_task(scf_task, ddk_tolerance, ph_tolerance=tolerance)
for qpt in qpoints:
if with_becs and np.sum(qpt ** 2) < 1e-12: continue
is_gamma = np.sum(qpt ** 2) < 1e-12
if with_becs and is_gamma: continue
multi = scf_task.input.make_ph_inputs_qpoint(qpt, tolerance=tolerance)
for ph_inp in multi:
# Here we set the value of prtwf for the DFPT tasks if q != Gamma.
if not is_gamma: ph_inp.set_vars(prtwf=prtwf)
new.register_phonon_task(ph_inp, deps={scf_task: "WFK"})
return new
@classmethod
def from_scf_input(cls, scf_input, qpoints, is_ngqpt=False, tolerance=None,
with_becs=False, ddk_tolerance=None, manager=None):
with_becs=False, ddk_tolerance=None, prtwf=-1, manager=None):
"""
Similar to `from_scf_task`, the difference is that this method requires
an input for SCF calculation. A new |ScfTask| is created and added to the Work.
@ -1551,9 +1560,12 @@ class PhononWork(Work, MergeDdb):
new.add_becs_from_scf_task(scf_task, ddk_tolerance, ph_tolerance=tolerance)
for qpt in qpoints:
if with_becs and np.sum(qpt ** 2) < 1e-12: continue
is_gamma = np.sum(qpt ** 2) < 1e-12
if with_becs and is_gamma: continue
multi = scf_task.input.make_ph_inputs_qpoint(qpt, tolerance=tolerance)
for ph_inp in multi:
# Here we set the value of prtwf for the DFPT tasks if q != Gamma.
if not is_gamma: ph_inp.set_vars(prtwf=prtwf)
new.register_phonon_task(ph_inp, deps={scf_task: "WFK"})
return new
@ -1562,7 +1574,7 @@ class PhononWork(Work, MergeDdb):
def on_all_ok(self):
"""
This method is called when all the q-points have been computed.
Ir runs `mrgddb` in sequential on the local machine to produce
It runs `mrgddb` in sequential on the local machine to produce
the final DDB file in the outdir of the |Work|.
"""
# Merge DDB files.
@ -2006,12 +2018,14 @@ class ConducWork(Work):
"""
Workflow for the computation of electrical conductivity.
Can be called from :
Can be called from:
1. MultiDataset and PhononWork
2. MultiDataset, DDB filepath and DVDB filepath.
Can use Kerange Capability using withKerange=True
This work consists of 3 tasks or 5 tasks with kerange :
This work consists of 3 tasks or 5 tasks with kerange:
1. SCF GS
2. NSCF
3. Kerange (Kerange only)
@ -2023,7 +2037,7 @@ class ConducWork(Work):
def from_phwork(cls, phwork, multi, nbr_proc=None, flow=None, with_kerange=False,
omp_nbr_thread=1, manager=None):
"""
Construct a |ConducWork| from a |PhononWork| and |MultiDataset|.
Construct a ConducWork from a |PhononWork| and |MultiDataset|.
Args:
phwork: a |PhononWork| object calculating the DDB and DVDB files.
@ -2081,7 +2095,7 @@ class ConducWork(Work):
def from_filepath(cls, ddb_path, dvdb_path, multi, nbr_proc=None, flow=None,
with_kerange=False, omp_nbr_thread=1, manager=None):
"""
Construct a |ConducWork| from previously calculated DDB/DVDB file and |MultiDataset|.
Construct a ConducWork from previously calculated DDB/DVDB file and |MultiDataset|.
Args:
multi: a |MultiDataset| object containing a list of 3 datasets or 5 with Kerange.

View File

@ -2,6 +2,28 @@ TODO list:
## High priority
* Add new section to manager.yml that allows users to customize limits according
to task.__class__.__name__.
Possible Yaml syntax for `task_class_limits`:
limits:
min_cores: 1
max_cores: 1000
timelimit: 2:0:0
task_class_limits:
# TaskClassName --> dict with new limits
# Accept absolute values or `scale_name` syntax to scale `name` (mutually exclusive)
# If a new limit is not specified, the global value is used.
#
NscfTask: {scale_max_cores: 0.5, scale_timelimit: 0.2}
KerangeTask: {max_cores: 2, timelimit: 0:5:0}
* Implement Task modifier i.e. operations that change the input file if some condition occurs.
This extra logic is require to handle problematic cases in which for instance the ScfTask does not converge
and modification in the input file are required
For instance, one may need to increase nline and/or diemac before restarting.
* Use angdeg instead of rprimd in structure_to_abivars if hex or rhomboedral lattice
(tricky because input settings should be preserved)
@ -26,14 +48,12 @@ TODO list:
* Refactor wrappers for mrgddb and mrgdvdb (problems with subprocess when
merging large number of partial files (likely due to Popen with large stderr/stdout)
* Move to new version of APSscheduler
* BECS: 3x3 Tensor is not symmetric. Remove get_voigt_dataframe
* Parse stderr to detect runtime errors such as
* Parse stderr to detect runtime errors such as
forrtl: severe (24): end-of-file during read, unit 5, file /proc/59090/fd/0
Image PC Routine Line Source
Image PC Routine Line Source
abinit 0000000008914AC2 for__io_return Unknown Unknown
abinit 000000000894378D for_read_seq_fmt Unknown Unknown
abinit 000000000194409E Unknown Unknown Unknown
@ -45,13 +65,15 @@ TODO list:
and kill the scheduler else the code gets stuck here (issue reported on lemaitre3)
* Remove/check the usage of line_density. Use ndivsm < 0 to activate line_density a la pymatgen.
## Medium priority
* Add support for PSML/UPF format
* Add support for new Abinit9 interface (getden_path, getwfk_path, pp_dirpath and pseudos)
but remember that strings in the input should not be too long.
but remember that strings in the input should not be too long.
Use common root for pseudos, what about getwfk_path? Need to refactor treatment of string lengths in Abinit!
* Interface abitk with AbiPy to compute DOS with tetra.

View File

@ -133,7 +133,7 @@ def itest_phonon_flow(fwp, tvars):
assert atask.status == atask.S_OK
# These output files should be produced in the task workdir.
# Actually they should be in outdir but anaddb uses different conventions.
# Actually they should be in the outdir but anaddb uses different conventions.
assert len(atask.wdir.list_filepaths(wildcard="*PHBST.nc")) == 1
assert len(atask.wdir.list_filepaths(wildcard="*PHDOS.nc")) == 1
@ -160,7 +160,8 @@ def itest_phonon_restart(fwp):
tolvrs=1.0e-5,
)
multi = abilab.MultiDataset(structure=structure, pseudos=abidata.pseudos("13al.981214.fhi", "33as.pspnc"),
multi = abilab.MultiDataset(structure=structure,
pseudos=abidata.pseudos("13al.981214.fhi", "33as.pspnc"),
ndtset=1 + len(qpoints))
multi.set_vars(global_vars)
@ -179,7 +180,7 @@ def itest_phonon_restart(fwp):
#kptopt 2 # Automatic generation of k points, taking
# i == 0 --> restart from WFK
if i == 1: multi[i+1].set_vars(prtwf=-1, nstep=5) # Restart with WFK and smart- io.
if i == 1: multi[i+1].set_vars(prtwf=-1, nstep=5) # Restart with WFK and smart-io.
if i == 2: multi[i+1].set_vars(prtwf=0, nstep=8) # Restart from 1DEN. Too long --> disabled.
all_inps = multi.split_datasets()

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,50 @@
""""Panels for DDB files."""
import sys
import param
import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from abipy.panels.core import AbipyParameterized, ButtonContext, mpl, ply, df
from abipy.panels.core import (AbipyParameterized, HasStructureParams, BaseRobotPanel,
mpl, ply, dfc, depends_on_btn_click)
from abipy.dfpt.ddb import PhononBandsPlotter
class HasAnaddbParams(AbipyParameterized):
class HasAnaddbParams(param.Parameterized):
"""
Base class for panel classes requiring widgets to invoke Anaddb via AbiPy.
Mixin for panel classes requiring widgets to invoke Anaddb via AbiPy.
Used, for instance, by DdbFilePanel and DdbRobotPanel so that we don't have to
repeat the same widget stuff over and over agains.
repeat the same parameters over and over again.
"""
verbose = param.Integer(0, bounds=(0, None), doc="Verbosity level")
mpi_procs = param.Integer(1, bounds=(1, None), doc="Number of MPI processes used in anaddb")
nqsmall = param.Integer(10, bounds=(1, None), doc="Number of divisions for smallest vector to generate Q-mesh")
ndivsm = param.Integer(5, bounds=(None, None), doc="Number of divisions for smallest segment in q-path")
ndivsm = param.Integer(5, bounds=(None, None), doc="Number of divisions for smallest vector to generate Q-path")
lo_to_splitting = param.ObjectSelector(default="automatic", objects=["automatic", True, False])
chneut = param.ObjectSelector(default=1, objects=[0, 1, 2], doc="Abinit variable")
dipdip = param.ObjectSelector(default=1, objects=[0, 1, -1], doc="Abinit variable")
# TODO: Add this widget, need to update anaget API.
#dipquad = param.ObjectSelector(default=0, objects=[0, 1], doc="Abinit variable")
#quadquad = param.ObjectSelector(default=0, objects=[0, 1], doc="Abinit variable")
asr = param.ObjectSelector(default=2, objects=[0, 1, 2], doc="Abinit variable")
units = param.ObjectSelector(default="eV", objects=["eV", "meV", "Ha", "cm-1", "Thz"], doc="Energy units")
dos_method = param.ObjectSelector(default="tetra", objects=["tetra", "gaussian"], doc="Integration method for DOS")
temp_range = pnw.RangeSlider(name="T-range", start=0.0, end=1000, value=(0.0, 300.0), step=20)
temp_range = param.Range(default=(0.0, 300.0), bounds=(0, 1000), doc="Temperature range in K.")
gamma_ev = param.Number(1e-4, bounds=(1e-20, None), doc="Phonon linewidth in eV")
w_range = pnw.RangeSlider(name="Frequency range (eV)", start=0.0, end=1.0,
value=(0.0, 0.1), step=0.001)
w_range = param.Range(default=(0.0, 0.1), bounds=(0.0, 1.0), doc="Frequency range (eV)")
# FIXME
nqsmall_list = pnw.LiteralInput(name='nsmalls (python list)', value=[10, 20, 30], type=list)
#nqqpt = pnw.LiteralInput(name='nsmalls (list)', value=[10, 20, 30], type=list)
warning_md = pn.pane.Markdown(
# Base buttons
plot_check_asr_dipdip_btn = pnw.Button(name="Compute phonons with/wo ASR and DIPDIP", button_type='primary')
warning = pn.pane.Markdown(
"""
Refresh the page if plotly figures are not shown.
Note that widgets for input variables such as *asr*, *chneut*, *dipdip*, *dos_method*, *etc.*
are **shared by the different tabs**.
@ -51,10 +57,6 @@ the results/figures are stil computed with the **old input variables** and you w
recompute the new results by clicking the button.
""", name="warning")
# Base buttons
plot_check_asr_dipdip_btn = pnw.Button(name="Compute phonons with/wo ASR and DIPDIP", button_type='primary')
def kwargs_for_anaget_phbst_and_phdos_files(self, **extra_kwargs):
"""
Return the parameters require to invoke anaget_phbst_and_phdos_files
@ -70,323 +72,324 @@ recompute the new results by clicking the button.
return d
class DdbFilePanel(HasAnaddbParams):
class DdbFilePanel(HasStructureParams, HasAnaddbParams):
"""
A panel to analyze a |DdbFile|. Provides widgets to invoke anaddb and visualize the results.
A panel to analyze a |DdbFile|.
Provides widgets to invoke anaddb and visualize the results.
"""
# Buttons
get_epsinf_btn = pnw.Button(name="Compute", button_type='primary')
plot_phbands_btn = pnw.Button(name="Plot Bands and DOS", button_type='primary')
plot_eps0w_btn = pnw.Button(name="Plot eps0(omega)", button_type='primary')
plot_vsound_btn = pnw.Button(name="Calculate speed of sound", button_type='primary')
plot_ifc_btn = pnw.Button(name="Compute IFC(R)", button_type='primary')
plot_phbands_quad_btn = pnw.Button(name="Plot PHbands with/without quadrupoles", button_type='primary')
plot_dos_vs_qmesh_btn = pnw.Button(name="Plot PHDos vs Qmesh", button_type='primary')
def __init__(self, ddb, **params):
super().__init__(**params)
self.ddb = ddb
@param.depends('get_epsinf_btn.clicks')
# Add buttons
self.get_epsinf_btn = pnw.Button(name="Compute", button_type='primary')
self.plot_phbands_btn = pnw.Button(name="Plot Bands and DOS", button_type='primary')
self.plot_eps0w_btn = pnw.Button(name="Plot eps0(omega)", button_type='primary')
self.plot_vsound_btn = pnw.Button(name="Calculate speed of sound", button_type='primary')
self.plot_ifc_btn = pnw.Button(name="Compute IFC(R)", button_type='primary')
self.plot_phbands_quad_btn = pnw.Button(name="Plot PHbands with/without quadrupoles", button_type='primary')
self.plot_dos_vs_qmesh_btn = pnw.Button(name="Plot PHDos vs Qmesh", button_type='primary')
self.stacked_pjdos = pnw.Checkbox(name="Stacked PJDOS", value=True)
super().__init__(**params)
@property
def structure(self):
"""Structure object provided by subclass."""
return self.ddb.structure
@depends_on_btn_click('get_epsinf_btn')
def get_epsinf(self):
"""Compute eps_infinity and Born effective charges from DDB."""
if self.get_epsinf_btn.clicks == 0: return
with ButtonContext(self.get_epsinf_btn):
epsinf, becs = self.ddb.anaget_epsinf_and_becs(chneut=self.chneut,
mpi_procs=self.mpi_procs, verbose=self.verbose)
epsinf, becs = self.ddb.anaget_epsinf_and_becs(chneut=self.chneut,
mpi_procs=self.mpi_procs, verbose=self.verbose)
gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
mpi_procs=self.mpi_procs, verbose=self.verbose,
return_input=True)
gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
mpi_procs=self.mpi_procs, verbose=self.verbose,
return_input=True)
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
#df_kwargs = dict(auto_edit=False, autosize_mode="fit_viewport")
df_kwargs = {}
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
#df_kwargs = dict(auto_edit=False, autosize_mode="fit_viewport")
df_kwargs = {}
eps0 = gen.tensor_at_frequency(w=0, gamma_ev=self.gamma_ev)
ca(r"## $\epsilon^0$ in Cart. coords (computed with Gamma_eV):")
ca(df(eps0.get_dataframe(cmode="real"), **df_kwargs))
ca(r"## $\epsilon^\infty$ in Cart. coords:")
ca(df(epsinf.get_dataframe(), **df_kwargs))
ca("## Born effective charges in Cart. coords:")
ca(df(becs.get_voigt_dataframe(), **df_kwargs))
ca("## Anaddb input file.")
ca(pn.pane.HTML(inp._repr_html_()))
eps0 = gen.tensor_at_frequency(w=0, gamma_ev=self.gamma_ev)
ca(r"## $\epsilon^0$ in Cart. coords (computed with Gamma_eV):")
ca(dfc(eps0.get_dataframe(cmode="real"), **df_kwargs))
ca(r"## $\epsilon^\infty$ in Cart. coords:")
ca(dfc(epsinf.get_dataframe(), **df_kwargs))
ca("## Born effective charges in Cart. coords:")
ca(dfc(becs.get_voigt_dataframe(), **df_kwargs))
ca("## Anaddb input file.")
ca(pn.pane.HTML(inp._repr_html_()))
return col
return col
@param.depends('plot_eps0w_btn.clicks')
@depends_on_btn_click('plot_eps0w_btn')
def plot_eps0w(self):
"""Compute eps0(omega) from DDB and plot the results."""
if self.plot_eps0w_btn.clicks == 0: return
gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
mpi_procs=self.mpi_procs, verbose=self.verbose,
return_input=True)
ws = self.w_range
w_max = ws[1]
if w_max == 1.0: w_max = None # Will compute w_max in plot routine from ph freqs.
with ButtonContext(self.plot_eps0w_btn):
gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
mpi_procs=self.mpi_procs, verbose=self.verbose,
return_input=True)
ws = self.w_range.value
w_max = ws[1]
if w_max == 1.0: w_max = None # Will compute w_max in plot routine from ph freqs.
def p(component, reim):
# Matplotlib
#fig = gen.plot(w_min=ws[0], w_max=w_max, gamma_ev=self.gamma_ev, num=500, component=component,
# reim=reim, units=self.units, **self.mpl_kwargs)
#return mpl(fig)
fig = gen.plotly(w_min=ws[0], w_max=w_max, gamma_ev=self.gamma_ev, num=500, component=component,
reim=reim, units=self.units, show=False)
return ply(fig, with_help=False)
def p(component, reim):
return gen.plot(w_min=ws[0], w_max=w_max, gamma_ev=self.gamma_ev, num=500, component=component,
reim=reim, units=self.units, **self.fig_kwargs)
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
# Build grid
gspec = pn.GridSpec(sizing_mode='scale_width')
gspec[0, 0] = p("diag", "re")
gspec[0, 1] = p("diag", "im")
gspec[1, 0] = p("offdiag", "re")
gspec[1, 1] = p("offdiag", "im")
gspec[2, :] = gen.get_oscillator_dataframe(reim="all", tol=1e-6)
# Add HTML pane with input.
gspec[3, 0] = pn.pane.HTML(inp._repr_html_())
# Add figures
ca("## epsilon(w):")
ca(p("diag", "re"))
ca(p("diag", "im"))
ca(p("offdiag", "re"))
ca(p("offdiag", "im"))
return gspec
#gspec[2, :] = gen.get_oscillator_dataframe(reim="all", tol=1e-6)
# TODO: FIX
# TypeError: Object of type complex is not JSON serializable
#dfc(gen.get_oscillator_dataframe(reim="all", tol=1e-6))
ca("## Oscillator matrix elements:")
ca(gen.get_oscillator_dataframe(reim="all", tol=1e-6))
# Add HTML pane with input.
ca("## Anaddb input file:")
ca(pn.pane.HTML(inp._repr_html_()))
@param.depends('plot_phbands_btn.clicks')
def plot_phbands_and_phdos(self, event=None):
#return gspec
return col
@depends_on_btn_click('plot_phbands_btn')
def plot_phbands_and_phdos(self):
"""Compute phonon bands and DOSes from DDB and plot the results."""
if self.plot_phbands_btn.clicks == 0: return
with ButtonContext(self.plot_phbands_btn):
# Computing phbands
kwargs = self.kwargs_for_anaget_phbst_and_phdos_files(return_input=True)
# Computing phbands
kwargs = self.kwargs_for_anaget_phbst_and_phdos_files(return_input=True)
with self.ddb.anaget_phbst_and_phdos_files(**kwargs) as g:
phbst_file, phdos_file = g
phbands, phdos = phbst_file.phbands, phdos_file.phdos
with self.ddb.anaget_phbst_and_phdos_files(**kwargs) as g:
phbst_file, phdos_file = g
phbands, phdos = phbst_file.phbands, phdos_file.phdos
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon band structure and DOS:")
ca(ply(phbands.plotly_with_phdos(phdos, units=self.units, show=False)))
#ca(mpl(phbands.plot_with_phdos(phdos, units=self.units, **self.fig_kwargs)))
#ca(mpl(phdos_file.plot_pjdos_type(units=self.units, exchange_xy=True, **self.fig_kwargs)))
#ca(mpl(phdos_file.msqd_dos.plot(units=self.units, **self.fig_kwargs)))
temps = self.temp_range.value
ca("## Thermodynamic properties in the harmonic approximation:")
#ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.fig_kwargs))
ca(ply(phdos.plotly_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, show=False)))
#msqd_dos.plot_tensor(**self.fig_kwargs)
#ca(mpl(phbands.plot_with_phdos(phdos, units=self.units, **self.mpl_kwargs)))
# Add HTML pane with input
ca("## Brillouin zone and q-path:")
#qpath_pane = mpl(phbands.qpoints.plot(**self.mpl_kwargs), with_divider=False)
qpath_pane = ply(phbands.qpoints.plotly(show=False), with_divider=False)
df_qpts = phbands.qpoints.get_highsym_datataframe()
ca(pn.Row(qpath_pane, df_qpts))
ca(pn.layout.Divider())
ca("## Type-projected phonon DOS:")
#ca(mpl(phdos_file.plot_pjdos_type(units=self.units, **self.mpl_kwargs)))
ca(ply(phdos_file.plotly_pjdos_type(units=self.units, stacked=self.stacked_pjdos.value, show=False)))
#ca(mpl(phdos_file.msqd_dos.plot(units=self.units, **self.mpl_kwargs)))
ca("## Thermodynamic properties in the harmonic approximation:")
temps = self.temp_range
#ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.mpl_kwargs))
ca(ply(phdos.plotly_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, show=False)))
#msqd_dos.plot_tensor(**self.mpl_kwargs)
# Add Anaddb input file
ca("## Anaddb input file:")
ca(pn.pane.HTML(g.input._repr_html_()))
ca(self.html_with_clipboard_btn(g.input._repr_html_()))
return col
@param.depends('plot_vsound_btn.clicks')
@depends_on_btn_click('plot_vsound_btn')
def plot_vsound(self):
"""
Compute the speed of sound by fitting phonon frequencies
along selected directions by linear least-squares fit.
"""
if self.plot_vsound_btn.clicks == 0: return
col = pn.Column(sizing_mode="stretch_width"); ca = col.append
with ButtonContext(self.plot_vsound_btn):
from abipy.dfpt.vsound import SoundVelocity
sv = SoundVelocity.from_ddb(self.ddb.filepath, num_points=20, qpt_norm=0.1,
ignore_neg_freqs=True, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
verbose=self.verbose, mpi_procs=self.mpi_procs)
from abipy.dfpt.vsound import SoundVelocity
sv = SoundVelocity.from_ddb(self.ddb.filepath, num_points=20, qpt_norm=0.1,
ignore_neg_freqs=True, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
verbose=self.verbose, mpi_procs=self.mpi_procs)
# Insert results in grid.
gspec = pn.GridSpec(sizing_mode='scale_width')
gspec[0, :1] = sv.get_dataframe()
gspec[1, :1] = sv.plot(**self.fig_kwargs)
ca("## Linear least-squares fit:")
#ca(mpl(sv.plot(**self.mpl_kwargs)))
ca(ply(sv.plotly(show=False)))
ca("## Speed of sound computed along different q-directions in reduced coords:")
ca(dfc(sv.get_dataframe()))
return gspec
return col
@param.depends('plot_check_asr_dipdip_btn.clicks')
@depends_on_btn_click('plot_check_asr_dipdip_btn')
def plot_without_asr_dipdip(self):
"""
Compare phonon bands and DOSes computed with/without the acoustic sum rule
and the treatment of the dipole-dipole interaction in the dynamical matrix.
Requires DDB file with eps_inf, BECS.
"""
if self.plot_check_asr_dipdip_btn.clicks == 0: return
asr_plotter = self.ddb.anacompare_asr(asr_list=(0, 2), chneut_list=(1, ), dipdip=1,
lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
with ButtonContext(self.plot_check_asr_dipdip_btn):
asr_plotter = self.ddb.anacompare_asr(asr_list=(0, 2), chneut_list=(1, ), dipdip=1,
lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
dipdip_plotter = self.ddb.anacompare_dipdip(chneut_list=(1,), asr=2, lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
dipdip_plotter = self.ddb.anacompare_dipdip(chneut_list=(1,), asr=2, lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon bands and DOS with/wo acoustic sum rule:")
#ca(mpl(asr_plotter.plot(**self.mpl_kwargs)))
ca(ply(asr_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/without the treatment of the dipole-dipole interaction:")
#ca(mpl(dipdip_plotter.plot(**self.mpl_kwargs)))
ca(ply(dipdip_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/wo acoustic sum rule:")
#ca(mpl(asr_plotter.plot(**self.fig_kwargs)))
ca(ply(asr_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/without the treatment of the dipole-dipole interaction:")
#ca(mpl(dipdip_plotter.plot(**self.fig_kwargs)))
ca(ply(dipdip_plotter.combiplotly(show=False)))
return col
return col
@param.depends('plot_dos_vs_qmesh_btn.clicks')
@depends_on_btn_click('plot_dos_vs_qmesh_btn')
def plot_dos_vs_qmesh(self):
"""
Compare phonon DOSes computed with/without the inclusion
of the dipole-quadrupole and quadrupole-quadrupole terms in the dynamical matrix.
Requires DDB file with eps_inf, BECS and dynamical quadrupoles.
"""
if self.plot_dos_vs_qmesh_btn.clicks == 0: return
num_cpus = 1
#print(self.nqsmall_list.value)
r = self.ddb.anacompare_phdos(self.nqsmall_list.value, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, num_cpus=num_cpus, stream=sys.stdout)
with ButtonContext(self.plot_dos_vs_qmesh_btn):
num_cpus = 1
#print(self.nqsmall_list.value)
r = self.ddb.anacompare_phdos(self.nqsmall_list.value, asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, num_cpus=num_cpus, stream=sys.stdout)
#r.phdoses: List of |PhononDos| objects
#r.phdoses: List of |PhononDos| objects
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon DOSes obtained with different q-meshes:")
ca(ply(r.plotter.combiplotly(show=False)))
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon DOSes obtained with different q-meshes:")
ca(ply(r.plotter.combiplotly(show=False)))
ca("## Convergence of termodynamic properties.")
temps = self.temp_range
ca(mpl(r.plotter.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50,
units=self.units, **self.mpl_kwargs)))
ca("## Convergence of termodynamic properties.")
temps = self.temp_range.value
ca(mpl(r.plotter.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50,
units=self.units, **self.fig_kwargs)))
return col
return col
@param.depends('plot_phbands_quad_btn.clicks')
@depends_on_btn_click('plot_phbands_quad_btn')
def plot_phbands_quad(self):
"""
Compare phonon bands and DOSes computed with/without the inclusion
of the dipole-quadrupole and quadrupole-quadrupole terms in the dynamical matrix.
Requires DDB file with eps_inf, BECS and dynamical quadrupoles.
"""
if self.plot_phbands_quad_btn.clicks == 0: return
plotter = self.ddb.anacompare_quad(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
lo_to_splitting=self.lo_to_splitting,
nqsmall=0, ndivsm=self.ndivsm, dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
with ButtonContext(self.plot_dos_vs_qmesh_btn):
plotter = self.ddb.anacompare_quad(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
lo_to_splitting=self.lo_to_splitting,
nqsmall=0, ndivsm=self.ndivsm, dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs)
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon Bands obtained with different q-meshes:")
ca(ply(plotter.combiplotly(show=False)))
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon Bands obtained with different q-meshes:")
ca(ply(plotter.combiplotly(show=False)))
return col
return col
@param.depends('plot_ifc_btn.clicks')
@depends_on_btn_click('plot_ifc_btn')
def plot_ifc(self):
if self.plot_ifc_btn.clicks == 0: return
ifc = self.ddb.anaget_ifc(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip)
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca(mpl(ifc.plot_longitudinal_ifc(title="Longitudinal IFCs", **self.fig_kwargs)))
ca(mpl(ifc.plot_longitudinal_ifc_short_range(title="Longitudinal IFCs short range", **self.fig_kwargs)))
ca(mpl(ifc.plot_longitudinal_ifc_ewald(title="Longitudinal IFCs Ewald", **self.fig_kwargs)))
ca(mpl(ifc.plot_longitudinal_ifc(title="Longitudinal IFCs", **self.mpl_kwargs)))
ca(mpl(ifc.plot_longitudinal_ifc_short_range(title="Longitudinal IFCs short range", **self.mpl_kwargs)))
ca(mpl(ifc.plot_longitudinal_ifc_ewald(title="Longitudinal IFCs Ewald", **self.mpl_kwargs)))
return col
def get_panel(self):
"""Return tabs with widgets to interact with the DDB file."""
def get_panel(self, as_dict=False, **kwargs):
"""
Return tabs with widgets to interact with the DDB file.
"""
d = {}
d["Summary"] = pn.Row(
bkw.PreText(text=self.ddb.to_string(verbose=self.verbose), sizing_mode="scale_both")
)
#if self.ddb.has_phonons
d["PH-bands"] = pn.Row(
self.pws_col(["### PH-bands options", "nqsmall", "ndivsm", "asr", "chneut", "dipdip",
"lo_to_splitting", "dos_method", "stacked_pjdos", "temp_range", "plot_phbands_btn",
self.helpc("plot_phbands_and_phdos")]),
self.plot_phbands_and_phdos
)
#if self.ddb.has_becs
d["BECs"] = pn.Row(
self.pws_col(["### Born effective charges options", "asr", "chneut", "dipdip", "gamma_ev",
"get_epsinf_btn", self.helpc("get_epsinf")]),
self.get_epsinf
)
#if self.ddb.has_e0
d["eps0"] = pn.Row(
self.pws_col(["### epsilon_0", "asr", "chneut", "dipdip", "gamma_ev", "w_range", "plot_eps0w_btn",
self.helpc("plot_eps0w")]),
self.plot_eps0w
)
#if self.ddb.has_phonons
d["Speed of sound"] = pn.Row(
self.pws_col(["### Speed of sound options", "asr", "chneut", "dipdip", "plot_vsound_btn",
self.helpc("plot_vsound")]),
self.plot_vsound
)
#if self.ddb.has_phonons
d["ASR & DIPDIP"] = pn.Row(
self.pws_col(["### ASR & DIPDIP options", "nqsmall", "ndivsm", "dos_method", "plot_check_asr_dipdip_btn",
self.helpc("plot_without_asr_dipdip")]),
self.plot_without_asr_dipdip
)
d["DOS vs q-mesh"] = pn.Row(
self.pws_col(["### DOS vs q-mesh options", "asr", "chneut", "dipdip", "dos_method", "nqsmall_list",
"temp_range", "plot_dos_vs_qmesh_btn", self.helpc("plot_dos_vs_qmesh")]),
self.plot_dos_vs_qmesh
)
#if self.ddb.has_dynamical_quadrupoles
d["Quadrupoles"] = pn.Row(
self.pws_col(["### Quadrupoles options", "asr", "chneut", "dipdip", "lo_to_splitting", "ndivsm", "dos_method",
"plot_phbands_quad_btn", self.helpc("plot_phbands_quad")]),
self.plot_phbands_quad
)
#if self.ddb.has_phonons
d["IFCs"] = pn.Row(
self.pws_col(["### IFCs options", "asr", "dipdip", "chneut", "plot_ifc_btn", self.helpc("plot_ifc")]),
self.plot_ifc
)
d["Structure"] = self.get_struct_view_tab_entry()
d["Global"] = pn.Row(
self.pws_col(["### Global options", "units", "mpi_procs", "verbose"]),
self.get_software_stack()
)
def info(method_name):
# Add accordion with brief desciption and warning after the button.
# The description of the tool is taken from the docstring of the callback.
col = pn.Column(); ca = col.append
acc = pn.Accordion(("Help", pn.pane.Markdown(getattr(self, method_name).__doc__)))
acc.append(("Warning", self.warning_md))
ca(pn.layout.Divider())
ca(acc)
return col
if as_dict: return d
tabs = pn.Tabs(); app = tabs.append
app(("Summary", pn.Row(
bkw.PreText(text=self.ddb.to_string(verbose=self.verbose), sizing_mode="scale_both"))
))
app(("PH-bands", pn.Row(
pn.Column("# PH-bands options",
*self.pws("nqsmall", "ndivsm", "asr", "chneut", "dipdip",
"lo_to_splitting", "dos_method", "temp_range", "plot_phbands_btn",
info("plot_phbands_and_phdos")),
),
self.plot_phbands_and_phdos)
))
app(("BECs", pn.Row(
pn.Column("# Born effective charges options",
*self.pws("asr", "chneut", "dipdip", "gamma_ev", "get_epsinf_btn", info("get_epsinf")),
),
self.get_epsinf)
))
app(("eps0", pn.Row(
pn.Column("# epsilon_0",
*self.pws("asr", "chneut", "dipdip", "gamma_ev", "w_range", "plot_eps0w_btn",
info("plot_eps0w")),
),
self.plot_eps0w)
))
app(("Speed of sound", pn.Row(
pn.Column("# Speed of sound options",
*self.pws("asr", "chneut", "dipdip", "plot_vsound_btn", info("plot_vsound")),
),
self.plot_vsound)
))
app(("ASR & DIPDIP", pn.Row(
pn.Column("# ASR & DIPDIP options",
*self.pws("nqsmall", "ndivsm", "dos_method", "plot_check_asr_dipdip_btn",
info("plot_without_asr_dipdip")),
),
self.plot_without_asr_dipdip)
))
app(("DOS vs q-mesh", pn.Row(
pn.Column("# DOS vs q-mesh options",
*self.pws("asr", "chneut", "dipdip", "dos_method", "nqsmall_list",
"temp_range", "plot_dos_vs_qmesh_btn", info("plot_dos_vs_qmesh")),
),
self.plot_dos_vs_qmesh)
))
app(("Quadrupoles", pn.Row(
pn.Column("# Quadrupoles options",
*self.pws("asr", "chneut", "dipdip", "lo_to_splitting", "ndivsm", "dos_method",
"plot_phbands_quad_btn", info("plot_phbands_quad")),
),
self.plot_phbands_quad)
))
app(("IFCs", pn.Row(
pn.Column("# IFCs options",
*self.pws("asr", "dipdip", "chneut", "plot_ifc_btn", info("plot_ifc")),
),
self.plot_ifc)
))
app(("Global options",
pn.Column("# Global options",
*self.pws("units", "mpi_procs", "verbose"),
)))
return tabs
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))
class DdbRobotPanel(HasAnaddbParams):
class DdbRobotPanel(BaseRobotPanel, HasAnaddbParams):
"""
A panel to analyze multiple |DdbFile| via the low-level API provided by DdbRobot.
Provides widgets to invoke anaddb and visualize the results.
"""
# Buttons
plot_combiplot_btn = pnw.Button(name="Compute", button_type='primary')
combiplot_check_btn = pnw.CheckButtonGroup(name='Check Button Group',
@ -408,36 +411,34 @@ class DdbRobotPanel(HasAnaddbParams):
return kwargs
@param.depends('plot_combiplot_btn.clicks')
def plot_combiplot(self):
@depends_on_btn_click('plot_combiplot_btn')
def plot_combiplot(self, **kwargs):
"""Plot phonon band structures."""
if self.plot_combiplot_btn.clicks == 0: return
kwargs = self.kwargs_for_anaget_phbst_and_phdos_files()
with ButtonContext(self.plot_combiplot_btn):
#TODO: Recheck lo-to automatic.
r = self.robot.anaget_phonon_plotters(**kwargs)
#r = self.robot.anaget_phonon_plotters()
#TODO: Recheck lo-to automatic.
r = self.robot.anaget_phonon_plotters(**kwargs)
#r = self.robot.anaget_phonon_plotters()
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
if "combiplot" in self.combiplot_check_btn.value:
ca("## Phonon band structure and DOS:")
ca(ply(r.phbands_plotter.combiplotly(units=self.units, show=False)))
# Fill column
col = pn.Column(sizing_mode='stretch_both'); ca = col.append
if "gridplot" in self.combiplot_check_btn.value:
ca("## Phonon band structure and DOS:")
ca(ply(r.phbands_plotter.gridplotly(units=self.units, show=False)))
if "combiplot" in self.combiplot_check_btn.value:
ca("## Combiplot:")
ca(ply(r.phbands_plotter.combiplotly(units=self.units, show=False)))
#if "temp_range" in self.combiplot_check_btn.value:
#temps = self.temp_range.value
#ca("## Thermodynamic properties in the harmonic approximation:")
##ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.fig_kwargs))
#ca(ply(phdos.plotly_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, show=False)))
if "gridplot" in self.combiplot_check_btn.value:
ca("## Gridplot:")
# FIXME implement with_dos = True
ca(ply(r.phbands_plotter.gridplotly(units=self.units, with_dos=False, show=False)))
#if "temp_range" in self.combiplot_check_btn.value:
#temps = self.temp_range.value
#ca("## Thermodynamic properties in the harmonic approximation:")
##ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.mpl_kwargs))
#ca(ply(phdos.plotly_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, show=False)))
return col
return col
#@param.depends('get_epsinf_btn.clicks')
#def get_epsinf(self):
@ -459,11 +460,11 @@ class DdbRobotPanel(HasAnaddbParams):
# eps0 = gen.tensor_at_frequency(w=0, gamma_ev=self.gamma_ev)
# ca(r"## $\epsilon^0$ in Cart. coords (computed with Gamma_eV):")
# ca(df(eps0.get_dataframe(cmode="real"), **df_kwargs))
# ca(dfc(eps0.get_dataframe(cmode="real"), **df_kwargs))
# ca(r"## $\epsilon^\infty$ in Cart. coords:")
# ca(df(epsinf.get_dataframe(), **df_kwargs))
# ca(dfc(epsinf.get_dataframe(), **df_kwargs))
# ca("## Born effective charges in Cart. coords:")
# ca(df(becs.get_voigt_dataframe(), **df_kwargs))
# ca(dfc(becs.get_voigt_dataframe(), **df_kwargs))
# ca("## Anaddb input file.")
# ca(pn.pane.HTML(inp._repr_html_()))
@ -478,13 +479,13 @@ class DdbRobotPanel(HasAnaddbParams):
# gen, inp = self.ddb.anaget_dielectric_tensor_generator(asr=self.asr, chneut=self.chneut, dipdip=self.dipdip,
# mpi_procs=self.mpi_procs, verbose=self.verbose,
# return_input=True)
# ws = self.w_range.value
# ws = self.w_range
# w_max = ws[1]
# if w_max == 1.0: w_max = None # Will compute w_max in plot routine from ph freqs.
# def p(component, reim):
# return gen.plot(w_min=ws[0], w_max=w_max, gamma_ev=self.gamma_ev, num=500, component=component,
# reim=reim, units=self.units, **self.fig_kwargs)
# reim=reim, units=self.units, **self.mpl_kwargs)
# # Build grid
# gspec = pn.GridSpec(sizing_mode='scale_width')
@ -519,14 +520,14 @@ class DdbRobotPanel(HasAnaddbParams):
# ca("## Phonon band structure and DOS:")
# ca(ply(phbands.plotly_with_phdos(phdos, units=self.units, show=False)))
# #ca(mpl(phbands.plot_with_phdos(phdos, units=self.units, **self.fig_kwargs)))
# #ca(mpl(phdos_file.plot_pjdos_type(units=self.units, exchange_xy=True, **self.fig_kwargs)))
# #ca(mpl(phdos_file.msqd_dos.plot(units=self.units, **self.fig_kwargs)))
# #ca(mpl(phbands.plot_with_phdos(phdos, units=self.units, **self.mpl_kwargs)))
# #ca(mpl(phdos_file.plot_pjdos_type(units=self.units, exchange_xy=True, **self.mpl_kwargs)))
# #ca(mpl(phdos_file.msqd_dos.plot(units=self.units, **self.mpl_kwargs)))
# temps = self.temp_range.value
# ca("## Thermodynamic properties in the harmonic approximation:")
# #ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.fig_kwargs))
# #ca(phdos.plot_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, **self.mpl_kwargs))
# ca(ply(phdos.plotly_harmonic_thermo(tstart=temps[0], tstop=temps[1], num=50, show=False)))
# #msqd_dos.plot_tensor(**self.fig_kwargs)
# #msqd_dos.plot_tensor(**self.mpl_kwargs)
# #self.plot_phbands_btn.button_type = "primary"
# # Add HTML pane with input
@ -552,148 +553,126 @@ class DdbRobotPanel(HasAnaddbParams):
# # Insert results in grid.
# gspec = pn.GridSpec(sizing_mode='scale_width')
# gspec[0, :1] = sv.get_dataframe()
# gspec[1, :1] = sv.plot(**self.fig_kwargs)
# gspec[1, :1] = sv.plot(**self.mpl_kwargs)
# return gspec
# THIS OK but I don't think it's very useful
@param.depends('plot_check_asr_dipdip_btn.clicks')
@depends_on_btn_click('plot_check_asr_dipdip_btn')
def plot_without_asr_dipdip(self):
"""
Compare phonon bands and DOSes computed with/without the acoustic sum rule
and the treatment of the dipole-dipole interaction in the dynamical matrix.
Requires DDB file with eps_inf, BECS.
"""
if self.plot_check_asr_dipdip_btn.clicks == 0: return
asr_plotter = PhononBandsPlotter()
dipdip_plotter = PhononBandsPlotter()
with ButtonContext(self.plot_check_asr_dipdip_btn):
for label, ddb in self.robot.items():
asr_p = ddb.anacompare_asr(asr_list=(0, 2), chneut_list=(1, ), dipdip=1,
lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs,
pre_label=label)
for label, ddb in self.robot.items():
asr_p = ddb.anacompare_asr(asr_list=(0, 2), chneut_list=(1, ), dipdip=1,
lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs,
pre_label=label)
asr_plotter.append_plotter(asr_p)
asr_plotter.append_plotter(asr_p)
dipdip_p = ddb.anacompare_dipdip(chneut_list=(1,), asr=2, lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs,
pre_label=label)
dipdip_p = ddb.anacompare_dipdip(chneut_list=(1,), asr=2, lo_to_splitting=self.lo_to_splitting,
nqsmall=self.nqsmall, ndivsm=self.ndivsm,
dos_method=self.dos_method, ngqpt=None,
verbose=self.verbose, mpi_procs=self.mpi_procs,
pre_label=label)
dipdip_plotter.append_plotter(dipdip_p)
dipdip_plotter.append_plotter(dipdip_p)
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
# Fill column
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
ca("## Phonon bands and DOS with/wo acoustic sum rule:")
ca(ply(asr_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/without the treatment of the dipole-dipole interaction:")
ca(ply(dipdip_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/wo acoustic sum rule:")
ca(ply(asr_plotter.combiplotly(show=False)))
ca("## Phonon bands and DOS with/without the treatment of the dipole-dipole interaction:")
ca(ply(dipdip_plotter.combiplotly(show=False)))
return col
return col
def get_panel(self):
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the DDB file."""
robot = self.robot
def info(method_name):
# Add accordion after the button with warning and help taken from the docstring of the callback
col = pn.Column(); ca = col.append
ca(pn.pane.Alert("Refresh the page if the plotly figure is not shown.", alert_type="danger"))
acc = pn.Accordion(("Help", pn.pane.Markdown(getattr(self, method_name).__doc__)))
acc.append(("Warning", self.warning_md))
ca(pn.layout.Divider())
ca(acc)
return col
d["Summary"] = pn.Row(
bkw.PreText(text=robot.to_string(verbose=self.verbose), sizing_mode="scale_both")
)
tabs = pn.Tabs(); app = tabs.append
d["Params"] = self.get_compare_params_widgets()
app(("Summary", pn.Row(
bkw.PreText(text=robot.to_string(verbose=self.verbose), sizing_mode="scale_both"))
))
dfs = robot.get_structure_dataframes()
app(("Structures",
pn.Column("# Lattice daframe", self._df(dfs.lattice),
"# Atomic positions", self._df(dfs.coords),)
))
app(("Combiplot", pn.Row(
d["Combiplot"] = pn.Row(
pn.Column("# PH-bands options",
*self.pws("nqsmall", "ndivsm", "asr", "chneut", "dipdip",
"lo_to_splitting", "dos_method", "temp_range",
"combiplot_check_btn", "plot_combiplot_btn",
info("plot_combiplot")),
self.helpc("plot_combiplot")),
),
self.plot_combiplot)
))
self.plot_combiplot
)
#app(("PH-bands", pn.Row(
# pn.Column("# PH-bands options",
# *self.pws("nqsmall", "ndivsm", "asr", "chneut", "dipdip",
# "lo_to_splitting", "dos_method", "temp_range", "plot_phbands_btn",
# info("plot_phbands_and_phdos")),
# self.helpc("plot_phbands_and_phdos")),
# ),
# self.plot_phbands_and_phdos)
#))
#app(("BECs", pn.Row(
# pn.Column("# Born effective charges options",
# *self.pws("asr", "chneut", "dipdip", "gamma_ev", "get_epsinf_btn",
# info("get_epsinf")),
# self.helpc("get_epsinf")),
# ),
# self.get_epsinf)
#))
#app(("eps0", pn.Row(
# pn.Column("# epsilon_0",
# *self.pws("asr", "chneut", "dipdip", "gamma_ev", "w_range", "plot_eps0w_btn",
# info("plot_eps0w")),
# self.helpc("plot_eps0w")),
# ),
# self.plot_eps0w)
#))
#app(("Speed of sound", pn.Row(
# pn.Column("# Speed of sound options",
# *self.pws("asr", "chneut", "dipdip", "plot_vsound_btn",
# info("plot_vsound")),
# self.helpc("plot_vsound")),
# ),
# self.plot_vsound)
#))
app(("ASR & DIPDIP", pn.Row(
pn.Column("# ASR & DIPDIP options",
*self.pws("nqsmall", "ndivsm", "dos_method", "plot_check_asr_dipdip_btn",
info("plot_without_asr_dipdip")),
),
self.plot_without_asr_dipdip)
))
d["ASR & DIPDIP"] = pn.Row(
self.pws_col(["### ASR & DIPDIP options", "nqsmall", "ndivsm", "dos_method", "plot_check_asr_dipdip_btn",
self.helpc("plot_without_asr_dipdip")]),
self.plot_without_asr_dipdip
)
#app(("DOS vs q-mesh", pn.Row(
# pn.Column("# DOS vs q-mesh options",
# *self.pws("asr", "chneut", "dipdip", "dos_method", "nqsmall_list", "plot_dos_vs_qmesh_btn",
# info("plot_dos_vs_qmesh")),
# self.helpc("plot_dos_vs_qmesh")),
# ),
# self.plot_dos_vs_qmesh)
#))
#app(("Quadrupoles", pn.Row(
# pn.Column("# Quadrupoles options",
# *self.pws("asr", "chneut", "dipdip", "lo_to_splitting", "ndivsm", "dos_method", "plot_phbands_quad_btn",
# info("plot_phbands_quad")),
# self.helpc("plot_phbands_quad")),
# ),
# self.plot_phbands_quad)
#))
#app(("IFCs", pn.Row(
# pn.Column("# IFCs options",
# *self.pws("asr", "dipdip", "chneut", "plot_ifc_btn",
# info("plot_ifc")),
# self.helpc("plot_ifc")),
# ),
# self.plot_ifc)
#))
app(("Global options",
pn.Column("# Global options",
*self.pws("units", "mpi_procs", "verbose"),
)))
d["Global"] = pn.pws_col(["### Global options", "units", "mpi_procs", "verbose"])
return tabs
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -5,7 +5,7 @@ import panel as pn
#import panel.widgets as pnw
import bokeh.models.widgets as bkw
from .core import PanelWithElectronBands #, PanelWithEbandsRobot
from .core import PanelWithElectronBands, ply, mpl, dfc #, PanelWithEbandsRobot
class FatBandsFilePanel(PanelWithElectronBands):
@ -13,32 +13,37 @@ class FatBandsFilePanel(PanelWithElectronBands):
Panel with widgets to interact with a |FatBandsFile|.
"""
def __init__(self, ncfile, **params):
self._ncfile = ncfile
super().__init__(**params)
self.ncfile = ncfile
@property
def ncfile(self):
return self._ncfile
@property
def ebands(self):
"""|ElectronBands|."""
return self.ncfile.ebands
return self._ncfile.ebands
def get_panel(self):
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the FATBANDS.nc 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)))
d = {}
d["Summary"] = pn.Row(bkw.PreText(text=self.ncfile.to_string(verbose=self.verbose), sizing_mode="scale_both"))
d["e-Bands"] = pn.Row(self.get_plot_ebands_widgets(), self.on_plot_ebands_btn)
if self.ncfile.ebands.kpoints.is_ibz:
# Add DOS tab only if k-sampling.
app(("e-DOS", pn.Row(self.get_plot_edos_widgets(), self.on_plot_edos_btn)))
# Add DOS tab but only if k-sampling.
d["e-DOS"] = pn.Row(self.get_plot_edos_widgets(), self.on_plot_edos_btn)
# Plot the L-PJDOS grouped by atomic type.
#self.ncfile.plot_pjdos_typeview(lmax=lmax, **self.fig_kwargs)
#self.ncfile.plot_pjdos_typeview(lmax=lmax, **self.mpl_kwargs)
# Plot the L-PJDOS grouped by L.
#self.ncfile.plot_pjdos_lview(lmax=lmax, **self.fig_kwargs)
#self.ncfile.plot_pjdos_lview(lmax=lmax, **self.mpl_kwargs)
# Fermi surface requires a gamma-centered k-mesh
if self.ncfile.ebands.supports_fermi_surface:
app(("Fermi Surface", pn.Row(self.get_plot_fermi_surface_widgets(), self.on_plot_fermi_surface_btn)))
d["Fermi Surface"] = pn.Row(self.get_plot_fermi_surface_widgets(), self.on_plot_fermi_surface_btn)
elif self.ncfile.ebands.kpoints.is_path:
# NC files have contributions up to L=4 (g channel)
@ -47,14 +52,16 @@ class FatBandsFilePanel(PanelWithElectronBands):
lmax = 2
# Plot the electronic fatbands grouped by atomic type.
#self.ncfile.plot_fatbands_typeview(lmax=lmax, **self.fig_kwargs)
#self.ncfile.plot_fatbands_typeview(lmax=lmax, **self.mpl_kwargs)
# Plot the electronic fatbands grouped by L.
#self.ncfile.plot_fatbands_lview(lmax=lmax, **self.fig_kwargs)
#self.ncfile.plot_fatbands_lview(lmax=lmax, **self.mpl_kwargs)
else:
raise ValueError("Neither a IBZ nor k-path!")
return tabs
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))
#class FatbandsRobotPanel(PanelWithEbandsRobot):
@ -72,7 +79,7 @@ class FatBandsFilePanel(PanelWithElectronBands):
# 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')
# return pn.Column(dfc(df), sizing_mode='stretch_width')
#
# def get_panel(self):
# """Return tabs with widgets to interact with the |GsrRobot|."""

View File

@ -1,77 +1,75 @@
""""Panels for AbiPy flows."""
""""Panels to interact with AbiPy flows."""
import param
import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from io import StringIO
from abipy.panels.core import AbipyParameterized
from abipy.panels.core import AbipyParameterized, mpl, ply, dfc, depends_on_btn_click
class FlowPanel(AbipyParameterized):
"""
Panel to interact with an AbiPy Flow.
"""
verbose = pn.widgets.IntSlider(start=0, end=10, step=1, value=0)
engine = pn.widgets.Select(value="fdp",
options=['dot', 'neato', 'twopi', 'circo', 'fdp', 'sfdp', 'patchwork', 'osage'])
dirtree = pn.widgets.Checkbox(name='Dirtree', value=False)
graphviz_btn = pn.widgets.Button(name="Show graph", button_type='primary')
status_btn = pn.widgets.Button(name="Show status", button_type='primary')
history_btn = pn.widgets.Button(name="Show history", button_type='primary')
debug_btn = pn.widgets.Button(name="Debug", button_type='primary')
events_btn = pn.widgets.Button(name="Events", button_type='primary')
corrections_btn = pn.widgets.Button(name="Corrections", button_type='primary')
handlers_btn = pn.widgets.Button(name="Handlers", button_type='primary')
vars_text = pn.widgets.TextInput(name='Abivars', placeholder='Enter list of variables separated by comma')
vars_btn = pn.widgets.Button(name="Show Variables", button_type='primary')
dims_btn = pn.widgets.Button(name="Show Dimensions", button_type='primary')
structures_btn = pn.widgets.Button(name="Show Structures", button_type='primary')
structures_io_checkbox = pn.widgets.CheckBoxGroup(
name='Input/Output Structure', value=['output'], options=['input', 'output'], inline=True)
# Widgets to plot ebands.
ebands_btn = pn.widgets.Button(name="Show Ebands", button_type='primary')
ebands_plotter_mode = pnw.Select(name="Plot Mode", value="gridplot",
options=["gridplot", "combiplot", "boxplot", "combiboxplot"]) # "animate",
ebands_plotter_btn = pnw.Button(name="Plot", button_type='primary')
ebands_df_checkbox = pnw.Checkbox(name='With Ebands DataFrame', value=False)
ebands_ksamp_checkbox = pn.widgets.CheckBoxGroup(
name='Input/Output Structure', value=["with_path", "with_ibz"], options=['with_path', 'with_ibz'], inline=True)
#TODO: Implement widget for selected_nids(flow, options),
#radio_group = pn.widgets.RadioButtonGroup(
# name='Radio Button Group', options=['Biology', 'Chemistry', 'Physics'], button_type='success')
def __init__(self, flow, **params):
super().__init__(**params)
self.flow = flow
@param.depends('status_btn.clicks')
self.engine = pnw.Select(value="fdp", options=['dot', 'neato', 'twopi', 'circo', 'fdp', 'sfdp', 'patchwork', 'osage'])
self.dirtree = pnw.Checkbox(name='Dirtree', value=False)
self.graphviz_btn = pnw.Button(name="Show graph", button_type='primary')
self.status_btn = pnw.Button(name="Show status", button_type='primary')
self.history_btn = pnw.Button(name="Show history", button_type='primary')
self.debug_btn = pnw.Button(name="Debug", button_type='primary')
self.events_btn = pnw.Button(name="Events", button_type='primary')
self.corrections_btn = pnw.Button(name="Corrections", button_type='primary')
self.handlers_btn = pnw.Button(name="Handlers", button_type='primary')
self.vars_text = pnw.TextInput(name='Abivars', placeholder='Enter list of variables separated by comma')
self.vars_btn = pnw.Button(name="Show Variables", button_type='primary')
self.dims_btn = pnw.Button(name="Show Dimensions", button_type='primary')
self.structures_btn = pnw.Button(name="Show Structures", button_type='primary')
self.structures_io_checkbox = pnw.CheckBoxGroup(
name='Input/Output Structure', value=['output'], options=['input', 'output'], inline=True)
# Widgets to plot ebands.
self.ebands_btn = pnw.Button(name="Show Ebands", button_type='primary')
self.ebands_plotter_mode = pnw.Select(name="Plot Mode", value="gridplot",
options=["gridplot", "combiplot", "boxplot", "combiboxplot"]) # "animate",
self.ebands_plotter_btn = pnw.Button(name="Plot", button_type='primary')
self.ebands_df_checkbox = pnw.Checkbox(name='With Ebands DataFrame', value=False)
self.ebands_ksamp_checkbox = pnw.CheckBoxGroup(
name='Input/Output Structure', value=["with_path", "with_ibz"], options=['with_path', 'with_ibz'], inline=True)
#TODO: Implement widget for selected_nids(flow, options),
#radio_group = pn.widgets.RadioButtonGroup(
# name='Radio Button Group', options=['Biology', 'Chemistry', 'Physics'], button_type='success')
super().__init__(**params)
@depends_on_btn_click("status_btn")
def on_status_btn(self):
if self.status_btn.clicks == 0: return
stream = StringIO()
self.flow.show_status(stream=stream, verbose=self.verbose.value)
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('history_btn.clicks')
@depends_on_btn_click("history_btn")
def on_history_btn(self):
if self.history_btn.clicks == 0: return
stream = StringIO()
#flow.show_history(status=options.task_status, nids=selected_nids(flow, options),
# full_history=options.full_history, metadata=options.metadata)
self.flow.show_history(stream=stream)
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('graphviz_btn.clicks')
@depends_on_btn_click("graphviz_btn")
def on_graphviz_btn(self):
"""
Visualize the flow with graphviz.
"""
if self.graphviz_btn.clicks == 0: return
node = self.flow
if self.dirtree.value:
graph = node.get_graphviz_dirtree(engine=self.engine.value)
@ -79,16 +77,15 @@ class FlowPanel(AbipyParameterized):
graph = node.get_graphviz(engine=self.engine.value)
return pn.Column(graph)
@param.depends('debug_btn.clicks')
@depends_on_btn_click("debug_btn")
def on_debug_btn(self):
if self.debug_btn.clicks == 0: return
#TODO https://github.com/ralphbean/ansi2html ?
stream = StringIO()
#flow.debug(status=options.task_status, nids=selected_nids(flow, options))
self.flow.debug(stream=stream)
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('events_btn.clicks')
@depends_on_btn_click("events_btn")
def on_events_btn(self):
if self.events_btn.clicks == 0: return
stream = StringIO()
@ -96,17 +93,15 @@ class FlowPanel(AbipyParameterized):
#flow.show_events(status=options.task_status, nids=selected_nids(flow, options))
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('corrections_btn.clicks')
@depends_on_btn_click("corrections_btn")
def on_corrections_btn(self):
if self.corrections_btn.clicks == 0: return
stream = StringIO()
self.flow.show_corrections(stream=stream)
#flow.show_corrections(status=options.task_status, nids=selected_nids(flow, options))
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('handlers_btn.clicks')
@depends_on_btn_click("handlers_btn_clicks")
def on_handlers_btn(self):
#if self.handlers_btn.clicks == 0: return
stream = StringIO()
#if options.doc:
# flowtk.autodoc_event_handlers()
@ -115,25 +110,22 @@ class FlowPanel(AbipyParameterized):
self.flow.show_event_handlers(verbose=self.verbose.value, stream=stream)
return pn.Row(bkw.PreText(text=stream.getvalue()))
@param.depends('vars_btn.clicks')
@depends_on_btn_click("vars_btn")
def on_vars_btn(self):
if self.vars_btn.clicks == 0: return
if not self.vars_text.value: return
varnames = [s.strip() for s in self.vars_text.value.split(",")]
df = self.flow.compare_abivars(varnames=varnames, # nids=selected_nids(flow, options),
printout=False, with_colors=False)
return pn.Row(self._df(df))
return pn.Row(dfc(df))
@param.depends('dims_btn.clicks')
@depends_on_btn_click("dims_btn")
def on_dims_btn(self):
if self.dims_btn.clicks == 0: return
df = self.flow.get_dims_dataframe(# nids=selected_nids(flow, options),
printout=False, with_colors=False)
return pn.Row(self._df(df), sizing_mode="scale_width")
return pn.Row(dfc(df), sizing_mode="scale_width")
@param.depends('structures_btn.clicks')
@depends_on_btn_click("structures_btn")
def on_structures_btn(self):
if self.structures_btn.clicks == 0: return
what = ""
if "input" in self.structures_io_checkbox.value: what += "i"
if "output" in self.structures_io_checkbox.value: what += "o"
@ -142,50 +134,54 @@ class FlowPanel(AbipyParameterized):
verbose=self.verbose.value, with_spglib=False, printout=False,
with_colors=False)
return pn.Row(self._df(dfs.lattice), sizing_mode="scale_width")
return pn.Row(dfc(dfs.lattice), sizing_mode="scale_width")
@param.depends('ebands_plotter_btn.clicks')
def on_ebands_btn(self):
if self.ebands_plotter_btn.clicks == 0: return
#@param.depends('ebands_plotter_btn.clicks')
#def on_ebands_btn(self):
# if self.ebands_plotter_btn.clicks == 0: return
df, ebands_plotter = self.flow.compare_ebands(
nids=None, # select_nids(flow, options),
with_path="with_path" in self.ebands_ksamp_checkbox.value,
with_ibz="with_ibz" in self.ebands_ksamp_checkbox.value,
verbose=self.verbose.value,
with_spglib=False
)
# df, ebands_plotter = self.flow.compare_ebands(
# nids=None, # select_nids(flow, options),
# with_path="with_path" in self.ebands_ksamp_checkbox.value,
# with_ibz="with_ibz" in self.ebands_ksamp_checkbox.value,
# verbose=self.verbose.value,
# with_spglib=False
# )
if ebands_plotter is None:
return
# if ebands_plotter is None:
# return
plot_mode = self.ebands_plotter_mode.value
plotfunc = getattr(ebands_plotter, plot_mode, None)
if plotfunc is None:
raise ValueError("Don't know how to handle plot_mode: %s" % plot_mode)
# plot_mode = self.ebands_plotter_mode.value
# plotfunc = getattr(ebands_plotter, plot_mode, None)
# if plotfunc is None:
# raise ValueError("Don't know how to handle plot_mode: %s" % plot_mode)
fig = plotfunc(**self.fig_kwargs)
col = pn.Column(self._mp(fig))
if self.ebands_df_checkbox.value:
col.append(self._df(df))
# fig = plotfunc(**self.mpl_kwargs)
# col = pn.Column(mpl(fig))
# if self.ebands_df_checkbox.value:
# col.append(dfc(df))
return pn.Row(col) #, sizing_mode='scale_width')
# return pn.Row(col) #, sizing_mode='scale_width')
def get_panel(self):
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the flow."""
tabs = pn.Tabs(); app = tabs.append
d = {}
#row = pn.Row(bkw.PreText(text=self.ddb.to_string(verbose=self.verbose.value), sizing_mode="scale_both"))
app(("Status", pn.Row(self.status_btn, self.on_status_btn)))
app(("History", pn.Row(self.history_btn, self.on_history_btn)))
app(("Events", pn.Row(self.events_btn, self.on_events_btn)))
app(("Corrections", pn.Row(self.corrections_btn, self.on_corrections_btn)))
app(("Handlers", pn.Row(self.handlers_btn, self.on_handlers_btn)))
app(("Structures", pn.Row(pn.Column(self.structures_io_checkbox, self.structures_btn), self.on_structures_btn)))
d["Status"] = pn.Row(self.status_btn, self.on_status_btn)
d["History"] = pn.Row(self.history_btn, self.on_history_btn)
d["Events"] = pn.Row(self.events_btn, self.on_events_btn)
d["Corrections"] = pn.Row(self.corrections_btn, self.on_corrections_btn)
d["Handlers"] = pn.Row(self.handlers_btn, self.on_handlers_btn)
d["Structures"] = pn.Row(pn.Column(self.structures_io_checkbox, self.structures_btn), self.on_structures_btn)
ws = pn.Column(self.ebands_plotter_mode, self.ebands_ksamp_checkbox, self.ebands_df_checkbox, self.ebands_plotter_btn)
app(("Ebands", pn.Row(ws, self.on_ebands_btn)))
app(("Abivars", pn.Row(pn.Column(self.vars_text, self.vars_btn), self.on_vars_btn)))
app(("Dims", pn.Row(pn.Column(self.dims_btn), self.on_dims_btn)))
app(("Debug", pn.Row(self.debug_btn, self.on_debug_btn)))
app(("Graphviz", pn.Row(pn.Column(self.engine, self.dirtree, self.graphviz_btn),
self.on_graphviz_btn)))
return tabs
#d["Ebands"] = pn.Row(ws, self.on_ebands_btn)
d["Abivars"] = pn.Row(pn.Column(self.vars_text, self.vars_btn), self.on_vars_btn)
d["Dims"] = pn.Row(pn.Column(self.dims_btn), self.on_dims_btn)
d["Debug"] = pn.Row(self.debug_btn, self.on_debug_btn)
d["Graphviz"] = pn.Row(pn.Column(self.engine, self.dirtree, self.graphviz_btn), self.on_graphviz_btn)
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None)

View File

@ -1,91 +1,116 @@
"""Panels to interact with GSR files."""
import param
import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from .core import PanelWithElectronBands, PanelWithEbandsRobot
from .core import (PanelWithElectronBands, HasStructureParams, PanelWithNcFile,
PanelWithEbandsRobot, ButtonContext, ply, mpl, dfc, depends_on_btn_click)
class GsrFilePanel(PanelWithElectronBands):
class GsrFilePanel(PanelWithElectronBands, HasStructureParams, PanelWithNcFile):
"""
Panel with widgets to interact with a |GsrFile|.
"""
def __init__(self, gsr, **params):
super().__init__(**params)
self.gsr = gsr
super().__init__(**params)
@property
def structure(self):
"""|Structure| object"""
return self.gsr.structure
@property
def ebands(self):
"""|ElectronBands|."""
"""|ElectronBands| object."""
return self.gsr.ebands
def get_panel(self):
"""Return tabs with widgets to interact with the DDB file."""
@property
def ncfile(self):
"""This for for the PanelWithNcFile mixin"""
return self.gsr
#def info(method_name):
# # Add accordion after the button with warning and help taken from the docstring of the callback
# col = pn.Column(); ca = col.append
# acc = pn.Accordion(("Help", pn.pane.Markdown(getattr(self, method_name).__doc__)))
# acc.append(("Warning", self.warning_md))
# ca(pn.layout.Divider())
# ca(acc)
# return col
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the GSR file."""
d = {}
tabs = pn.Tabs(); app = tabs.append
app(("Summary",
pn.Row(bkw.PreText(text=self.gsr.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.
d["Summary"] = pn.Row(
bkw.PreText(text=self.gsr.to_string(verbose=self.verbose), sizing_mode="scale_both")
)
d["e-Bands"] = pn.Row(
pn.Column("### e-Bands Options", self.get_plot_ebands_widgets(), self.helpc("on_plot_ebands_btn"),
self.on_plot_ebands_btn),
)
# Add DOS tab but only if k-sampling.
kpoints = self.gsr.ebands.kpoints
if kpoints.is_ibz:
app(("e-DOS", pn.Row(
self.get_plot_edos_widgets(), self.on_plot_edos_btn)
))
d["e-DOS"] = pn.Row(
pn.Column("### Options", self.get_plot_edos_widgets(), self.helpc("on_plot_edos_btn")),
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)))
d["SKW"] = self.get_plot_skw_widgets()
return tabs
#if self.gsr.ebands.supports_fermi_surface:
# # Fermi surface requires Gamma-centered k-mesh
# app(("Fermi Surface", pn.Row(
# pn.Column("# Options",
# self.get_plot_fermi_surface_widgets(),
# self.helpc("on_plot_fermi_surface_btn"),
# ),
# self.on_plot_fermi_surface_btn)
# ))
d["Structure"] = self.get_struct_view_tab_entry()
# TODO
#app(("NcFile", self.get_ncfile_panel()))
#app(("Global", pn.Row(
# pn.Column("# Global options",
# *self.pws("units", "mpi_procs", "verbose"),
# ),
# self.get_software_stack())
#))
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))
class GsrRobotPanel(PanelWithEbandsRobot):
"""
A Panel to interoperate with multiple GSR files.
"""
gsr_dataframe_btn = pnw.Button(name="Compute", button_type='primary')
transpose_gsr_dataframe = pnw.Checkbox(name='Transpose GSR dataframe')
def __init__(self, robot, **params):
super().__init__(**params)
self.robot = robot
@param.depends("gsr_dataframe_btn.clicks")
@depends_on_btn_click('gsr_dataframe_btn_btn')
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')
transpose = self.transpose_gsr_dataframe.value
return pn.Column(dfc(df, transpose=transpose), sizing_mode='stretch_width')
def get_panel(self):
def get_panel(self, **kwargs):
"""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)))
d = {}
d["Summary"] = pn.Row(bkw.PreText(text=self.robot.to_string(verbose=self.verbose),
sizing_mode="scale_both"))
d["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.
# Add e-DOS tab but 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)))
d["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)))
d["GSR-dataframe"] = pn.Row(
pn.Column(self.transpose_gsr_dataframe, self.gsr_dataframe_btn),
self.on_gsr_dataframe_btn)
return tabs
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -1,9 +1,10 @@
""""Panels for HIST files."""
import param
import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from abipy.panels.core import sizing_mode_select, AbipyParameterized, ButtonContext
from abipy.panels.core import AbipyParameterized, ButtonContext, mpl, ply, depends_on_btn_click
_what_list = ["pressure", "forces", "energy", "abc", "angles", "volume"]
@ -13,60 +14,57 @@ class HistFilePanel(AbipyParameterized):
"""
Panel with widgets to interact with a |HistFile|.
"""
what_list = pn.widgets.CheckBoxGroup(name="Select", value=_what_list, options=_what_list, inline=False)
plot_relax_btn = pn.widgets.Button(name="Show relaxation", button_type="primary")
sizing_mode = sizing_mode_select(value="stretch_width")
appname = pn.widgets.Select(name="Viewer", value="ovito", options=["ovito", "mayavi", "vtk"])
to_unit_cell = pn.widgets.Checkbox(name="To unit cell")
view_relax_btn = pn.widgets.Button(name="View relaxation", button_type="primary")
def __init__(self, hist, **params):
super().__init__(**params)
self.hist = hist
self.what_list = pnw.CheckBoxGroup(name="Select", value=_what_list, options=_what_list, inline=False)
self.plot_relax_btn = pnw.Button(name="Show relaxation", button_type="primary")
self.appname = pnw.Select(name="Viewer", value="ovito", options=["ovito", "mayavi", "vtk"])
self.to_unit_cell = pnw.Checkbox(name="To unit cell")
self.view_relax_btn = pnw.Button(name="View relaxation", button_type="primary")
super().__init__(**params)
def get_plot_relax_widgets(self):
"""Widgets to visualize the structure relaxation."""
return pn.Column(self.what_list, self.sizing_mode, self.plot_relax_btn)
return pn.Column(self.what_list, self.plot_relax_btn, sizing_mode="stretch_width")
@param.depends('plot_relax_btn.clicks')
@depends_on_btn_click('plot_relax_btn')
def on_plot_relax_btn(self):
"""
Plot the evolution of structural parameters (lattice lengths, angles and volume)
as well as pressure, info on forces and total energy.
"""
if self.plot_relax_btn.clicks == 0: return
num_plots, nrows, ncols = len(self.what_list.value), 1, 1
if num_plots > 1:
ncols = 2
nrows = (num_plots // ncols) + (num_plots % ncols)
with ButtonContext(self.plot_relax_btn):
box = pn.GridBox(nrows=nrows, ncols=ncols, sizing_mode=self.sizing_mode.value) #'scale_width')
for i, what in enumerate(self.what_list.value):
irow, icol = divmod(i, ncols)
box.append(mpl(self.hist.plot(what, title=what, **self.mpl_kwargs)))
num_plots, nrows, ncols = len(self.what_list.value), 1, 1
if num_plots > 1:
ncols = 2
nrows = (num_plots // ncols) + (num_plots % ncols)
return box
#return pn.Column(box, box.controls(jslink=True))
box = pn.GridBox(nrows=nrows, ncols=ncols, sizing_mode=self.sizing_mode.value) #'scale_width')
for i, what in enumerate(self.what_list.value):
irow, icol = divmod(i, ncols)
box.append(self._mp(self.hist.plot(what, title=what, **self.fig_kwargs)))
return box
#return pn.Column(box, box.controls(jslink=True))
@param.depends('view_relax_btn.clicks')
@depends_on_btn_click('view_relax_btn')
def on_view_relax_btn(self):
if self.view_relax_btn.clicks == 0: return
return self.hist.visualize(appname=self.appname.value, to_unit_cell=self.to_unit_cell.value)
with ButtonContext(self.view_relax_btn):
return self.hist.visualize(appname=self.appname.value, to_unit_cell=self.to_unit_cell.value)
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the HIST.nc file."""
d = {}
def get_panel(self):
"""Return tabs with widgets to interact with the DDB file."""
tabs = pn.Tabs()
tabs.append(("Summary", pn.Row(bkw.PreText(text=self.hist.to_string(verbose=self.verbose),
sizing_mode="scale_both"))))
tabs.append(("Relaxation", pn.Row(self.get_plot_relax_widgets(), self.on_plot_relax_btn)))
tabs.append(("Visualize", pn.Row(pn.Column(self.appname, self.to_unit_cell, self.view_relax_btn),
self.on_view_relax_btn)))
d["Summary"] = pn.Row(bkw.PreText(text=self.hist.to_string(verbose=self.verbose),
sizing_mode="scale_both"))
return tabs
d["Relaxation"] = pn.Row(self.get_plot_relax_widgets(), self.on_plot_relax_btn)
d["Visualize"] = pn.Row(pn.Column(self.appname, self.to_unit_cell, self.view_relax_btn),
self.on_view_relax_btn)
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -4,12 +4,12 @@
import panel as pn
#import panel.widgets as pnw
import bokeh.models.widgets as bkw
from abipy.panels.core import AbipyParameterized #, ButtonContext
from abipy.panels.core import AbipyParameterized, mpl, ply, dfc #, ButtonContext
class AbinitOutputFilePanel(AbipyParameterized):
"""
Panel with widgets to interact with an Abinit output file.
Panel with widgets to interact with the Abinit main output file.
"""
def __init__(self, outfile, **params):
super().__init__(**params)
@ -33,28 +33,31 @@ class AbinitOutputFilePanel(AbipyParameterized):
box = pn.GridBox(nrows=nrows, ncols=ncols) #, sizing_mode='scale_both')
for icycle, cycle in enumerate(cycles):
box.append(self._mp(cycle.plot(title="%s cycle #%d" % (what, icycle), **self.fig_kwargs)))
box.append(mpl(cycle.plot(title="%s cycle #%d" % (what, icycle), **self.mpl_kwargs)))
return box
def get_panel(self):
def get_panel(self, as_dict=True, **kwargs):
"""Return tabs with widgets to interact with the Abinit output file."""
tabs = pn.Tabs(); app = tabs.append
d = {}
app(("Summary", pn.Row(
bkw.PreText(text=self.outfile.to_string(verbose=self.verbose), sizing_mode="scale_both"))
))
d["Summary"] = pn.Row(
bkw.PreText(text=self.outfile.to_string(verbose=self.verbose), sizing_mode="scale_both")
)
df = self.outfile.get_dims_spginfo_dataframe().transpose()
df.index.name = "Dataset"
app(("Dims", self._df(df)))
d["Dims"] = dfc(df)
# Add tabs with plots for the GS/DFPT SCF cycles.
for what in ("GS", "DFPT"):
box = self._get_gridbox(what)
if box is not None:
app(("%s cycles" % what, box))
d["%s cycles" % what] = box
#timer = self.get_timer()
#timer.plot_all(**self.fig_kwargs)
#timer.plot_all(**self.mpl_kwargs)
return tabs
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -3,41 +3,41 @@ import param
import panel as pn
import panel.widgets as pnw
from abipy.panels.core import AbipyParameterized, ButtonContext
from abipy.panels.core import AbipyParameterized, ButtonContext, mpl, ply, dfc, depends_on_btn_click
class PhononBandsPlotterPanel(AbipyParameterized):
phbands_plotter_mode = pnw.Select(name="Plot Mode", value="gridplot",
options=["gridplot", "combiplot", "boxplot", "combiboxplot"]) # "animate",
phbands_plotter_units = pnw.Select(name="Units", value="eV",
options=["eV", "meV", "Ha", "cm-1", "Thz"])
phbands_plotter_btn = pnw.Button(name="Plot", button_type='primary')
def __init__(self, plotter, **params):
super().__init__(**params)
self.phbands_plotter_mode = pnw.Select(name="Plot Mode", value="gridplot",
options=["gridplot", "combiplot", "boxplot", "combiboxplot"]) # "animate",
self.phbands_plotter_units = pnw.Select(name="Units", value="eV",
options=["eV", "meV", "Ha", "cm-1", "Thz"])
self.phbands_plotter_btn = pnw.Button(name="Plot", button_type='primary')
self.plotter = plotter
super().__init__(**params)
@param.depends("phbands_plotter_btn.clicks")
@depends_on_btn_click('phbands_blotter_btn')
def on_phbands_plot_btn(self):
if self.phbands_plotter_btn.clicks == 0: return
plot_mode = self.phbands_plotter_mode.value
plotfunc = getattr(self.plotter, plot_mode, None)
if plotfunc is None:
raise ValueError("Don't know how to handle plot_mode: %s" % plot_mode)
with ButtonContext(self.phbands_plotter_btn):
fig = plotfunc(units=self.phbands_plotter_units.value, **self.mpl_kwargs)
df = self.plotter.get_phbands_frame(with_spglib=True)
return pn.Row(pn.Column(mpl(fig), dfc(df)), sizing_mode='scale_width')
plot_mode = self.phbands_plotter_mode.value
plotfunc = getattr(self.plotter, plot_mode, None)
if plotfunc is None:
raise ValueError("Don't know how to handle plot_mode: %s" % plot_mode)
fig = plotfunc(units=self.phbands_plotter_units.value, **self.fig_kwargs)
df = self.plotter.get_phbands_frame(with_spglib=True)
return pn.Row(pn.Column(self._mp(fig), self._df(df)), sizing_mode='scale_width')
def get_panel(self):
def get_panel(self, as_dict=False, **kwargs):
"""Return tabs with widgets to interact with the |PhononBandsPlotter|."""
tabs = pn.Tabs() #; app = tabs.append
d = {}
ws = pn.Column(self.phbands_plotter_mode, self.phbands_plotter_units, self.phbands_plotter_btn)
tabs.append(("PhbandsPlotter", pn.Row(ws, self.on_phbands_plot_btn, sizing_mode='scale_width')))
d["PhbandsPlotter"] = pn.Row(ws, self.on_phbands_plot_btn, sizing_mode='scale_width')
return tabs
if as_dict: return d
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -14,7 +14,7 @@ from abipy.abilab import abiopen, extcls_supporting_panel
class UploadFile(param.Parameterized):
file_input = pn.widgets.FileInput()
file_input = pnw.FileInput()
abipath = param.Filename()
#ready = param.Boolean(default=False, precedence=-1)
@ -112,7 +112,7 @@ def analyze_file_app(**kwargs):
#class UploadFiles(param.Parameterized):
#
# file_inputs = pn.widgets.FileInput(multiple=True)
# file_inputs = pnw.FileInput(multiple=True)
#
# #abipath = param.Filename()
#

View File

@ -5,295 +5,159 @@ import panel as pn
import panel.widgets as pnw
import bokeh.models.widgets as bkw
from abipy.panels.core import AbipyParameterized, gen_id, ButtonContext
pn.config.js_files.update({
#'$': 'https://code.jquery.com/jquery-3.4.1.slim.min.js',
"clipboard": "https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js",
})
#pn.config.js_files["ngl"] = "https://cdn.jsdelivr.net/gh/arose/ngl@v2.0.0-dev.33/dist/ngl.js"
from abipy.panels.core import HasStructureParams, ButtonContext, dfc, mpl, ply, depends_on_btn_click
_html_with_copy_to_clipboard_ncalls = 0
def html_with_copy_to_clipboard(html, btn_cls="btn-primary btn-sm"):
global _html_with_copy_to_clipboard_ncalls
_html_with_copy_to_clipboard_ncalls += 1
myid = gen_id()
html = f"""
<button class="btn {btn_cls}" data-clipboard-target="#{myid}"> Copy to clipboard </button>
<div id="{myid}"> {html} </div>
"""
if _html_with_copy_to_clipboard_ncalls == 1:
html += "<script>$(document).ready(function () {new ClipboardJS('.btn')})</script>"
return pn.pane.HTML(html)
class StructurePanel(AbipyParameterized):
class StructurePanel(HasStructureParams):
"""
Panel with widgets to interact with an AbiPy Structure
"""
verbose = param.Integer(0, bounds=(0, None), doc="Verbosity Level")
# Convert widgets.
output_format = pnw.Select(name="format", value="abinit",
options="abinit,cif,xsf,poscar,qe,siesta,wannier90,cssr,json".split(","))
# Spglib widgets
spglib_symprec = pnw.Spinner(name="symprec", value=0.01, start=0.0, end=None, step=0.01)
spglib_angtol = pnw.Spinner(name="angtol", value=5, start=0.0, end=None, step=1)
# K-path widgets
kpath_format = pnw.Select(name="format", value="abinit", options=["abinit", "siesta", "wannier90"])
line_density = pnw.Spinner(name="line density", value=10, step=5, start=0, end=None)
# Viewer widgets.
viewer_btn = pnw.Button(name="View structure", button_type='primary')
viewer = pnw.Select(name="Viewer", value="vesta",
options=["jsmol", "vesta", "xcrysden", "vtk", "crystalk", "ngl", "matplotlib", "mayavi"])
# Mp-match
mp_match_btn = pnw.Button(name="Connect to Materials Project", button_type='primary')
# Mp-search
#mp_search_btn = pnw.Button(name="Connect to Materials Project", button_type='primary')
#mp_api_key
# GS input generator widgets.
gs_input_btn = pnw.Button(name="Generate input", button_type='primary')
gs_type = pnw.Select(name="GS type", value="scf", options=["scf", "relax"])
kppra = pnw.Spinner(name="kppra", value=1000, step=1000, start=0, end=None)
label2mode = {
"unpolarized": 'unpolarized',
"polarized": 'polarized',
"anti-ferromagnetic": "afm",
"non-collinear with magnetism": "spinor",
"non-collinear, no magnetism": "spinor_nomag",
}
spin_mode = pnw.Select(name="SpinMode", value="unpolarized", options=list(label2mode.keys()))
def __init__(self, structure, **params):
super().__init__(**params)
self.structure = structure
self._structure = structure
@param.depends("output_format.value")
# Convert widgets.
self.output_format = pnw.Select(name="format", value="abinit",
options="abinit,cif,xsf,poscar,qe,siesta,wannier90,cssr,json".split(","))
# Spglib widgets
self.spglib_symprec = pnw.Spinner(name="symprec", value=0.01, start=0.0, end=None, step=0.01)
self.spglib_angtol = pnw.Spinner(name="angtol", value=5, start=0.0, end=None, step=1)
# K-path widgets
self.kpath_format = pnw.Select(name="format", value="abinit", options=["abinit", "siesta", "wannier90"])
self.line_density = pnw.Spinner(name="line density", value=10, step=5, start=0, end=None)
self.plot_kpath = pnw.Checkbox(name='Plot k-path', value=False)
# MP-match
self.mp_match_btn = pnw.Button(name="Connect to Materials Project", button_type='primary')
# MP-search
#mp_search_btn = pnw.Button(name="Connect to Materials Project", button_type='primary')
#mp_api_key
# GS input generator widgets.
self.gs_input_btn = pnw.Button(name="Generate input", button_type='primary')
self.gs_type = pnw.Select(name="GS type", value="scf", options=["scf", "relax"])
self.kppra = pnw.Spinner(name="kppra", value=1000, step=1000, start=0, end=None)
self.label2mode = {
"unpolarized": 'unpolarized',
"polarized": 'polarized',
"anti-ferromagnetic": "afm",
"non-collinear with magnetism": "spinor",
"non-collinear, no magnetism": "spinor_nomag",
}
self.spin_mode = pnw.Select(name="SpinMode", value="unpolarized", options=list(self.label2mode.keys()))
super().__init__(**params)
@property
def structure(self):
return self._structure
@pn.depends("output_format.value")
def convert(self):
"""Convert the input structure to one of the format selected by the user."""
return pn.Row(bkw.PreText(text=self.structure.convert(fmt=self.output_format.value)),
sizing_mode='stretch_width')
s = self.structure.convert(fmt=self.output_format.value)
return self.html_with_clipboard_btn(f"<pre> {s} </pre>")
@param.depends("spglib_symprec.value", "spglib_angtol.value")
@pn.depends("spglib_symprec.value", "spglib_angtol.value")
def spglib_summary(self):
"""Call spglib to find space group symmetries and Wyckoff positions."""
s = self.structure.spget_summary(symprec=self.spglib_symprec.value,
angle_tolerance=self.spglib_angtol.value)
return pn.Row(bkw.PreText(text=s, sizing_mode='stretch_width'))
@param.depends("kpath_format.value", "line_density.value")
@pn.depends("kpath_format.value", "line_density.value")
def get_kpath(self):
"""Generate high-symmetry k-path from input structure in ABINIT format.."""
"""Generate high-symmetry k-path from input structure in the ABINIT format."""
col = pn.Column(sizing_mode='stretch_width'); ca = col.append
s = self.structure.get_kpath_input_string(fmt=self.kpath_format.value,
line_density=self.line_density.value)
return pn.Row(bkw.PreText(text=s, sizing_mode='stretch_width'))
ca(self.html_with_clipboard_btn(f"<pre> {s} </pre>"))
@param.depends("viewer_btn.clicks")
def view(self):
"""Visualize input structure."""
if self.viewer_btn.clicks == 0: return
if self.plot_kpath.value:
ca("## Brillouin zone and **k**-path:")
kpath_pane = ply(self.structure.plotly_bz(pmg_path=True, show=False), with_divider=False)
df_kpts = dfc(self.structure.hsym_kpoints.get_highsym_datataframe(), with_divider=False)
ca(pn.Row(kpath_pane, df_kpts))
ca(pn.layout.Divider())
with ButtonContext(self.viewer_btn):
v = self.viewer.value
return col
if v == "jsmol":
pn.extension(comms='ipywidgets') #, js_files=js_files)
view = self.structure.get_jsmol_view()
from ipywidgets_bokeh import IPyWidget
view = IPyWidget(widget=view) #, width=800, height=300)
#import ipywidgets as ipw
from IPython.display import display
#display(view)
return pn.Row(display(view))
if v == "crystalk":
view = self.structure.get_crystaltk_view()
return pn.panel(view)
if v == "ngl":
js_files = {'ngl': 'https://cdn.jsdelivr.net/gh/arose/ngl@v2.0.0-dev.33/dist/ngl.js'}
pn.extension(comms='ipywidgets', js_files=js_files)
view = self.structure.get_ngl_view()
return pn.panel(view)
#pn.config.js_files["ngl"]="https://cdn.jsdelivr.net/gh/arose/ngl@v2.0.0-dev.33/dist/ngl.js"
#pn.extension()
html = """<div id="viewport" style="width:100%; height:100%;"></div>
<script>
stage = new NGL.Stage("viewport");
stage.loadFile("rcsb://1NKT.mmtf", {defaultRepresentation: true});
</script>"""
# html = """
# <script>
# document.addeventlistener("domcontentloaded", function () {
# var stage = new ngl.stage("viewport");
# stage.loadfile("rcsb://1crn", {defaultrepresentation: true});
# });
# </script>"""
# html = """
#<script>
#document.addeventlistener("domcontentloaded", function () {
# // create a `stage` object
# var stage = new NGL.Stage("viewport");
# // load a PDB structure and consume the returned `Promise`
# stage.loadFile("rcsb://1CRN").then(function (component) {
# // add a "cartoon" representation to the structure component
# component.addRepresentation("cartoon");
# // provide a "good" view of the structure
# component.autoView();
# });
#});
#</script>"""
ngl_pane = pn.pane.HTML(html, height=500, width=500)
return pn.Row(ngl_pane)
view = self.structure.get_ngl_view()
#return self.structure.crystaltoolkitview()
#import nglview as nv
#view = nv.demo(gui=False)
#from bokeh.models import ColumnDataSource
#from bokeh.io import show, curdoc
#from bokeh.models.widgets import Button, TextInput
#from bokeh.layouts import layout, widgetbox
#from jsmol_bokeh_extension import JSMol
#script_source = ColumnDataSource()
#info = dict(
# height="100%",
# width="100%",
# serverURL="https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php",
# use="HTML5",
# j2sPath="https://chemapps.stolaf.edu/jmol/jsmol/j2s",
# script=
# "background black;load https://chemapps.stolaf.edu/jmol/jsmol-2013-09-18/data/caffeine.mol",
#)
#applet = JSMol(
# width=600,
# height=600,
# script_source=script_source,
# info=info,
#)
#button = Button(label='Execute')
#inp_script = TextInput(value='background white;')
#def run_script():
# script_source.data['script'] = [inp_script.value]
#button.on_click(run_script)
#ly = layout([applet, widgetbox(button, inp_script)])
#show(ly)
#curdoc().add_root(ly)
#return pn.Row(applet)
return self.structure.visualize(appname=self.viewer.value)
@param.depends("gs_input_btn.clicks")
@depends_on_btn_click('gs_input_btn')
def on_gs_input_btn(self):
"""Generate minimalistic input file from the input file."""
if self.gs_input_btn.clicks == 0: return
"""Generate minimalistic input file from the input structure."""
from abipy.abio.factories import gs_input
from abipy.data.hgh_pseudos import HGH_TABLE
with ButtonContext(self.gs_input_btn):
from abipy.abio.factories import gs_input
from abipy.data.hgh_pseudos import HGH_TABLE
gs_inp = gs_input(
self.structure, HGH_TABLE, kppa=self.kppra.value, ecut=8,
spin_mode=self.label2mode[self.spin_mode.value],
smearing=None)
gs_inp = gs_input(
self.structure, HGH_TABLE, kppa=self.kppra.value, ecut=8,
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",
pseudos='"pseudo1, pseudo2, ..."'
)
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)
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)
gs_inp.set_mnemonics(False)
return self.html_with_clipboard_btn(gs_inp._repr_html_())
return html_with_copy_to_clipboard(gs_inp._repr_html_())
@param.depends("mp_match_btn.clicks")
@depends_on_btn_click('mp_match_btn')
def on_mp_match_btn(self):
"""Match input structure with the structures available on the materials project."""
if self.mp_match_btn.clicks == 0: return
from abipy.core.structure import mp_match_structure
mp = mp_match_structure(self.structure, api_key=None, endpoint=None, final=True)
if not mp.structures:
raise RuntimeError("No structure found in the MP database")
with ButtonContext(self.mp_match_btn):
from abipy.core.structure import mp_match_structure
mp = mp_match_structure(self.structure, api_key=None, endpoint=None, final=True)
if not mp.structures:
raise RuntimeError("No structure found in MP database")
return pn.Column(dfc(mp.lattice_dataframe), sizing_mode='stretch_width')
return pn.Column(self._df(mp.lattice_dataframe), sizing_mode='stretch_width')
#@param.depends("mp_search_btn.clicks")
#@depends_on_btn_click('mp_search_btn')
#def on_mp_search_btn(self):
# if self.mp_search_btn.clicks == 0: return
# from abipy.core.structure import mp_search
# chemsys_formula_id = self.stucture.formula
# mp = mp_search(chemsys_formula_id, api_key=None, endpoint=None, final=True)
# if not mp.structures:
# raise RuntimeError("No structure found in MP database")
# return pn.Column(self._df(mp.lattice_dataframe), sizing_mode='stretch_width')
# return pn.Column(dfc(mp.lattice_dataframe), sizing_mode='stretch_width')
def get_panel(self):
def get_panel(self, as_dict=False, **kwargs):
"""Build panel with widgets to interact with the structure either in a notebook or in a bokeh app"""
d = {}
def info(method_name):
# Add accordion after the button with warning and help taken from the docstring of the callback
col = pn.Column(); ca = col.append
acc = pn.Accordion(("Help", pn.pane.Markdown(getattr(self, method_name).__doc__)))
ca(pn.layout.Divider())
ca(acc)
return col
d["Summary"] = pn.Row(bkw.PreText(text=self.structure.to_string(verbose=self.verbose),
sizing_mode="scale_both"))
d["Spglib"] = pn.Row(
self.pws_col(['### Spglib options', "spglib_symprec", "spglib_angtol", self.helpc("spglib_summary")]),
self.spglib_summary
)
d["Kpath"] = pn.Row(
self.pws_col(['### K-path options', "kpath_format", "line_density", "plot_kpath", self.helpc("get_kpath")]),
self.get_kpath
)
d["Convert"] = pn.Row(
self.pws_col(["### Convert structure", "output_format", self.helpc("convert")]),
self.convert
)
d["Structure"] = self.get_struct_view_tab_entry()
d["GS-input"] = pn.Row(
self.pws_col(['### Generate GS input', "gs_type", "spin_mode", "kppra", "gs_input_btn",
self.helpc("on_gs_input_btn")]),
self.on_gs_input_btn
)
d["MP-match"] = pn.Row(pn.Column(self.mp_match_btn), self.on_mp_match_btn)
tabs = pn.Tabs(); app = tabs.append
if as_dict: return d
app(("Summary",
pn.Row(bkw.PreText(text=self.structure.to_string(verbose=self.verbose), sizing_mode="scale_both"))
))
app(("Spglib", pn.Row(
pn.Column('# Spglib options', *self.pws("spglib_symprec", "spglib_angtol", info("spglib_summary"))),
self.spglib_summary)
))
app(("Kpath", pn.Row(
pn.Column('# K-path options', *self.pws("kpath_format", "line_density", info("get_kpath"))),
self.get_kpath)
))
app(("Convert", pn.Row(
pn.Column("# Convert structure", *self.pws("output_format", info("convert"))),
self.convert)
))
app(("View", pn.Row(
pn.Column("# Visualize structure", *self.pws("viewer", "viewer_btn", info("view"))),
self.view)
))
app(("GS-input", pn.Row(
pn.Column('# Generate GS input', *self.pws("gs_type", "spin_mode", "kppra", "gs_input_btn",
info("on_gs_input_btn"))),
self.on_gs_input_btn)
))
app(("MP-match", pn.Row(
pn.Column(self.mp_match_btn),
self.on_mp_match_btn)
))
return tabs
tabs = pn.Tabs(*d.items())
return self.get_template_from_tabs(tabs, template=kwargs.get("template", None))

View File

@ -377,7 +377,8 @@ def abicomp_phbands(options):
# Select the plot method to call.
if options.plot_mode == "panel":
plotter.get_panel().show()
#template=options.panel_template)
plotter.get_panel().show(debug=options.verbose > 0)
elif options.plot_mode != "None":
plotfunc = getattr(plotter, options.plot_mode, None)
@ -639,14 +640,11 @@ def _invoke_robot(options):
no_browser=options.no_browser)
elif options.panel:
try:
import panel # noqa: F401
except ImportError as exc:
cprint("Use `conda install panel` or `pip install panel` to install the python package.", "red")
raise exc
abilab.abipanel()
if hasattr(robot, "get_panel"):
robot.get_panel().show()
app = robot.get_panel(template=options.panel_template)
app.show(debug=options.verbose > 0)
return 0
else:
cprint(f"`{type(robot)} does not provide get_panel method", color="red")
@ -975,7 +973,7 @@ codes), a looser tolerance of 0.1 (the value used in Materials Project) is often
help="Used if --expose to iterate over figures. Expose all figures at once if not given on the CLI.")
expose_parser.add_argument("-t", "--slide-timeout", type=int, default=None,
help="Close figure after slide-timeout seconds (only if slide-mode). Block if not specified.")
expose_parser.add_argument("--plotly", default=False, action="store_true",
expose_parser.add_argument("-ply", "--plotly", default=False, action="store_true",
help='Generate plotly plots in browser instead of matplotlib. WARNING: Not all the features are supported.')
expose_parser.add_argument("-cs", "--chart-studio", default=False, action="store_true",
help="Push figure to plotly chart studio ." +
@ -1089,6 +1087,11 @@ the full set of atoms. Note that a value larger than 0.01 is considered to be un
robot_parser.add_argument('--no-walk', default=False, action="store_true", help="Don't enter subdirectories.")
robot_parser.add_argument("-pn", '--panel', default=False, action="store_true",
help="Open GUI in web browser, requires panel package. WARNING: Experimental")
robot_parser.add_argument("-pnt", "--panel-template", default="FastList", type=str,
help="Specify template for panel dasboard." +
"Possible values are: FastList, FastGrid, Golden, Bootstrap, Material, React, Vanilla." +
"Default: FastList"
)
robot_parents = [copts_parser, robot_ipy_parser, robot_parser, expose_parser, pandas_parser]
p_gsr = subparsers.add_parser('gsr', parents=robot_parents, help=abicomp_gsr.__doc__)

View File

@ -75,17 +75,20 @@ from abipy import abilab
def get_epilog():
s = """\
======================================================================================================
Usage example:
abiopen.py FILE => Open file in ipython shell.
abiopen.py FILE -nb => Generate jupyter notebook.
abiopen.py FILE -p => Print info on object to terminal.
abiopen.py FILE -e => Generate matplotlib figures automatically.
Use -sns to activate seaborn settings.
abiopen.py FILE -e --plotly => Generate plotly figures automatically. Show them in the BROWSER.
abiopen.py FILE -eweb => Generate matplotlib figures, show them in the $BROWSER.
abiopen.py FILE -ply => Generate plotly figures automatically. Show them in the $BROWSER.
Note that not all FILEs support plotly.
abiopen.py FILE --panel => Generate GUI in web BROWSER to interact with FILE
abiopen.py FILE -pn => Generate GUI in web BROWSER to interact with FILE
Requires panel package (WARNING: still under development!)
abiopen.py FILE -nb => Generate jupyter-lab notebook.
abiopen.py FILE -cnb => Generate classic jupyter notebook.
where `FILE` is any file supported by abipy/pymatgen e.g. Netcdf files, Abinit input, POSCAR, xsf.
File extensions supported (including zipped files with extension in ".bz2", ".gz", ".z"):
@ -94,6 +97,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` or `--notebook` or `--panel` to print the JSON dictionary without reconstructing python objects.
======================================================================================================
Table mapping file extension to AbiPy object:
@ -116,8 +120,8 @@ def get_parser(with_epilog=False):
# notebook options.
parser.add_argument('-nb', '--notebook', action='store_true', default=False, help="Open file in jupyter notebook")
parser.add_argument('--classic-notebook', action='store_true', default=False,
help="Use classic notebook instead of jupyterlab.")
parser.add_argument('--classic-notebook', "-cnb", action='store_true', default=False,
help="Use classic jupyter notebook instead of jupyterlab.")
parser.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"
@ -130,7 +134,13 @@ def get_parser(with_epilog=False):
# panel option
parser.add_argument("-pn", '--panel', action='store_true', default=False,
help="Open GUI in web browser, requires panel package.")
help="Open Dashboard in web browser, requires panel package.")
parser.add_argument("-pnt", "--panel-template", default="FastList", type=str,
help="Specify template for panel dasboard." +
"Possible values are: FastList, FastGrid, Golden, Bootstrap, Material, React, Vanilla." +
"Default: FastList"
)
# Expose option.
parser.add_argument('-e', '--expose', action='store_true', default=False,
@ -145,11 +155,10 @@ 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("--plotly", default=False, action="store_true",
help='Generate plotly plots in browser instead of matplotlib. WARNING: Not all the features are supported.')
parser.add_argument("-cs", "--chart-studio", default=False, action="store_true",
help="Push figure to plotly chart studio. " +
"Requires --plotly and user account at https://chart-studio.plotly.com.")
parser.add_argument("-ew", "--expose-web", default=False, action="store_true",
help='Generate matplotlib plots in $BROWSER instead of X-server. WARNING: Not all the features are supported.')
parser.add_argument("-ply", "--plotly", default=False, action="store_true",
help='Generate plotly plots in $BROWSER instead of matplotlib. WARNING: Not all the features are supported.')
return parser
@ -179,8 +188,12 @@ def main():
raise ValueError('Invalid log level: %s' % options.loglevel)
logging.basicConfig(level=numeric_level)
# Plotly automatically activate expose mode.
##############################################################################################
# Handle meta options i.e. options that set other options.
# OK, it's not very clean but I haven't find any parse API to express this kind of dependency.
##############################################################################################
if options.plotly: options.expose = True
if options.expose_web: options.expose = True
if options.classic_notebook: options.notebook = True
if options.verbose > 2: print(options)
@ -214,23 +227,24 @@ def main():
return 0
elif options.expose:
# Generate matplotlib plots automatically.
# Print info to terminal
if hasattr(abifile, "to_string"):
print(abifile.to_string(verbose=options.verbose))
else:
print(abifile)
# Generate plots automatically.
if options.plotly:
# plotly version
if hasattr(abifile, "plotly_expose"):
abifile.plotly_expose(chart_studio=options.chart_studio, verbose=options.verbose)
abifile.plotly_expose(verbose=options.verbose)
else:
cprint("`%s` does not implement plotly_expose method" % type(abifile), "red")
elif hasattr(abifile, "expose"):
# matplotlib version
abifile.expose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout,
verbose=options.verbose)
use_web=options.expose_web, verbose=options.verbose)
else:
if not hasattr(abifile, "yield_figs"):
raise TypeError("Object of type `%s` does not implement (expose or yield_figs methods" % type(abifile))
@ -242,25 +256,15 @@ def main():
return 0
elif options.panel:
try:
import panel as pn
except ImportError as exc:
cprint("Use `conda install panel` or `pip install panel` to install the python package.", "red")
raise exc
import matplotlib
matplotlib.use("Agg")
abilab.abipanel()
if not hasattr(abifile, "get_panel"):
raise TypeError("Object of type `%s` does not implement get_panel method" % type(abifile))
import matplotlib
matplotlib.use("Agg")
#pn.extension("katex")
try:
pn.extension("plotly")
except Exception:
cprint("Use `conda install plotly` or `pip install plotly` to install the plotly package.", "red")
abifile.get_panel().show() # threaded=True)
app = abifile.get_panel(template=options.panel_template)
app.show(debug=options.verbose > 0)
return 0
# Start ipython shell with namespace

View File

@ -1270,8 +1270,19 @@ def main():
else:
graph = node.get_graphviz(engine=options.engine)
if options.verbose:
# Print DOT string. Can be used with e.g. http://viz-js.com/
print(graph)
graph.view(directory=directory, cleanup=False)
if options.verbose > 1:
# Write graph to file in png format.
graph.format = "png"
graph.attr(dpi=str(300))
path = graph.render("graph", view=False, cleanup=False)
print("Saving png file to:", path)
elif options.command == "listext":
if not options.listexts:
print("\nPlease specify the file extension(s), e.g. GSR SIGRES.\nList of available extensions:\n")

View File

@ -346,6 +346,11 @@ closest points in this particular structure. This is usually what you want in a
p_panel = subparsers.add_parser('panel', parents=[copts_parser, path_selector],
help="Open GUI in web browser, requires panel package.")
p_panel.add_argument("-pnt", "--panel-template", default="FastList", type=str,
help="Specify template for panel dasboard." +
"Possible values are: FastList, FastGrid, Golden, Bootstrap, Material, React, Vanilla." +
"Default: FastList"
)
# Subparser for kpath.
p_kpath = subparsers.add_parser('kpath', parents=[copts_parser, path_selector],
@ -763,13 +768,9 @@ def main():
elif options.command == "panel":
structure = abilab.Structure.from_file(options.filepath)
try:
import panel # noqa: F401
except ImportError as exc:
cprint("Use `conda install panel` or `pip install panel` to install the python package.", "red")
raise exc
structure.get_panel().show() #threaded=True)
abilab.abipanel()
app = structure.get_panel(template=options.panel_template)
app.show(debug=options.verbose > 0)
return 0
elif options.command == "visualize":

View File

@ -11,7 +11,7 @@ from monty.functools import prof_main
from monty.termcolor import cprint
from abipy import abilab
from abipy.iotools.visualizer import Visualizer
from abipy.tools.plotting import MplExpose, GenericDataFilePlotter, plotlyfigs_to_browser, push_to_chart_studio
from abipy.tools.plotting import MplExpose, PanelExpose, GenericDataFilePlotter, plotlyfigs_to_browser, push_to_chart_studio
def handle_overwrite(path, options):
@ -124,10 +124,12 @@ def abiview_ebands(options):
elif options.bxsf:
outpath = options.filepath + ".bxsf"
abifile.ebands.to_bxsf(handle_overwrite(outpath, options))
#elif options.plotly:
# print(abifile.to_string(verbose=options.verbose))
else:
print(abifile.to_string(verbose=options.verbose))
abifile.expose_ebands(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout,
verbose=options.verbose)
expose_web=options.expose_web, verbose=options.verbose)
return 0
@ -195,6 +197,7 @@ asr: {asr}, chneut: {chneut}, dipdip: {dipdip}, lo_to_splitting: {lo_to_splittin
outpath = options.filepath + ".agr"
phbands.to_xmgrace(handle_overwrite(outpath, options))
return 0
#elif options.bxsf:
# outpath = options.filepath + ".bxsf"
# phbands.to_bxsf(handle_overwrite(outpath, options))
@ -204,34 +207,34 @@ asr: {asr}, chneut: {chneut}, dipdip: {dipdip}, lo_to_splitting: {lo_to_splittin
return phbands.view_phononwebsite(browser=options.browser, verbose=options.verbose)
elif options.plotly:
# plotly output
# Plotly + Panel version.
phdos = phdos_file.phdos
figs = []
e = figs.append
e(phbands.plotly_with_phdos(phdos, units=units, show=False))
e(phdos.plotly(units=units, show=False))
push_to_chart_studio(figs) if options.chart_studio else plotlyfigs_to_browser(figs)
with PanelExpose(title=f"Vibrational properties of {phdos_file.structure.formula}") as e:
e(phbands.qpoints.plotly(show=False))
e(phbands.plotly_with_phdos(phdos, units=units, show=False))
e(phbands.plot_colored_matched(units=units, show=False))
e(phbands.plot_fatbands(units=units, phdos_file=phdos_file, show=False))
e(phdos.plotly(units=units, show=False))
e(phdos_file.plotly_pjdos_type(units=units, show=False))
#push_to_chart_studio(figs) if options.chart_studio else plotlyfigs_to_browser(figs)
else:
# matplotlib output
phdos = phdos_file.phdos
with MplExpose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout) as e:
#e(phbands.expose())
#e(phdos.expose())
if not options.expose_web:
# matplotlib figure and X-server.
e = MplExpose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout)
else:
# panel version with matplotlib.
e = PanelExpose(title=f"Vibrational properties of {phdos_file.structure.formula}")
with e:
e(phbands.qpoints.plot(show=False))
e(phbands.plot_with_phdos(phdos, units=units, show=False))
e(phbands.plot_colored_matched(units=units, show=False))
e(phbands.plot_fatbands(units=units, phdos_file=phdos_file, show=False))
e(phdos.plot(units=units, show=False))
e(phdos_file.plot_pjdos_type(units=units, show=False))
#try
# msq_dos = phdos_file.msq_dos
#except RuntimeError:
# msq_dos = None
#if msq_dos is not None:
# e(msq_dos.plot_uiso(show=False))
# e(msq_dos.plot_uiso(show=False))
phbst_file.close()
phdos_file.close()
@ -257,7 +260,7 @@ num_points: {num_points}, asr: {asr}, chneut: {chneut}, dipdip: {dipdip}
df = sv.get_dataframe()
abilab.print_dataframe(df, title="Speed of sound for different directions:")
df_to_clipboard(options, df)
sv.plot()
sv.plotly if options.plotly else sv.plot()
return 0
@ -278,7 +281,10 @@ asr: {asr}, chneut: {chneut}, dipdip: {dipdip}
gamma_ev = 1e-4
print("Plotting dielectric tensor with constant phonon damping: %s (eV)" % gamma_ev)
tgen.plot_all(gamma_ev=gamma_ev)
if options.plotly:
tgen.plotly_all(gamma_ev=gamma_ev)
else:
tgen.plot_all(gamma_ev=gamma_ev)
return 0
@ -332,7 +338,7 @@ def abiview_ddb_quad(options):
title = ddb.structure.formula
renderer = "browser" if not options.chart_studio else "chart_studio"
plotter.combiplotly(renderer=renderee, title=title) if options.plotly else plotter.plot(title=title)
plotter.combiplotly(renderer=renderer, title=title) if options.plotly else plotter.plot(title=title)
return 0
@ -376,7 +382,15 @@ asr: {asr}, chneut: {chneut}, dipdip: {dipdip}
# Execute anaddb to compute the interatomic force constants.
ifc = ddb.anaget_ifc(asr=asr, chneut=chneut, dipdip=dipdip)
#print(ifc)
with MplExpose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout) as e:
if not options.expose_web:
# matplotlib figure and X-server.
e = MplExpose(slide_mode=options.slide_mode, slide_timeout=options.slide_timeout)
else:
# panel version
e = PanelExpose(title=f"Interatomic force constants of {phdos_file.structure.formula}")
with e:
e(ifc.plot_longitudinal_ifc(title="Longitudinal IFCs", show=False))
e(ifc.plot_longitudinal_ifc_short_range(title="Longitudinal IFCs short range", show=False))
e(ifc.plot_longitudinal_ifc_ewald(title="Longitudinal IFCs Ewald", show=False))
@ -537,6 +551,8 @@ def get_parser(with_epilog=False):
help="Iterate over figures. Expose all figures at once if not given on the CLI.")
slide_parser.add_argument("-t", "--slide-timeout", type=int, default=None,
help="Close figure after slide-timeout seconds (only if slide-mode). Block if not specified.")
slide_parser.add_argument("-ew", "--expose-web", default=False, action="store_true",
help='Generate matplotlib plots in $BROWSER instead of X-server. WARNING: Not all the features are supported.')
# Parent parser for commands that operating on pandas dataframes
pandas_parser = argparse.ArgumentParser(add_help=False)
@ -555,7 +571,7 @@ def get_parser(with_epilog=False):
# Parent parser for commands supporting plotly plots
plotly_parser = argparse.ArgumentParser(add_help=False)
plotly_parser.add_argument('--plotly', default=False, action="store_true",
plotly_parser.add_argument("-ply", '--plotly', default=False, action="store_true",
help='Generate plotly plots in browser instead of matplotlib.')
plotly_parser.add_argument("-cs", "--chart-studio", default=False, action="store_true",
help="Push figure to plotly chart studio. " +

View File

@ -28,7 +28,9 @@ class AbilabTest(AbipyTest):
abilab.print_dataframe(df, title="foo")
d = abilab.software_stack()
assert d
assert d and "pymatgen" in d
df = abilab.software_stack(as_dataframe=True)
assert df is not None
filepath = self.get_tmpname(text=True, suffix=".json")
data = {"foo": "bar"}

View File

@ -55,6 +55,12 @@ linestyles = OrderedDict(
# Matplotlib tools
###################
def is_mpl_figure(obj):
"""Return True if obj is a matplotlib Figure."""
from matplotlib import pyplot as plt
return isinstance(obj, plt.Figure)
def ax_append_title(ax, title, loc="center", fontsize=None):
"""Add title to previous ax.title. Return new title."""
prev_title = ax.get_title(loc=loc)
@ -456,6 +462,10 @@ class Marker(namedtuple("Marker", "x y s")):
class MplExpose(object): # pragma: no cover
"""
Context manager used to produce several matplotlib figures and then show
all them at the end so that the user does not need to close the window to
visualize to the next one.
Example:
with MplExpose() as e:
@ -465,8 +475,8 @@ class MplExpose(object): # pragma: no cover
def __init__(self, slide_mode=False, slide_timeout=None, verbose=1):
"""
Args:
slide_mode: If true, iterate over figures. Default: Expose all figures at once.
slide_timeout: Close figure after slide-timeout seconds Block if None.
slide_mode: If Rrue, iterate over figures. Default: Expose all figures at once.
slide_timeout: Close figure after slide-timeout seconds. Block if None.
verbose: verbosity level
"""
self.figures = []
@ -487,8 +497,8 @@ class MplExpose(object): # pragma: no cover
def __call__(self, obj):
"""
Add an object to MplExpose. Support mpl figure, list of figures or
generator yielding figures.
Add an object to MplExpose.
Support mpl figure, list of figures or generator yielding figures.
"""
import types
if isinstance(obj, (types.GeneratorType, list, tuple)):
@ -521,6 +531,7 @@ class MplExpose(object): # pragma: no cover
def __exit__(self, exc_type, exc_val, exc_tb):
"""Activated at the end of the with statement. """
if exc_type is not None: return
self.expose()
def expose(self):
@ -533,6 +544,91 @@ class MplExpose(object): # pragma: no cover
fig.clear()
class PanelExpose(object): # pragma: no cover
"""
Context manager used to produce several matplotlib/plotly figures and then show
all them inside the Browser using a panel template.
Example:
with PanelExpose() as e:
e(obj.plot1(show=False))
e(obj.plot2(show=False))
"""
def __init__(self, title=None, verbose=1):
"""
Args:
title: String to be show in the header.
verbose: verbosity level
"""
self.title = title
self.figures = []
self.verbose = verbose
if self.verbose:
print("\nLoading all figures before showing them. It may take some time...")
self.start_time = time.time()
def __call__(self, obj):
"""
Add an object to MplPanelExpose.
Support mpl figure, list of figures or generator yielding figures.
"""
import types
if isinstance(obj, (types.GeneratorType, list, tuple)):
for fig in obj:
self.add_fig(fig)
else:
self.add_fig(obj)
def add_fig(self, fig):
"""Add a matplotlib figure."""
if fig is None: return
self.figures.append(fig)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Activated at the end of the with statement. """
if exc_type is not None: return
self.expose()
def expose(self):
"""Show all figures. Clear figures if needed."""
import panel as pn
pn.config.sizing_mode = 'stretch_width'
from abipy.panels.core import get_template_cls_from_name
cls = get_template_cls_from_name("FastGridTemplate")
template = cls(
title=self.title if self.title is not None else self.__class__.__name__,
header_background="#ff8c00 ", # Dark orange
)
#pn.config.sizing_mode = 'stretch_width'
from abipy.panels.core import mpl, ply
for i, fig in enumerate(self.figures):
row, col = divmod(i, 2)
if is_plotly_figure(fig):
p = ply(fig, with_divider=False)
elif is_mpl_figure(fig):
p = mpl(fig, with_divider=False)
else:
raise TypeError(f"Don't know how to handle type: `{type(fig)}`")
if hasattr(template.main, "append"):
template.main.append(p)
else:
# Assume .main area acts like a GridSpec
row_slice = slice(3 * row, 3 * (row + 1))
if col == 0: template.main[row_slice, :6] = p
if col == 1: template.main[row_slice, 6:] = p
return template.show()
def plot_unit_cell(lattice, ax=None, **kwargs):
"""
Adds the unit cell of the lattice to a matplotlib Axes3D
@ -607,10 +703,10 @@ def ax_add_cartesian_frame(ax, start=(0, 0, 0)):
def plot_structure(structure, ax=None, to_unit_cell=False, alpha=0.7,
style="points+labels", color_scheme="VESTA", **kwargs):
"""
Plot structure with matplotlib (minimalistic version)
Plot structure with matplotlib (minimalistic version).
Args:
structure: Structure object
structure: |Structure| object
ax: matplotlib :class:`Axes3D` or None if a new figure should be created.
alpha: The alpha blending value, between 0 (transparent) and 1 (opaque)
to_unit_cell: True if sites should be wrapped into the first unit cell.
@ -857,6 +953,12 @@ class GenericDataFilesPlotter(object):
# Plotly helper functions
##########################
def is_plotly_figure(obj):
"""Return True if obj is a plotly Figure."""
import plotly.graph_objs as go
return isinstance(obj, go.Figure)
#return isinstance(obj, (go.Figure, go.FigureWidget))
class PlotlyRowColDesc(object):
"""
@ -939,6 +1041,7 @@ def get_fig_plotly(fig=None, **fig_kw):
if fig is None:
fig = go.Figure(**fig_kw)
#fig = go.FigureWidget(**fig_kw)
return fig, go
@ -948,7 +1051,7 @@ def plotly_set_lims(fig, lims, axname):
Set the data limits for the axis ax.
Args:
lims: tuple(2) for (left, right), tuple(1) or scalar for left only.
lims: tuple(2) for (left, right), if tuple(1) or scalar for left only, none is set.
axname: "x" for x-axis, "y" for y-axis.
Return: (left, right)
@ -983,12 +1086,23 @@ def plotly_set_lims(fig, lims, axname):
#if right is not None: ax_range[1] = right
# Example: fig.update_layout(yaxis_range=[-4,4])
k = dict(x="xaxis_range", y="yaxis_range")[axname]
fig.update_layout(k=[left, right])
k = dict(x="xaxis", y="yaxis")[axname]
fig.layout[k].range = [left, right]
return left, right
_PLOTLY_DEFAULT_SHOW = [True]
def set_plotly_default_show(true_or_false):
"""
Set the default value of show in the add_plotly_fig_kwargs decorator.
Usefule for instance when generating the sphinx gallery of plotly plots.
"""
_PLOTLY_DEFAULT_SHOW[0] = true_or_false
def add_plotly_fig_kwargs(func):
"""
Decorator that adds keyword arguments for functions returning plotly figures.
@ -1002,12 +1116,13 @@ def add_plotly_fig_kwargs(func):
def wrapper(*args, **kwargs):
# pop the kwds used by the decorator.
title = kwargs.pop("title", None)
show = kwargs.pop("show", True)
show = kwargs.pop("show", _PLOTLY_DEFAULT_SHOW[0])
hovermode = kwargs.pop("hovermode", False)
savefig = kwargs.pop("savefig", None)
write_json = kwargs.pop("write_json", None)
config = kwargs.pop("config", None)
renderer = kwargs.pop("renderer", None)
chart_studio = kwargs.pop("chart_studio", False)
# Allow users to specify the renderer via shell env.
if renderer is not None and os.getenv("PLOTLY_RENDERER", default=None) is not None:
@ -1031,11 +1146,11 @@ def add_plotly_fig_kwargs(func):
fig.layout.hovermode = hovermode
if show:
if renderer == "chart_studio":
push_to_chart_studio(fig)
else:
fig.show(renderer=renderer, config=config)
if show: # and _PLOTLY_DEFAULT_SHOW:
fig.show(renderer=renderer, config=config)
if chart_studio:
push_to_chart_studio(fig)
return fig
@ -1062,9 +1177,9 @@ def add_plotly_fig_kwargs(func):
(separated by + characters) or None. If None, then the default
renderers specified in plotly.io.renderers.default are used.
See https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html
Note that if renderee is equal to `chart_studio`, the file is uploaded to the chart studio
cloud. This is an AbiPy extension on top of the plotly API.
config (dict) A dict of parameters to configure the figure. The defaults are set in plotly.js.
chart_studio True to push figure to chart_studio server. Requires authenticatios.
Default: False.
================ ====================================================================
"""
)
@ -1118,20 +1233,21 @@ def plotlyfigs_to_browser(figs, filename=None, browser=None):
return filename
def plotly_klabels(labels):
def plotly_klabels(labels, allow_dupes=False):
"""
This helper function polish a list of k-points labels before calling plotly by:
- Checking if we have two equivalent consequtive labels (only the first one is shown and the second one is set to "")
- Replacing particulat Latex tokens with unicode as plotly support for Latex is far from optimal.
- Checking if we have two equivalent consecutive labels (only the first one is shown and the second one is set to "")
- Replacing particular Latex tokens with unicode as plotly support for Latex is far from optimal.
Return: New list labels, same length as input labels.
"""
new_labels = labels.copy()
# Don't show label if previous k-point is the same.
for il in range(1, len(new_labels)):
if new_labels[il] == new_labels[il - 1]: new_labels[il] = ""
if not allow_dupes:
# Don't show label if previous k-point is the same.
for il in range(1, len(new_labels)):
if new_labels[il] == new_labels[il - 1]: new_labels[il] = ""
replace = {
r"$\Gamma$": "Γ",
@ -1144,6 +1260,15 @@ def plotly_klabels(labels):
return new_labels
def plotly_set_xylabels(fig, xlabel, ylabel, exchange_xy):
"""
Set the x- and the y-label of axis ax, exchanging x and y if exchange_xy
"""
if exchange_xy: xlabel, ylabel = ylabel, xlabel
fig.layout.xaxis.title.text = xlabel
fig.layout.yaxis.title.text = ylabel
_PLOTLY_AUTHEHTICATED = False
@ -1151,6 +1276,10 @@ def plotly_chartstudio_authenticate():
"""
Authenticate the user on the chart studio portal by reading `PLOTLY_USERNAME` and `PLOTLY_API_KEY`
from the pymatgen configuration file located in $HOME/.pmgrc.yaml.
PLOTLY_USERNAME: johndoe
PLOTLY_API_KEY: XXXXXXXXXXXXXXXXXXXX
"""
global _PLOTLY_AUTHEHTICATED
if _PLOTLY_AUTHEHTICATED: return
@ -1165,7 +1294,7 @@ def plotly_chartstudio_authenticate():
Add it to $HOME/.pmgrc.yaml using the follow syntax:
PLOTLY_USERNAME: john_doe
PMG_MAPI_KEY: secret # to the your api_key go to profile > settings > regenerate key
PLOTLY_API_KEY: secret # to get your api_key go to profile > settings > regenerate key
"""
@ -1192,3 +1321,595 @@ def push_to_chart_studio(figs):
if not isinstance(figs, (list, tuple)): figs = [figs]
for fig in figs:
py.plot(fig, auto_open=True)
####################################################
# This code is shamelessy taken from Adam's package
####################################################
import plotly.graph_objects as go
def go_points(points, size=4, color="black", labels=None, **kwargs):
#textposition = 'top right',
#textfont = dict(color='#E58606'),
mode = "markers" if labels is None else "markers+text"
#text = labels
if labels is not None:
labels = plotly_klabels(labels, allow_dupes=True)
return go.Scatter3d(
x=[v[0] for v in points],
y=[v[1] for v in points],
z=[v[2] for v in points],
marker=dict(size=size, color=color),
mode=mode,
text=labels,
**kwargs
)
def _add_if_not_in(d, key, value):
if key not in d:
d[key] = value
def go_line(v1, v2, color="black", width=2, mode="lines", **kwargs):
_add_if_not_in(kwargs, "line_color", "black")
_add_if_not_in(kwargs, "line_width", 2)
return go.Scatter3d(
mode=mode,
x=[v1[0], v2[0]],
y=[v1[1], v2[1]],
z=[v1[2], v2[2]],
#line=dict(color=color, width=width),
**kwargs
)
def go_lines(V, name=None, color="black", width=2, **kwargs):
gen = ((v1, v2) for (v1, v2) in V)
v1, v2 = next(gen)
out = [
go_line(v1, v2, width=width, color=color, name=name, legendgroup=name, **kwargs)
]
out.extend(
go_line(
v1,
v2,
width=width,
color=color,
showlegend=False,
legendgroup=name,
**kwargs
)
for (v1, v2) in gen
)
return out
def vectors(lattice, name=None, color="black", width=4, **kwargs):
gen = zip(lattice, ["a", "b", "c"])
v, label = next(gen)
out = [
go_line(
[0, 0, 0],
v,
text=["", label],
width=width,
color=color,
name=name,
legendgroup=name,
mode="lines+text",
**kwargs
)
]
out.extend(
go_line(
[0, 0, 0],
v,
text=["", label],
width=width,
color=color,
showlegend=False,
legendgroup=name,
mode="lines+text",
**kwargs
)
for (v, label) in gen
)
return out
def get_vectors(lattice_mat, name=None, color="black", width=2, **kwargs):
return go_lines([[[0, 0, 0], v] for v in lattice_mat], **kwargs)
def get_box(lattice_mat, **kwargs):
a, b, c = lattice_mat
segments = [
[[0, 0, 0], a],
[[0, 0, 0], b],
[[0, 0, 0], c],
[a, a + b],
[a, a + c],
[b, b + a],
[b, b + c],
[c, c + a],
[c, c + b],
[a + b, a + b + c],
[a + c, a + b + c],
[b + c, a + b + c],
]
return go_lines(segments, **kwargs)
def plot_fcc_conv():
fcc_conv = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
fcc_vectors = vectors(
fcc_conv, name="conv lattice vectors", color="darkblue", width=6
)
fcc_box = get_box(fcc_conv, name="conv lattice")
atoms = go_points(
[[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]],
size=10,
color="orange",
name="atoms",
legendgroup="atoms",
)
fig = go.Figure(data=[*fcc_box, *fcc_vectors, atoms])
return fig
def plot_fcc_prim():
fcc_prim = np.array([[0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]])
fcc_prim_vectors = vectors(
fcc_prim, name="prim lattice vectors", color="green", width=6
)
fcc_prim_box = get_box(fcc_prim, name="prim lattice", color="green")
atoms = go_points(
[[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]],
size=10,
color="orange",
name="atoms",
legendgroup="atoms",
)
fcc_conv = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
fcc_conv_box = get_box(fcc_conv, name="conv lattice")
fig = go.Figure(data=[*fcc_prim_box, *fcc_prim_vectors, *fcc_conv_box, atoms])
return fig
def plot_fcc_100():
# fcc_100_cell = np.array([[0, 0.5, -0.5], [0, 0.5, 0.5], [1.0, 0.0, 0]])
fcc_100_cell = np.array([[0.5, -0.5, 0], [0.5, 0.5, 0], [0.0, 0, 1.0]])
fcc_100_vectors = vectors(
fcc_100_cell, name="100 lattice vectors", color="red", width=6
)
fcc_100_box = get_box(fcc_100_cell, name="100 lattice", color="red")
fig = plot_fcc_conv()
fig.add_traces([*fcc_100_box, *fcc_100_vectors])
return fig
def plot_fcc_110():
fcc_110_cell = np.array([[0, 0.0, 1.0], [0.5, -0.5, 0], [0.5, 0.5, 0.0]])
fcc_110_vectors = vectors(
fcc_110_cell, name="reduced lattice vectors", color="red", width=6
)
fcc_110_box = get_box(fcc_110_cell, name="reduced lattice", color="red")
fig = plot_fcc_conv()
fig.add_traces([*fcc_110_box, *fcc_110_vectors])
return fig
def plot_fcc_111():
fcc_111_cell = np.array([[0.5, 0, -0.5], [0, 0.5, -0.5], [1, 1, 1]])
fcc_111_vectors = vectors(
fcc_111_cell, name="reduced lattice vectors", color="red", width=6
)
fcc_111_box = get_box(fcc_111_cell, name="reduced lattice", color="red")
fig = plot_fcc_conv()
fig.add_traces([*fcc_111_box, *fcc_111_vectors])
return fig
def plotly_structure(structure, ax=None, to_unit_cell=False, alpha=0.7,
style="points+labels", color_scheme="VESTA", **kwargs):
"""
Plot structure with plotly (minimalistic version).
Args:
structure: |Structure| object
ax: matplotlib :class:`Axes3D` or None if a new figure should be created.
alpha: The alpha blending value, between 0 (transparent) and 1 (opaque)
to_unit_cell: True if sites should be wrapped into the first unit cell.
style: "points+labels" to show atoms sites with labels.
color_scheme: color scheme for atom types. Allowed values in ("Jmol", "VESTA")
Returns: |matplotlib-Figure|
"""
#fig, ax = plot_unit_cell(structure.lattice, ax=ax, linewidth=1)
box = get_box(structure.lattice.matrix) #, **kwargs):
from pymatgen.analysis.molecule_structure_comparator import CovalentRadius
from pymatgen.vis.structure_vtk import EL_COLORS
#symb2data = {}
#for symbol in structure.symbol_set:
# symb2data[symbol] = d = {}
# d["color"] = color = tuple(i / 255 for i in EL_COLORS[color_scheme][symbol])
# d["radius"] = CovalentRadius.radius[symbol]
# inds = structure.indices_from_symbol(symbol)
# sites = [structure[i] for i in inds]
# d["xyz"] = []
# for site in sites:
# if to_unit_cell and hasattr(site, "to_unit_cell"): site = site.to_unit_cell()
# Use cartesian coordinates.
# x, y, z = site.coords
# d["xyz"].append((x, y ,z)
xyz, sizes, colors = np.empty((len(structure), 3)), [], []
for i, site in enumerate(structure):
symbol = site.specie.symbol
color = tuple(i / 255 for i in EL_COLORS[color_scheme][symbol])
radius = CovalentRadius.radius[symbol]
if to_unit_cell and hasattr(site, "to_unit_cell"): site = site.to_unit_cell()
# Use cartesian coordinates.
x, y, z = site.coords
xyz[i] = (x, y, z) # , radius)
sizes.append(radius)
colors.append(color)
#if "labels" in style:
# ax.text(x, y, z, symbol)
atoms = go_points(
#[[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5]],
xyz,
size=10,
color="orange",
name="atoms",
legendgroup="atoms",
)
#marker = [dict(size=size, color=color) for (size, color) in zip(sizes, colors)]
#atoms = go.Scatter3d(
# x=[v[0] for v in xyz],
# y=[v[1] for v in xyz],
# z=[v[2] for v in xyz],
# #marker=dict(size=size, color=color),
# marker=marker,
# mode="markers",
# #**kwargs
#)
# The definition of sizes is not optimal because matplotlib uses points
# wherease we would like something that depends on the radius (5000 seems to give reasonable plots)
# For possibile approaches, see
# https://stackoverflow.com/questions/9081553/python-scatter-plot-size-and-style-of-the-marker/24567352#24567352
# https://gist.github.com/syrte/592a062c562cd2a98a83
#if "points" in style:
# x, y, z, s = xyzs.T.copy()
# s = 5000 * s ** 2
# ax.scatter(x, y, zs=z, s=s, c=colors, alpha=alpha) #facecolors="white", #edgecolors="blue"
#ax.set_title(structure.composition.formula)
#ax.set_axis_off()
#fig = go.Figure(data=[*box, *vectors, atoms])
fig = go.Figure(data=[*box, atoms])
return fig
# This is the matplotlib API to plot the BZ.
def plotly_wigner_seitz(lattice, fig=None, **kwargs):
"""
Adds the skeleton of the Wigner-Seitz cell of the lattice to a plotly figure.
Args:
lattice: Lattice object
fig: plotly figure or None if a new figure should be created.
kwargs: kwargs passed to the matplotlib function 'plot'. Color defaults to black
and linewidth to 1.
Returns: Plotly figure
"""
#ax, fig, plt = get_ax3d_fig_plt(ax)
fig, go = get_fig_plotly(fig=fig) #, **fig_kw)
if "line_color" not in kwargs:
kwargs["line_color"] = "black"
if "line_width" not in kwargs:
kwargs["line_width"] = 1
bz = lattice.get_wigner_seitz_cell()
#ax, fig, plt = get_ax3d_fig_plt(ax)
for iface in range(len(bz)): # pylint: disable=C0200
for line in itertools.combinations(bz[iface], 2):
for jface in range(len(bz)):
if (
iface < jface
and any(np.all(line[0] == x) for x in bz[jface])
and any(np.all(line[1] == x) for x in bz[jface])
):
#ax.plot(*zip(line[0], line[1]), **kwargs)
fig.add_trace(go_line(line[0], line[1], showlegend=False, **kwargs))
return fig
def plotly_lattice_vectors(lattice, fig=None, **kwargs):
"""
Adds the basis vectors of the lattice provided to a matplotlib Axes
Args:
lattice: Lattice object
fig: plotly figure or None if a new figure should be created.
kwargs: kwargs passed to the matplotlib function 'plot'. Color defaults to green
and linewidth to 3.
Returns:
matplotlib figure and matplotlib ax
"""
#ax, fig, plt = get_ax3d_fig_plt(ax)
fig, go = get_fig_plotly(fig=fig) #, **fig_kw)
if "line_color" not in kwargs:
kwargs["line_color"] = "green"
if "line_width" not in kwargs:
kwargs["line_width"] = 3
if "showlegend" not in kwargs:
kwargs["showlegend"] = False
vertex1 = lattice.get_cartesian_coords([0.0, 0.0, 0.0])
vertex2 = lattice.get_cartesian_coords([1.0, 0.0, 0.0])
#ax.plot(*zip(vertex1, vertex2), **kwargs)
fig.add_trace(go_line(vertex1, vertex2, name="a", **kwargs))
vertex2 = lattice.get_cartesian_coords([0.0, 1.0, 0.0])
#ax.plot(*zip(vertex1, vertex2), **kwargs)
fig.add_trace(go_line(vertex1, vertex2, name="b", **kwargs))
vertex2 = lattice.get_cartesian_coords([0.0, 0.0, 1.0])
#ax.plot(*zip(vertex1, vertex2), **kwargs)
fig.add_trace(go_line(vertex1, vertex2, name="c", **kwargs))
return fig
def plotly_path(line, lattice=None, coords_are_cartesian=False, fig=None, **kwargs):
"""
Adds a line passing through the coordinates listed in 'line' to a matplotlib Axes
Args:
line: list of coordinates.
lattice: Lattice object used to convert from reciprocal to cartesian coordinates
coords_are_cartesian: Set to True if you are providing
coordinates in cartesian coordinates. Defaults to False.
Requires lattice if False.
fig: plotly figure or None if a new figure should be created.
kwargs: kwargs passed to the matplotlib function 'plot'. Color defaults to red
and linewidth to 3.
Returns:
matplotlib figure and matplotlib ax
"""
#ax, fig, plt = get_ax3d_fig_plt(ax)
fig, go = get_fig_plotly(fig=fig) #, **fig_kw)
if "line_color" not in kwargs:
kwargs["line_color"] = "red"
if "line_width" not in kwargs:
kwargs["line_width"] = 3
for k in range(1, len(line)):
vertex1 = line[k - 1]
vertex2 = line[k]
if not coords_are_cartesian:
if lattice is None:
raise ValueError("coords_are_cartesian False requires the lattice")
vertex1 = lattice.get_cartesian_coords(vertex1)
vertex2 = lattice.get_cartesian_coords(vertex2)
#ax.plot(*zip(vertex1, vertex2), **kwargs)
fig.add_trace(go_line(vertex1, vertex2, showlegend=False, **kwargs))
return fig
def plotly_labels(labels, lattice=None, coords_are_cartesian=False, ax=None, **kwargs):
"""
Adds labels to a matplotlib Axes
Args:
labels: dict containing the label as a key and the coordinates as value.
lattice: Lattice object used to convert from reciprocal to cartesian coordinates
coords_are_cartesian: Set to True if you are providing.
coordinates in cartesian coordinates. Defaults to False.
Requires lattice if False.
ax: matplotlib :class:`Axes` or None if a new figure should be created.
kwargs: kwargs passed to the matplotlib function 'text'. Color defaults to blue
and size to 25.
Returns:
matplotlib figure and matplotlib ax
"""
ax, fig, plt = get_ax3d_fig_plt(ax)
if "color" not in kwargs:
kwargs["color"] = "b"
if "size" not in kwargs:
kwargs["size"] = 25
for k, coords in labels.items():
label = k
if k.startswith("\\") or k.find("_") != -1:
label = "$" + k + "$"
off = 0.01
if coords_are_cartesian:
coords = np.array(coords)
else:
if lattice is None:
raise ValueError("coords_are_cartesian False requires the lattice")
coords = lattice.get_cartesian_coords(coords)
ax.text(*(coords + off), s=label, **kwargs)
return fig, ax
def plotly_points(points, lattice=None, coords_are_cartesian=False, fold=False, labels=None, fig=None, **kwargs):
"""
Adds points to a matplotlib Axes
Args:
points: list of coordinates
lattice: Lattice object used to convert from reciprocal to cartesian coordinates
coords_are_cartesian: Set to True if you are providing
coordinates in cartesian coordinates. Defaults to False.
Requires lattice if False.
fold: whether the points should be folded inside the first Brillouin Zone.
Defaults to False. Requires lattice if True.
fig: plotly figure or None if a new figure should be created.
kwargs: kwargs passed to the matplotlib function 'scatter'. Color defaults to blue
Returns:
matplotlib figure and matplotlib ax
"""
#ax, fig, plt = get_ax3d_fig_plt(ax)
fig, go = get_fig_plotly(fig=fig) #, **fig_kw)
if "marker_color" not in kwargs:
kwargs["marker_color"] = "blue"
if (not coords_are_cartesian or fold) and lattice is None:
raise ValueError("coords_are_cartesian False or fold True require the lattice")
from pymatgen.electronic_structure.plotter import fold_point
vecs = []
for p in points:
if fold:
p = fold_point(p, lattice, coords_are_cartesian=coords_are_cartesian)
elif not coords_are_cartesian:
p = lattice.get_cartesian_coords(p)
vecs.append(p)
#ax.scatter(*p, **kwargs)
kws = dict(textposition="top right", showlegend=False) #, textfont=dict(color='#E58606'))
kws.update(kwargs)
fig.add_trace(go_points(vecs, labels=labels, **kws))
return fig
@add_plotly_fig_kwargs
def plotly_brillouin_zone_from_kpath(kpath, fig=None, **kwargs):
"""
Gives the plot (as a matplotlib object) of the symmetry line path in
the Brillouin Zone.
Args:
kpath (HighSymmKpath): a HighSymmKPath object
ax: matplotlib :class:`Axes` or None if a new figure should be created.
**kwargs: provided by add_fig_kwargs decorator
Returns: plotly figure.
"""
lines = [[kpath.kpath["kpoints"][k] for k in p] for p in kpath.kpath["path"]]
return plotly_brillouin_zone(
bz_lattice=kpath.prim_rec,
lines=lines,
fig=fig,
labels=kpath.kpath["kpoints"],
**kwargs,
)
@add_plotly_fig_kwargs
def plotly_brillouin_zone(
bz_lattice,
lines=None,
labels=None,
kpoints=None,
fold=False,
coords_are_cartesian=False,
fig=None,
**kwargs,
):
"""
Plots a 3D representation of the Brillouin zone of the structure.
Can add to the plot paths, labels and kpoints
Args:
bz_lattice: Lattice object of the Brillouin zone
lines: list of lists of coordinates. Each list represent a different path
labels: dict containing the label as a key and the coordinates as value.
kpoints: list of coordinates
fold: whether the points should be folded inside the first Brillouin Zone.
Defaults to False. Requires lattice if True.
coords_are_cartesian: Set to True if you are providing
coordinates in cartesian coordinates. Defaults to False.
ax: matplotlib :class:`Axes` or None if a new figure should be created.
kwargs: provided by add_fig_kwargs decorator
Returns: plotly figure
"""
fig = plotly_lattice_vectors(bz_lattice, fig=fig)
plotly_wigner_seitz(bz_lattice, fig=fig)
if lines is not None:
for line in lines:
plotly_path(line, bz_lattice, coords_are_cartesian=coords_are_cartesian, fig=fig)
if labels is not None:
# TODO
#plotly_labels(labels, bz_lattice, coords_are_cartesian=coords_are_cartesian, ax=ax)
plotly_points(
labels.values(),
lattice=bz_lattice,
coords_are_cartesian=coords_are_cartesian,
fold=False,
labels=list(labels.keys()),
fig=fig,
)
if kpoints is not None:
plotly_points(
kpoints,
lattice=bz_lattice,
coords_are_cartesian=coords_are_cartesian,
fold=fold,
fig=fig,
)
#ax.set_xlim3d(-1, 1)
#ax.set_ylim3d(-1, 1)
#ax.set_zlim3d(-1, 1)
# ax.set_aspect('equal')
#ax.axis("off")
return fig

View File

@ -1,4 +1,4 @@
"""Utilities for pandas dataframe"""
"""Utilities for pandas dataframes"""
import sys

View File

@ -6,6 +6,7 @@ import numpy as np
from abipy import abilab
import abipy.data as abidata
from abipy.tools.plotting import *
from abipy.tools.plotting import is_mpl_figure, is_plotly_figure
from abipy.core.testing import AbipyTest
@ -18,6 +19,8 @@ class TestPlotting(AbipyTest):
raise self.SkipTest("This test requires matplotlib")
ax, fig, plt = get_ax_fig_plt(ax=None)
assert is_mpl_figure(fig)
assert not is_plotly_figure(fig)
left, right = set_axlims(ax, None, "x")
assert left is None and right is None

View File

@ -1,111 +1,253 @@
#name: binder3.6
##name: env3.8
channels:
- gmatteo
- abinit
- matsci
- conda-forge
- defaults
- conda-forge
- defaults
dependencies:
- apscheduler=2.1.0=py36_0
- html2text=2016.9.19=py36_0
- asn1crypto=0.22.0=py36_0
- backports=1.0=py36_1
- backports.functools_lru_cache=1.4=py36_1
- blas=1.1=openblas
- ca-certificates=2017.11.5=0
- certifi=2017.11.5=py36_0
- cffi=1.11.2=py36_0
- chardet=3.0.4=py36_0
- cryptography=2.1.4=py36_0
- curl=7.55.1=0
- cycler=0.10.0=py36_0
- dbus=1.10.22=0
- decorator=4.1.2=py36_0
- expat=2.2.5=0
- fastcache=1.0.2=py36_0
- fontconfig=2.12.6=0
- freetype=2.8.1=0
- gettext=0.19.7=1
- glib=2.55.0=0
- gmp=6.1.2=0
- gmpy2=2.0.8=py36_1
- gst-plugins-base=1.8.0=0
- gstreamer=1.8.0=1
- hdf4=4.2.13=0
- hdf5=1.10.1=1
- icu=58.2=0
- idna=2.6=py36_1
- jpeg=9b=2
- krb5=1.14.2=0
- libffi=3.2.1=3
- libiconv=1.15=0
- libnetcdf=4.4.1.1=10
- libpng=1.6.34=0
- libssh2=1.8.0=2
- libxcb=1.12=1
- libxml2=2.9.5=2
- matplotlib=2.1.1=py36_2
- mpc=1.0.3=4
- mpfr=3.1.5=0
- mpich=3.2=5
- mpmath=1.0.0=py_0
- ncurses=5.9=10
- netcdf-fortran=4.4.4=6
- netcdf4=1.3.1=py36_1
- networkx=2.0=py36_1
- numpy=1.13.3=py36_blas_openblas_201
- openblas=0.2.20=6
- openssl=1.0.2n=0
- pandas=0.22.0=py36_0
- patsy=0.4.1=py36_0
- pcre=8.39=0
- pip=9.0.1=py36_1
- prettytable=0.7.2=py36_1
- pycparser=2.18=py36_0
- pyopenssl=17.4.0=py36_0
- pyparsing=2.2.0=py36_0
- pyqt=5.6.0=py36_4
- pysocks=1.6.7=py36_0
- python=3.6.4=0
- python-dateutil=2.6.1=py36_0
- pytz=2017.3=py_2
- pyyaml=3.12=py36_1
- qt=5.6.2=7
- readline=7.0=0
- requests=2.18.4=py36_1
- scipy=1.0.0=py36_blas_openblas_201
- seaborn=0.8.1=py36_0
- setuptools=38.2.4=py36_0
- sip=4.18=py36_1
- six=1.11.0=py36_1
- sqlite=3.20.1=2
- statsmodels=0.8.0=py36_0
- sympy=1.1.1=py36_0
- tk=8.6.7=0
- tornado=4.5.2=py36_0
- urllib3=1.22=py36_0
- wheel=0.30.0=py_1
- xorg-libxau=1.0.8=3
- xorg-libxdmcp=1.1.2=3
- xz=5.2.3=0
- yaml=0.1.6=0
- zlib=1.2.11=0
- libgcc=7.2.0=h69d50b8_2
- libgcc-ng=7.2.0=h7cc24e2_2
- libgfortran=3.0.0=1
- libstdcxx-ng=7.2.0=h7a57d05_2
- abinit=8.6.1=1
- fftw=3.3.6=0
- libxc=2.2.2=0
- monty=1.0.2=py36h8689505_2
- palettable=2.1.1=py36h1b737fa_2
- pydispatcher=2.0.5=py36h30c4b39_1
- pymatgen=2017.12.16=py36_0
- ruamel.yaml=0.15.25=py36_0
- spglib=1.9.9.44=py36_0
- tabulate=0.7.7=py36h0a86d05_2
- tqdm=4.10.0=py36_1
- pip:
- backports.functools-lru-cache==1.4
#prefix: /home/ucl/naps/gmatteo/miniconda3/envs/binder3.6
- _libgcc_mutex=0.1=conda_forge
- _openmp_mutex=4.5=1_gnu
- abinit=9.4.0=h321dead_0
- apipkg=1.5=py_0
- appdirs=1.4.4=pyh9f0ad1d_0
- apscheduler=3.7.0=py38h578d9bd_0
- argon2-cffi=20.1.0=py38h497a2fe_2
- ase=3.21.1=pyhd8ed1ab_0
- async_generator=1.10=py_0
- attrs=21.1.0=pyhd8ed1ab_0
- backcall=0.2.0=pyh9f0ad1d_0
- backports=1.0=py_2
- backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0
- bleach=3.3.0=pyh44b312d_0
- bokeh=2.3.1=py38h578d9bd_0
- brotlipy=0.7.0=py38h497a2fe_1001
- bzip2=1.0.8=h7f98852_4
- c-ares=1.17.1=h7f98852_1
- ca-certificates=2020.12.5=ha878542_0
- cached-property=1.5.2=hd8ed1ab_1
- cached_property=1.5.2=pyha770c72_1
- certifi=2020.12.5=py38h578d9bd_1
- cffi=1.14.5=py38ha65f79e_0
- cftime=1.4.1=py38h5c078b8_0
- chardet=4.0.0=py38h578d9bd_1
- chart-studio=1.1.0=pyh9f0ad1d_0
- click=7.1.2=pyh9f0ad1d_0
- cloudpickle=1.6.0=py_0
- coverage=5.5=py38h497a2fe_0
- cryptography=3.4.7=py38ha5dfef3_0
- curl=7.76.1=h979ede3_1
- cycler=0.10.0=py_2
- cytoolz=0.11.0=py38h497a2fe_3
- dask-core=2021.4.1=pyhd8ed1ab_0
- dbus=1.13.18=hb2f20db_0
- decorator=4.4.2=py_0
- defusedxml=0.7.1=pyhd8ed1ab_0
- entrypoints=0.3=pyhd8ed1ab_1003
- enum34=1.1.10=py38h32f6830_2
- execnet=1.8.0=pyh44b312d_0
- expat=2.3.0=h9c3ff4c_0
- fftw=3.3.9=mpi_mpich_h245ceca_1
- flask=1.1.2=pyh9f0ad1d_0
- fontconfig=2.13.1=hba837de_1005
- freetype=2.10.4=h0708190_1
- fsspec=2021.4.0=pyhd8ed1ab_0
- future=0.18.2=py38h578d9bd_3
- gettext=0.19.8.1=h0b5b191_1005
- glib=2.68.1=h9c3ff4c_0
- glib-tools=2.68.1=h9c3ff4c_0
- gmp=6.2.1=h58526e2_0
- gmpy2=2.1.0b1=py38hd744826_1
- gst-plugins-base=1.14.0=hbbd80ab_1
- gstreamer=1.14.0=h28cd5cc_2
- h5py=3.2.1=nompi_py38h9915d05_100
- hdf4=4.2.13=h10796ff_1005
- hdf5=1.10.6=mpi_mpich_h996c276_1014
- icu=58.2=hf484d3e_1000
- idna=2.10=pyh9f0ad1d_0
- imagecodecs-lite=2019.12.3=py38h5c078b8_3
- imageio=2.9.0=py_0
- importlib-metadata=4.0.1=py38h578d9bd_0
- iniconfig=1.1.1=pyh9f0ad1d_0
- ipykernel=5.5.4=py38hd0cf306_0
- ipython=7.23.1=py38hd0cf306_0
- ipython_genutils=0.2.0=py_1
- ipywidgets=7.6.3=pyhd3deb0d_0
- itsdangerous=1.1.0=py_0
- jedi=0.18.0=py38h578d9bd_2
- jinja2=2.11.3=pyh44b312d_0
- jpeg=9d=h36c2ea0_0
- jsoncpp=1.8.4=hc9558a2_1002
- jsonschema=3.2.0=pyhd8ed1ab_3
- jupyter=1.0.0=py38h578d9bd_6
- jupyter_client=6.1.12=pyhd8ed1ab_0
- jupyter_console=6.4.0=pyhd8ed1ab_0
- jupyter_core=4.7.1=py38h578d9bd_0
- jupyterlab_pygments=0.1.2=pyh9f0ad1d_0
- jupyterlab_widgets=1.0.0=pyhd8ed1ab_1
- kiwisolver=1.3.1=py38h1fd1430_1
- krb5=1.17.2=h926e7f8_0
- latexcodec=2.0.1=pyh9f0ad1d_0
- lcms2=2.12=hddcbb42_0
- ld_impl_linux-64=2.33.1=h53a641e_7
- libblas=3.9.0=9_openblas
- libcblas=3.9.0=9_openblas
- libcurl=7.76.1=hc4aaa36_1
- libedit=3.1.20191231=he28a2e2_2
- libev=4.33=h516909a_1
- libffi=3.3=he6710b0_2
- libgcc-ng=9.3.0=h2828fa1_19
- libgfortran-ng=9.3.0=hff62375_19
- libgfortran5=9.3.0=hff62375_19
- libglib=2.68.1=h3e27bee_0
- libgomp=9.3.0=h2828fa1_19
- libiconv=1.16=h516909a_0
- liblapack=3.9.0=9_openblas
- libnetcdf=4.7.4=mpi_mpich_hdef422e_7
- libnghttp2=1.43.0=h812cca2_0
- libopenblas=0.3.15=pthreads_h8fe5266_0
- libpng=1.6.37=h21135ba_2
- libsodium=1.0.18=h36c2ea0_1
- libssh2=1.9.0=ha56f1ee_6
- libstdcxx-ng=9.3.0=h6de172a_19
- libtiff=4.2.0=hdc55705_0
- libuuid=2.32.1=h7f98852_1000
- libwebp-base=1.2.0=h7f98852_2
- libxc=4.3.4=h86c2bf4_2
- libxcb=1.13=h7f98852_1003
- libxml2=2.9.10=hb55368b_3
- locket=0.2.0=py_2
- lz4-c=1.9.2=he1b5a44_3
- markdown=3.3.4=pyhd8ed1ab_0
- markupsafe=1.1.1=py38h497a2fe_3
- matplotlib=3.4.1=py38h578d9bd_0
- matplotlib-base=3.4.1=py38hcc49a3a_0
- matplotlib-inline=0.1.2=pyhd8ed1ab_2
- mistune=0.8.4=py38h497a2fe_1003
- monty=2021.3.3=pyhd8ed1ab_0
- more-itertools=8.7.0=pyhd8ed1ab_1
- mpc=1.1.0=h04dde30_1009
- mpfr=4.0.2=he80fd80_1
- mpi=1.0=mpich
- mpich=3.4.1=h846660c_104
- mpmath=1.2.1=pyhd8ed1ab_0
- nbclient=0.5.3=pyhd8ed1ab_0
- nbconvert=6.0.7=py38h578d9bd_3
- nbformat=5.1.3=pyhd8ed1ab_0
- ncurses=6.2=he6710b0_1
- nest-asyncio=1.5.1=pyhd8ed1ab_0
- netcdf-fortran=4.5.3=mpi_mpich_hafa3f36_3
- netcdf4=1.5.6=nompi_py38hf887595_102
- networkx=2.5.1=pyhd8ed1ab_0
- notebook=6.3.0=pyha770c72_1
- numpy=1.20.2=py38h9894fe3_0
- olefile=0.46=pyh9f0ad1d_1
- openjpeg=2.4.0=hf7af979_0
- openssl=1.1.1k=h7f98852_0
- packaging=20.9=pyh44b312d_0
- palettable=3.3.0=py_0
- pandas=1.2.4=py38h1abd341_0
- pandoc=2.12=h7f98852_0
- pandocfilters=1.4.2=py_1
- panel=0.11.3=pyhd8ed1ab_0
- param=1.10.1=pyhd3deb0d_0
- parso=0.8.2=pyhd8ed1ab_0
- partd=1.2.0=pyhd8ed1ab_0
- pathlib=1.0.1=py38h578d9bd_4
- patsy=0.5.1=py_0
- pcre=8.44=he1b5a44_0
- pexpect=4.8.0=pyh9f0ad1d_2
- phonopy=2.9.3=py38h5c078b8_0
- pickleshare=0.7.5=py_1003
- pillow=8.1.2=py38ha0e1e83_1
- pip=21.0.1=py38h06a4308_0
- plotly=4.14.3=pyh44b312d_0
- pluggy=0.13.1=py38h578d9bd_4
- pooch=1.3.0=pyhd8ed1ab_0
- prometheus_client=0.10.1=pyhd8ed1ab_0
- prompt-toolkit=3.0.18=pyha770c72_0
- prompt_toolkit=3.0.18=hd8ed1ab_0
- pthread-stubs=0.4=h36c2ea0_1001
- ptyprocess=0.7.0=pyhd3deb0d_0
- py=1.10.0=pyhd3deb0d_0
- pybtex=0.24.0=py38h578d9bd_0
- pycparser=2.20=pyh9f0ad1d_2
- pyct=0.4.6=py_0
- pyct-core=0.4.6=py_0
- pydispatcher=2.0.5=py_1
- pygments=2.9.0=pyhd8ed1ab_0
- pymatgen=2022.0.7=py38h1fd1430_0
- pyopenssl=20.0.1=pyhd8ed1ab_0
- pyparsing=2.4.7=pyh9f0ad1d_0
- pyqt=5.9.2=py38h05f1152_4
- pyrsistent=0.17.3=py38h497a2fe_2
- pysocks=1.7.1=py38h578d9bd_3
- pytest=6.2.4=py38h578d9bd_0
- pytest-cov=2.11.1=pyh44b312d_0
- pytest-forked=1.3.0=pyhd3deb0d_0
- pytest-sugar=0.9.4=pyh9f0ad1d_1
- pytest-xdist=2.2.1=pyhd8ed1ab_0
- python=3.8.8=hdb3f193_5
- python-coveralls=2.9.3=py_0
- python-dateutil=2.8.1=py_0
- python_abi=3.8=1_cp38
- pytz=2021.1=pyhd8ed1ab_0
- pyviz_comms=2.0.1=pyhd3deb0d_0
- pywavelets=1.1.1=py38h5c078b8_3
- pyyaml=5.4.1=py38h497a2fe_0
- pyzmq=22.0.3=py38h2035c66_1
- qt=5.9.7=h5867ecd_1
- qtconsole=5.1.0=pyhd8ed1ab_0
- qtpy=1.9.0=py_0
- readline=8.1=h27cfd23_0
- requests=2.25.1=pyhd3deb0d_0
- retrying=1.3.3=py_2
- ruamel.yaml=0.16.12=py38h497a2fe_2
- ruamel.yaml.clib=0.2.2=py38h497a2fe_2
- scikit-image=0.18.1=py38h51da96c_0
- scipy=1.6.3=py38h7b17777_0
- scripttest=1.3.0=py_1
- seaborn=0.11.1=hd8ed1ab_1
- seaborn-base=0.11.1=pyhd8ed1ab_1
- send2trash=1.5.0=py_0
- setuptools=52.0.0=py38h06a4308_0
- sip=4.19.13=py38he6710b0_0
- six=1.16.0=pyh6c4a22f_0
- spglib=1.16.1=py38h5c078b8_0
- sqlite=3.35.4=hdfb4753_0
- statsmodels=0.12.2=py38h5c078b8_0
- sympy=1.8=py38h578d9bd_0
- tabulate=0.8.9=pyhd8ed1ab_0
- tbb=2020.2=h4bd325d_4
- termcolor=1.1.0=py_2
- terminado=0.9.4=py38h578d9bd_0
- testpath=0.4.4=py_0
- tifffile=2019.7.26.2=py38_0
- tk=8.6.10=hbc83047_0
- toml=0.10.2=pyhd8ed1ab_0
- toolz=0.11.1=py_0
- tornado=6.1=py38h497a2fe_1
- tqdm=4.60.0=pyhd8ed1ab_0
- traitlets=5.0.5=py_0
- typing_extensions=3.7.4.3=py_0
- tzlocal=2.0.0=py_0
- uncertainties=3.1.5=pyhd8ed1ab_0
- urllib3=1.26.4=pyhd8ed1ab_0
- vtk=8.2.0=py38hf2e56f5_218
- wcwidth=0.2.5=pyh9f0ad1d_2
- webencodings=0.5.1=py_1
- werkzeug=1.0.1=pyh9f0ad1d_0
- wheel=0.36.2=pyhd3eb1b0_0
- widgetsnbextension=3.5.1=py38h578d9bd_4
- xorg-kbproto=1.0.7=h7f98852_1002
- xorg-libice=1.0.10=h7f98852_0
- xorg-libsm=1.2.3=hd9c2040_1000
- xorg-libx11=1.7.0=h7f98852_0
- xorg-libxau=1.0.9=h7f98852_0
- xorg-libxdmcp=1.1.3=h7f98852_0
- xorg-libxt=1.2.1=h7f98852_2
- xorg-xproto=7.0.31=h7f98852_1007
- xz=5.2.5=h7b6447c_0
- yaml=0.2.5=h516909a_0
- zeromq=4.3.4=h9c3ff4c_0
- zipp=3.4.1=pyhd8ed1ab_0
- zlib=1.2.11=h7b6447c_3
- zstd=1.4.5=h9ceee32_0
#prefix: /home/gmatteo/miniconda3/envs/my_binder

2336
binder/example.ipynb Normal file

File diff suppressed because one or more lines are too long

8
binder/get_newenv.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/band
# WARNING: Must be executed on a linux box.
conda env remove -n abipy_binder
conda create -n abipy_binder python=3.8
conda install --file ../requirements.txt -y -c conda-forge
conda install --file ../requirements-optional.txt -y -c conda-forge
conda install abinit -c conda-forge
conda env export > environment.yml

View File

@ -7,9 +7,9 @@
# Notebooks w/ extensions that auto-run code must be "trusted" to work the first time
#jupyter trust index.ipynb
# Install abipy scripts and configuration files.
# Install AbiPy scripts and configuration files.
python setup.py install
mkdir -p ${HOME}/.abinit/abipy
cp abipy/data/managers/travis_scheduler.yml ${HOME}/.abinit/abipy/scheduler.yml
cp abipy/data/managers/travis_manager.yml ${HOME}/.abinit/abipy/manager.yml
#./dev_scripts/pyclean.py .
#./dev_scripts/pyclean.py .

View File

@ -2,7 +2,7 @@
#
# You can set these variables from the command line.
SPHINXOPTS = -j1
SPHINXOPTS = -j2
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build

BIN
docs/_static/plotly_logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

7
docs/_templates/layout.html vendored Normal file
View File

@ -0,0 +1,7 @@
{%- extends "!layout.html" %}
{%- block scripts %}
<script type="text/javascript" src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{{ super() }}
{%- endblock %}

View File

@ -78,7 +78,7 @@ panels Package
:show-inheritance:
:mod:`pipelines` Module
---------------------
----------------------
.. automodule:: abipy.panels.pipelines
:members:
@ -88,7 +88,7 @@ panels Package
:mod:`sigeph` Module
---------------------
.. automodule:: abipy.panels.pipelines
.. automodule:: abipy.panels.sigeph
:members:
:undoc-members:
:show-inheritance:

View File

@ -9,20 +9,24 @@ import sys
import os
import shutil
#import warnings
# Remove matplotlib agg warnings from generated doc when using plt.show
import warnings
#warnings.filterwarnings("ignore", category=UserWarning,
# message='Matplotlib is currently using agg, which is a'
# ' non-GUI backend, so cannot show the figure.')
if not sys.warnoptions:
warnings.simplefilter("ignore")
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
ABIPY_ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
sys.path.insert(0, ABIPY_ROOT)
import imp
mod_name = os.path.join(ABIPY_ROOT, "abipy", "core", "release.py")
relmod = imp.load_source(mod_name, mod_name)
@ -53,7 +57,6 @@ extensions = [
"sphinxarg.ext", # CLI doc
'sphinxcontrib.bibtex',
"jupyter_sphinx",
#"jupyter_sphinx.execute",
#'nbsphinx',
#"releases",
#'sphinx.ext.coverage',
@ -113,7 +116,16 @@ mpl.rcParams['figure.dpi'] = 300
# return figure_rst(image_names, gallery_conf['src_dir'])
from sphinx_gallery.sorting import FileNameSortKey, NumberOfCodeLinesSortKey
# Set plotly renderer to capture _repr_html_ for sphinx-gallery
# https://sphinx-gallery.github.io/stable/auto_examples/plot_9_plotly.html
import plotly.io as pio
pio.renderers.default = 'sphinx_gallery'
# Here we change the default value of show used in the plotly decorator.
from abipy.tools.plotting import set_plotly_default_show
set_plotly_default_show(False)
from sphinx_gallery.sorting import ExampleTitleSortKey
sphinx_gallery_conf = {
# path to your examples scripts
@ -121,15 +133,14 @@ sphinx_gallery_conf = {
"../abipy/examples/plot",
"../abipy/examples/flows",
],
#'examples_dirs': [],
# path where to save gallery generated examples
'gallery_dirs': [
"gallery",
"flow_gallery",
],
'filename_pattern': "(/plot_*|/run_*)",
'filename_pattern': "(/plot*|/run_*)",
'default_thumb_file': '_static/abipy_logo.png',
'within_subsection_order': NumberOfCodeLinesSortKey,
'within_subsection_order': ExampleTitleSortKey,
'backreferences_dir': None,
#'reset_modules': (reset_mpl,),
#'find_mayavi_figures': True,
@ -143,17 +154,33 @@ sphinx_gallery_conf = {
#'image_scrapers': ('matplotlib',),
#'image_scrapers': ('matplotlib', 'mayavi'),
#'image_scrapers': ('matplotlib', PNGScraper()),
# TODO
#https://sphinx-gallery.github.io/advanced_configuration.html#generate-binder-links-for-gallery-notebooks-experimental
#'binder': {
# 'org': 'abinit',
# #'repo': 'abipy',
# #'repo': 'https://github.com/abinit/abipy',
# "repo": "http://abinit.github.io/abipy/",
# 'url': 'https://mybinder.org', # URL serving binders (e.g. mybinder.org)
# 'branch': 'develop', # Can also be a tag or commit hash
# 'dependencies': '../binder/environment.yml' # list_of_paths_to_dependency_files>'
# },
#'image_scrapers': ('matplotlib', plotly),
# capture raw HTML or, if not present, __repr__ of last expression in
# each code block
'capture_repr': ('_repr_html_', '__repr__'),
#
# https://sphinx-gallery.github.io/stable/configuration.html#binder-links
'binder': {
# Required keys
'org': 'abinit',
'repo': 'abipy',
# Can be any branch, tag, or commit hash. Use a branch that hosts your docs.
'branch': 'gh-pages',
# Any URL of a binderhub deployment. Must be full URL (e.g. https://mybinder.org).
'binderhub_url': 'https://mybinder.org',
# A list of paths (relative to conf.py) to dependency files that Binder uses to infer
# the environment needed to run your examples
'dependencies': ["../binder/environment.yml", "../binder/postBuild"],
# Optional keys
# A prefix to prepend to any filepaths in Binder links.
#'filepath_prefix': '<prefix>'
# Jupyter notebooks for Binder will be copied to this directory (relative to built documentation root).
#'notebooks_dir': '<notebooks-directory-name>'
# Whether Binder links should start Jupyter Lab instead of the Jupyter Notebook interface.
#'use_jupyter_lab': False,
},
}
# Generate the API documentation when building
@ -174,7 +201,7 @@ master_doc = 'index'
# General information about the project.
project = 'abipy'
copyright = '2018, ' + relmod.author
copyright = '2021, ' + relmod.author
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -182,6 +209,7 @@ copyright = '2018, ' + relmod.author
#
# The short X.Y version.
version = relmod.__version__
# The full version, including alpha/beta/rc tags.
release = relmod.__version__
@ -223,89 +251,21 @@ pygments_style = 'sphinx'
# -- Options for HTML output ---------------------------------------------------
# Activate the theme.
import sphinx_bootstrap_theme
html_theme = 'bootstrap'
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# (Optional) Logo. Should be small enough to fit the navbar (ideally 24x24).
# Path should be relative to the ``_static`` files directory.
#html_logo = "my_logo.png"
# Theme options are theme-specific and customize the look and feel of a
# theme further.
html_theme_options = {
# Navigation bar title. (Default: ``project`` value)
#'navbar_title': "Demo",
# Tab name for entire site. (Default: "Site")
'navbar_site_name': "Site",
# A list of tuples containing pages or urls to link to.
# Valid tuples should be in the following forms:
# (name, page) # a link to a page
# (name, "/aa/bb", 1) # a link to an arbitrary relative url
# (name, "http://example.com", True) # arbitrary absolute url
# Note the "1" or "True" value above as the third argument to indicate
# an arbitrary url.
#'navbar_links': [
# ("Examples", "examples"),
# ("Link", "http://example.com", True),
#],
# Render the next and previous page links in navbar. (Default: true)
'navbar_sidebarrel': True,
# Render the current pages TOC in the navbar. (Default: true)
'navbar_pagenav': True,
# Tab name for the current pages TOC. (Default: "Page")
'navbar_pagenav_name': "Page",
# Global TOC depth for "site" navbar tab. (Default: 1)
# Switching to -1 shows all levels.
'globaltoc_depth': 1,
# Include hidden TOCs in Site navbar?
#
# Note: If this is "false", you cannot have mixed ``:hidden:`` and
# non-hidden ``toctree`` directives in the same page, or else the build
# will break.
#
# Values: "true" (default) or "false"
'globaltoc_includehidden': "true",
# HTML navbar class (Default: "navbar") to attach to <div> element.
# For black navbar, do "navbar navbar-inverse"
#'navbar_class': "navbar navbar-inverse",
# Fix navigation bar to top of page?
# Values: "true" (default) or "false"
'navbar_fixed_top': "true",
# Location of link to source.
# Options are "nav" (default), "footer" or anything else to exclude.
'source_link_position': "nav",
# Bootswatch (http://bootswatch.com/) theme.
# Options are nothing (default) or the name of a valid theme
# such as "cosmo" or "sandstone".
#'bootswatch_theme': "united",
#'bootswatch_theme': "flatly",
#'bootswatch_theme': "litera",
#'bootswatch_theme': "simplex",
#'bootswatch_theme': "sandstone",
# Choose Bootstrap version.
# Values: "3" (default) or "2" (in quotes)
'bootstrap_version': "3",
}
def setup(app):
"""
Sphinx automatically calls your setup function defined in "conf.py" during the build process for you.
There is no need to, nor should you, call this function directly in your code.
http://www.sphinx-doc.org/en/stable/extdev/appapi.html
See http://www.sphinx-doc.org/en/stable/extdev/appapi.html
"""
# Add custom css in _static
#app.add_stylesheet("my_style.css")
@ -505,6 +465,5 @@ from pybtex.plugin import register_plugin
register_plugin('pybtex.style.labels', 'abipy', AbiPyLabelStyle)
register_plugin('pybtex.style.formatting', 'abipystyle', AbiPyStyle)
# This is for releases http://releases.readthedocs.io/en/latest/usage.html
releases_github_path = "abinit/abipy"

View File

@ -7,15 +7,25 @@ Graphical interface
:maxdepth: 2
:caption: Contents:
AbiPy provides interactive dashboards that can be used either as a standalone web apps
with the `bokeh server <http://docs.bokeh.org/>`_ or inside jupyter notebooks.
AbiPy provides interactive dashboards that can be used either as a standalone web applications
(**dashboards**) with the `bokeh server <http://docs.bokeh.org/>`_ or inside jupyter notebooks.
This document explains how to install the required dependencies and how to
generate dashboards either with the command line interface or inside jupyter notebooks.
generate dashboards/GUIs either with the command line interface (CLI) or inside jupyter notebooks.
.. important::
Note that you will need a running python kernel to execute the callbacks triggerered
by the GUI hence the examples given in this page are only meant to show how to build the the GUI.
Please note that one needs a **running python backend**
to execute the callbacks triggerered by the GUI widgets.
This part, indeed, is implemented in HTML/CSS/JS code executed
by the frontend (i.e. **your browser**) that sends the signal
to the python server (the **backend**).
The python server is supposed to process the data
and send the results back to the frontend for visualization purposes
Don't be surprised if you start to click buttons and **nothing happens**!
The examples provided in this page are only meant to show how to build GUI
or dashboards with AbiPy.
Installation
------------
@ -24,16 +34,16 @@ Install the `panel <https://panel.pyviz.org/>`_ package either from pip with:
.. code-block:: bash
pip install panel
pip install panel
or with conda:
or with conda (**recommended**) using:
.. code-block:: bash
conda install panel -c conda-forge
conda install panel -c conda-forge
If you want to work with JupyterLab, you will also need to install
the optional PyViz JupyterLab extension:
If you plan to use panel within JupyterLab, you will also need to install
the PyViz JupyterLab extension and activate it with:
.. code-block:: bash
@ -44,24 +54,37 @@ the optional PyViz JupyterLab extension:
Basic Usage
-----------
The AbiPy structure and many AbiPY files provide a ``get_panel`` method that returns
a dashboard that can be used inside the jupyter notebook.
To enable the integration with ``panel`` inside a jupyter notebook, execute the below code:
Several AbiPy objects provide a ``get_panel`` method returning
an object that can be displayed inside the jupyter notebook or inside a Bokeh server.
When running inside a jupyter notebook, remember enable the integration
with the ``panel`` infrastructure by executing:
.. jupyter-execute::
# Import panel and activate extensions
import panel as pn
pn.extension()
from abipy import abilab
abilab.abipanel();
Now one can start to construct AbiPy objects and use the ``get_panel`` method to generate graphical interfaces.
In our first example, we use the ``abiopen`` function to open a ``GSR`` file
and then we call ``get_panel`` to build a set of widgets that allows us to interact with the object:
**before calling** any AbiPy ``get_panel`` method.
.. note::
The ``abipanel`` function is needed to load extensions and javascript packages
required by AbiPy.
This function is just a small wrapper around the official panel API:
.. code-block:: bash
import panel as pn
pn.extension()
At this point, we can start to construct AbiPy objects.
For our first example, we use the ``abiopen`` function to open a ``GSR`` file,
then we call ``get_panel`` to build a set of widgets that allows us to interact
with the `GsrFile`:
.. jupyter-execute::
# Import AbiPy modules.
from abipy import abilab
import abipy.data as abidata
@ -70,75 +93,92 @@ and then we call ``get_panel`` to build a set of widgets that allows us to inter
gsr.get_panel()
The **summary** tab provides a string representation of the file
but there is not widget to interact with it.
If you select the **e-Bands** tab, you will see several widgets and a button
that activates the visualization of the KS band energies.
Again, in this HTML page there is no python server running in background so
if you click the **Plot e-bands** button nothing happens (this is not a bug!).
The same approach can be used with a ``DDB`` file.
In this case, we get more tabs and options because one can use the GUI
to set the input parameters, invoke ``anaddb`` and visualize the results:
The advantage of this notebook-based approach is that it is possible to mix
the panel GUIs with python code that can be used to perform
more advanced tasks not supported by the GUI.
.. jupyter-execute::
# Open DDB file with abiopen and invoke get_panel method.
#ddb_path = abidata.ref_file("mp-1009129-9x9x10q_ebecs_DDB")
#abilab.abiopen(ddb_path).get_panel()
Calling ``get_structure`` with an AbiPy structure, creates a set of widgets
to facilitate common operations such as exporting to a different format or
generating a Abinit input file for GS calculations:
Obviously it is possible to have multiple panels running in the same notebook.
Calling ``get_structure`` with an AbiPy structure, for instance, creates a set of widgets
to facilitate common operations such as exporting the structure to a different format or
generating a basic Abinit input file for e.g. GS calculations:
.. jupyter-execute::
gsr.structure.get_panel()
.. note::
There are, however, cases in which you don't need the interactive environment provided
by jupyter notebooks as you are mainly interested in the visualization of the results.
In this case, it is possible to use the command line interface to automatically generate
a dashboard with widgets without having to start a jupyter-lab application.
At present, not all the AbiPy objects support the ``get_panel`` protocol
but we plan to gradually support more objects, especially the most important
netcdf files produced by Abinit
To build a dashboard for a ``Structure`` object extract from ``FILE``, use:
To generate a notebook from the command line, use the abiopen.py_ script:
.. code-block:: shell
.. code-block:: bash
abistruct.py panel FILE
abiopen.py si_nscf_GSR.nc -nb # short for --notebook
that will automatically open the notebook inside jupyterlab.
If you prefer classic jupyter notebooks, use the ``-nb --classic-notebook`` options
where ``FILE`` is any file providing a ``Structure`` object e.g. netcdf file, cif files, abi, abo etc.
If you do not need to execute python code, you may want to generate a panel dashboard with:
To build a dashboard associated to one of the AbiPy file, use the syntax:
.. code-block:: bash
.. code-block:: shell
abiopen.py si_nscf_GSR.nc -pn # short for --panel
abiopen.py FILE --panel
where ``FILE`` is one of the Abinit files supported by ``abiopen.py``.
For instance, one can create a dashboard to interact with a ``DDB`` file with:
.. code-block:: shell
abiopen.py out_DDB --panel
.. important::
To build a dashboard for an AbiPy Flow use:
abirun.py FLOWDIR panel
or, alternatively:
abiopen.py FLOWDIR/__AbinitFlow__.pickle --panel
The same approach can be used with a ``DDB`` file.
In this case, we get more tabs and options because one can use the GUI
to set the input parameters, invoke ``anaddb`` and visualize the results:
.. jupyter-execute::
import numpy as np
from matplotlib import pyplot
%matplotlib inline
# Open DDB file with abiopen and invoke get_panel method.
ddb_path = abidata.ref_file("mp-1009129-9x9x10q_ebecs_DDB")
abilab.abiopen(ddb_path).get_panel()
x = np.linspace(1E-3, 2 * np.pi)
The same result can be obtained from the CLI with
pyplot.plot(x, np.sin(x) / x)
pyplot.plot(x, np.cos(x))
pyplot.grid()
.. code-block:: bash
abiopen.py mp-1009129-9x9x10q_ebecs_DDB -nb
There are, however, cases in which you don't need the interactive environment provided
by jupyter notebooks as you are mainly interested in the visualization of the results.
In this case, it is possible to use the command line interface to automatically generate
a dashboard with widgets without having to start a jupyter-lab application.
To build a dashboard for a ``Structure`` object extracted from ``FILE``, use::
abistruct.py panel FILE
where ``FILE`` is **any** file providing a ``Structure`` object
e.g. netcdf files, cif files, abi, abo files etc.
To build a dashboard associated to one of the AbiPy file, use the syntax::
abiopen.py FILE --panel
where ``FILE`` is one of the Abinit files supported by ``abiopen.py``.
For instance, one can create a dashboard to interact with a ``DDB`` file with::
abiopen.py out_DDB --panel
.. important::
To build a dashboard for an AbiPy Flow use::
abirun.py FLOWDIR panel
or alternatively::
abiopen.py FLOWDIR/__AbinitFlow__.pickle --panel

View File

@ -59,9 +59,9 @@ API
Indices and tables
==================
:ref:`genindex`
:ref:`modindex`
:ref:`search`
:ref:`genindex`
:ref:`modindex`
:ref:`search`
License
=======

Some files were not shown because too many files have changed in this diff Show More