ipyvasp 0.8.3__py2.py3-none-any.whl → 0.8.5__py2.py3-none-any.whl

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/_lattice.py CHANGED
@@ -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, zoom=2, tol=1e-2):
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 `zoom` to increase the size of given cell to include any possible site in new cell.
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
- def get_cell_vertices(basis):
2431
- verts = get_bz(basis, primitive=True).vertices
2432
- return verts - np.mean(
2433
- verts, axis=0
2434
- ) # Center around origin, otherwise it never going to be inside convex hull
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
- "TM"
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
- if s_p.size == 0:
2477
- raise Exception(
2478
- f"No sites found for {key!r}, transformation stopped! You may need to modify `transformation` or increase `zoom` value."
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
- unique_dict[key] = range(shift, shift + len(s_p))
2482
- positions = [*positions, *s_p] # Pick sites
2483
- shift += len(s_p) # Update for next element
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
- raise Exception(
2492
- f"Transformation failed, atoms proportion changed: {old_numbers} -> {new_numbers}, if your transformation is an allowed one for this structure, increase `zoom` value."
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"] = unique_dict
2496
- new_poscar["positions"] = np.array(positions)
2460
+ new_poscar["types"] = uelems
2461
+ new_poscar["positions"] = np.array(pos[:,:3])
2497
2462
  return serializer.PoscarData(new_poscar)
2498
2463
 
2499
2464
 
ipyvasp/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.8.3"
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)
ipyvasp/lattice.py CHANGED
@@ -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, zoom=2, tol=1e-2):
688
+ def transform(self, transformation, fill_factor=2, tol=1e-2):
689
689
  return self.__class__(
690
- data=plat.transform_poscar(self.data, transformation, zoom=zoom, tol=tol)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyvasp
3
- Version: 0.8.3
3
+ Version: 0.8.5
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
@@ -1,12 +1,12 @@
1
1
  ipyvasp/__init__.py,sha256=7o41i5eYlNKg1Hsv0DLNFZ81GilxB02IXAJN-QiJQi0,1420
2
2
  ipyvasp/__main__.py,sha256=eJV1TZSiT8mC_VqAeksNnBI2I8mKMiPkEIlwikbtOjI,216
3
3
  ipyvasp/_enplots.py,sha256=D38paN8zqZgluNAwmCwcocd7-_h_T0HTGolI1eBkDes,37484
4
- ipyvasp/_lattice.py,sha256=GlSoBwS_LckiZIArtZ3CbFfktG29oBqEAZtLHwYtzak,104390
5
- ipyvasp/_version.py,sha256=AM0-MB9td9-Rmee_1ARKY1bWYwq-qbjwR15DCGe0x6o,23
4
+ ipyvasp/_lattice.py,sha256=-0Zo12xSPgwWy1TEPtrsX6iEBah6VhpH4GuZ7f98K8g,103373
5
+ ipyvasp/_version.py,sha256=80NvSNse6emwcC7J2uzxIjsOMz3EsAiMsuAyJRp8sE0,23
6
6
  ipyvasp/bsdos.py,sha256=ZtQji-W11UdFFicAoWZjlqVhI5tqYu_jpKyPPWKkeeo,30634
7
7
  ipyvasp/cli.py,sha256=aWFEVhNmnW8eSOp5uh95JaDwLQ9K9nlCQcbnOSuhWgw,6844
8
8
  ipyvasp/evals_dataframe.py,sha256=-sqxK7LPV6sYDO_XXmZ80FznOaXTkVdbqJKKvTUtMak,20637
9
- ipyvasp/lattice.py,sha256=t_aIkfVtD9Thi0b3ACEtAYcTuzt0SGCtub7hKV-Q83c,28468
9
+ ipyvasp/lattice.py,sha256=ElvILd6TbIGvbH3jj5537EhHl6T5gx6rYhejNMGzyNs,28489
10
10
  ipyvasp/misc.py,sha256=SZJ_ePUR2-HEKYTEpDHVRVE7zpIQVTCjiuw0BCC9UTU,2349
11
11
  ipyvasp/potential.py,sha256=tzA73c5lkp6ahLSJchMrU043-QWaOV0nIOUA7VMmfKQ,11408
12
12
  ipyvasp/surface.py,sha256=MjE5oB0wW6Pca_C-xu8rN6OMH7lUEeNPNyM7Kz_Im-8,23766
@@ -15,11 +15,11 @@ ipyvasp/widgets.py,sha256=fZ2b7EYYxuaAVfklnLa0VJ00U9Uyd7SqXrzt0hbLOvI,45400
15
15
  ipyvasp/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  ipyvasp/core/parser.py,sha256=C3CaZsJbPME_ttYlYy4DXeOdL7dnkXs-cHRwFZL6bio,38058
17
17
  ipyvasp/core/plot_toolkit.py,sha256=cktbPZTJ4K0_6-vKYqtQ1xIIPZg-gHJY5793M9XoYQ0,35754
18
- ipyvasp/core/serializer.py,sha256=cHGlaDEyhlfsNgy5SDMcZewc2DIy7uyMyCmsrvOAqlM,33539
18
+ ipyvasp/core/serializer.py,sha256=OsnYhlwt8O6UeJQMKmjp1-hwRjhxiqobV6bybfDECUY,35777
19
19
  ipyvasp/core/spatial_toolkit.py,sha256=8DBYTiBFWJ7OBKuvOPw7UoEVCyNjJhSW0OcudjYZvAw,14748
20
- ipyvasp-0.8.3.dist-info/LICENSE,sha256=F3SO5RiAZOMfmMGf1KOuk2g_c4ObvuBJhd9iBLDgXoQ,1263
21
- ipyvasp-0.8.3.dist-info/METADATA,sha256=D-Nih1mFlk8fKFpiqyWI-BRn-WjTO71qajTgY0V_IRc,2436
22
- ipyvasp-0.8.3.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
23
- ipyvasp-0.8.3.dist-info/entry_points.txt,sha256=C7m0Sjmr14wFjflCkWXLzr5N6-cQj8uJC9n82mUtzt8,44
24
- ipyvasp-0.8.3.dist-info/top_level.txt,sha256=ftziWlMWu_1VpDP1sRTFrkfBnWxAi393HYDVu4wRhUk,8
25
- ipyvasp-0.8.3.dist-info/RECORD,,
20
+ ipyvasp-0.8.5.dist-info/LICENSE,sha256=F3SO5RiAZOMfmMGf1KOuk2g_c4ObvuBJhd9iBLDgXoQ,1263
21
+ ipyvasp-0.8.5.dist-info/METADATA,sha256=zDQVItXjnjkNp9_9hmGBltvvx1m85_5NHlGKjmzvNms,2436
22
+ ipyvasp-0.8.5.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
23
+ ipyvasp-0.8.5.dist-info/entry_points.txt,sha256=C7m0Sjmr14wFjflCkWXLzr5N6-cQj8uJC9n82mUtzt8,44
24
+ ipyvasp-0.8.5.dist-info/top_level.txt,sha256=ftziWlMWu_1VpDP1sRTFrkfBnWxAi393HYDVu4wRhUk,8
25
+ ipyvasp-0.8.5.dist-info/RECORD,,