ipyvasp 0.9.8__tar.gz → 0.9.82__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.9.8 → ipyvasp-0.9.82}/PKG-INFO +1 -1
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/_lattice.py +16 -8
- ipyvasp-0.9.82/ipyvasp/_version.py +1 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/core/serializer.py +10 -5
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/core/spatial_toolkit.py +3 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/lattice.py +60 -7
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/PKG-INFO +1 -1
- ipyvasp-0.9.8/ipyvasp/_version.py +0 -1
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/LICENSE +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/README.md +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/__init__.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/__main__.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/_enplots.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/bsdos.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/cli.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/core/__init__.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/core/parser.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/core/plot_toolkit.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/evals_dataframe.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/misc.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/potential.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/utils.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp/widgets.py +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/SOURCES.txt +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/dependency_links.txt +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/entry_points.txt +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/requires.txt +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/ipyvasp.egg-info/top_level.txt +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/setup.cfg +0 -0
- {ipyvasp-0.9.8 → ipyvasp-0.9.82}/setup.py +0 -0
|
@@ -1572,6 +1572,7 @@ def _get_bond_length(poscar_data, bond_length=None):
|
|
|
1572
1572
|
class _Atom(NamedTuple):
|
|
1573
1573
|
"Object passed to POSCAR operations `func` where atomic sites are modified. Additinal property p -> array([x,y,z])."
|
|
1574
1574
|
symbol : str
|
|
1575
|
+
number : int
|
|
1575
1576
|
index : int
|
|
1576
1577
|
x : float
|
|
1577
1578
|
y : float
|
|
@@ -1590,14 +1591,14 @@ class _AtomLabel(str):
|
|
|
1590
1591
|
|
|
1591
1592
|
def _validate_func(func):
|
|
1592
1593
|
if not callable(func):
|
|
1593
|
-
raise ValueError("`func` must be a callable function with single parameter `Atom(symbol,index,x,y,z)`.")
|
|
1594
|
+
raise ValueError("`func` must be a callable function with single parameter `Atom(symbol,number, index,x,y,z)`.")
|
|
1594
1595
|
|
|
1595
1596
|
if len(inspect.signature(func).parameters) != 1:
|
|
1596
1597
|
raise ValueError(
|
|
1597
|
-
"`func` takes exactly 1 argument: `Atom(symbol,index,x,y,z)` in fractional coordinates"
|
|
1598
|
+
"`func` takes exactly 1 argument: `Atom(symbol, number, index,x,y,z)` in fractional coordinates"
|
|
1598
1599
|
)
|
|
1599
1600
|
|
|
1600
|
-
ret = func(_Atom('',0,0,0,0))
|
|
1601
|
+
ret = func(_Atom('',0,0,0,0,0))
|
|
1601
1602
|
if not isinstance(ret, (bool, np.bool_)):
|
|
1602
1603
|
raise ValueError(
|
|
1603
1604
|
f"`func` must be a function that returns a bool, got {type(ret)}."
|
|
@@ -1611,7 +1612,7 @@ def _masked_data(poscar_data, func):
|
|
|
1611
1612
|
pick = []
|
|
1612
1613
|
for i, pos in enumerate(poscar_data.positions):
|
|
1613
1614
|
idx = eqv_inds[i] if eqv_inds else i # map to original index
|
|
1614
|
-
if func(_Atom(poscar_data.
|
|
1615
|
+
if func(_Atom(*poscar_data._sn[i], idx, *pos)): # labels based on i, not eqv_idx
|
|
1615
1616
|
pick.append(i)
|
|
1616
1617
|
return pick # could be duplicate indices
|
|
1617
1618
|
|
|
@@ -1633,10 +1634,13 @@ def _filter_pairs(labels, pairs, dist, bond_length):
|
|
|
1633
1634
|
return pairs # None -> auto calculate bond_length, number -> use that number
|
|
1634
1635
|
|
|
1635
1636
|
def filter_atoms(poscar_data, func, tol = 0.01):
|
|
1636
|
-
"""Filter atomic sites based on a function that acts on an atom such as `lambda a: (a.p < 1/2).all()`.
|
|
1637
|
+
"""Filter atomic sites based on a function that acts on an atom such as `lambda a: (a.p < 1/2).all()`.
|
|
1638
|
+
`atom` passed to function is a namedtuple like `Atom(symbol,number,index,x,y,z)` which has extra attribute `p = array([x,y,z])`.
|
|
1637
1639
|
This may include equivalent sites, so it should be used for plotting purpose only, e.g. showing atoms on a plane.
|
|
1638
1640
|
An attribute `source_indices` is added to metadata which is useful to pick other things such as `OUTCAR.ion_pot[POSCAR.filter(...).data.metadata.source_indices]`.
|
|
1639
1641
|
|
|
1642
|
+
>>> filter_atoms(..., lambda a: a.symbol=='Ga' or a.number in range(2)) # picks all Ga atoms and first two atoms of every other types.
|
|
1643
|
+
|
|
1640
1644
|
Note: If you are filtering a plane with more than one non-zero hkl like 110, you may first need to translate or set boundary on POSCAR to bring desired plane in full view to include all atoms.
|
|
1641
1645
|
"""
|
|
1642
1646
|
if hasattr(poscar_data.metadata, 'source_indices'):
|
|
@@ -1931,7 +1935,8 @@ def splot_lattice(
|
|
|
1931
1935
|
bond_kws : dict
|
|
1932
1936
|
Keyword arguments to pass to `LineCollection`/`Line3DCollection` for plotting bonds.
|
|
1933
1937
|
fmt_label : callable
|
|
1934
|
-
If given, each site label is passed to it as a subclass of str 'Ga 1' with extra attributes `symbol` and `number` and a method `to_latex
|
|
1938
|
+
If given, each site label is passed to it as a subclass of str 'Ga 1' with extra attributes `symbol` and `number` and a method `to_latex`.
|
|
1939
|
+
You can show specific labels based on condition, e.g. `lambda lab: lab.to_latex() if lab.number in [1,5] else ''` will show 1st and 5th atom of each types.
|
|
1935
1940
|
It must return a string or a list/tuple of length 2 with first item as label and second item as dictionary of keywords to pass to `plt.text`.
|
|
1936
1941
|
plot_cell : bool
|
|
1937
1942
|
Default is True, plot unit cell with default settings.
|
|
@@ -2583,9 +2588,12 @@ def sort_poscar(poscar_data, new_order):
|
|
|
2583
2588
|
return serializer.PoscarData(data)
|
|
2584
2589
|
|
|
2585
2590
|
def remove_atoms(poscar_data, func, fillby=None):
|
|
2586
|
-
"""Remove atoms that satisfy `func(atom) -> bool` on their fractional coordinates like `lambda a: all(a.p < 1/2)`.
|
|
2591
|
+
"""Remove atoms that satisfy `func(atom) -> bool` on their fractional coordinates like `lambda a: all(a.p < 1/2)`.
|
|
2592
|
+
`atom` passed to function is a namedtuple like `Atom(symbol,number,index,x,y,z)` which has extra attribute `p = array([x,y,z])`.
|
|
2587
2593
|
If `fillby` is given, it will fill the removed atoms with atoms from fillby POSCAR.
|
|
2588
2594
|
|
|
2595
|
+
>>> remove_atoms(..., lambda a: sum((a.p - 0.5)**2) <= 0.25**2) # remove atoms in center of cell inside radius of 0.25
|
|
2596
|
+
|
|
2589
2597
|
.. note::
|
|
2590
2598
|
The coordinates of fillby POSCAR are transformed to basis of given POSCAR, before filling.
|
|
2591
2599
|
So a good filling is only guaranteed if both POSCARs have smaller lattice mismatch.
|
|
@@ -2612,7 +2620,7 @@ def remove_atoms(poscar_data, func, fillby=None):
|
|
|
2612
2620
|
|
|
2613
2621
|
def keep_pos(i, x, y, z): # keep positions in basis of given data
|
|
2614
2622
|
u, v, w = to_basis(poscar_data.basis, to_R3(fillby.basis, [[x, y, z]]))[0]
|
|
2615
|
-
return bool(func(_Atom('', 0, u, v, w)))
|
|
2623
|
+
return bool(func(_Atom('', 0, 0, u, v, w)))
|
|
2616
2624
|
|
|
2617
2625
|
mask = _masked_data(fillby, keep_pos)
|
|
2618
2626
|
N_prev = len(data["positions"]) # before filling
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.82"
|
|
@@ -329,6 +329,10 @@ class PoscarData(Dict2Data):
|
|
|
329
329
|
"Returns the symbols of the atoms in the poscar data without numbers"
|
|
330
330
|
return np.array([lab.split()[0] for lab in self.labels])
|
|
331
331
|
|
|
332
|
+
@property
|
|
333
|
+
def _sn(self): # symbol and number
|
|
334
|
+
return np.array([[f(v) for f,v in zip((str,int),lab.split())] for lab in self.labels],dtype=object)
|
|
335
|
+
|
|
332
336
|
@property
|
|
333
337
|
def sites(self):
|
|
334
338
|
"Returns data with types mapped to their positions."
|
|
@@ -493,7 +497,8 @@ class PoscarData(Dict2Data):
|
|
|
493
497
|
"""
|
|
494
498
|
Returns a dictionary of {'Ga 1': 'T T T', 'As 1': 'T F F',...} for each atom in the poscar data.
|
|
495
499
|
|
|
496
|
-
`func` should be a callable like `func(
|
|
500
|
+
`func` should be a callable like `func(atom) -> (bool, bool, bool)` which turns on/off selective dynamics for each atom based in each dimension.
|
|
501
|
+
`atom` passed to function is a namedtuple like `Atom(symbol,number,index,x,y,z)` which has extra attribute `p = array([x,y,z])`.
|
|
497
502
|
|
|
498
503
|
You can visualize selective dynamics sites by their labels as follows:
|
|
499
504
|
|
|
@@ -504,17 +509,17 @@ class PoscarData(Dict2Data):
|
|
|
504
509
|
"""
|
|
505
510
|
if not callable(func):
|
|
506
511
|
raise TypeError(
|
|
507
|
-
"`func` should be a callable with one paramter `Atom(symbol,index, x,y,z)`"
|
|
512
|
+
"`func` should be a callable with one paramter `Atom(symbol,number, index, x,y,z)`"
|
|
508
513
|
)
|
|
509
514
|
|
|
510
515
|
if len(inspect.signature(func).parameters) != 1:
|
|
511
516
|
raise ValueError(
|
|
512
|
-
"`func` should be a callable function with one paramter `Atom(symbol,index, x,y,z)` in fractional coordinates."
|
|
517
|
+
"`func` should be a callable function with one paramter `Atom(symbol,number, index, x,y,z)` in fractional coordinates."
|
|
513
518
|
)
|
|
514
519
|
|
|
515
520
|
from .._lattice import _Atom # avoids circular import
|
|
516
521
|
|
|
517
|
-
test_output = func(_Atom('',0, 0, 0, 0))
|
|
522
|
+
test_output = func(_Atom('',0, 0, 0, 0, 0))
|
|
518
523
|
if (
|
|
519
524
|
not isinstance(test_output, (list, tuple, np.ndarray))
|
|
520
525
|
or len(test_output) != 3
|
|
@@ -530,7 +535,7 @@ class PoscarData(Dict2Data):
|
|
|
530
535
|
)
|
|
531
536
|
|
|
532
537
|
sd_list = [
|
|
533
|
-
" ".join("T" if s else "F" for s in func(_Atom(self.
|
|
538
|
+
" ".join("T" if s else "F" for s in func(_Atom(*self._sn[i],i, *p)))
|
|
534
539
|
for i, p in enumerate(self.positions)
|
|
535
540
|
]
|
|
536
541
|
labels = np.array(
|
|
@@ -315,6 +315,9 @@ def get_bz(basis, loop=True, primitive=False):
|
|
|
315
315
|
"vertices": verts,
|
|
316
316
|
"faces": idx_faces,
|
|
317
317
|
"primitive": primitive,
|
|
318
|
+
"edges": np.unique(
|
|
319
|
+
np.sort([[i,j] for f in idx_faces for i,j in zip(f[:-1],f[1:])], axis=1),
|
|
320
|
+
axis=0)
|
|
318
321
|
}
|
|
319
322
|
from .serializer import BrZoneData # to avoid circular import
|
|
320
323
|
|
|
@@ -18,11 +18,13 @@ from contextlib import suppress
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
from pandas.io.clipboard import clipboard_get, clipboard_set
|
|
20
20
|
import matplotlib.colors as mcolors
|
|
21
|
+
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
|
22
|
+
import plotly.graph_objects as go
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
from .core import serializer
|
|
24
26
|
from .core import spatial_toolkit as stk
|
|
25
|
-
from .core.plot_toolkit import iplot2widget
|
|
27
|
+
from .core.plot_toolkit import get_axes, iplot2widget
|
|
26
28
|
from .utils import _sig_kwargs, _sub_doc
|
|
27
29
|
from . import _lattice as plat
|
|
28
30
|
from ._lattice import (
|
|
@@ -313,13 +315,13 @@ class POSCAR:
|
|
|
313
315
|
|
|
314
316
|
Prefrence order: data > content > path
|
|
315
317
|
|
|
316
|
-
Note:
|
|
318
|
+
Note: POSCAR operations that need a `func` accept basis, atom tuple, label etc. Read their documentation.
|
|
317
319
|
|
|
318
320
|
```python
|
|
319
321
|
pc = POSCAR()
|
|
320
|
-
pc.filter_atoms(lambda a: a.
|
|
321
|
-
pc.
|
|
322
|
-
pc.
|
|
322
|
+
pc.filter_atoms(lambda a: a.symbol == 'Ga') # a is namedtuple `Atom(symbol,number,index,x,y,z)` which has extra attribute `p = array([x,y,z])`.
|
|
323
|
+
pc.transform(lambda a,b,c: (a+b,a-b,c)) # basis or transform matrix
|
|
324
|
+
pc.splot_lattice(lambda lab: lab.to_latex()) # lab is str subclass like `AtomLabel('Ga 1')` with extra attributes `symbol,number, to_latex()` that can be used to show specific sites labels only.
|
|
323
325
|
```
|
|
324
326
|
|
|
325
327
|
Tip: You can use `self.auto_renderer.on()` to keep doing opertions and visualize while last line of any cell is a POSCAR object.
|
|
@@ -354,11 +356,11 @@ class POSCAR:
|
|
|
354
356
|
|
|
355
357
|
@property
|
|
356
358
|
def last(self):
|
|
357
|
-
"""Points to last created POSCAR instance during chained operations!
|
|
359
|
+
"""Points to last created POSCAR instance during chained operations! You don't need to store results.
|
|
358
360
|
|
|
359
361
|
```python
|
|
360
362
|
pc = POSCAR()
|
|
361
|
-
pc.filter_atoms(lambda a: a.index in pc.data.types.Ga) # FINE
|
|
363
|
+
pc.filter_atoms(lambda a: a.index in pc.data.types.Ga) # FINE, can use a.symbol == 'Ga' too, but we need to show a point below
|
|
362
364
|
pc.set_boundary([-2,2]).filter_atoms(lambda a: a.index in pc.data.types.Ga) # INCORRECT sites picked
|
|
363
365
|
pc.set_boundary([-2,2]).filter_atoms(lambda a: a.index in pc.last.data.types.Ga) # PERFECT, pc.last is output of set_boundary
|
|
364
366
|
```
|
|
@@ -626,6 +628,57 @@ class POSCAR:
|
|
|
626
628
|
if not hasattr(self, "_cell"):
|
|
627
629
|
self._cell = self.get_cell()
|
|
628
630
|
return self._cell
|
|
631
|
+
|
|
632
|
+
def get_plane(self, hkl, d=1/2,tol=1e-2):
|
|
633
|
+
"""Returns Nx3 vertices of a plane bound inside cell. .
|
|
634
|
+
hkl should be list of three miller indices. d is fractional distance in range 0,1 in direction of hkl.
|
|
635
|
+
e.g. if there are 8 planes of atoms in a cubic cell, d = 0, 1/8,...7/8, 1 match position of those planes.
|
|
636
|
+
"""
|
|
637
|
+
from sympy import Point3D, Line3D, Plane
|
|
638
|
+
V = self.data.rec_basis.dot(hkl)
|
|
639
|
+
normal = V/np.linalg.norm(V)
|
|
640
|
+
point = d*normal*np.linalg.norm(self.data.basis.dot(hkl)) # to make d 0-1
|
|
641
|
+
P = Plane(Point3D(*point),normal_vector=normal)
|
|
642
|
+
|
|
643
|
+
pts = []
|
|
644
|
+
for e in self.cell.vertices[self.cell.edges]:
|
|
645
|
+
L = Line3D(Point3D(*e[0]),Point3D(*e[1]))
|
|
646
|
+
if (isc := P.intersection(L)) and isinstance(isc[0],Point3D):
|
|
647
|
+
pts.append(isc[0])
|
|
648
|
+
|
|
649
|
+
pts = np.unique(np.array(pts,dtype=float),axis=0)
|
|
650
|
+
pts = pts[stk.order(pts,)]
|
|
651
|
+
qts = self.cell.to_fractional(pts)
|
|
652
|
+
qts = qts[(qts >= -tol).all(axis=1) & (qts <= 1 + tol).all(axis=1)]
|
|
653
|
+
pts = self.cell.to_cartesian(qts)
|
|
654
|
+
return pts
|
|
655
|
+
|
|
656
|
+
def splot_plane(self, hkl, d=1/2,tol=1e-2,ax=None, **kwargs):
|
|
657
|
+
"""Provide hkl and a 3D axes to plot plane. kwargs are passed to `mpl_toolkits.mplot3d.art3d.Poly3DCollection`
|
|
658
|
+
Note: You may get wrong plane if your basis are not aligned to axes. So you can use `transpose` or `set_zdir` methods before plottling cell.
|
|
659
|
+
"""
|
|
660
|
+
P = self.get_plane(hkl,d=d,tol=tol)
|
|
661
|
+
if ax is None:
|
|
662
|
+
ax = get_axes(axes_3d=True)
|
|
663
|
+
ax.set( # it does not show otherwise
|
|
664
|
+
xlim=[P[:,0].min(),P[:,0].max()],
|
|
665
|
+
ylim=[P[:,1].min(),P[:,1].max()],
|
|
666
|
+
zlim=[P[:,2].min(),P[:,2].max()]
|
|
667
|
+
)
|
|
668
|
+
kwargs = {'alpha':0.5,'color':'#898','shade': False, **kwargs}
|
|
669
|
+
ax.add_collection(Poly3DCollection([P],**kwargs))
|
|
670
|
+
ax.autoscale_view()
|
|
671
|
+
return ax
|
|
672
|
+
|
|
673
|
+
def iplot_plane(self, hkl, d = 1/2, tol=1e-3, fig=None,**kwargs):
|
|
674
|
+
"Plot plane on a plotly Figure. kwargs are passed to `plotly.graph_objects.Mesh3d`."
|
|
675
|
+
if fig is None:
|
|
676
|
+
fig = go.Figure()
|
|
677
|
+
|
|
678
|
+
P = self.get_plane(hkl,d=d,tol=tol)
|
|
679
|
+
kwargs = {**dict(color='#8a8',opacity=0.7,alphahull=0, showlegend=True,name=str(hkl)),**kwargs}
|
|
680
|
+
fig.add_trace(go.Mesh3d({k:v for v,k in zip(P.T, 'xyz')},**kwargs))
|
|
681
|
+
return fig
|
|
629
682
|
|
|
630
683
|
@_sub_doc(stk.get_bz, {"basis :.*loop :": "loop :"})
|
|
631
684
|
@_sig_kwargs(stk.get_bz, ("basis",))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.9.8"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|