ipyvasp 0.8.3__tar.gz → 0.8.5__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.8.3 → ipyvasp-0.8.5}/PKG-INFO +1 -1
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/_lattice.py +25 -60
- ipyvasp-0.8.5/ipyvasp/_version.py +1 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/core/serializer.py +46 -2
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/lattice.py +2 -2
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/PKG-INFO +1 -1
- ipyvasp-0.8.3/ipyvasp/_version.py +0 -1
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/LICENSE +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/README.md +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/__init__.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/__main__.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/_enplots.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/bsdos.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/cli.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/core/__init__.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/core/parser.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/core/plot_toolkit.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/core/spatial_toolkit.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/evals_dataframe.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/misc.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/potential.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/utils.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp/widgets.py +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/SOURCES.txt +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/dependency_links.txt +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/entry_points.txt +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/requires.txt +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/ipyvasp.egg-info/top_level.txt +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/setup.cfg +0 -0
- {ipyvasp-0.8.3 → ipyvasp-0.8.5}/setup.py +0 -0
|
@@ -23,7 +23,7 @@ from .core import parser as vp, serializer
|
|
|
23
23
|
from .core.spatial_toolkit import (
|
|
24
24
|
to_plane,
|
|
25
25
|
rotation,
|
|
26
|
-
inside_convexhull,
|
|
26
|
+
inside_convexhull, # be there for export
|
|
27
27
|
to_basis,
|
|
28
28
|
to_R3,
|
|
29
29
|
get_TM,
|
|
@@ -31,6 +31,7 @@ from .core.spatial_toolkit import (
|
|
|
31
31
|
coplanar,
|
|
32
32
|
)
|
|
33
33
|
from .core.plot_toolkit import quiver3d
|
|
34
|
+
from .utils import color as tcolor
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
# These colors are taken from Mathematica's ColorData["Atoms"]
|
|
@@ -2397,12 +2398,12 @@ def convert_poscar(poscar_data, atoms_mapping, basis_factor):
|
|
|
2397
2398
|
return serializer.PoscarData(poscar_data) # Return new POSCAR
|
|
2398
2399
|
|
|
2399
2400
|
|
|
2400
|
-
def transform_poscar(poscar_data, transformation,
|
|
2401
|
+
def transform_poscar(poscar_data, transformation, fill_factor=2, tol=1e-2):
|
|
2401
2402
|
"""Transform a POSCAR with a given transformation matrix or function that takes old basis and return target basis.
|
|
2402
2403
|
Use `get_TM(basis1, basis2)` to get transformation matrix from one basis to another or function to return new basis of your choice.
|
|
2403
2404
|
An example of transformation function is `lambda a,b,c: a + b, a-b, c` which will give a new basis with a+b, a-b, c as basis vectors.
|
|
2404
2405
|
|
|
2405
|
-
You may find errors due to missing atoms in the new basis, use `
|
|
2406
|
+
You may find errors due to missing atoms in the new basis, use `fill_factor` and `tol` to include any possible site in new cell.
|
|
2406
2407
|
|
|
2407
2408
|
Examples
|
|
2408
2409
|
--------
|
|
@@ -2426,74 +2427,38 @@ def transform_poscar(poscar_data, transformation, zoom=2, tol=1e-2):
|
|
|
2426
2427
|
raise Exception(
|
|
2427
2428
|
"transformation should be a function that accept 3 arguemnts or 3x3 matrix"
|
|
2428
2429
|
)
|
|
2430
|
+
if not isinstance(fill_factor,int):
|
|
2431
|
+
raise TypeError(f'fill_factor should be int, got {type(fill_factor)}')
|
|
2429
2432
|
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
TM = get_TM(
|
|
2437
|
-
poscar_data.basis, new_basis
|
|
2438
|
-
) # Transformation matrix to save before scaling
|
|
2439
|
-
old_numbers = [len(v) for v in poscar_data.types.to_dict().values()]
|
|
2440
|
-
chull = ConvexHull(
|
|
2441
|
-
np.max([zoom, 2]) * get_cell_vertices(new_basis)
|
|
2442
|
-
) # Convex hull of new cell, make it at least two times to avoid losing sites because of translation
|
|
2443
|
-
|
|
2444
|
-
while any(
|
|
2445
|
-
inside_convexhull(chull, get_cell_vertices(poscar_data.basis))
|
|
2446
|
-
): # Check if all vertices of old cell are inside new cell
|
|
2447
|
-
poscar_data = scale_poscar(
|
|
2448
|
-
poscar_data, [2, 2, 2], tol=tol
|
|
2449
|
-
) # Repeat in all directions
|
|
2450
|
-
|
|
2451
|
-
points = to_basis(
|
|
2452
|
-
new_basis, poscar_data.coords
|
|
2453
|
-
) # Transform coordinates to new basis around origin
|
|
2454
|
-
|
|
2455
|
-
# Let's bring the most central point to origin
|
|
2456
|
-
close_to_origin = np.linalg.norm(points - np.mean(points, axis=0), axis=1)
|
|
2457
|
-
nearest_idx = np.argsort(close_to_origin)[0]
|
|
2458
|
-
points = points - points[nearest_idx] # one point is at origin now
|
|
2433
|
+
_p = range(-fill_factor, fill_factor + 1)
|
|
2434
|
+
pos = np.concatenate([poscar_data.positions,[[i] for i,_ in enumerate(poscar_data.positions)]], axis=1) # keep track of index
|
|
2435
|
+
pos = np.concatenate([pos + [*p,0] for p in set(product(_p,_p,_p))],axis=0) # increaser by fill_factor^3
|
|
2436
|
+
pos[:,:3] = to_basis(new_basis, poscar_data.to_cartesian(pos[:,:3])) # convert to coords in this and to points in new
|
|
2437
|
+
pos = pos[(pos[:,:3] <= 1 - tol).all(axis=1) & (pos[:,:3] >= -tol).all(axis=1)]
|
|
2438
|
+
pos = pos[pos[:,-1].argsort()] # sort for species
|
|
2459
2439
|
|
|
2460
2440
|
new_poscar = poscar_data.to_dict() # Update in it
|
|
2461
2441
|
new_poscar["basis"] = new_basis
|
|
2462
2442
|
new_poscar["metadata"]["scale"] = np.linalg.norm(new_basis[0])
|
|
2463
2443
|
new_poscar["metadata"]["comment"] = f"Transformed by ipyvasp"
|
|
2464
|
-
new_poscar["metadata"][
|
|
2465
|
-
|
|
2466
|
-
] = TM # Save transformation matrix in both function and matrix given
|
|
2444
|
+
new_poscar["metadata"]["TM"] = get_TM(poscar_data.basis, new_basis) # save Transformation matrix
|
|
2445
|
+
old_numbers = [len(v) for v in poscar_data.types.values()]
|
|
2467
2446
|
|
|
2468
|
-
uelems = poscar_data.types.to_dict()
|
|
2469
|
-
positions, shift, unique_dict = [], 0, {}
|
|
2470
|
-
for key, value in uelems.items():
|
|
2471
|
-
s_p = points[value]
|
|
2472
|
-
s_p = s_p[
|
|
2473
|
-
((s_p > -tol) & (s_p < 1 - tol)).all(axis=1)
|
|
2474
|
-
] # Get sites within tolerance, for very far sites
|
|
2475
2447
|
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2448
|
+
uelems, start = {}, 0
|
|
2449
|
+
for k, v in poscar_data.types.items():
|
|
2450
|
+
uelems[k] = range(start, start + len(pos[(pos[:,-1] >= v.start) & (pos[:,-1] < v.stop)]))
|
|
2451
|
+
start = uelems[k].stop
|
|
2480
2452
|
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
# Final check if crystal is still same
|
|
2486
|
-
new_numbers = [len(v) for v in unique_dict.values()]
|
|
2487
|
-
ratio = [
|
|
2488
|
-
round(new / old, 4) for new, old in zip(new_numbers, old_numbers)
|
|
2489
|
-
] # Round to avoid floating point errors,can cover 1 to 10000 atoms transformation
|
|
2453
|
+
# warn if crystal formula changes
|
|
2454
|
+
new_numbers = [len(v) for v in uelems.values()]
|
|
2455
|
+
ratio = (np.array(new_numbers)/old_numbers).round(4) # Round to avoid floating point errors,can cover 1 to 10000 atoms transformation
|
|
2490
2456
|
if len(np.unique(ratio)) != 1:
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
)
|
|
2457
|
+
print(tcolor.rb(f"WARNING: Transformation failed, atoms proportion changed: {old_numbers} -> {new_numbers}."
|
|
2458
|
+
" If your transformation is an allowed one for this structure, increase `fill_factor` or `tol`."))
|
|
2494
2459
|
|
|
2495
|
-
new_poscar["types"] =
|
|
2496
|
-
new_poscar["positions"] = np.array(
|
|
2460
|
+
new_poscar["types"] = uelems
|
|
2461
|
+
new_poscar["positions"] = np.array(pos[:,:3])
|
|
2497
2462
|
return serializer.PoscarData(new_poscar)
|
|
2498
2463
|
|
|
2499
2464
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.5"
|
|
@@ -12,11 +12,12 @@ import json, re
|
|
|
12
12
|
import pickle
|
|
13
13
|
import inspect
|
|
14
14
|
from collections import namedtuple
|
|
15
|
-
from itertools import product
|
|
15
|
+
from itertools import product,combinations
|
|
16
16
|
from copy import deepcopy
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
20
|
+
from pandas import DataFrame
|
|
20
21
|
from scipy.spatial import KDTree
|
|
21
22
|
|
|
22
23
|
from .spatial_toolkit import (
|
|
@@ -352,7 +353,6 @@ class PoscarData(Dict2Data):
|
|
|
352
353
|
tree = KDTree(cs)
|
|
353
354
|
_, inn = tree.query(cs, k=k)
|
|
354
355
|
output = (inn % N)[:N] # to get the index of the atom in the original list
|
|
355
|
-
output[:,1:] = np.sort(output[:,1:], axis=1) # sort the indices of neighbors
|
|
356
356
|
return output
|
|
357
357
|
|
|
358
358
|
get_knn = get_neighbors # important alias
|
|
@@ -402,6 +402,50 @@ class PoscarData(Dict2Data):
|
|
|
402
402
|
dists = dists[dists > 0] # Remove distance with itself
|
|
403
403
|
return np.min(dists) if dists.size else np.nan
|
|
404
404
|
|
|
405
|
+
|
|
406
|
+
def get_distances(self, type1, type2, min=-np.infty, max=np.infty):
|
|
407
|
+
"""Get an array of all distnaces in a range set by min and max between type 1 and type2.
|
|
408
|
+
For example `get_distances('Ga','As',2,3)[:,-1].mean()` can be used to get average bond length between Ga and As in GaAs.
|
|
409
|
+
Returned array is of shape (N,3) where first two entries in columns are indices of pairs between which distance was calculated.
|
|
410
|
+
"""
|
|
411
|
+
out = []
|
|
412
|
+
for i in self.types[type1]:
|
|
413
|
+
for j in [k for k in self.types[type2] if k != i]:
|
|
414
|
+
a = self.coords[i]
|
|
415
|
+
bs = [self.to_cartesian(self.positions[j] + p) for p in set(product([-1,0,1],[-1,0,1],[-1,0,1]))]
|
|
416
|
+
ds = np.array([np.linalg.norm(a-b) for b in bs])
|
|
417
|
+
d = ds[ds > 0].min() # no same site distance
|
|
418
|
+
if min < d < max:
|
|
419
|
+
out.append([i, j, d])
|
|
420
|
+
out = np.array(out,dtype=object)
|
|
421
|
+
return out[out[:,-1].argsort()] if out.size else out
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def get_bond_data(self, site_indices, k = 5):
|
|
425
|
+
"Returns a DataFrame with bonds angle, bond length, vector positions etc. that can be used for plotting."
|
|
426
|
+
if k < 3:
|
|
427
|
+
raise ValueError("k >= 3 is required!")
|
|
428
|
+
|
|
429
|
+
idxs = self.get_knn(k)[list(site_indices)]
|
|
430
|
+
out = []
|
|
431
|
+
for i, *js in idxs:
|
|
432
|
+
a = self.coords[i]
|
|
433
|
+
nears = [] # neaigbors could be on other side, bring close
|
|
434
|
+
for j in js:
|
|
435
|
+
bs = np.array([self.to_cartesian(self.positions[j] + p) for p in product([-1,0,1],[-1,0,1],[-1,0,1])])
|
|
436
|
+
ds = np.array([np.linalg.norm(a-b) for b in bs])
|
|
437
|
+
nears.append((j, bs[ds.argsort()][0]))
|
|
438
|
+
|
|
439
|
+
for (m,b),(n, c) in combinations(nears,2):
|
|
440
|
+
v1, v2 = b-a, c-a
|
|
441
|
+
n1, n2 = np.linalg.norm(v1), np.linalg.norm(v2)
|
|
442
|
+
name = '-'.join(self.symbols[[m,i,n]])
|
|
443
|
+
angle = np.degrees(np.arccos(v1.dot(v2)/(n1*n2)))
|
|
444
|
+
out.append([name, m,i,n, angle, n1, n2, *b, *a, *c])
|
|
445
|
+
|
|
446
|
+
columns = 'bond a o b angle d_ao d_bo ax ay az ox oy oz bx by bz'.split()
|
|
447
|
+
return DataFrame(out, columns=columns)
|
|
448
|
+
|
|
405
449
|
def to_fractional(self, coords):
|
|
406
450
|
"Converts cartesian coordinates to fractional coordinates in the basis of cell."
|
|
407
451
|
return to_basis(self.basis, coords)
|
|
@@ -685,9 +685,9 @@ class POSCAR:
|
|
|
685
685
|
return stk.get_TM(self.data.basis, target_basis)
|
|
686
686
|
|
|
687
687
|
@_sub_doc(plat.transform_poscar)
|
|
688
|
-
def transform(self, transformation,
|
|
688
|
+
def transform(self, transformation, fill_factor=2, tol=1e-2):
|
|
689
689
|
return self.__class__(
|
|
690
|
-
data=plat.transform_poscar(self.data, transformation,
|
|
690
|
+
data=plat.transform_poscar(self.data, transformation, fill_factor=fill_factor, tol=tol)
|
|
691
691
|
)
|
|
692
692
|
|
|
693
693
|
@_sub_doc(plat.transpose_poscar)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.3"
|
|
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
|
|
File without changes
|