ipyvasp 0.9.2__py2.py3-none-any.whl → 0.9.3__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/__init__.py +3 -3
- ipyvasp/_lattice.py +33 -18
- ipyvasp/_version.py +1 -1
- ipyvasp/bsdos.py +1 -7
- ipyvasp/lattice.py +2 -2
- ipyvasp/utils.py +0 -32
- ipyvasp/widgets.py +181 -248
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/METADATA +1 -1
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/RECORD +13 -13
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/LICENSE +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/WHEEL +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/entry_points.txt +0 -0
- {ipyvasp-0.9.2.dist-info → ipyvasp-0.9.3.dist-info}/top_level.txt +0 -0
ipyvasp/__init__.py
CHANGED
|
@@ -6,6 +6,8 @@ widgets for interactive visualization and bulk analysis.
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
__all__ = [ # For documentation purpose
|
|
9
|
+
"Files",
|
|
10
|
+
"OUTCAR",
|
|
9
11
|
"get_axes",
|
|
10
12
|
"plt2text",
|
|
11
13
|
"plt2html",
|
|
@@ -14,11 +16,9 @@ __all__ = [ # For documentation purpose
|
|
|
14
16
|
"image2plt",
|
|
15
17
|
"iplot2plt",
|
|
16
18
|
"webshow",
|
|
17
|
-
"list_files",
|
|
18
19
|
"load_results",
|
|
19
20
|
"parse_text",
|
|
20
21
|
"summarize",
|
|
21
|
-
"OUTCAR",
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
from ._version import __version__
|
|
@@ -30,7 +30,7 @@ from .bsdos import *
|
|
|
30
30
|
from .potential import *
|
|
31
31
|
from .evals_dataframe import *
|
|
32
32
|
from .utils import *
|
|
33
|
-
from .widgets import BandsWidget, KpathWidget,
|
|
33
|
+
from .widgets import Files, BandsWidget, KpathWidget, summarize, load_results
|
|
34
34
|
from .core import plot_toolkit, spatial_toolkit
|
|
35
35
|
from .core.spatial_toolkit import to_basis, to_R3, get_TM, get_bz, rotation
|
|
36
36
|
from .core.plot_toolkit import (
|
ipyvasp/_lattice.py
CHANGED
|
@@ -11,7 +11,8 @@ from scipy.spatial import ConvexHull, KDTree
|
|
|
11
11
|
import plotly.graph_objects as go
|
|
12
12
|
|
|
13
13
|
import matplotlib.pyplot as plt # For viewpoint
|
|
14
|
-
from matplotlib.collections import LineCollection
|
|
14
|
+
from matplotlib.collections import LineCollection, PatchCollection
|
|
15
|
+
from matplotlib.patches import Rectangle
|
|
15
16
|
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
|
|
16
17
|
import matplotlib.colors as mplc
|
|
17
18
|
|
|
@@ -171,17 +172,25 @@ def atoms_color():
|
|
|
171
172
|
)
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def periodic_table():
|
|
175
|
-
"Display colorerd elements in periodic table."
|
|
175
|
+
def periodic_table(selection=None):
|
|
176
|
+
"Display colorerd elements in periodic table. Use a list of atoms to only color a selection."
|
|
176
177
|
_copy_names = np.array(
|
|
177
178
|
[f"$^{{{str(i+1)}}}${k}" for i, k in enumerate(_atom_colors.keys())]
|
|
178
179
|
)
|
|
179
|
-
|
|
180
|
+
blank = []
|
|
181
|
+
if isinstance(selection,(list, tuple, str)):
|
|
182
|
+
if isinstance(selection, str):
|
|
183
|
+
selection = selection.split()
|
|
184
|
+
blank = [key for key in _atom_colors if not (key in selection)]
|
|
185
|
+
|
|
186
|
+
_copy_array = np.array([[1,1,1,0] if key in blank else [*value,1] for key, value in _atom_colors.items()])
|
|
180
187
|
|
|
181
|
-
array = np.ones((180, 3))
|
|
182
188
|
names = ["" for i in range(180)] # keep as list before modification
|
|
189
|
+
fc = np.ones((180, 4))
|
|
190
|
+
ec = np.zeros((180,3)) + (0.4 if blank else 0.9 )
|
|
191
|
+
offsets = np.array([[(i,j) for i in range(18)] for j in range(10)]).reshape((-1,2)) - 0.5
|
|
183
192
|
|
|
184
|
-
inds = [
|
|
193
|
+
inds = np.array([
|
|
185
194
|
(0, 0),
|
|
186
195
|
(17, 1),
|
|
187
196
|
(18, 2),
|
|
@@ -196,23 +205,29 @@ def periodic_table():
|
|
|
196
205
|
*[(111 + i, 103 + i) for i in range(15)],
|
|
197
206
|
*[(147 + i, 57 + i) for i in range(14)],
|
|
198
207
|
*[(165 + i, 89 + i) for i in range(14)],
|
|
199
|
-
]
|
|
208
|
+
], dtype=int)
|
|
200
209
|
|
|
201
210
|
for i, j in inds:
|
|
202
|
-
|
|
211
|
+
fc[i,:] = _copy_array[j]
|
|
203
212
|
names[i] = _copy_names[j]
|
|
204
213
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
fidx = [i for i, _ in inds] # only plot at elements posistions,otherwise they overlap
|
|
215
|
+
offsets = offsets[fidx]
|
|
216
|
+
fc, ec = fc[fidx], ec[fidx]
|
|
217
|
+
names = np.array(names)[fidx]
|
|
218
|
+
|
|
219
|
+
# We are adding patches, because imshow does not properly appear in PDF of latex
|
|
220
|
+
ax = ptk.get_axes((7, 3.9),left=0.01,right=0.99,top=0.99,bottom=0.01)
|
|
221
|
+
patches = np.array([Rectangle(offset,0.9 if i in [92,110] else 1,1) for i, offset in zip(fidx,offsets)])
|
|
222
|
+
pc = PatchCollection(patches, facecolors=fc, edgecolors=ec,linewidths=(0.7,))
|
|
223
|
+
ax.add_collection(pc)
|
|
224
|
+
|
|
225
|
+
for (x,y), text, c in zip(offsets + 0.5, names, fc):
|
|
226
|
+
c = "k" if np.linalg.norm(c[:3]) > 1 else "w"
|
|
227
|
+
plt.text(x,y, text, color=c, ha="center", va="center")
|
|
209
228
|
|
|
210
|
-
for i in range(18):
|
|
211
|
-
for j in range(10):
|
|
212
|
-
c = "k" if np.linalg.norm(array[j, i]) > 1 else "w"
|
|
213
|
-
plt.text(i, j, names[j, i], color=c, ha="center", va="center")
|
|
214
229
|
ax.set_axis_off()
|
|
215
|
-
|
|
230
|
+
ax.set(xlim=[-0.6,17.6],ylim=[9.6,-0.6]) # to show borders correctly
|
|
216
231
|
return ax
|
|
217
232
|
|
|
218
233
|
|
|
@@ -299,7 +314,7 @@ def export_poscar(path=None, content=None):
|
|
|
299
314
|
SYSTEM = header[0].strip()
|
|
300
315
|
comment = header[1].strip() if len(header) > 1 else "Exported by Pivopty"
|
|
301
316
|
|
|
302
|
-
scale = float(file_lines[1].strip())
|
|
317
|
+
scale = float(file_lines[1].strip().split()[0]) # some people add comments here too
|
|
303
318
|
if scale < 0: # If that is for volume
|
|
304
319
|
scale = 1
|
|
305
320
|
|
ipyvasp/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.3"
|
ipyvasp/bsdos.py
CHANGED
|
@@ -578,13 +578,7 @@ class Bands(_BandsDosBase):
|
|
|
578
578
|
|
|
579
579
|
def view_bands(self, height="450px"):
|
|
580
580
|
"Initialize and return `ipyvasp.widgets.BandsWidget` to view bandstructure interactively."
|
|
581
|
-
|
|
582
|
-
return wdg.BandsWidget(
|
|
583
|
-
use_vaspout=use_vaspout,
|
|
584
|
-
path=str(self.source.path.parent),
|
|
585
|
-
glob=self.source.path.name,
|
|
586
|
-
height=height,
|
|
587
|
-
)
|
|
581
|
+
return wdg.BandsWidget([self.source.path,],height=height)
|
|
588
582
|
|
|
589
583
|
|
|
590
584
|
_multiply_doc = """multiply : float
|
ipyvasp/lattice.py
CHANGED
|
@@ -432,11 +432,11 @@ class POSCAR:
|
|
|
432
432
|
def view_weas(self, **kwargs):
|
|
433
433
|
return weas_viewer(self, **kwargs)
|
|
434
434
|
|
|
435
|
-
def view_kpath(self):
|
|
435
|
+
def view_kpath(self, height='400px'):
|
|
436
436
|
"Initialize a KpathWidget instance to view kpath for current POSCAR, and you can select others too."
|
|
437
437
|
from .widgets import KpathWidget
|
|
438
438
|
|
|
439
|
-
return KpathWidget(
|
|
439
|
+
return KpathWidget([self.path,],height=height)
|
|
440
440
|
|
|
441
441
|
@_sub_doc(plat.iplot_lattice)
|
|
442
442
|
@_sig_kwargs(plat.iplot_lattice, ("poscar_data",))
|
ipyvasp/utils.py
CHANGED
|
@@ -3,7 +3,6 @@ __all__ = [
|
|
|
3
3
|
"set_dir",
|
|
4
4
|
"interpolate_data",
|
|
5
5
|
"rolling_mean",
|
|
6
|
-
"list_files",
|
|
7
6
|
"color",
|
|
8
7
|
"transform_color",
|
|
9
8
|
"create_colormap",
|
|
@@ -257,37 +256,6 @@ def rolling_mean(
|
|
|
257
256
|
|
|
258
257
|
return mean_all
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
def list_files(path=".", glob="*", exclude=None, files_only=False, dirs_only=False):
|
|
262
|
-
"""
|
|
263
|
-
Returns a tuple of files in a directory recursively based on glob pattern.
|
|
264
|
-
|
|
265
|
-
Parameters
|
|
266
|
-
----------
|
|
267
|
-
path : str, current directory by default
|
|
268
|
-
glob : str, glob pattern, '*' by default
|
|
269
|
-
exclude : str, regular expression pattern to exclude files
|
|
270
|
-
files_only : bool, if True, returns only files
|
|
271
|
-
dirs_only : bool, if True, returns only directories
|
|
272
|
-
|
|
273
|
-
Returns
|
|
274
|
-
-------
|
|
275
|
-
tuple of pathlib.Path objects
|
|
276
|
-
"""
|
|
277
|
-
if files_only and dirs_only:
|
|
278
|
-
raise ValueError("files_only and dirs_only cannot be both True")
|
|
279
|
-
|
|
280
|
-
path = Path(path)
|
|
281
|
-
files = [p for p in path.glob(glob)]
|
|
282
|
-
if exclude:
|
|
283
|
-
files = [p for p in files if not re.search(exclude, str(p))]
|
|
284
|
-
if files_only:
|
|
285
|
-
files = [p for p in files if p.is_file()]
|
|
286
|
-
if dirs_only:
|
|
287
|
-
files = [p for p in files if p.is_dir()]
|
|
288
|
-
return tuple(sorted(files)) # sorting is important here
|
|
289
|
-
|
|
290
|
-
|
|
291
259
|
@contextmanager
|
|
292
260
|
def prevent_overwrite(path) -> Path:
|
|
293
261
|
"""Contextmanager to prevents overwiting as file by adding numbers in given path.
|
ipyvasp/widgets.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
__all__ = [
|
|
2
2
|
"load_results",
|
|
3
3
|
"summarize",
|
|
4
|
-
"
|
|
4
|
+
"Files",
|
|
5
5
|
"PropsPicker",
|
|
6
6
|
"BandsWidget",
|
|
7
7
|
"KpathWidget",
|
|
8
8
|
]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
import inspect
|
|
11
|
+
import inspect, re
|
|
12
12
|
from time import time
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from collections.abc import Iterable
|
|
@@ -57,8 +57,8 @@ def summarize(files, func, **kwargs):
|
|
|
57
57
|
if not callable(func):
|
|
58
58
|
raise TypeError("Argument `func` must be a function.")
|
|
59
59
|
|
|
60
|
-
if not isinstance(files, Iterable):
|
|
61
|
-
raise TypeError("Argument `files` must be an iterable of PathLike objects
|
|
60
|
+
if not isinstance(files, Iterable): # Files is instance of Iterable due to __iter__ method
|
|
61
|
+
raise TypeError("Argument `files` must be an iterable of PathLike objects")
|
|
62
62
|
|
|
63
63
|
if not isinstance(files, dict):
|
|
64
64
|
files = {str(path): path for path in files} # make a dictionary of paths
|
|
@@ -78,15 +78,12 @@ def summarize(files, func, **kwargs):
|
|
|
78
78
|
{**output, "FILE": name}
|
|
79
79
|
) # add the file name to the output at the end
|
|
80
80
|
|
|
81
|
-
unique_keys =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for key in [key for out in outputs for key in out.keys()]:
|
|
85
|
-
if key not in unique_keys:
|
|
86
|
-
unique_keys.append(key)
|
|
81
|
+
unique_keys = {} # handle missing keys with types
|
|
82
|
+
for key,value in [item for out in outputs for item in out.items()]:
|
|
83
|
+
unique_keys[key] = '' if isinstance(value, str) else None
|
|
87
84
|
|
|
88
85
|
return pd.DataFrame(
|
|
89
|
-
{
|
|
86
|
+
{key: [out.get(key, ph) for out in outputs] for key,ph in unique_keys.items()}
|
|
90
87
|
)
|
|
91
88
|
|
|
92
89
|
|
|
@@ -102,236 +99,171 @@ def fix_signature(cls):
|
|
|
102
99
|
cls.__signature__ = inspect.signature(cls.__init__)
|
|
103
100
|
return cls
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"""A widget for selecting files from a directory and its subdirectories.
|
|
102
|
+
class Files:
|
|
103
|
+
"""Creates a Batch of files in a directory recursively based on glob pattern or given list of files.
|
|
104
|
+
This is a boilerplate abstraction to do analysis in multiple calculations simultaneously.
|
|
109
105
|
|
|
110
106
|
Parameters
|
|
111
107
|
----------
|
|
112
|
-
|
|
113
|
-
glob : str,
|
|
114
|
-
exclude : str,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
To add extra controls and widgets, use `.interactive`/`.interact` methods instead.
|
|
118
|
-
|
|
119
|
-
Returns
|
|
120
|
-
-------
|
|
121
|
-
A FilesWidget object where you can filter files by typing in the text box, and select files from the dropdown.
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
def __init__(
|
|
125
|
-
self,
|
|
126
|
-
path: str = ".",
|
|
127
|
-
glob: str = "*",
|
|
128
|
-
exclude: str = None,
|
|
129
|
-
on_file_changed=None,
|
|
130
|
-
) -> None:
|
|
131
|
-
for prop in (path, glob, exclude):
|
|
132
|
-
if prop and not isinstance(prop, str):
|
|
133
|
-
raise ValueError(f"Expected string, got {type(prop)}")
|
|
134
|
-
|
|
135
|
-
super().__init__(_dom_classes=["FilesWidget"]) # This makes it truely a widget
|
|
136
|
-
self._files = [] # Selections stored as Path objects
|
|
137
|
-
self._widgets = {
|
|
138
|
-
"input": Text(
|
|
139
|
-
value=path,
|
|
140
|
-
description="Path:",
|
|
141
|
-
tooltip="The path to the directory to search.",
|
|
142
|
-
),
|
|
143
|
-
"glob": Text(
|
|
144
|
-
value=glob,
|
|
145
|
-
description="Glob:",
|
|
146
|
-
tooltip="The glob pattern to match files against. See https://docs.python.org/3/library/glob.html",
|
|
147
|
-
),
|
|
148
|
-
"exclude": Text(
|
|
149
|
-
value=exclude or "",
|
|
150
|
-
description="Exclude:",
|
|
151
|
-
tooltip="A regex pattern to exclude files from the selection.",
|
|
152
|
-
),
|
|
153
|
-
"lock": Checkbox(
|
|
154
|
-
value=False,
|
|
155
|
-
description="Lock selection",
|
|
156
|
-
tooltip="Lock the current selection and prevent changes.",
|
|
157
|
-
),
|
|
158
|
-
"files": Dropdown(
|
|
159
|
-
options=[], description="File:", tooltip="Select a file from the list."
|
|
160
|
-
),
|
|
161
|
-
}
|
|
162
|
-
self.children = [
|
|
163
|
-
self._widgets["input"],
|
|
164
|
-
self._widgets["glob"],
|
|
165
|
-
self._widgets["exclude"],
|
|
166
|
-
]
|
|
167
|
-
self._widgets["lock"].observe(self._lock_selection, names=["value"])
|
|
168
|
-
|
|
169
|
-
for key, value in self._widgets.items():
|
|
170
|
-
if key not in ["files", "lock"]:
|
|
171
|
-
value.on_submit(self._process)
|
|
172
|
-
|
|
173
|
-
if on_file_changed:
|
|
174
|
-
if not callable(on_file_changed):
|
|
175
|
-
raise TypeError(
|
|
176
|
-
"Argument `on_file_changed` must be a function that takes path as an argument."
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
out = ipw.Output()
|
|
180
|
-
self.layout.max_height = "90vh" # Only if output is present
|
|
181
|
-
self._widgets["output"] = out
|
|
108
|
+
path_or_files : str, current directory by default or list of files or an instance of Files.
|
|
109
|
+
glob : str, glob pattern, '*' by default. Not used if files supplied above.
|
|
110
|
+
exclude : str, regular expression pattern to exclude files.
|
|
111
|
+
files_only : bool, if True, returns only files.
|
|
112
|
+
dirs_only : bool, if True, returns only directories.
|
|
182
113
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
self.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if self._widgets["lock"].value:
|
|
199
|
-
self._widgets["lock"].description = f"{len(self._files)} files selected"
|
|
200
|
-
self.children = [self._widgets["lock"], self._widgets["files"]]
|
|
114
|
+
Use methods on return such as `summarize`, `with_name`, `filtered`, `interact` and others.
|
|
115
|
+
"""
|
|
116
|
+
def __init__(self, path_or_files = '.', glob = '*', exclude = None,files_only = False, dirs_only=False):
|
|
117
|
+
if isinstance(path_or_files, Files):
|
|
118
|
+
self._files = path_or_files._files
|
|
119
|
+
return # Do nothing
|
|
120
|
+
|
|
121
|
+
if files_only and dirs_only:
|
|
122
|
+
raise ValueError("files_only and dirs_only cannot be both True")
|
|
123
|
+
|
|
124
|
+
files = []
|
|
125
|
+
if isinstance(path_or_files,(str, Path)):
|
|
126
|
+
path = Path(path_or_files)
|
|
127
|
+
files = [p for p in path.glob(glob)]
|
|
201
128
|
else:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
self.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def
|
|
240
|
-
"
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
@property
|
|
245
|
-
def path(self): # This is in consistent with other widgets too
|
|
246
|
-
"Return currently selected path."
|
|
247
|
-
return self.selected
|
|
129
|
+
others = []
|
|
130
|
+
for item in path_or_files:
|
|
131
|
+
if isinstance(item, str):
|
|
132
|
+
item = Path(item)
|
|
133
|
+
elif not isinstance(item, Path):
|
|
134
|
+
raise TypeError(f"Expected str or Path in sequence, got {type(item)}")
|
|
135
|
+
|
|
136
|
+
if item.exists():
|
|
137
|
+
files.append(item)
|
|
138
|
+
else:
|
|
139
|
+
others.append(str(item))
|
|
140
|
+
|
|
141
|
+
if others:
|
|
142
|
+
print(f"Skipping paths that do not exist: {list(set(others))}")
|
|
143
|
+
|
|
144
|
+
if exclude:
|
|
145
|
+
files = [p for p in files if not re.search(exclude, str(p))]
|
|
146
|
+
if files_only:
|
|
147
|
+
files = [p for p in files if p.is_file()]
|
|
148
|
+
if dirs_only:
|
|
149
|
+
files = [p for p in files if p.is_dir()]
|
|
150
|
+
|
|
151
|
+
self._files = tuple(sorted(files))
|
|
152
|
+
|
|
153
|
+
def __repr__(self):
|
|
154
|
+
if not self: return "Files()"
|
|
155
|
+
return "Files(\n" + ',\n'.join(f' {f!r}' for f in self._files) + "\n)"
|
|
156
|
+
|
|
157
|
+
def __getitem__(self, index): return self._files[index]
|
|
158
|
+
def __iter__(self): return self._files.__iter__()
|
|
159
|
+
def __len__(self): return len(self._files)
|
|
160
|
+
def __bool__(self): return bool(self._files)
|
|
161
|
+
|
|
162
|
+
def with_name(self, name):
|
|
163
|
+
"Change name of all files. Only keeps existing files."
|
|
164
|
+
return self.__class__([f.with_name(name) for f in self._files])
|
|
165
|
+
|
|
166
|
+
def filtered(self, include=None, exclude=None, files_only = False, dirs_only=False):
|
|
167
|
+
"Filter all files. Only keeps existing file."
|
|
168
|
+
files = [p for p in self._files if re.search(include, str(p))] if include else self._files
|
|
169
|
+
return self.__class__(files, exclude=exclude,dirs_only=dirs_only,files_only=files_only)
|
|
248
170
|
|
|
249
|
-
def
|
|
250
|
-
|
|
251
|
-
func,
|
|
171
|
+
def summarize(self, func, **kwargs):
|
|
172
|
+
"Apply a func(apth) -> dict and create a dataframe."
|
|
173
|
+
return summarize(self._files,func, **kwargs)
|
|
174
|
+
|
|
175
|
+
def load_results(self):
|
|
176
|
+
"Load result.json files from these paths into a dataframe."
|
|
177
|
+
return load_results(self._files)
|
|
178
|
+
|
|
179
|
+
def input_info(self, *tags):
|
|
180
|
+
"Grab input information into a dataframe from POSCAR and INCAR. Provide INCAR tags (case-insinsitive) to select only few of them."
|
|
181
|
+
from .lattice import POSCAR
|
|
182
|
+
|
|
183
|
+
def info(path, tags):
|
|
184
|
+
p = POSCAR(path).data
|
|
185
|
+
lines = [[v.strip() for v in line.split('=')]
|
|
186
|
+
for line in path.with_name('INCAR').read_text().splitlines()
|
|
187
|
+
if '=' in line]
|
|
188
|
+
if tags:
|
|
189
|
+
tags = [t.upper() for t in tags] # can send lowercase tag
|
|
190
|
+
lines = [(k,v) for k,v in lines if k in tags]
|
|
191
|
+
d = {k:v for k,v in lines if not k.startswith('#')}
|
|
192
|
+
d.update({k:len(v) for k,v in p.types.items()})
|
|
193
|
+
d.update(zip('abcvαβγ', [*p.norms,p.volume,*p.angles]))
|
|
194
|
+
return d
|
|
195
|
+
|
|
196
|
+
return self.with_name('POSCAR').summarize(info, tags=tags)
|
|
197
|
+
|
|
198
|
+
def update(self, path_or_files, glob = '*',**kwargs):
|
|
199
|
+
"Update files inplace with similar parameters as initialization. Useful for widgets such as BandsWidget to preserve their state while files swapping."
|
|
200
|
+
self._files = self.__class__(path_or_files, glob = glob, **kwargs)
|
|
201
|
+
if (dd := getattr(self, '_dd', None)): # update dropdown
|
|
202
|
+
old = dd.value
|
|
203
|
+
dd.options = self._files
|
|
204
|
+
if old in dd.options:
|
|
205
|
+
dd.value = old
|
|
206
|
+
|
|
207
|
+
def interactive(self, func,
|
|
252
208
|
other_widgets=None,
|
|
253
209
|
other_controls=None,
|
|
254
210
|
options={"manual": False},
|
|
255
211
|
height="400px",
|
|
256
|
-
**kwargs
|
|
257
|
-
):
|
|
212
|
+
**kwargs):
|
|
258
213
|
"""
|
|
259
214
|
Interact with a function that takes selected Path as first argument. Returns a widget that saves attributes of the function call such as .f, .args, .kwargs.
|
|
260
215
|
See docs of self.interact for more details on the parameters. kwargs are passed to ipywidgets.interactive to create controls.
|
|
261
216
|
|
|
262
217
|
|
|
263
|
-
>>>
|
|
264
|
-
>>> out =
|
|
218
|
+
>>> fls = Files()
|
|
219
|
+
>>> out = fls.interactive(lambda path: print(path.read_text())) # prints contents of selected file on output widget
|
|
265
220
|
>>> out.f # function
|
|
266
221
|
>>> out.args # arguments
|
|
267
222
|
>>> out.kwargs # keyword arguments
|
|
268
223
|
>>> out.result # result of function call which is same as out.f(*out.args, **out.kwargs)
|
|
269
|
-
>>> out.files_widget # reference to FilesWidget created, not the same as fw because it is a new instance
|
|
270
|
-
|
|
271
224
|
|
|
272
225
|
.. note::
|
|
273
226
|
If you don't need to interpret the result of the function call, you can use the @self.interact decorator instead.
|
|
274
|
-
|
|
275
|
-
.. note::
|
|
276
|
-
Each time an underlying new FilesWidget instance is created which picks input from previous one but stays separate. You can access it with `.files_widget` attribute of interactive.
|
|
277
227
|
"""
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
path=self._widgets["input"].value,
|
|
281
|
-
glob=self._widgets["glob"].value,
|
|
282
|
-
exclude=self._widgets["exclude"].value,
|
|
283
|
-
)
|
|
284
|
-
info = ipw.HTML().add_class("FW-Progess")
|
|
228
|
+
info = ipw.HTML().add_class("fprogess")
|
|
229
|
+
dd = Dropdown(description='File', options=self._files)
|
|
285
230
|
|
|
286
231
|
def interact_func(fname, **kwargs):
|
|
287
232
|
if fname: # This would be None if no file is selected
|
|
288
233
|
info.value = _progress_svg
|
|
289
234
|
try:
|
|
290
235
|
start = time()
|
|
291
|
-
print(
|
|
292
|
-
|
|
293
|
-
) # it also serves as removing the output errors
|
|
294
|
-
func(
|
|
295
|
-
Path(fname).absolute(), **kwargs
|
|
296
|
-
) # Have Path object absolue if user changes directory
|
|
236
|
+
print(f"Running {func.__name__}({fname!r}, {kwargs})")
|
|
237
|
+
func(Path(fname).absolute(), **kwargs) # Have Path object absolue if user changes directory
|
|
297
238
|
print(f"Finished in {time() - start:.3f} seconds.")
|
|
298
239
|
finally:
|
|
299
240
|
info.value = ""
|
|
300
241
|
|
|
301
|
-
out = ipw.interactive(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
out.files_widget = new_fw # save reference to FilesWidget
|
|
306
|
-
out.output_widget = out.children[
|
|
307
|
-
-1
|
|
308
|
-
] # save reference to output widget for other widgets to use
|
|
242
|
+
out = ipw.interactive(interact_func, options, fname=dd, **kwargs)
|
|
243
|
+
out._dd = dd # save reference to dropdown
|
|
244
|
+
out.output_widget = out.children[-1] # save reference to output widget for other widgets to use
|
|
309
245
|
|
|
310
246
|
if options.get("manual", False):
|
|
311
|
-
out.interact_button = out.children[
|
|
312
|
-
-2
|
|
313
|
-
] # save reference to interact button for other widgets to use
|
|
247
|
+
out.interact_button = out.children[-2] # save reference to interact button for other widgets to use
|
|
314
248
|
|
|
315
249
|
output = out.children[-1] # get output widget
|
|
316
|
-
output.clear_output(
|
|
317
|
-
wait=True
|
|
318
|
-
) # clear output by waiting to avoid flickering, this is important
|
|
250
|
+
output.clear_output(wait=True) # clear output by waiting to avoid flickering, this is important
|
|
319
251
|
output.layout = Layout(
|
|
320
252
|
overflow="auto", max_height="100%", width="100%"
|
|
321
253
|
) # make output scrollable and avoid overflow
|
|
322
254
|
|
|
323
255
|
others = out.children[1:-1] # exclude files_dd and Output widget
|
|
324
256
|
_style = """<style>
|
|
325
|
-
.
|
|
257
|
+
.files-interact {
|
|
326
258
|
--jp-widgets-inline-label-width: 4em;
|
|
327
259
|
--jp-widgets-inline-width: 18em;
|
|
328
260
|
--jp-widgets-inline-width-short: 9em;
|
|
329
261
|
}
|
|
330
|
-
.
|
|
331
|
-
.
|
|
332
|
-
.
|
|
333
|
-
.
|
|
334
|
-
.
|
|
262
|
+
.files-interact {max-height:90vh;width:100%;}
|
|
263
|
+
.files-interact > div {overflow:auto;max-height:100%;padding:8px;}
|
|
264
|
+
.files-interact > div:first-child {width:20em}
|
|
265
|
+
.files-interact > div:last-child {width:calc(100% - 20em)}
|
|
266
|
+
.files-interact .fprogess {position:absolute !important; left:50%; top:50%; transform:translate(-50%,-50%); z-index:1}
|
|
335
267
|
</style>"""
|
|
336
268
|
if others:
|
|
337
269
|
others = [ipw.HTML(f"<hr/>{_style}"), *others]
|
|
@@ -368,19 +300,24 @@ class FilesWidget(VBox):
|
|
|
368
300
|
HBox(
|
|
369
301
|
[ # reset children to include new widgets
|
|
370
302
|
VBox(
|
|
371
|
-
children=[
|
|
303
|
+
children=[dd, VBox(others)]
|
|
372
304
|
), # other widgets in box to make scrollable independent file selection
|
|
373
305
|
VBox(
|
|
374
306
|
children=[Box([output]), *(other_widgets or []), info]
|
|
375
307
|
), # output in box to make scrollable,
|
|
376
308
|
],
|
|
377
309
|
layout=Layout(height=height, max_height=height),
|
|
378
|
-
).add_class("
|
|
310
|
+
).add_class("files-interact")
|
|
379
311
|
] # important for every widget separately
|
|
380
312
|
return out
|
|
381
|
-
|
|
382
|
-
def
|
|
383
|
-
self
|
|
313
|
+
|
|
314
|
+
def _attributed_interactive(self, box, func, *args, **kwargs):
|
|
315
|
+
box._files = self
|
|
316
|
+
box._interact = self.interactive(func, *args, **kwargs)
|
|
317
|
+
box.children = box._interact.children
|
|
318
|
+
box._files._dd = box._interact._dd
|
|
319
|
+
|
|
320
|
+
def interact(self,
|
|
384
321
|
other_widgets=None,
|
|
385
322
|
other_controls=None,
|
|
386
323
|
options={"manual": False},
|
|
@@ -388,7 +325,7 @@ class FilesWidget(VBox):
|
|
|
388
325
|
**kwargs,
|
|
389
326
|
):
|
|
390
327
|
"""Interact with a function that takes a selected Path as first argument.
|
|
391
|
-
A CSS class '
|
|
328
|
+
A CSS class 'files-interact' is added to the final widget to let you style it.
|
|
392
329
|
|
|
393
330
|
Parameters
|
|
394
331
|
----------
|
|
@@ -402,53 +339,43 @@ class FilesWidget(VBox):
|
|
|
402
339
|
height : str
|
|
403
340
|
Default is '90vh'. height of the final widget. This is important to avoid very long widgets.
|
|
404
341
|
|
|
405
|
-
|
|
406
342
|
kwargs are passed to ipywidgets.interactive and decorated function. Resulting widgets are placed below the file selection widget.
|
|
407
|
-
|
|
408
343
|
`other_widgets` can be controlled by `other_controls` externally. For example, you can add a button to update a plotly's FigureWidget.
|
|
409
344
|
|
|
410
345
|
The decorated function can be called later separately as well, and has .args and .kwargs attributes to access the latest arguments
|
|
411
346
|
and .result method to access latest. For a function `f`, `f.result` is same as `f(*f.args, **f.kwargs)`.
|
|
412
347
|
|
|
413
348
|
|
|
414
|
-
>>>
|
|
415
|
-
>>> @
|
|
349
|
+
>>> fls = Files()
|
|
350
|
+
>>> @fls.interact(x = False)
|
|
416
351
|
>>> def f(path,x):
|
|
417
352
|
>>> print('path:',path)
|
|
418
353
|
>>> print('Path Type: ', type(path))
|
|
419
354
|
>>> print('x: ',x)
|
|
420
355
|
|
|
421
|
-
|
|
422
356
|
.. note::
|
|
423
357
|
Use self.interactive to get a widget that stores the argements and can be called later in a notebook cell.
|
|
424
358
|
"""
|
|
425
359
|
|
|
426
360
|
def inner(func):
|
|
427
|
-
display(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
height=height,
|
|
434
|
-
**kwargs,
|
|
435
|
-
)
|
|
361
|
+
display(self.interactive(func,
|
|
362
|
+
other_widgets=other_widgets,
|
|
363
|
+
other_controls=other_controls,
|
|
364
|
+
options=options,
|
|
365
|
+
height=height,
|
|
366
|
+
**kwargs)
|
|
436
367
|
)
|
|
437
368
|
return func
|
|
438
|
-
|
|
439
369
|
return inner
|
|
370
|
+
|
|
440
371
|
|
|
441
|
-
def
|
|
442
|
-
"
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
},
|
|
449
|
-
func,
|
|
450
|
-
**kwargs,
|
|
451
|
-
)
|
|
372
|
+
def kpath_widget(self, height='400px'):
|
|
373
|
+
"Get KpathWidget instance with these files."
|
|
374
|
+
return KpathWidget(files = self._files, height = height)
|
|
375
|
+
|
|
376
|
+
def bands_widget(self, height='450px'):
|
|
377
|
+
"Get BandsWidget instance with these files."
|
|
378
|
+
return BandsWidget(files=self._files, height=height)
|
|
452
379
|
|
|
453
380
|
|
|
454
381
|
@fix_signature
|
|
@@ -647,12 +574,12 @@ class BandsWidget(VBox):
|
|
|
647
574
|
Two attributes are important:
|
|
648
575
|
self.clicked_data returns the last clicked point, that can also be stored as VBM, CBM etc, using Click dropdown.
|
|
649
576
|
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.
|
|
577
|
+
You can use `self.files.update` method to change source files without effecting state of widget.
|
|
650
578
|
"""
|
|
651
579
|
|
|
652
|
-
def __init__(self,
|
|
580
|
+
def __init__(self, files, height="450px"):
|
|
653
581
|
super().__init__(_dom_classes=["BandsWidget"])
|
|
654
582
|
self._bands = None
|
|
655
|
-
self._use_vaspout = use_vaspout
|
|
656
583
|
self._fig = go.FigureWidget()
|
|
657
584
|
self._tsd = Dropdown(
|
|
658
585
|
description="Style", options=["plotly_white", "plotly_dark"]
|
|
@@ -668,11 +595,8 @@ class BandsWidget(VBox):
|
|
|
668
595
|
self._click_dict = {} # store clicked data
|
|
669
596
|
self._select_dict = {} # store selection data
|
|
670
597
|
self._kwargs = {}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
**file_widget_kwargs,
|
|
674
|
-
}
|
|
675
|
-
self._interact = FilesWidget(**file_widget_kwargs).interactive(
|
|
598
|
+
|
|
599
|
+
Files(files)._attributed_interactive(self,
|
|
676
600
|
self._load_data,
|
|
677
601
|
other_widgets=[self._fig],
|
|
678
602
|
other_controls=[
|
|
@@ -686,8 +610,7 @@ class BandsWidget(VBox):
|
|
|
686
610
|
],
|
|
687
611
|
height=height,
|
|
688
612
|
)
|
|
689
|
-
|
|
690
|
-
self.children = self._interact.children
|
|
613
|
+
|
|
691
614
|
self._tsd.observe(self._change_theme, "value")
|
|
692
615
|
self._click.observe(self._click_save_data, "value")
|
|
693
616
|
self._ktcicks.observe(self._warn_update, "value")
|
|
@@ -696,13 +619,18 @@ class BandsWidget(VBox):
|
|
|
696
619
|
@property
|
|
697
620
|
def path(self):
|
|
698
621
|
"Returns currently selected path."
|
|
699
|
-
return self.
|
|
622
|
+
return self._interact._dd.value
|
|
623
|
+
|
|
624
|
+
@property
|
|
625
|
+
def files(self):
|
|
626
|
+
"Use slef.files.update(...) to keep state of widget preserved."
|
|
627
|
+
return self._files
|
|
700
628
|
|
|
701
629
|
def _load_data(self, path): # Automatically redirectes to output widget
|
|
702
630
|
self._interact.output_widget.clear_output(wait=True) # Why need again?
|
|
703
631
|
with self._interact.output_widget:
|
|
704
632
|
self._bands = (
|
|
705
|
-
vp.
|
|
633
|
+
vp.Vasprun(path) if path.parts[-1].endswith('xml') else vp.Vaspout(path)
|
|
706
634
|
).bands
|
|
707
635
|
self._ppicks.update(self.bands.source.summary)
|
|
708
636
|
self._ktcicks.value = ", ".join(
|
|
@@ -714,7 +642,7 @@ class BandsWidget(VBox):
|
|
|
714
642
|
else:
|
|
715
643
|
self._click.options = ["None", "VBM", "CBM"]
|
|
716
644
|
|
|
717
|
-
if (file :=
|
|
645
|
+
if (file := path.parent / "result.json").is_file():
|
|
718
646
|
self._result = serializer.load(str(file.absolute())) # Old data loaded
|
|
719
647
|
|
|
720
648
|
pdata = self.bands.source.poscar.data
|
|
@@ -811,7 +739,7 @@ class BandsWidget(VBox):
|
|
|
811
739
|
serializer.dump(
|
|
812
740
|
data_dict,
|
|
813
741
|
format="json",
|
|
814
|
-
outfile=self.
|
|
742
|
+
outfile=self.path.parent / "result.json",
|
|
815
743
|
)
|
|
816
744
|
|
|
817
745
|
if (
|
|
@@ -854,8 +782,8 @@ class BandsWidget(VBox):
|
|
|
854
782
|
|
|
855
783
|
@property
|
|
856
784
|
def results(self):
|
|
857
|
-
"Generate a
|
|
858
|
-
return load_results(self.
|
|
785
|
+
"Generate a dataframe form result.json file in each folder."
|
|
786
|
+
return load_results(self._interact._dd.options)
|
|
859
787
|
|
|
860
788
|
|
|
861
789
|
@fix_signature
|
|
@@ -870,9 +798,11 @@ class KpathWidget(VBox):
|
|
|
870
798
|
- To update point(s), select point(s) from the select box and click on a scatter point in figure or use KPOINT input to update it manually, e.g. if a point is not available on plot.
|
|
871
799
|
- Add labels to the points by typing in the "Labels" box such as "Γ,X" or "Γ 5,X" that will add 5 points in interval.
|
|
872
800
|
- To break the path between two points "Γ" and "X" type "Γ 0,X" in the "Labels" box, zero means no points in interval.
|
|
801
|
+
|
|
802
|
+
You can use `self.files.update` method to change source files without effecting state of widget.
|
|
873
803
|
"""
|
|
874
804
|
|
|
875
|
-
def __init__(self, height="400px"
|
|
805
|
+
def __init__(self, files, height="400px"):
|
|
876
806
|
super().__init__(_dom_classes=["KpathWidget"])
|
|
877
807
|
self._fig = go.FigureWidget()
|
|
878
808
|
self._sm = SelectMultiple(options=[], layout=Layout(width="auto"))
|
|
@@ -894,13 +824,11 @@ class KpathWidget(VBox):
|
|
|
894
824
|
self._lab,
|
|
895
825
|
self._kpt,
|
|
896
826
|
]
|
|
897
|
-
|
|
898
|
-
|
|
827
|
+
|
|
828
|
+
Files(files)._attributed_interactive(self,
|
|
899
829
|
self._update_fig, [self._fig], other_controls, height=height
|
|
900
830
|
)
|
|
901
|
-
|
|
902
|
-
self.children = self._interact.children
|
|
903
|
-
|
|
831
|
+
|
|
904
832
|
self._tsb.on_click(self._update_theme)
|
|
905
833
|
self._add.on_click(self._toggle_lock)
|
|
906
834
|
self._del.on_click(self._del_point)
|
|
@@ -910,7 +838,12 @@ class KpathWidget(VBox):
|
|
|
910
838
|
@property
|
|
911
839
|
def path(self):
|
|
912
840
|
"Returns currently selected path."
|
|
913
|
-
return self._interact.
|
|
841
|
+
return self._interact._dd.value # itself a Path object
|
|
842
|
+
|
|
843
|
+
@property
|
|
844
|
+
def files(self):
|
|
845
|
+
"Use slef.files.update(...) to keep state of widget preserved."
|
|
846
|
+
return self._files
|
|
914
847
|
|
|
915
848
|
@property
|
|
916
849
|
def poscar(self):
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
ipyvasp/__init__.py,sha256=
|
|
1
|
+
ipyvasp/__init__.py,sha256=rlorju9arMtHw1QRYPljday-PyZWJdSCxg4lw3g6t0Q,1409
|
|
2
2
|
ipyvasp/__main__.py,sha256=eJV1TZSiT8mC_VqAeksNnBI2I8mKMiPkEIlwikbtOjI,216
|
|
3
3
|
ipyvasp/_enplots.py,sha256=D38paN8zqZgluNAwmCwcocd7-_h_T0HTGolI1eBkDes,37484
|
|
4
|
-
ipyvasp/_lattice.py,sha256=
|
|
5
|
-
ipyvasp/_version.py,sha256=
|
|
6
|
-
ipyvasp/bsdos.py,sha256=
|
|
4
|
+
ipyvasp/_lattice.py,sha256=GxG0C4lwVGvBYIy3jwR1kahWR7L6kJlqjIiQGgESjcM,104135
|
|
5
|
+
ipyvasp/_version.py,sha256=5uBZ3sUaocnyIWuJeW9M94Z77vA1kKmwKxiblrnbKlc,23
|
|
6
|
+
ipyvasp/bsdos.py,sha256=1rG68S-dLEYveIWGK7r8CRa7Qqlqno0l1ncfo2ocihk,30424
|
|
7
7
|
ipyvasp/cli.py,sha256=aWFEVhNmnW8eSOp5uh95JaDwLQ9K9nlCQcbnOSuhWgw,6844
|
|
8
8
|
ipyvasp/evals_dataframe.py,sha256=-sqxK7LPV6sYDO_XXmZ80FznOaXTkVdbqJKKvTUtMak,20637
|
|
9
|
-
ipyvasp/lattice.py,sha256=
|
|
9
|
+
ipyvasp/lattice.py,sha256=t4s1qh6IJsoeXcCa9M9IhjAQp2s78lqiGhOfEkCW19s,30638
|
|
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
|
|
13
|
-
ipyvasp/utils.py,sha256=
|
|
14
|
-
ipyvasp/widgets.py,sha256=
|
|
13
|
+
ipyvasp/utils.py,sha256=rVyD5SkO_Y7ok5W-fJeQys9X8pLLbDK7VOgbAbcE4WU,14227
|
|
14
|
+
ipyvasp/widgets.py,sha256=mP1tqYO-tPk4Xq_fgkGPGN2AfRf9V-al3Yl7vzKp5lU,43758
|
|
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=8t5svyWbOm-PS7ZvIptnK6F46kp6uwoGNdohPv5dQKA,35962
|
|
18
18
|
ipyvasp/core/serializer.py,sha256=XpqnfVGsUXiN2CuVRPyqsSVouxRBO-UH6AnsHnPYvZY,36729
|
|
19
19
|
ipyvasp/core/spatial_toolkit.py,sha256=8DBYTiBFWJ7OBKuvOPw7UoEVCyNjJhSW0OcudjYZvAw,14748
|
|
20
|
-
ipyvasp-0.9.
|
|
21
|
-
ipyvasp-0.9.
|
|
22
|
-
ipyvasp-0.9.
|
|
23
|
-
ipyvasp-0.9.
|
|
24
|
-
ipyvasp-0.9.
|
|
25
|
-
ipyvasp-0.9.
|
|
20
|
+
ipyvasp-0.9.3.dist-info/LICENSE,sha256=F3SO5RiAZOMfmMGf1KOuk2g_c4ObvuBJhd9iBLDgXoQ,1263
|
|
21
|
+
ipyvasp-0.9.3.dist-info/METADATA,sha256=v2jvMQ--zKh-b0GAF5XywZDMKJi8x3hUAMMvKGdfiD0,2420
|
|
22
|
+
ipyvasp-0.9.3.dist-info/WHEEL,sha256=iYlv5fX357PQyRT2o6tw1bN-YcKFFHKqB_LwHO5wP-g,110
|
|
23
|
+
ipyvasp-0.9.3.dist-info/entry_points.txt,sha256=C7m0Sjmr14wFjflCkWXLzr5N6-cQj8uJC9n82mUtzt8,44
|
|
24
|
+
ipyvasp-0.9.3.dist-info/top_level.txt,sha256=ftziWlMWu_1VpDP1sRTFrkfBnWxAi393HYDVu4wRhUk,8
|
|
25
|
+
ipyvasp-0.9.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|