ipyvasp 1.0.9__tar.gz → 1.1.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.
Files changed (30) hide show
  1. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/PKG-INFO +1 -1
  2. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/_lattice.py +63 -40
  3. ipyvasp-1.1.1/ipyvasp/_version.py +1 -0
  4. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/core/serializer.py +41 -0
  5. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/PKG-INFO +1 -1
  6. ipyvasp-1.0.9/ipyvasp/_version.py +0 -1
  7. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/LICENSE +0 -0
  8. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/README.md +0 -0
  9. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/__init__.py +0 -0
  10. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/__main__.py +0 -0
  11. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/_enplots.py +0 -0
  12. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/bsdos.py +0 -0
  13. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/cli.py +0 -0
  14. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/core/__init__.py +0 -0
  15. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/core/parser.py +0 -0
  16. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/core/plot_toolkit.py +0 -0
  17. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/core/spatial_toolkit.py +0 -0
  18. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/evals_dataframe.py +0 -0
  19. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/lattice.py +0 -0
  20. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/misc.py +0 -0
  21. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/potential.py +0 -0
  22. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/utils.py +0 -0
  23. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp/widgets.py +0 -0
  24. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/SOURCES.txt +0 -0
  25. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/dependency_links.txt +0 -0
  26. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/entry_points.txt +0 -0
  27. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/requires.txt +0 -0
  28. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/ipyvasp.egg-info/top_level.txt +0 -0
  29. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/setup.cfg +0 -0
  30. {ipyvasp-1.0.9 → ipyvasp-1.1.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyvasp
3
- Version: 1.0.9
3
+ Version: 1.1.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
@@ -231,6 +231,21 @@ def periodic_table(selection=None):
231
231
  ax.set(xlim=[-0.6,17.6],ylim=[9.6,-0.6]) # to show borders correctly
232
232
  return ax
233
233
 
234
+ def _write_text(dest, text: str, *, encoding: str = "utf-8") -> None:
235
+ "Write unicode text either to a path-like destination or to a writable text stream."
236
+ # Treat file-like objects (streams) first (avoid Path("CON") / weird Windows devices, etc.)
237
+ if hasattr(dest, "write") and callable(getattr(dest, "write")):
238
+ dest.write(text)
239
+ # Best-effort flush (sys.stdout has it, StringIO doesn't need it)
240
+ flush = getattr(dest, "flush", None)
241
+ if callable(flush):
242
+ flush()
243
+ return
244
+
245
+ # Otherwise treat as a filesystem path
246
+ path = Path(dest)
247
+ with path.open("w", encoding=encoding) as f:
248
+ f.write(text)
234
249
 
235
250
  def write_poscar(poscar_data, outfile=None, selective_dynamics=None, overwrite=False, comment="", scale=None, system=None):
236
251
  """Writes POSCAR data to a file or returns string
@@ -285,19 +300,18 @@ def write_poscar(poscar_data, outfile=None, selective_dynamics=None, overwrite=F
285
300
  pos_list = [f"{p} {s}" for p, s in zip(pos_list, sd)]
286
301
 
287
302
  out_str += "\n".join(pos_list)
288
- if outfile:
289
- path = Path(outfile)
290
- if not path.is_file():
291
- with path.open("w", encoding="utf-8") as f:
292
- f.write(out_str)
293
-
294
- elif overwrite and path.is_file():
295
- with path.open("w", encoding="utf-8") as f:
296
- f.write(out_str)
303
+ if outfile is not None:
304
+ # If it's a writable stream (sys.stdout, StringIO, open file handle), write directly.
305
+ if hasattr(outfile, "write") and callable(getattr(outfile, "write")):
306
+ _write_text(outfile, out_str)
297
307
  else:
298
- raise FileExistsError(
299
- f"{outfile!r} exists, can not overwrite, \nuse overwrite=True if you want to change."
300
- )
308
+ # Otherwise treat as path-like with overwrite protection.
309
+ path = Path(outfile)
310
+ if path.exists() and not overwrite:
311
+ raise FileExistsError(
312
+ f"{str(path)!r} exists, can not overwrite; use overwrite=True."
313
+ )
314
+ _write_text(path, out_str)
301
315
  else:
302
316
  print(out_str)
303
317
 
@@ -555,9 +569,8 @@ class InvokeMaterialsProject:
555
569
  return f"Structure(unit={self.unit},mp_id={self.mp_id!r},symbol={self.symbol!r},crystal={self.crystal!r},cif='{self._cif[:10]}...')"
556
570
 
557
571
  def write_cif(self, outfile=None):
558
- if isinstance(outfile, str):
559
- with open(outfile, "w") as f:
560
- f.write(self._cif)
572
+ if outfile is not None:
573
+ _write_text(outfile, self._cif)
561
574
  else:
562
575
  print(self._cif)
563
576
 
@@ -640,12 +653,12 @@ def get_kpath(
640
653
  ibzkpt : PathLike
641
654
  Path to ibzkpt file, required for HSE calculations.
642
655
  outfile : PathLike
643
- Path/to/file to write kpoints.
656
+ Path/to/file to write kpoints. Use sys.stdout to print to console.
644
657
  rec_basis : array_like
645
658
  Reciprocal basis 3x3 array to use for calculating uniform points.
646
659
 
647
660
 
648
- If `outfile = None`, a tuple of header and kpoints array (Nx3) is returned.
661
+ If `outfile = None`, kpoints array (Nx3) is returned.
649
662
  """
650
663
  if isinstance(kpoints, str):
651
664
  kpoints = _str2kpoints(kpoints)
@@ -766,10 +779,9 @@ def get_kpath(
766
779
  )
767
780
  out_str = "{}\n{}".format(top_str, out_str)
768
781
  if outfile != None:
769
- with open(outfile, "w", encoding="utf-8") as f: # allow unicode
770
- f.write(out_str)
782
+ _write_text(outfile, out_str)
771
783
  else:
772
- return top_str, points # return points as well for any processing by user.
784
+ return points # return points for any processing by user.
773
785
 
774
786
 
775
787
  # Cell
@@ -799,12 +811,12 @@ def get_kmesh(
799
811
  ibzkpt : PathLike
800
812
  Path to ibzkpt file, required for HSE calculations.
801
813
  outfile : PathLike
802
- Path/to/file to write kpoints.
814
+ Path/to/file to write kpoints. Use sys.stdout to print to console.
803
815
  endpoint : bool
804
816
  Default True, include endpoints in mesh at edges away from origin.
805
817
 
806
818
 
807
- If `outfile = None`, a tuple of header and kpoints array (Nx3) is returned.
819
+ If `outfile = None`, kpoints array (Nx3) is returned.
808
820
 
809
821
  """
810
822
  if len(args) not in [1, 3]:
@@ -879,10 +891,9 @@ def get_kmesh(
879
891
  )
880
892
  out_str = "{}\n{}".format(top_str, out_str)
881
893
  if outfile != None:
882
- with open(outfile, "w", encoding="utf-8") as f:
883
- f.write(out_str)
894
+ _write_text(outfile, out_str)
884
895
  else:
885
- return top_str, points # return points as well for any processing by user.
896
+ return points # return points for any processing by user.
886
897
 
887
898
 
888
899
  # Cell
@@ -898,6 +909,7 @@ def splot_bz(
898
909
  shade=True,
899
910
  alpha=0.4,
900
911
  zoffset=0,
912
+ center=(0,0,0),
901
913
  **kwargs,
902
914
  ):
903
915
  """Plots matplotlib's static figure of BZ/Cell. You can also plot in 2D on a 3D axes.
@@ -926,8 +938,9 @@ def splot_bz(
926
938
  alpha : float
927
939
  Opacity of filling in range [0,1]. Increase for clear viewpoint.
928
940
  zoffset : float
929
- Only used if plotting in 2D over a 3D axis. Default is 0. Any plane 'xy','yz' etc.
930
-
941
+ Only used if plotting in 2D over a 3D axis. Default is 0. Any plane 'xy','yz' etc can be offset to it's own normal.
942
+ center : (3,) array_like
943
+ Translation of origin in *basis coordinates* (fractional along the plotted basis). Use this to tile BZ with help of ``BrZoneData.tile`` fuction.
931
944
 
932
945
  kwargs are passed to `plt.plot` or `Poly3DCollection` if `fill=True`.
933
946
 
@@ -951,6 +964,17 @@ def splot_bz(
951
964
  if v not in [0, 1, 2]:
952
965
  raise ValueError(f"`vectors` expects values in [0,1,2], got {vectors!r}")
953
966
 
967
+ if not isinstance(center, (tuple, list, np.ndarray)) or len(center) != 3:
968
+ raise ValueError("`center` must be a 3-sequence like (0,0,0) in basis coordinates.")
969
+ try:
970
+ center = np.array(center, dtype=float).reshape(3)
971
+ except Exception as e:
972
+ raise ValueError(f"`center` must be numeric, got {center!r}") from e
973
+
974
+ origin = to_R3(bz_data.basis, [center])[0] # (3,) cartesian shift
975
+ bz_data = bz_data.copy()
976
+ bz_data.vertices[:,:] += origin # apply on view, assignment is restricted
977
+
954
978
  name = kwargs.pop("label", None) # will set only on single line
955
979
  kwargs.pop("zdir", None) # remove , no need
956
980
  is_subzone = hasattr(bz_data, "_specials") # For subzone
@@ -1020,13 +1044,14 @@ def splot_bz(
1020
1044
 
1021
1045
  if vectors and not is_subzone:
1022
1046
  s_basis = to_plane(normals[plane], bz_data.basis[(vectors,)])
1047
+ s_origin = to_plane(normals[plane], [origin]*len(vectors))
1023
1048
 
1024
1049
  for k, b in zip(vectors, s_basis):
1025
1050
  x, y = b[idxs[plane]]
1026
1051
  l = r" ${}_{} $".format(_label, k + 1)
1027
1052
  l = l + "\n" if y < 0 else "\n" + l
1028
1053
  ha = "right" if x < 0 else "left"
1029
- xyz = 0.8 * b + z0 if is3d else np.array([0.8 * x, 0.8 * y])
1054
+ xyz = 0.8 * b + z0 + s_origin[0] if is3d else np.array([0.8 * x, 0.8 * y]) + s_origin[0, idxs[plane]]
1030
1055
  ax.text(
1031
1056
  *xyz, l, va="center", ha=ha, clip_on=True
1032
1057
  ) # must clip to have limits of axes working.
@@ -1034,7 +1059,7 @@ def splot_bz(
1034
1059
  *(xyz / 0.8), color="w", s=0.0005
1035
1060
  ) # Must be to scale below arrow.
1036
1061
  if is3d:
1037
- XYZ, UVW = (np.ones_like(s_basis) * z0).T, s_basis.T
1062
+ XYZ, UVW = (np.ones_like(s_basis) * z0 + s_origin).T, s_basis.T
1038
1063
  quiver3d(
1039
1064
  *XYZ,
1040
1065
  *UVW,
@@ -1045,10 +1070,8 @@ def splot_bz(
1045
1070
  mutation_scale=7,
1046
1071
  )
1047
1072
  else:
1048
- s_zero = [0 for _ in s_basis] # either 3 or 2.
1049
1073
  ax.quiver(
1050
- s_zero,
1051
- s_zero,
1074
+ *s_origin[:, idxs[plane]].T,
1052
1075
  *s_basis[:, idxs[plane]].T,
1053
1076
  lw=0.7,
1054
1077
  color=color,
@@ -1115,9 +1138,9 @@ def splot_bz(
1115
1138
 
1116
1139
  if vectors and not is_subzone:
1117
1140
  for k, v in enumerate(0.35 * bz_data.basis):
1118
- ax.text(*v, r"${}_{}$".format(_label, k + 1), va="center", ha="center")
1141
+ ax.text(*(v + origin), r"${}_{}$".format(_label, k + 1), va="center", ha="center")
1119
1142
 
1120
- XYZ, UVW = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], 0.3 * bz_data.basis.T
1143
+ XYZ, UVW = np.array([origin] * 3).T, 0.3 * bz_data.basis.T
1121
1144
  quiver3d(
1122
1145
  *XYZ, *UVW, C="k", L=0.7, ax=ax, arrowstyle="-|>", mutation_scale=7
1123
1146
  )
@@ -1136,7 +1159,7 @@ def splot_bz(
1136
1159
  ax.set_zlabel(label.format("z"))
1137
1160
 
1138
1161
  if vname == "b": # These needed for splot_kpath internally
1139
- type(bz_data)._splot_kws = dict(plane=plane, zoffset=zoffset, ax=ax)
1162
+ type(bz_data)._splot_kws = dict(plane=plane, zoffset=zoffset, ax=ax, shift=origin)
1140
1163
 
1141
1164
  return ax
1142
1165
 
@@ -1144,7 +1167,7 @@ def splot_bz(
1144
1167
  def splot_kpath(
1145
1168
  bz_data, kpoints, labels=None, fmt_label=lambda x: (x, {"color": "blue"}), **kwargs
1146
1169
  ):
1147
- """Plot k-path over existing BZ. It will take ``ax``, ``plane`` and ``zoffset`` internally from most recent call to ``splot_bz``/``bz.splot``.
1170
+ """Plot k-path over last plotted BZ. It will take ``ax``, ``plane`` and ``zoffset`` internally from most recent call to ``splot_bz``/``bz.splot``.
1148
1171
 
1149
1172
  Parameters
1150
1173
  ----------
@@ -1170,9 +1193,9 @@ def splot_kpath(
1170
1193
  if not np.ndim(kpoints) == 2 and np.shape(kpoints)[-1] == 3:
1171
1194
  raise ValueError("kpoints must be 2D array of shape (N,3)")
1172
1195
 
1173
- plane, ax, zoffset = [
1196
+ plane, ax, zoffset, shift = [
1174
1197
  bz_data._splot_kws.get(attr, default) # class level attributes
1175
- for attr, default in zip(["plane", "ax", "zoffset"], [None, None, 0])
1198
+ for attr, default in zip(["plane", "ax", "zoffset", "shift"], [None, None, 0,np.array([0.0, 0.0, 0.0])])
1176
1199
  ]
1177
1200
 
1178
1201
  ijk = [0, 1, 2]
@@ -1208,9 +1231,9 @@ def splot_kpath(
1208
1231
  if fmt_label is None:
1209
1232
  fmt_label = lambda x: (x, {"color": "blue"})
1210
1233
 
1211
- _validate_label_func(fmt_label,labels[0])
1234
+ _validate_label_func(fmt_label,labels[0])
1212
1235
 
1213
- coords = bz_data.to_cartesian(kpoints)
1236
+ coords = bz_data.to_cartesian(kpoints) + shift
1214
1237
  if _zoffset and plane:
1215
1238
  normal = (
1216
1239
  [0, 0, 1] if plane in "xyx" else [0, 1, 0] if plane in "xzx" else [1, 0, 0]
@@ -0,0 +1 @@
1
+ __version__ = "1.1.1"
@@ -765,10 +765,51 @@ class BrZoneData(Dict2Data):
765
765
  d = self.copy().to_dict()
766
766
  d.update({"faces": faces, "vertices": vertices, "_specials": specials})
767
767
  return self.__class__(d)
768
+
769
+ def tile(self, nxyz, filter=None):
770
+ """Create a tiled array of BZ centers for visualization.
771
+
772
+ Parameters
773
+ ----------
774
+ nxyz : list or tuple of 3 ints
775
+ Number of tiles along each cartesian direction [nx, ny, nz].
776
+ Must be 3 positive integers.
777
+ filter : callable, optional
778
+ Function filter(x,y,z) that takes cartesian coordinates and returns bool.
779
+
780
+ Returns
781
+ -------
782
+ numpy.ndarray
783
+ Array of shape (N, 3) containing BZ center positions in fractional coordinates.
784
+
785
+ Examples
786
+ --------
787
+ >>> centers = bz_data.tile([3, 3, 3])
788
+ >>> centers = bz_data.tile([5, 5, 1], filter=lambda x,y,z: x**2 + y**2 <= 2**2)
789
+ """
790
+ if not isinstance(nxyz, (list, tuple, np.ndarray)) or len(nxyz) != 3:
791
+ raise ValueError("nxyz must be a list/tuple/array of 3 integers")
792
+
793
+ for i, n in enumerate(nxyz):
794
+ if not isinstance(n, int) or n < 1:
795
+ raise ValueError(f"nxyz[{i}] must be a positive integer")
796
+
797
+ xyz = self.to_cartesian(np.indices(np.ceil(nxyz).astype(int)).reshape((3,-1)).T)
798
+ # Apply filter if provided
799
+ if filter is not None:
800
+ if not callable(filter):
801
+ raise TypeError("filter must be callable")
802
+
803
+ mask = np.array([filter(x, y, z) for x, y, z in xyz])
804
+ xyz = xyz[mask]
805
+
806
+ # Convert to fractional coordinates and return
807
+ return self.to_fractional(xyz)
768
808
 
769
809
 
770
810
  class CellData(Dict2Data):
771
811
  splot, iplot = _methods_imported()
812
+ tile = BrZoneData.tile
772
813
  _req_keys = ("basis", "faces", "vertices")
773
814
 
774
815
  def __init__(self, d):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ipyvasp
3
- Version: 1.0.9
3
+ Version: 1.1.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
@@ -1 +0,0 @@
1
- __version__ = "1.0.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