ipyvasp 0.7.9__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/PKG-INFO +6 -3
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/README.md +5 -2
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/_lattice.py +122 -65
- ipyvasp-0.8.1/ipyvasp/_version.py +1 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/bsdos.py +22 -17
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/cli.py +37 -22
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/core/serializer.py +16 -4
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/lattice.py +105 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/misc.py +12 -1
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/widgets.py +10 -9
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/PKG-INFO +6 -3
- ipyvasp-0.7.9/ipyvasp/_version.py +0 -1
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/LICENSE +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/__init__.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/__main__.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/_enplots.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/core/__init__.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/core/parser.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/core/plot_toolkit.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/core/spatial_toolkit.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/evals_dataframe.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/potential.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp/utils.py +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/SOURCES.txt +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/dependency_links.txt +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/entry_points.txt +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/requires.txt +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/ipyvasp.egg-info/top_level.txt +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/setup.cfg +0 -0
- {ipyvasp-0.7.9 → ipyvasp-0.8.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ipyvasp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: A processing tool for VASP DFT input/output processing in Jupyter Notebook.
|
|
5
5
|
Home-page: https://github.com/massgh/ipyvasp
|
|
6
6
|
Author: Abdul Saboor
|
|
@@ -32,7 +32,7 @@ Requires-Dist: nglview>=3.0.4; extra == "extra"
|
|
|
32
32
|
|
|
33
33
|
# ipyvasp
|
|
34
34
|
|
|
35
|
-
An
|
|
35
|
+
An VASP-based DFT pre and post processing tool.
|
|
36
36
|
|
|
37
37
|
## Install
|
|
38
38
|
Currently the package is being built and not stable. If you want to use development version, install this way:(recommended to install in a virtual environment)
|
|
@@ -68,6 +68,9 @@ Interactively select bandstructure path by clicking on high symmetry points on p
|
|
|
68
68
|
|
|
69
69
|

|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Apply operations on POSCAR and simultaneously view using plotly's `FigureWidget` or `WeasWidget` in Jupyterlab side by side.
|
|
72
|
+
|
|
73
|
+

|
|
72
74
|
|
|
73
75
|
|
|
76
|
+
More coming soon!
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ipyvasp
|
|
2
2
|
|
|
3
|
-
An
|
|
3
|
+
An VASP-based DFT pre and post processing tool.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
Currently the package is being built and not stable. If you want to use development version, install this way:(recommended to install in a virtual environment)
|
|
@@ -36,6 +36,9 @@ Interactively select bandstructure path by clicking on high symmetry points on p
|
|
|
36
36
|
|
|
37
37
|

|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Apply operations on POSCAR and simultaneously view using plotly's `FigureWidget` or `WeasWidget` in Jupyterlab side by side.
|
|
40
40
|
|
|
41
|
+

|
|
41
42
|
|
|
43
|
+
|
|
44
|
+
More coming soon!
|
|
@@ -4,7 +4,7 @@ import numpy as np
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import requests as req
|
|
6
6
|
import inspect
|
|
7
|
-
from itertools import combinations
|
|
7
|
+
from itertools import combinations, product
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
|
|
10
10
|
from scipy.spatial import ConvexHull, KDTree
|
|
@@ -215,7 +215,7 @@ def periodic_table():
|
|
|
215
215
|
return ax
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
def write_poscar(poscar_data, outfile=None, selective_dynamics=None, overwrite=False):
|
|
218
|
+
def write_poscar(poscar_data, outfile=None, selective_dynamics=None, overwrite=False, comment=""):
|
|
219
219
|
"""Writes POSCAR data to a file or returns string
|
|
220
220
|
|
|
221
221
|
Parameters
|
|
@@ -227,13 +227,15 @@ def write_poscar(poscar_data, outfile=None, selective_dynamics=None, overwrite=F
|
|
|
227
227
|
See `ipyvasp.POSCAR.data.get_selective_dynamics` for more info.
|
|
228
228
|
overwrite: bool
|
|
229
229
|
If file already exists, overwrite=True changes it.
|
|
230
|
+
comment: str
|
|
231
|
+
Add comment, previous comment will be there too.
|
|
230
232
|
|
|
231
233
|
|
|
232
234
|
.. note::
|
|
233
235
|
POSCAR is only written in direct format even if it was loaded from cartesian format.
|
|
234
236
|
"""
|
|
235
|
-
_comment = poscar_data.metadata.comment
|
|
236
|
-
out_str = f"{poscar_data.SYSTEM} # " + (_comment or "Created by
|
|
237
|
+
_comment = poscar_data.metadata.comment + comment
|
|
238
|
+
out_str = f"{poscar_data.SYSTEM} # " + (_comment or "Created by ipyvasp")
|
|
237
239
|
scale = poscar_data.metadata.scale
|
|
238
240
|
out_str += "\n {:<20.14f}\n".format(scale)
|
|
239
241
|
out_str += "\n".join(
|
|
@@ -531,9 +533,9 @@ class InvokeMaterialsProject:
|
|
|
531
533
|
else:
|
|
532
534
|
print(self._cif)
|
|
533
535
|
|
|
534
|
-
def write_poscar(self, outfile=None, overwrite=False):
|
|
536
|
+
def write_poscar(self, outfile=None, overwrite=False, comment=""):
|
|
535
537
|
"Use `ipyvasp.lattice.POSCAR.write` if you need extra options."
|
|
536
|
-
write_poscar(self.export_poscar(), outfile=outfile, overwrite=overwrite)
|
|
538
|
+
write_poscar(self.export_poscar(), outfile=outfile, overwrite=overwrite, comment=comment)
|
|
537
539
|
|
|
538
540
|
def export_poscar(self):
|
|
539
541
|
"Export poscar data form cif content."
|
|
@@ -1578,18 +1580,18 @@ def _get_bond_length(poscar_data, bond_length=None):
|
|
|
1578
1580
|
) # Add 5% margin over mean distance, this covers same species too, and in multiple species, this will stop bonding between same species.
|
|
1579
1581
|
|
|
1580
1582
|
|
|
1581
|
-
def _masked_data(poscar_data,
|
|
1582
|
-
"Returns indices of sites which satisfy the
|
|
1583
|
-
if not callable(
|
|
1584
|
-
raise TypeError("`
|
|
1583
|
+
def _masked_data(poscar_data, func):
|
|
1584
|
+
"Returns indices of sites which satisfy the func."
|
|
1585
|
+
if not callable(func):
|
|
1586
|
+
raise TypeError("`func` should be a callable function.")
|
|
1585
1587
|
|
|
1586
|
-
if len(inspect.signature(
|
|
1588
|
+
if len(inspect.signature(func).parameters) != 4:
|
|
1587
1589
|
raise ValueError(
|
|
1588
|
-
"`
|
|
1590
|
+
"`func` takes exactly 4 arguments: (index,x,y,z) in fractional coordinates"
|
|
1589
1591
|
)
|
|
1590
1592
|
|
|
1591
|
-
if not isinstance(
|
|
1592
|
-
raise TypeError("`
|
|
1593
|
+
if not isinstance(func(0, 0, 0, 0), bool):
|
|
1594
|
+
raise TypeError("`func` should return a boolean value.")
|
|
1593
1595
|
|
|
1594
1596
|
eqv_inds = None
|
|
1595
1597
|
if hasattr(poscar_data.metadata, "eqv_indices"):
|
|
@@ -1598,7 +1600,7 @@ def _masked_data(poscar_data, mask_sites):
|
|
|
1598
1600
|
pick = []
|
|
1599
1601
|
for i, pos in enumerate(poscar_data.positions):
|
|
1600
1602
|
idx = eqv_inds[i] if eqv_inds else i # map to original index
|
|
1601
|
-
if
|
|
1603
|
+
if func(idx, *pos):
|
|
1602
1604
|
pick.append(i)
|
|
1603
1605
|
return pick # could be duplicate indices
|
|
1604
1606
|
|
|
@@ -1620,6 +1622,43 @@ def _filter_pairs(labels, pairs, dist, bond_length):
|
|
|
1620
1622
|
return pairs # None -> auto calculate bond_length, number -> use that number
|
|
1621
1623
|
|
|
1622
1624
|
|
|
1625
|
+
def filter_sites(poscar_data, func, tol = 0.01):
|
|
1626
|
+
"""Filter sites based on a function that acts on index and fractional positions such as `lambda i,x,y,z: condition`.
|
|
1627
|
+
This may include equivalent sites, so it should be used for plotting purpose only, e.g. showing atoms on a plane."""
|
|
1628
|
+
idxs = _masked_data(poscar_data, func)
|
|
1629
|
+
data = poscar_data.to_dict()
|
|
1630
|
+
all_pos, npos = [], []
|
|
1631
|
+
for value in poscar_data.types.values():
|
|
1632
|
+
indices = [i for i in value if i in idxs]
|
|
1633
|
+
others = [j for j in value if not (j in indices)]
|
|
1634
|
+
pos = data['positions'][indices]
|
|
1635
|
+
qos = data['positions'][others] # need edge items to include
|
|
1636
|
+
|
|
1637
|
+
if qos.shape:
|
|
1638
|
+
qos = np.concatenate([[[i] for i in others], qos], axis=1) # need to keep index
|
|
1639
|
+
qos = np.array([qos + [0, *p] for p in product([-1,0,1],[-1,0,1],[-1,0,1])]).reshape((-1,4)) # all possible translations
|
|
1640
|
+
qos = qos[(qos[:,1:] < 1 + tol).all(axis=1) & (qos[:,1:] > -tol).all(axis=1)]# only in cell range
|
|
1641
|
+
qos = qos[[func(*q) for q in qos]] # masked only those are true
|
|
1642
|
+
|
|
1643
|
+
if qos.shape:
|
|
1644
|
+
pos = np.concatenate([pos, qos[:,1:]],axis=0)
|
|
1645
|
+
|
|
1646
|
+
all_pos.append(pos)
|
|
1647
|
+
npos.append(len(pos))
|
|
1648
|
+
|
|
1649
|
+
if not np.sum(npos):
|
|
1650
|
+
raise ValueError("No sites found with given filter func!")
|
|
1651
|
+
|
|
1652
|
+
if not 'prev_positions' in data['metadata']: # keep always the starting one
|
|
1653
|
+
data['metadata']['prev_positions'] = poscar_data.positions
|
|
1654
|
+
data['metadata']['prev_types'] = poscar_data.types
|
|
1655
|
+
|
|
1656
|
+
data['positions'] = np.concatenate(all_pos, axis = 0)
|
|
1657
|
+
|
|
1658
|
+
ranges = np.cumsum([0, *npos])
|
|
1659
|
+
data['types'] = {key: range(i,j) for key, i,j in zip(data['types'],ranges[:-1],ranges[1:]) if range(i,j)} # avoid empty
|
|
1660
|
+
return serializer.PoscarData(data)
|
|
1661
|
+
|
|
1623
1662
|
# Cell
|
|
1624
1663
|
def iplot_lattice(
|
|
1625
1664
|
poscar_data,
|
|
@@ -1632,7 +1671,6 @@ def iplot_lattice(
|
|
|
1632
1671
|
origin=(0, 0, 0),
|
|
1633
1672
|
fig=None,
|
|
1634
1673
|
ortho3d=True,
|
|
1635
|
-
mask_sites=None,
|
|
1636
1674
|
bond_kws=dict(line_width=4),
|
|
1637
1675
|
site_kws=dict(line_color="rgba(1,1,1,0)", line_width=0.001, opacity=1),
|
|
1638
1676
|
plot_cell=True,
|
|
@@ -1648,9 +1686,6 @@ def iplot_lattice(
|
|
|
1648
1686
|
Sequence of colors for each type. Automatically generated if not provided.
|
|
1649
1687
|
bond_length : float or dict
|
|
1650
1688
|
Length of bond in Angstrom. Auto calculated if not provides. Can be a dict like {'Fe-O':3.2,...} to specify bond length between specific types.
|
|
1651
|
-
mask_sites : callable
|
|
1652
|
-
Provide a mask function `f(index, x,y,z) -> bool` to show only selected sites.
|
|
1653
|
-
For example, to show only sites with z > 0.5, use `mask_sites = lambda i, x,y,z: x > 0.5`.
|
|
1654
1689
|
bond_kws : dict
|
|
1655
1690
|
Keyword arguments passed to `plotly.graph_objects.Scatter3d` for bonds.
|
|
1656
1691
|
Default is jus hint, you can use any keyword argument that is accepted by `plotly.graph_objects.Scatter3d`.
|
|
@@ -1664,23 +1699,17 @@ def iplot_lattice(
|
|
|
1664
1699
|
|
|
1665
1700
|
kwargs are passed to `iplot_bz`.
|
|
1666
1701
|
"""
|
|
1702
|
+
if len(poscar_data.positions) < 1:
|
|
1703
|
+
raise ValueError("Need at least 1 atom!")
|
|
1704
|
+
|
|
1667
1705
|
poscar_data = _fix_sites(
|
|
1668
1706
|
poscar_data, tol=tol, eqv_sites=eqv_sites, translate=translate, origin=origin
|
|
1669
1707
|
)
|
|
1708
|
+
|
|
1670
1709
|
blen = _get_bond_length(poscar_data, bond_length)
|
|
1671
1710
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
if (
|
|
1675
|
-
mask_sites is not None
|
|
1676
|
-
): # not None is important, as it can be False given by user
|
|
1677
|
-
sites = _masked_data(poscar_data, mask_sites)
|
|
1678
|
-
coords = poscar_data.coords[sites]
|
|
1679
|
-
if not sites:
|
|
1680
|
-
raise ValueError("No sites found with given mask_sites function.")
|
|
1681
|
-
|
|
1682
|
-
coords, pairs, dist = get_pairs(coords, r=blen)
|
|
1683
|
-
_labels = poscar_data.labels[sites] if sites else poscar_data.labels
|
|
1711
|
+
coords, pairs, dist = get_pairs(poscar_data.coords, r=blen)
|
|
1712
|
+
_labels = poscar_data.labels
|
|
1684
1713
|
pairs = _filter_pairs(_labels, pairs, dist, bond_length)
|
|
1685
1714
|
|
|
1686
1715
|
if not fig:
|
|
@@ -1738,13 +1767,8 @@ def iplot_lattice(
|
|
|
1738
1767
|
**site_kws,
|
|
1739
1768
|
}
|
|
1740
1769
|
for (k, v), c, s in zip(uelems.items(), colors, sizes):
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
coords = poscar_data.coords[v]
|
|
1744
|
-
labs = poscar_data.labels[v]
|
|
1745
|
-
else:
|
|
1746
|
-
coords = poscar_data.coords[v]
|
|
1747
|
-
labs = poscar_data.labels[v]
|
|
1770
|
+
coords = poscar_data.coords[v]
|
|
1771
|
+
labs = poscar_data.labels[v]
|
|
1748
1772
|
|
|
1749
1773
|
fig.add_trace(
|
|
1750
1774
|
go.Scatter3d(
|
|
@@ -1830,7 +1854,6 @@ def splot_lattice(
|
|
|
1830
1854
|
translate=None,
|
|
1831
1855
|
origin=(0, 0, 0),
|
|
1832
1856
|
ax=None,
|
|
1833
|
-
mask_sites=None,
|
|
1834
1857
|
showlegend=True,
|
|
1835
1858
|
fmt_label=None,
|
|
1836
1859
|
site_kws=dict(alpha=0.7),
|
|
@@ -1852,9 +1875,6 @@ def splot_lattice(
|
|
|
1852
1875
|
Length of bond in Angstrom. Auto calculated if not provides. Can be a dict like {'Fe-O':3.2,...} to specify bond length between specific types.
|
|
1853
1876
|
alpha : float
|
|
1854
1877
|
Opacity of points and bonds.
|
|
1855
|
-
mask_sites : callable
|
|
1856
|
-
Provide a mask function `f(index, x,y,z) -> bool` to show only selected sites.
|
|
1857
|
-
For example, to show only sites with z > 0.5, use `mask_sites = lambda i,x,y,z: x > 0.5`.
|
|
1858
1878
|
showlegend : bool
|
|
1859
1879
|
Default is True, show legend for each ion type.
|
|
1860
1880
|
site_kws : dict
|
|
@@ -1875,6 +1895,9 @@ def splot_lattice(
|
|
|
1875
1895
|
.. tip::
|
|
1876
1896
|
Use `plt.style.use('ggplot')` for better 3D perception.
|
|
1877
1897
|
"""
|
|
1898
|
+
if len(poscar_data.positions) < 1:
|
|
1899
|
+
raise ValueError("Need at least 1 atom!")
|
|
1900
|
+
|
|
1878
1901
|
# Plane fix
|
|
1879
1902
|
if plane and plane not in "xyzxzyx":
|
|
1880
1903
|
raise ValueError("plane expects in 'xyzxzyx' or None.")
|
|
@@ -1887,21 +1910,8 @@ def splot_lattice(
|
|
|
1887
1910
|
poscar_data, tol=tol, eqv_sites=eqv_sites, translate=translate, origin=origin
|
|
1888
1911
|
)
|
|
1889
1912
|
blen = _get_bond_length(poscar_data, bond_length)
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
coords = poscar_data.coords # take all sites
|
|
1893
|
-
filtered_elems = list(poscar_data.types.keys())
|
|
1894
|
-
if mask_sites is not None: # not None is important, user can give anything
|
|
1895
|
-
sites = _masked_data(poscar_data, mask_sites)
|
|
1896
|
-
if not sites:
|
|
1897
|
-
raise ValueError("No sites found with given mask_sites function.")
|
|
1898
|
-
|
|
1899
|
-
coords = poscar_data.coords[sites]
|
|
1900
|
-
filtered_elems = np.unique(poscar_data.symbols[sites]).tolist()
|
|
1901
|
-
|
|
1902
|
-
labels = [poscar_data.labels[i] for i in sites] if sites else poscar_data.labels
|
|
1903
|
-
|
|
1904
|
-
coords, pairs, dist = get_pairs(coords, r=blen)
|
|
1913
|
+
labels = poscar_data.labels
|
|
1914
|
+
coords, pairs, dist = get_pairs(poscar_data.coords, r=blen)
|
|
1905
1915
|
pairs = _filter_pairs(labels, pairs, dist, bond_length)
|
|
1906
1916
|
|
|
1907
1917
|
if fmt_label is not None:
|
|
@@ -1939,10 +1949,7 @@ def splot_lattice(
|
|
|
1939
1949
|
# Before doing other stuff, create something for legend.
|
|
1940
1950
|
if showlegend:
|
|
1941
1951
|
for key, c, s in zip(uelems.keys(), colors, sizes):
|
|
1942
|
-
|
|
1943
|
-
ax.scatter(
|
|
1944
|
-
[], [], s=s, color=c, label=key, **site_kws
|
|
1945
|
-
) # Works both for 3D and 2D.
|
|
1952
|
+
ax.scatter([], [], s=s, color=c, label=key, **site_kws) # Works both for 3D and 2D.
|
|
1946
1953
|
ptk.add_legend(ax)
|
|
1947
1954
|
|
|
1948
1955
|
# Now change colors and sizes to whole array size
|
|
@@ -1951,10 +1958,6 @@ def splot_lattice(
|
|
|
1951
1958
|
)
|
|
1952
1959
|
sizes = np.array([sizes[i] for i, vs in enumerate(uelems.values()) for v in vs])
|
|
1953
1960
|
|
|
1954
|
-
if sites:
|
|
1955
|
-
colors = colors[sites]
|
|
1956
|
-
sizes = sizes[sites]
|
|
1957
|
-
|
|
1958
1961
|
if np.any(pairs):
|
|
1959
1962
|
coords_p = coords[pairs] # paired points
|
|
1960
1963
|
_colors = colors[pairs] # Colors at pairs
|
|
@@ -2218,6 +2221,40 @@ def scale_poscar(poscar_data, scale=(1, 1, 1), tol=1e-2):
|
|
|
2218
2221
|
new_poscar["positions"] = np.array(positions)
|
|
2219
2222
|
return serializer.PoscarData(new_poscar)
|
|
2220
2223
|
|
|
2224
|
+
def set_boundary(poscar_data, a = [0,1], b = [0,1], c = [0,1]):
|
|
2225
|
+
"View atoms outside cell boundary along a,b,c directions."
|
|
2226
|
+
for d, name in zip([a,b,c],'abc'):
|
|
2227
|
+
if not isinstance(d,(list,tuple)) or len(d) != 2:
|
|
2228
|
+
raise ValueError(f"{name} should be a list/tuple of type [min, max]")
|
|
2229
|
+
if d[1] < d[0]:
|
|
2230
|
+
raise ValueError(f"{name} should be in increasing order as [min, max]")
|
|
2231
|
+
|
|
2232
|
+
data = poscar_data.to_dict()
|
|
2233
|
+
upos = {}
|
|
2234
|
+
for key, value in poscar_data.types.items():
|
|
2235
|
+
pos = data['positions'][value]
|
|
2236
|
+
for i, (l,h), shift in zip(range(3), [a,b,c],np.eye(3)):
|
|
2237
|
+
while pos[:,i].min() > np.floor(l):
|
|
2238
|
+
pos = np.concatenate([pos, pos - shift],axis=0)
|
|
2239
|
+
|
|
2240
|
+
while pos[:,i].max() < np.ceil(h):
|
|
2241
|
+
pos = np.concatenate([pos, pos + shift],axis=0)
|
|
2242
|
+
|
|
2243
|
+
pos = pos[(pos[:,i] >= l) & (pos[:,i] <= h)]
|
|
2244
|
+
|
|
2245
|
+
upos[key] = pos
|
|
2246
|
+
|
|
2247
|
+
data['positions'] = np.concatenate(list(upos.values()), axis = 0)
|
|
2248
|
+
|
|
2249
|
+
ranges = np.cumsum([0, *[len(v) for v in upos.values()]])
|
|
2250
|
+
data['types'] = {key: range(i,j) for key, i,j in zip(upos,ranges[:-1],ranges[1:])}
|
|
2251
|
+
del upos
|
|
2252
|
+
return serializer.PoscarData(data)
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
|
|
2221
2258
|
|
|
2222
2259
|
def rotate_poscar(poscar_data, angle_deg, axis_vec):
|
|
2223
2260
|
"""Rotate a given POSCAR.
|
|
@@ -2560,6 +2597,26 @@ def replace_atoms(poscar_data, func, name):
|
|
|
2560
2597
|
}
|
|
2561
2598
|
return serializer.PoscarData(data) # Return new POSCAR
|
|
2562
2599
|
|
|
2600
|
+
def sort_poscar(poscar_data, new_order):
|
|
2601
|
+
"sort poscar with new_order list/tuple of species."
|
|
2602
|
+
if not isinstance(new_order, (list, tuple)):
|
|
2603
|
+
raise TypeError(f"new_order should be a list/tuple of types, got {type(new_order)}")
|
|
2604
|
+
|
|
2605
|
+
data = poscar_data.to_dict()
|
|
2606
|
+
if not all([set(new_order).issubset(data["types"]), set(data["types"]).issubset(new_order)]):
|
|
2607
|
+
raise ValueError(f"new_order should contain all existings types {list(data['types'])}")
|
|
2608
|
+
|
|
2609
|
+
data["types"] = {key:data["types"][key] for key in new_order}
|
|
2610
|
+
data["positions"] = data["positions"][[i for tp in data["types"].values() for i in tp]]
|
|
2611
|
+
idxs = np.cumsum([0, *map(len, data["types"].values())])
|
|
2612
|
+
data["types"] = {
|
|
2613
|
+
k: range(idxs[i], idxs[i + 1])
|
|
2614
|
+
for i, k in enumerate(data["types"].keys())
|
|
2615
|
+
if len(data["types"][k]) != 0
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
return serializer.PoscarData(data)
|
|
2619
|
+
|
|
2563
2620
|
|
|
2564
2621
|
def remove_atoms(poscar_data, func, fillby=None):
|
|
2565
2622
|
"""Remove atoms that satisfy `func(x,y,z) -> bool` on their fractional coordinates x,y,z.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.1"
|
|
@@ -149,7 +149,9 @@ _spin_doc = """spin : int
|
|
|
149
149
|
plotting other with same parameters will use the same data."""
|
|
150
150
|
_kind_doc = """kpairs : list/tuple
|
|
151
151
|
List of pair of indices to rearrange a computed path. For example, if you computed
|
|
152
|
-
0:L, 15:G, 25:X, 34:M path and want to plot it as X-G|M-X, use [(25,15), (34,25)] as kpairs.
|
|
152
|
+
0:L, 15:G, 25:X, 34:M path and want to plot it as X-G|M-X, use [(25,15), (34,25)] as kpairs.
|
|
153
|
+
bands : list/tuple
|
|
154
|
+
List of indices of bands. If given, this ovverides elim."""
|
|
153
155
|
_proj_doc = """projections : dict
|
|
154
156
|
Mapping from str -> [atoms, orbs]. Use dict to select specific projections,
|
|
155
157
|
e.g. {'Ga-s': (0,[0]), 'Ga1-p': ([0],[1,2,3])} in case of GaAs. If values of the dict
|
|
@@ -335,7 +337,7 @@ class Bands(_BandsDosBase):
|
|
|
335
337
|
|
|
336
338
|
return serializer.Dict2Data(out)
|
|
337
339
|
|
|
338
|
-
def get_data(self, elim=None, ezero=None, projections: dict = None, kpairs=None):
|
|
340
|
+
def get_data(self, elim=None, ezero=None, projections: dict = None, kpairs=None, bands=None):
|
|
339
341
|
"""
|
|
340
342
|
Selects bands and projections to use in plotting functions. If input arguments are same as previous call, returns cached data.
|
|
341
343
|
|
|
@@ -345,12 +347,13 @@ class Bands(_BandsDosBase):
|
|
|
345
347
|
ezero : float, None by default. If not None, elim is applied around this energy.
|
|
346
348
|
projections : dict, str -> [atoms, orbs]. Use dict to select specific projections, e.g. {'Ga-s': (0,[0]), 'Ga1-p': ([0],[1,2,3])} in case of GaAs. If values of the dict are callable, they must accept two arguments evals, occs of shape (spin,kpoints, bands) and return array of shape (kpoints, bands).
|
|
347
349
|
kpairs : list, tuple of integers, None by default to select all kpoints in given order. Use this to select specific kpoints intervals in specific order.
|
|
350
|
+
bands : list,tuple of integers, this ovverides elim if given.
|
|
348
351
|
|
|
349
352
|
Returns
|
|
350
353
|
-------
|
|
351
354
|
data : Selected bands and projections data to be used in bandstructure plotting functions under this class as `data` argument.
|
|
352
355
|
"""
|
|
353
|
-
if self.data and self._data_args == (elim, ezero, projections, kpairs):
|
|
356
|
+
if self.data and self._data_args == (elim, ezero, projections, kpairs, bands):
|
|
354
357
|
return self.data
|
|
355
358
|
|
|
356
359
|
if kpairs and not isinstance(kpairs, (list, tuple)):
|
|
@@ -377,7 +380,7 @@ class Bands(_BandsDosBase):
|
|
|
377
380
|
kpairs[-1][-1],
|
|
378
381
|
] # flatten and add last index
|
|
379
382
|
|
|
380
|
-
self._data_args = (elim, ezero, projections)
|
|
383
|
+
self._data_args = (elim, ezero, projections, bands)
|
|
381
384
|
|
|
382
385
|
(
|
|
383
386
|
(spins, uspins),
|
|
@@ -393,7 +396,7 @@ class Bands(_BandsDosBase):
|
|
|
393
396
|
atoms=uatoms,
|
|
394
397
|
orbs=uorbs,
|
|
395
398
|
spins=uspins or None,
|
|
396
|
-
bands=
|
|
399
|
+
bands=bands,
|
|
397
400
|
) # picks available spins if uspins is None
|
|
398
401
|
|
|
399
402
|
if not spins:
|
|
@@ -508,9 +511,9 @@ class Bands(_BandsDosBase):
|
|
|
508
511
|
{"K :.*ax :": f"{_spin_doc}\n{_kind_doc}\nax :"},
|
|
509
512
|
)
|
|
510
513
|
@_sig_kwargs(splot_bands, ("K", "E"))
|
|
511
|
-
def splot_bands(self, spin=0, kpairs=None, ezero=None, **kwargs):
|
|
514
|
+
def splot_bands(self, spin=0, kpairs=None, ezero=None, bands=None, **kwargs):
|
|
512
515
|
kwargs, elim = self._handle_kwargs(spin=spin, **kwargs)
|
|
513
|
-
data = self.get_data(elim=elim, ezero=ezero, kpairs=kpairs)
|
|
516
|
+
data = self.get_data(elim=elim, ezero=ezero, kpairs=kpairs, bands=bands)
|
|
514
517
|
return splot_bands(data.kpath, data.evals[spin] - data.ezero, **kwargs)
|
|
515
518
|
|
|
516
519
|
@_sub_doc(
|
|
@@ -518,9 +521,9 @@ class Bands(_BandsDosBase):
|
|
|
518
521
|
{"K :.*ax :": f"{_proj_doc}\n{_spin_doc}\n{_kind_doc}\nax :"},
|
|
519
522
|
)
|
|
520
523
|
@_sig_kwargs(splot_rgb_lines, ("K", "E", "pros", "labels"))
|
|
521
|
-
def splot_rgb_lines(self, projections, spin=0, kpairs=None, ezero=None, **kwargs):
|
|
524
|
+
def splot_rgb_lines(self, projections, spin=0, kpairs=None, ezero=None, bands=None, **kwargs):
|
|
522
525
|
kwargs, elim = self._handle_kwargs(spin=spin, **kwargs)
|
|
523
|
-
data = self.get_data(elim, ezero, projections, kpairs=kpairs)
|
|
526
|
+
data = self.get_data(elim, ezero, projections, kpairs=kpairs, bands=bands)
|
|
524
527
|
return splot_rgb_lines(
|
|
525
528
|
data.kpath, data.evals[spin] - data.ezero, data.pros, data.labels, **kwargs
|
|
526
529
|
)
|
|
@@ -530,10 +533,10 @@ class Bands(_BandsDosBase):
|
|
|
530
533
|
{"K :.*axes :": f"{_proj_doc}\n{_spin_doc}\n{_kind_doc}\naxes :"},
|
|
531
534
|
)
|
|
532
535
|
@_sig_kwargs(splot_color_lines, ("K", "E", "pros", "labels"))
|
|
533
|
-
def splot_color_lines(self, projections, spin=0, kpairs=None, ezero=None, **kwargs):
|
|
536
|
+
def splot_color_lines(self, projections, spin=0, kpairs=None, ezero=None, bands=None, **kwargs):
|
|
534
537
|
kwargs, elim = self._handle_kwargs(spin=spin, **kwargs)
|
|
535
538
|
data = self.get_data(
|
|
536
|
-
elim, ezero, projections, kpairs=kpairs
|
|
539
|
+
elim, ezero, projections, kpairs=kpairs, bands=bands
|
|
537
540
|
) # picked relative limit
|
|
538
541
|
return splot_color_lines(
|
|
539
542
|
data.kpath, data.evals[spin] - data.ezero, data.pros, data.labels, **kwargs
|
|
@@ -544,9 +547,9 @@ class Bands(_BandsDosBase):
|
|
|
544
547
|
{"K :.*fig :": f"{_proj_doc}\n{_spin_doc}\n{_kind_doc}\nfig :"},
|
|
545
548
|
)
|
|
546
549
|
@_sig_kwargs(iplot_rgb_lines, ("K", "E", "pros", "labels", "occs", "kpoints"))
|
|
547
|
-
def iplot_rgb_lines(self, projections, spin=0, kpairs=None, ezero=None, **kwargs):
|
|
550
|
+
def iplot_rgb_lines(self, projections, spin=0, kpairs=None, ezero=None, bands=None, **kwargs):
|
|
548
551
|
kwargs, elim = self._handle_kwargs(spin=spin, **kwargs)
|
|
549
|
-
data = self.get_data(elim, ezero, projections, kpairs=kpairs)
|
|
552
|
+
data = self.get_data(elim, ezero, projections, kpairs=kpairs, bands=bands)
|
|
550
553
|
# Send K and bands in place of K for use in iplot_rgb_lines to depict correct band number
|
|
551
554
|
return iplot_rgb_lines(
|
|
552
555
|
{"K": data.kpath, "indices": data.bands},
|
|
@@ -563,9 +566,9 @@ class Bands(_BandsDosBase):
|
|
|
563
566
|
{"K :.*fig :": f"{_spin_doc}\n{_kind_doc}\nfig :"},
|
|
564
567
|
)
|
|
565
568
|
@_sig_kwargs(iplot_bands, ("K", "E"))
|
|
566
|
-
def iplot_bands(self, spin=0, kpairs=None, ezero=None, **kwargs):
|
|
569
|
+
def iplot_bands(self, spin=0, kpairs=None, ezero=None, bands=None, **kwargs):
|
|
567
570
|
kwargs, elim = self._handle_kwargs(spin=spin, **kwargs)
|
|
568
|
-
data = self.get_data(elim, ezero, kpairs=kpairs)
|
|
571
|
+
data = self.get_data(elim, ezero, kpairs=kpairs, bands=bands)
|
|
569
572
|
# Send K and bands in place of K for use in iplot_rgb_lines to depict correct band number
|
|
570
573
|
return iplot_bands(
|
|
571
574
|
{"K": data.kpath, "indices": data.bands},
|
|
@@ -573,13 +576,14 @@ class Bands(_BandsDosBase):
|
|
|
573
576
|
**kwargs,
|
|
574
577
|
)
|
|
575
578
|
|
|
576
|
-
def view_bands(self):
|
|
579
|
+
def view_bands(self, height="450px"):
|
|
577
580
|
"Initialize and return `ipyvasp.widgets.BandsWidget` to view bandstructure interactively."
|
|
578
581
|
use_vaspout = True if isinstance(self.source, vp.Vaspout) else False
|
|
579
582
|
return wdg.BandsWidget(
|
|
580
583
|
use_vaspout=use_vaspout,
|
|
581
|
-
path=self.source.path.parent,
|
|
584
|
+
path=str(self.source.path.parent),
|
|
582
585
|
glob=self.source.path.name,
|
|
586
|
+
height=height,
|
|
583
587
|
)
|
|
584
588
|
|
|
585
589
|
|
|
@@ -677,6 +681,7 @@ class DOS(_BandsDosBase):
|
|
|
677
681
|
raise ValueError("spin must be 0,1,2,3 for dos")
|
|
678
682
|
|
|
679
683
|
kwargs.pop("spin", None) # remove from kwargs as plots don't need it
|
|
684
|
+
kwargs.pop("bands", None) # not required internally
|
|
680
685
|
return kwargs, kwargs.get("elim", None)
|
|
681
686
|
|
|
682
687
|
@_sub_doc(
|
|
@@ -112,14 +112,16 @@ def _get_E0(files: List[Path]):
|
|
|
112
112
|
def _set_dir(
|
|
113
113
|
paths: List[Path], command: Annotated[str, typer.Option("-c", "--command")] = "",
|
|
114
114
|
ignore_error: Annotated[bool, typer.Option("-i", "--ignore-error")] = False,
|
|
115
|
-
time_interval: Annotated[int, typer.Option("-t",'--time-interval')] = 0
|
|
115
|
+
time_interval: Annotated[int, typer.Option("-t",'--time-interval')] = 0,
|
|
116
|
+
extra_command: Annotated[str, typer.Option("-e", "--extra-command")] = "",
|
|
116
117
|
):
|
|
117
118
|
"""Set multiple directories like a for loop to execute a shell command within each of them.
|
|
118
119
|
It will raise an error if the command fails in any of the directories.
|
|
119
120
|
To ignore the error and keep running in other directiories in sequence, use -i/--ignore-error.
|
|
120
121
|
It will raise the shell errors but python will go through all the directories.
|
|
121
122
|
|
|
122
|
-
To keep repeating a command after some time interval, use -t/--time-interval (seconds). Only works for a command
|
|
123
|
+
To keep repeating a command after some time interval, use -t/--time-interval (seconds). Only works for a command.
|
|
124
|
+
Use -e/--extra-command to run (in pwd) before looping over directories. This can be used to cleanup (previous results etc.) with each time recursion.
|
|
123
125
|
|
|
124
126
|
Examples:
|
|
125
127
|
|
|
@@ -143,30 +145,43 @@ def _set_dir(
|
|
|
143
145
|
|
|
144
146
|
abs_paths = [f.absolute() for f in dirs] # absolute path is must but after filtering
|
|
145
147
|
|
|
146
|
-
def
|
|
148
|
+
def exec_cmd(command, path, ignore_error=ignore_error):
|
|
149
|
+
if command:
|
|
150
|
+
if os == "Windows":
|
|
151
|
+
try:
|
|
152
|
+
p = Popen("pwsh.exe -NoProfile -c " + command, shell=False)
|
|
153
|
+
except:
|
|
154
|
+
p = Popen("powershell.exe -NoProfile -c " + command, shell=False)
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
p = Popen(command, shell=True) # Linux, MacOS, shell to get args
|
|
158
|
+
|
|
159
|
+
p.wait()
|
|
160
|
+
if not ignore_error and p.returncode != 0:
|
|
161
|
+
raise RuntimeError(f"Command {command} failed in {path}. Exiting...\n" +
|
|
162
|
+
"Use -i or --ignore-error switch to suppress error and continue in other directories silently."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def run(abs_paths, dirs, command, ec=extra_command):
|
|
166
|
+
if ec:
|
|
167
|
+
exec_cmd(ec, Path('.').absolute())
|
|
168
|
+
print(color.bb("\n"+f" {command} ".center(80,"-")))
|
|
169
|
+
|
|
147
170
|
for path, d in zip(abs_paths,dirs):
|
|
148
171
|
with set_dir(path):
|
|
149
172
|
print(color.gb(f"📁 {str(d)!r}"))
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if os == "Windows":
|
|
153
|
-
try:
|
|
154
|
-
p = Popen("pwsh.exe -NoProfile -c " + command, shell=False)
|
|
155
|
-
except:
|
|
156
|
-
p = Popen("powershell.exe -NoProfile -c " + command, shell=False)
|
|
157
|
-
|
|
158
|
-
else:
|
|
159
|
-
p = Popen(command, shell=True) # Linux, MacOS, shell to get args
|
|
160
|
-
|
|
161
|
-
p.wait()
|
|
162
|
-
if not ignore_error and p.returncode != 0:
|
|
163
|
-
raise RuntimeError(f"Command {command} failed in {path}. Exiting...\n" +
|
|
164
|
-
"Use -i or --ignore-error switch to suppress error and continue in other directories silently."
|
|
165
|
-
)
|
|
166
|
-
|
|
173
|
+
exec_cmd(command, path)
|
|
174
|
+
|
|
167
175
|
if time_interval > 0 and command: # repeat only if command given
|
|
168
176
|
while True: # keep going until interrupted
|
|
169
|
-
run(abs_paths, dirs, command
|
|
177
|
+
run(abs_paths, dirs, command)
|
|
170
178
|
sleep(time_interval)
|
|
171
179
|
else:
|
|
172
|
-
run(abs_paths, dirs, command
|
|
180
|
+
run(abs_paths, dirs, command)
|
|
181
|
+
|
|
182
|
+
@app.command('get-bib')
|
|
183
|
+
def _get_bib(doi: List[str]):
|
|
184
|
+
"Get bibliography entries from doi. Can provide many DOIs."
|
|
185
|
+
from .misc import get_bib
|
|
186
|
+
for d in doi: # all of them
|
|
187
|
+
print(get_bib(d))
|
|
@@ -211,11 +211,20 @@ class Dict2Data:
|
|
|
211
211
|
def values(self):
|
|
212
212
|
return self.__dict__.values()
|
|
213
213
|
|
|
214
|
-
def __getitem__(self, key):
|
|
215
|
-
return self.__dict__[key]
|
|
216
|
-
|
|
217
214
|
def items(self):
|
|
218
215
|
return self.__dict__.items()
|
|
216
|
+
|
|
217
|
+
def __getitem__(self, key):
|
|
218
|
+
return self.__dict__[key]
|
|
219
|
+
|
|
220
|
+
def __contains__(self, key):
|
|
221
|
+
return key in self.__dict__
|
|
222
|
+
|
|
223
|
+
def __iter__(self):
|
|
224
|
+
return iter(self.__dict__)
|
|
225
|
+
|
|
226
|
+
def __len__(self):
|
|
227
|
+
return len(self.__dict__)
|
|
219
228
|
|
|
220
229
|
|
|
221
230
|
class SpinData(Dict2Data):
|
|
@@ -375,6 +384,9 @@ class PoscarData(Dict2Data):
|
|
|
375
384
|
raise ValueError(
|
|
376
385
|
"atom2 must be a string such as 'As' or a dict with a single key as {'As':0}"
|
|
377
386
|
)
|
|
387
|
+
|
|
388
|
+
if len(set([*idx1, *idx2])) < 2: # itself or no atom exists
|
|
389
|
+
return np.nan # No mean of distnace in this case
|
|
378
390
|
|
|
379
391
|
dists = []
|
|
380
392
|
for idx in idx1:
|
|
@@ -388,7 +400,7 @@ class PoscarData(Dict2Data):
|
|
|
388
400
|
|
|
389
401
|
dists = np.array(dists)
|
|
390
402
|
dists = dists[dists > 0] # Remove distance with itself
|
|
391
|
-
return np.min(dists)
|
|
403
|
+
return np.min(dists) if dists.size else np.nan
|
|
392
404
|
|
|
393
405
|
def to_fractional(self, coords):
|
|
394
406
|
"Converts cartesian coordinates to fractional coordinates in the basis of cell."
|
|
@@ -12,6 +12,8 @@ __all__ = [
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from contextlib import redirect_stdout
|
|
14
14
|
from io import StringIO
|
|
15
|
+
from itertools import permutations
|
|
16
|
+
from contextlib import suppress
|
|
15
17
|
|
|
16
18
|
import numpy as np
|
|
17
19
|
from pandas.io.clipboard import clipboard_get, clipboard_set
|
|
@@ -198,6 +200,78 @@ def ngl_viewer(
|
|
|
198
200
|
return view
|
|
199
201
|
|
|
200
202
|
|
|
203
|
+
def weas_viewer(poscar,
|
|
204
|
+
sizes=None,
|
|
205
|
+
colors=None,
|
|
206
|
+
bond_length=None,
|
|
207
|
+
model_style = 1,
|
|
208
|
+
plot_cell=True
|
|
209
|
+
):
|
|
210
|
+
"""
|
|
211
|
+
colors : list or str
|
|
212
|
+
List of colors for each atom type. If str, use 'VESTA','JMOL','CPK'.
|
|
213
|
+
By default, ipyvasp colors are used.
|
|
214
|
+
sizes : list
|
|
215
|
+
List of sizes for each atom type.
|
|
216
|
+
model_type: int
|
|
217
|
+
whether to show Balls (0), Ball + Stick (1), Polyheda (2) or Sticks (3).
|
|
218
|
+
plot_cell : bool
|
|
219
|
+
Plot unit cell. Default True.
|
|
220
|
+
bond_length : float or dict
|
|
221
|
+
Length of bond in Angstrom. Auto calculated if not provides. Can be a dict like {'Fe-O':3.2,...} to specify bond length between specific types.
|
|
222
|
+
|
|
223
|
+
Returns a WeasWidget instance. You can use `.export_image`, `save_image` and other operations on it.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
from weas_widget import WeasWidget
|
|
227
|
+
|
|
228
|
+
if len(poscar.data.positions) < 1:
|
|
229
|
+
raise ValueError("Need at least 1 atom!")
|
|
230
|
+
|
|
231
|
+
w = WeasWidget(from_ase=poscar.to_ase())
|
|
232
|
+
w.avr.show_bonded_atoms = True
|
|
233
|
+
w.avr.model_style = model_style
|
|
234
|
+
w.avr.show_cell = plot_cell
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
if bond_length:
|
|
238
|
+
if isinstance(bond_length,(int,float)):
|
|
239
|
+
for a,b in permutations(list(poscar.data.types.keys()),2):
|
|
240
|
+
with suppress(KeyError):
|
|
241
|
+
w.avr.bond.settings[f'{a}-{b}'].update({'max': bond_length})
|
|
242
|
+
elif isinstance(bond_length, dict):
|
|
243
|
+
for key, value in bond_length.items():
|
|
244
|
+
w.avr.bond.settings[key].update({'max': value})
|
|
245
|
+
|
|
246
|
+
if sizes is not None:
|
|
247
|
+
if not isinstance(sizes, (list,tuple)) or len(sizes) != len(poscar.data.types):
|
|
248
|
+
raise ValueError(f"sizes should be list/tuple of same sizes as atom types = {len(poscar.data.types)}.")
|
|
249
|
+
for key, value in zip(poscar.data.types,sizes):
|
|
250
|
+
w.avr.species.settings[key].update({"radius": value})
|
|
251
|
+
|
|
252
|
+
if colors is None:
|
|
253
|
+
colors = [plat._atom_colors[key] for key in poscar.data.types]
|
|
254
|
+
|
|
255
|
+
if isinstance(colors, str):
|
|
256
|
+
if not colors in ['VESTA','JMOL','CPK']:
|
|
257
|
+
raise ValueError("colors should be one of ['VESTA','JMOL','CPK'] if given as string!")
|
|
258
|
+
|
|
259
|
+
w.avr.color_type = colors
|
|
260
|
+
|
|
261
|
+
if not isinstance(colors, str):
|
|
262
|
+
if not isinstance(colors, (list,tuple)) or len(colors) != len(poscar.data.types):
|
|
263
|
+
raise ValueError(f"colors should be list/tuple of same sizes as atom types = {len(poscar.data.types)}.")
|
|
264
|
+
|
|
265
|
+
colors = {key: value for key, value in zip(poscar.data.types,colors)}
|
|
266
|
+
for key,value in colors.items():
|
|
267
|
+
w.avr.species.settings[key].update({"color": value})
|
|
268
|
+
for (k1,c1), (k2,c2) in permutations(colors.items(),2):
|
|
269
|
+
with suppress(KeyError):
|
|
270
|
+
w.avr.bond.settings[f'{k1}-{k2}'].update({'color1':c1,'color2':c2})
|
|
271
|
+
|
|
272
|
+
return w
|
|
273
|
+
|
|
274
|
+
|
|
201
275
|
class POSCAR:
|
|
202
276
|
_cb_instance = {} # Loads last clipboard data if not changed
|
|
203
277
|
_mp_instance = {} # Loads last mp data if not changed
|
|
@@ -272,6 +346,8 @@ class POSCAR:
|
|
|
272
346
|
"""
|
|
273
347
|
if viewer is None:
|
|
274
348
|
return plat.view_poscar(self.data, **kwargs)
|
|
349
|
+
elif viewer in "weas":
|
|
350
|
+
return weas_viewer(self, **kwargs)
|
|
275
351
|
elif viewer in "nglview":
|
|
276
352
|
return print(
|
|
277
353
|
f"Use `self.view_ngl()` for better customization in case of viewer={viewer!r}"
|
|
@@ -285,6 +361,12 @@ class POSCAR:
|
|
|
285
361
|
@_sig_kwargs(ngl_viewer, ("poscar",))
|
|
286
362
|
def view_ngl(self, **kwargs):
|
|
287
363
|
return ngl_viewer(self, **kwargs)
|
|
364
|
+
|
|
365
|
+
@_sub_doc(weas_viewer)
|
|
366
|
+
@_sig_kwargs(weas_viewer, ("poscar",))
|
|
367
|
+
def view_weas(self, **kwargs):
|
|
368
|
+
return weas_viewer(self, **kwargs)
|
|
369
|
+
|
|
288
370
|
|
|
289
371
|
def view_kpath(self):
|
|
290
372
|
"Initialize a KpathWidget instance to view kpath for current POSCAR, and you can select others too."
|
|
@@ -446,6 +528,14 @@ class POSCAR:
|
|
|
446
528
|
"Writes POSCAR to clipboard (as implemented by pandas library) for copy in other programs such as vim."
|
|
447
529
|
clipboard_set(self.content) # write to clipboard
|
|
448
530
|
|
|
531
|
+
|
|
532
|
+
def update_weas(self, handle):
|
|
533
|
+
"""Send result of any options to view on opened weas widget handle with default parameters becuase any operations
|
|
534
|
+
can be incompatible with presious state of POSCAR. VESTA color scheme is used here.
|
|
535
|
+
Useful in Jupyterlab side panel to look operations results on POSCAR."""
|
|
536
|
+
handle.from_ase(self.to_ase())
|
|
537
|
+
handle.avr.color_type = 'VESTA'
|
|
538
|
+
|
|
449
539
|
@property
|
|
450
540
|
def data(self):
|
|
451
541
|
"Data object in POSCAR."
|
|
@@ -454,6 +544,11 @@ class POSCAR:
|
|
|
454
544
|
def copy(self):
|
|
455
545
|
"Copy POSCAR object. It avoids accidental changes to numpy arrays in original object."
|
|
456
546
|
return self.__class__(data=self.data.copy())
|
|
547
|
+
|
|
548
|
+
@_sub_doc(plat.sort_poscar)
|
|
549
|
+
def sort(self, new_order):
|
|
550
|
+
return self.__class__(data=plat.sort_poscar(self.data, new_order))
|
|
551
|
+
|
|
457
552
|
|
|
458
553
|
@property
|
|
459
554
|
def content(self):
|
|
@@ -546,6 +641,16 @@ class POSCAR:
|
|
|
546
641
|
@_sig_kwargs(plat.scale_poscar, ("poscar_data",))
|
|
547
642
|
def scale(self, scale=(1, 1, 1), **kwargs):
|
|
548
643
|
return self.__class__(data=plat.scale_poscar(self.data, scale, **kwargs))
|
|
644
|
+
|
|
645
|
+
@_sub_doc(plat.set_boundary)
|
|
646
|
+
@_sig_kwargs(plat.set_boundary,("poscar_data",))
|
|
647
|
+
def set_boundary(self, a = [0,1], b=[0,1],c=[0,1]):
|
|
648
|
+
return self.__class__(data = plat.set_boundary(self.data, a=a,b=b,c=c))
|
|
649
|
+
|
|
650
|
+
@_sub_doc(plat.filter_sites)
|
|
651
|
+
@_sig_kwargs(plat.filter_sites,("poscar_data",))
|
|
652
|
+
def filter_sites(self, func):
|
|
653
|
+
return self.__class__(data = plat.filter_sites(self.data, func))
|
|
549
654
|
|
|
550
655
|
@_sub_doc(plat.set_origin)
|
|
551
656
|
def set_origin(self, origin):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
__all__ = ["parse_text", "get_E0", "OUTCAR"]
|
|
1
|
+
__all__ = ["parse_text", "get_bib", "get_E0", "OUTCAR"]
|
|
2
2
|
|
|
3
|
+
import requests
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from itertools import islice
|
|
5
6
|
|
|
@@ -24,6 +25,16 @@ def parse_text(path, shape, slice, **kwargs):
|
|
|
24
25
|
) # should be under open file context
|
|
25
26
|
return data
|
|
26
27
|
|
|
28
|
+
def get_bib(DOI):
|
|
29
|
+
"Get bibligraphy entry from DOI"
|
|
30
|
+
response = requests.get(
|
|
31
|
+
f'http://dx.doi.org/{DOI}',
|
|
32
|
+
headers={'Accept':'text/bibliography;style=bibtex'})
|
|
33
|
+
if response.status_code == 200:
|
|
34
|
+
return response.content.decode('utf-8')
|
|
35
|
+
|
|
36
|
+
return response.status_code
|
|
37
|
+
|
|
27
38
|
def get_E0(path: Path = 'OSZICAR'):
|
|
28
39
|
"Get the E0 from the last line of OSZICAR."
|
|
29
40
|
fh = Path(path)
|
|
@@ -252,7 +252,7 @@ class FilesWidget(VBox):
|
|
|
252
252
|
other_widgets=None,
|
|
253
253
|
other_controls=None,
|
|
254
254
|
options={"manual": False},
|
|
255
|
-
height="
|
|
255
|
+
height="400px",
|
|
256
256
|
**kwargs,
|
|
257
257
|
):
|
|
258
258
|
"""
|
|
@@ -384,7 +384,7 @@ class FilesWidget(VBox):
|
|
|
384
384
|
other_widgets=None,
|
|
385
385
|
other_controls=None,
|
|
386
386
|
options={"manual": False},
|
|
387
|
-
height="
|
|
387
|
+
height="400px",
|
|
388
388
|
**kwargs,
|
|
389
389
|
):
|
|
390
390
|
"""Interact with a function that takes a selected Path as first argument.
|
|
@@ -649,7 +649,7 @@ class BandsWidget(VBox):
|
|
|
649
649
|
self.selected_data returns the last selection of points within a box or lasso. You can plot that output separately as plt.plot(data.xs, data.ys) after a selection.
|
|
650
650
|
"""
|
|
651
651
|
|
|
652
|
-
def __init__(self, use_vaspout=False, height="
|
|
652
|
+
def __init__(self, use_vaspout=False, height="450px", **file_widget_kwargs):
|
|
653
653
|
super().__init__(_dom_classes=["BandsWidget"])
|
|
654
654
|
self._bands = None
|
|
655
655
|
self._use_vaspout = use_vaspout
|
|
@@ -659,7 +659,7 @@ class BandsWidget(VBox):
|
|
|
659
659
|
)
|
|
660
660
|
self._click = Dropdown(description="Click", options=["None", "VBM", "CBM"])
|
|
661
661
|
self._ktcicks = Text(description="kticks")
|
|
662
|
-
self.
|
|
662
|
+
self._brange = ipw.IntRangeSlider(description="bands",min=1, max=1) # number, not index
|
|
663
663
|
self._ppicks = PropsPicker(
|
|
664
664
|
on_button_click=self._update_graph, on_selection_changed=self._warn_update
|
|
665
665
|
)
|
|
@@ -677,7 +677,7 @@ class BandsWidget(VBox):
|
|
|
677
677
|
other_widgets=[self._fig],
|
|
678
678
|
other_controls=[
|
|
679
679
|
self._tsd,
|
|
680
|
-
self.
|
|
680
|
+
self._brange,
|
|
681
681
|
self._ktcicks,
|
|
682
682
|
ipw.HTML("<hr/>"),
|
|
683
683
|
self._ppicks,
|
|
@@ -691,7 +691,7 @@ class BandsWidget(VBox):
|
|
|
691
691
|
self._tsd.observe(self._change_theme, "value")
|
|
692
692
|
self._click.observe(self._click_save_data, "value")
|
|
693
693
|
self._ktcicks.observe(self._warn_update, "value")
|
|
694
|
-
self.
|
|
694
|
+
self._brange.observe(self._warn_update, "value")
|
|
695
695
|
|
|
696
696
|
@property
|
|
697
697
|
def path(self):
|
|
@@ -708,6 +708,7 @@ class BandsWidget(VBox):
|
|
|
708
708
|
self._ktcicks.value = ", ".join(
|
|
709
709
|
f"{k}:{v}" for k, v in self.bands.get_kticks()
|
|
710
710
|
)
|
|
711
|
+
self._brange.max = self.bands.source.summary.NBANDS
|
|
711
712
|
if self.bands.source.summary.LSORBIT:
|
|
712
713
|
self._click.options = ["None", "VBM", "CBM", "so_max", "so_min"]
|
|
713
714
|
else:
|
|
@@ -762,8 +763,8 @@ class BandsWidget(VBox):
|
|
|
762
763
|
for vs in self._ktcicks.value.split(",")
|
|
763
764
|
]
|
|
764
765
|
kticks = [(int(vs[0]), vs[1]) for vs in hsk if len(vs) == 2] or None
|
|
765
|
-
|
|
766
|
-
|
|
766
|
+
self._kwargs = {"kticks": kticks, # below numbers instead of index and full shown range
|
|
767
|
+
"bands": range(self._brange.value[0] - 1, self._brange.value[1]) if self._brange.value else None}
|
|
767
768
|
|
|
768
769
|
if self._ppicks.projections:
|
|
769
770
|
self._kwargs = {"projections": self._ppicks.projections, **self._kwargs}
|
|
@@ -871,7 +872,7 @@ class KpathWidget(VBox):
|
|
|
871
872
|
- To break the path between two points "Γ" and "X" type "Γ 0,X" in the "Labels" box, zero means no points in interval.
|
|
872
873
|
"""
|
|
873
874
|
|
|
874
|
-
def __init__(self, height="
|
|
875
|
+
def __init__(self, height="400px", **files_widget_kwargs):
|
|
875
876
|
super().__init__(_dom_classes=["KpathWidget"])
|
|
876
877
|
self._fig = go.FigureWidget()
|
|
877
878
|
self._sm = SelectMultiple(options=[], layout=Layout(width="auto"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ipyvasp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: A processing tool for VASP DFT input/output processing in Jupyter Notebook.
|
|
5
5
|
Home-page: https://github.com/massgh/ipyvasp
|
|
6
6
|
Author: Abdul Saboor
|
|
@@ -32,7 +32,7 @@ Requires-Dist: nglview>=3.0.4; extra == "extra"
|
|
|
32
32
|
|
|
33
33
|
# ipyvasp
|
|
34
34
|
|
|
35
|
-
An
|
|
35
|
+
An VASP-based DFT pre and post processing tool.
|
|
36
36
|
|
|
37
37
|
## Install
|
|
38
38
|
Currently the package is being built and not stable. If you want to use development version, install this way:(recommended to install in a virtual environment)
|
|
@@ -68,6 +68,9 @@ Interactively select bandstructure path by clicking on high symmetry points on p
|
|
|
68
68
|
|
|
69
69
|

|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Apply operations on POSCAR and simultaneously view using plotly's `FigureWidget` or `WeasWidget` in Jupyterlab side by side.
|
|
72
|
+
|
|
73
|
+

|
|
72
74
|
|
|
73
75
|
|
|
76
|
+
More coming soon!
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.7.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|