kim-tools 0.2.3__tar.gz → 0.2.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.
- {kim_tools-0.2.3/kim_tools.egg-info → kim_tools-0.2.5}/PKG-INFO +9 -2
- kim_tools-0.2.5/README.md +8 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/__init__.py +1 -1
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/aflow_util/core.py +21 -12
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/core.py +196 -13
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/test_driver/core.py +161 -63
- {kim_tools-0.2.3 → kim_tools-0.2.5/kim_tools.egg-info}/PKG-INFO +9 -2
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools.egg-info/requires.txt +3 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/pyproject.toml +7 -2
- {kim_tools-0.2.3 → kim_tools-0.2.5}/tests/test_aflow_util.py +7 -1
- {kim_tools-0.2.3 → kim_tools-0.2.5}/tests/test_symmetry_util.py +23 -0
- kim_tools-0.2.3/README.md +0 -4
- {kim_tools-0.2.3 → kim_tools-0.2.5}/LICENSE.CDDL +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/MANIFEST.in +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/aflow_util/__init__.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/aflow_util/data/README_PROTO.TXT +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/ase/__init__.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/ase/core.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/kimunits.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/__init__.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/possible_primitive_shifts.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/primitive_GENPOS_ops.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/space_groups_for_each_bravais_lattice.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/wyckoff_multiplicities.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/wyckoff_sets.json +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/test_driver/__init__.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/vc/__init__.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/vc/core.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools.egg-info/SOURCES.txt +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools.egg-info/dependency_links.txt +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools.egg-info/top_level.txt +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/setup.cfg +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/setup.py +0 -0
- {kim_tools-0.2.3 → kim_tools-0.2.5}/tests/test_test_driver.py +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: kim-tools
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5
|
4
4
|
Summary: Base classes and helper routines for writing KIM Tests
|
5
|
-
Author-email: ilia Nikiforov <nikif002@umn.edu>, Ellad Tadmor <tadmor@umn.edu>, Claire Waters <bwaters@umn.edu>, "Daniel S. Karls" <karl0100umn@gmail.com>, Matt Bierbaum <matt.bierbaum@gmail.com>, Eric Fuemmeler <efuemmel@umn.edu>
|
5
|
+
Author-email: ilia Nikiforov <nikif002@umn.edu>, Ellad Tadmor <tadmor@umn.edu>, Claire Waters <bwaters@umn.edu>, "Daniel S. Karls" <karl0100umn@gmail.com>, Matt Bierbaum <matt.bierbaum@gmail.com>, Eric Fuemmeler <efuemmel@umn.edu>, Philipp Hoellmer <ph2484@nyu.edu>, Guanming Zhang <gz2241@nyu.edu>
|
6
6
|
Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
|
7
7
|
Project-URL: Homepage, https://kim-tools.readthedocs.io
|
8
8
|
Project-URL: Issues, https://github.com/openkim/kim-tools/issues
|
@@ -24,9 +24,16 @@ Requires-Dist: sympy>=1.13.2
|
|
24
24
|
Requires-Dist: numpy>=1.13.1
|
25
25
|
Requires-Dist: scipy>=1.3.0
|
26
26
|
Requires-Dist: jinja2>=2.7.2
|
27
|
+
Requires-Dist: matplotlib
|
28
|
+
Requires-Dist: scipy
|
29
|
+
Requires-Dist: scikit-learn
|
27
30
|
Dynamic: license-file
|
28
31
|
|
29
32
|
# kim-tools
|
30
33
|
|
34
|
+

|
35
|
+
[](https://kim-tools.readthedocs.io/en/latest/)
|
36
|
+
[](https://pypi.org/project/kim-tools/)
|
37
|
+
|
31
38
|
KIMTestDriver and SingleCrystalTestDriver classes for creating OpenKIM Test Drivers, and helper routines for writing
|
32
39
|
KIM Tests and Verification Checks. Documentation at https://kim-tools.readthedocs.io.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# kim-tools
|
2
|
+
|
3
|
+

|
4
|
+
[](https://kim-tools.readthedocs.io/en/latest/)
|
5
|
+
[](https://pypi.org/project/kim-tools/)
|
6
|
+
|
7
|
+
KIMTestDriver and SingleCrystalTestDriver classes for creating OpenKIM Test Drivers, and helper routines for writing
|
8
|
+
KIM Tests and Verification Checks. Documentation at https://kim-tools.readthedocs.io.
|
@@ -12,7 +12,7 @@ from math import acos, cos, degrees, radians, sin, sqrt
|
|
12
12
|
from os import PathLike
|
13
13
|
from random import random
|
14
14
|
from tempfile import NamedTemporaryFile
|
15
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
15
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
16
16
|
|
17
17
|
import ase
|
18
18
|
import numpy as np
|
@@ -63,6 +63,8 @@ __all__ = [
|
|
63
63
|
"AFLOW",
|
64
64
|
]
|
65
65
|
|
66
|
+
AFLOW_EXECUTABLE = "aflow"
|
67
|
+
|
66
68
|
|
67
69
|
class IncorrectSpaceGroupException(Exception):
|
68
70
|
"""
|
@@ -113,7 +115,7 @@ class EquivalentAtomSet:
|
|
113
115
|
|
114
116
|
|
115
117
|
def write_tmp_poscar_from_atoms_and_run_function(
|
116
|
-
atoms: Atoms, function:
|
118
|
+
atoms: Atoms, function: Callable, *args, **kwargs
|
117
119
|
) -> Any:
|
118
120
|
"""
|
119
121
|
Write the Atoms file to a NamedTemporaryFile and run 'function' on it.
|
@@ -141,7 +143,7 @@ def check_number_of_atoms(
|
|
141
143
|
has the correct number of atoms according to prototype_label
|
142
144
|
|
143
145
|
Raises:
|
144
|
-
|
146
|
+
IncorrectNumAtomsException
|
145
147
|
"""
|
146
148
|
prototype_label_list = prototype_label.split("_")
|
147
149
|
pearson = prototype_label_list[1]
|
@@ -267,8 +269,9 @@ def get_equivalent_atom_sets_from_prototype_and_atom_map(
|
|
267
269
|
`atoms`, and `prototype_label` is the detected prototype label
|
268
270
|
|
269
271
|
Args:
|
270
|
-
sort_atoms:
|
271
|
-
|
272
|
+
sort_atoms:
|
273
|
+
If `atom_map` was obtained by sorting `atoms` before writing it to
|
274
|
+
POSCAR, set this to True
|
272
275
|
"""
|
273
276
|
|
274
277
|
if sort_atoms:
|
@@ -741,7 +744,10 @@ class AFLOW:
|
|
741
744
|
"""
|
742
745
|
|
743
746
|
def __init__(
|
744
|
-
self,
|
747
|
+
self,
|
748
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
749
|
+
aflow_work_dir: str = "",
|
750
|
+
np: int = 4,
|
745
751
|
):
|
746
752
|
"""
|
747
753
|
Args:
|
@@ -752,7 +758,7 @@ class AFLOW:
|
|
752
758
|
self.aflow_executable = aflow_executable
|
753
759
|
|
754
760
|
try:
|
755
|
-
subprocess.check_output([
|
761
|
+
subprocess.check_output([aflow_executable, "--proto=A_cF4_225_a"])
|
756
762
|
except Exception:
|
757
763
|
raise self.AFLOWNotFoundException(
|
758
764
|
"Failed to run an AFLOW test command. It is likely "
|
@@ -1209,9 +1215,10 @@ class AFLOW:
|
|
1209
1215
|
sort_atoms2: Whether to sort atoms2 before comparing.
|
1210
1216
|
|
1211
1217
|
Returns:
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1218
|
+
* basis transformation
|
1219
|
+
* rotation
|
1220
|
+
* origin shift
|
1221
|
+
* atom_map (atom_map[index_in_structure_1] = index_in_structure_2)
|
1215
1222
|
|
1216
1223
|
Raises:
|
1217
1224
|
AFLOW.FailedToMatchException: if AFLOW fails to match the crystals
|
@@ -1494,9 +1501,11 @@ class AFLOW:
|
|
1494
1501
|
nl_len = nl.size
|
1495
1502
|
cov_mult += 1
|
1496
1503
|
# set the maximum error to 1% of NN distance to follow AFLOW convention
|
1497
|
-
|
1504
|
+
# rescale by cube root of cell volume to get rough conversion from
|
1505
|
+
# cartesian to fractional
|
1506
|
+
max_resid = nl.min() * 0.01 * atoms.get_volume() ** (-1 / 3)
|
1498
1507
|
logger.info(
|
1499
|
-
"Automatically set max residual for solving position "
|
1508
|
+
"Automatically set max fractional residual for solving position "
|
1500
1509
|
f"equations to {max_resid}"
|
1501
1510
|
)
|
1502
1511
|
|
@@ -7,13 +7,17 @@ import logging
|
|
7
7
|
import os
|
8
8
|
from itertools import product
|
9
9
|
from math import ceil
|
10
|
-
from typing import Dict, List, Union
|
10
|
+
from typing import Dict, List, Optional, Tuple, Union
|
11
11
|
|
12
|
+
import matplotlib.pyplot as plt
|
12
13
|
import numpy as np
|
14
|
+
import numpy.typing as npt
|
13
15
|
from ase import Atoms
|
14
16
|
from ase.cell import Cell
|
15
|
-
from ase.geometry import get_duplicate_atoms
|
16
|
-
from
|
17
|
+
from ase.geometry import get_distances, get_duplicate_atoms
|
18
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
19
|
+
from scipy.stats import kstest
|
20
|
+
from sklearn.decomposition import PCA
|
17
21
|
from sympy import Matrix, cos, matrix2numpy, sin, sqrt, symbols
|
18
22
|
|
19
23
|
logger = logging.getLogger(__name__)
|
@@ -85,6 +89,12 @@ class IncorrectNumAtomsException(Exception):
|
|
85
89
|
"""
|
86
90
|
|
87
91
|
|
92
|
+
class PeriodExtensionException(Exception):
|
93
|
+
"""
|
94
|
+
Raised when a period-extending phase transition is detected.
|
95
|
+
"""
|
96
|
+
|
97
|
+
|
88
98
|
def _check_space_group(sgnum: Union[int, str]):
|
89
99
|
try:
|
90
100
|
assert 1 <= int(sgnum) <= 230
|
@@ -96,8 +106,8 @@ def _check_space_group(sgnum: Union[int, str]):
|
|
96
106
|
|
97
107
|
|
98
108
|
def cartesian_to_fractional_itc_rotation_from_ase_cell(
|
99
|
-
cart_rot: ArrayLike, cell: ArrayLike
|
100
|
-
) -> ArrayLike:
|
109
|
+
cart_rot: npt.ArrayLike, cell: npt.ArrayLike
|
110
|
+
) -> npt.ArrayLike:
|
101
111
|
"""
|
102
112
|
Convert Cartesian to fractional rotation. Read the arguments and returns carefully,
|
103
113
|
as there is some unfortunate mixing of row and columns because of the different
|
@@ -132,8 +142,8 @@ def cartesian_to_fractional_itc_rotation_from_ase_cell(
|
|
132
142
|
|
133
143
|
|
134
144
|
def fractional_to_cartesian_itc_rotation_from_ase_cell(
|
135
|
-
frac_rot: ArrayLike, cell: ArrayLike
|
136
|
-
) -> ArrayLike:
|
145
|
+
frac_rot: npt.ArrayLike, cell: npt.ArrayLike
|
146
|
+
) -> npt.ArrayLike:
|
137
147
|
"""
|
138
148
|
Convert fractional to Cartesian rotation. Read the arguments and returns carefully,
|
139
149
|
as there is some unfortunate mixing of row and columns because of the different
|
@@ -168,9 +178,9 @@ def fractional_to_cartesian_itc_rotation_from_ase_cell(
|
|
168
178
|
|
169
179
|
|
170
180
|
def cartesian_rotation_is_in_point_group(
|
171
|
-
cart_rot: ArrayLike,
|
181
|
+
cart_rot: npt.ArrayLike,
|
172
182
|
sgnum: Union[int, str],
|
173
|
-
cell: ArrayLike,
|
183
|
+
cell: npt.ArrayLike,
|
174
184
|
rtol: float = 1e-2,
|
175
185
|
atol: float = 1e-2,
|
176
186
|
) -> bool:
|
@@ -215,7 +225,7 @@ def cartesian_rotation_is_in_point_group(
|
|
215
225
|
return False
|
216
226
|
|
217
227
|
|
218
|
-
def get_cell_from_poscar(poscar: os.PathLike) -> ArrayLike:
|
228
|
+
def get_cell_from_poscar(poscar: os.PathLike) -> npt.ArrayLike:
|
219
229
|
"""
|
220
230
|
Extract the unit cell from a POSCAR file, including the specified scaling
|
221
231
|
"""
|
@@ -437,7 +447,7 @@ def get_symbolic_cell_from_formal_bravais_lattice(
|
|
437
447
|
|
438
448
|
def get_change_of_basis_matrix_to_conventional_cell_from_formal_bravais_lattice(
|
439
449
|
formal_bravais_lattice: str,
|
440
|
-
) -> ArrayLike:
|
450
|
+
) -> npt.ArrayLike:
|
441
451
|
"""
|
442
452
|
Get a change of basis matrix **P** as defined in ITA 1.5.1.2, with "old basis"
|
443
453
|
being the primitive cell of the provided Bravais lattice, and the "new basis" being
|
@@ -498,7 +508,7 @@ def get_change_of_basis_matrix_to_conventional_cell_from_formal_bravais_lattice(
|
|
498
508
|
return np.round(change_of_basis_matrix)
|
499
509
|
|
500
510
|
|
501
|
-
def change_of_basis_atoms(atoms: Atoms, change_of_basis: ArrayLike) -> Atoms:
|
511
|
+
def change_of_basis_atoms(atoms: Atoms, change_of_basis: npt.ArrayLike) -> Atoms:
|
502
512
|
"""
|
503
513
|
Perform an arbitrary basis change on an ``Atoms`` object, duplicating or cropping
|
504
514
|
atoms as needed. A basic check is made that the determinant of ``change_of_basis``
|
@@ -506,7 +516,7 @@ def change_of_basis_atoms(atoms: Atoms, change_of_basis: ArrayLike) -> Atoms:
|
|
506
516
|
that ``change_of_basis`` is appropriate for the particuar crystal described by
|
507
517
|
``atoms``, which is up to the user.
|
508
518
|
|
509
|
-
|
519
|
+
TODO: Incorporate :func:`kstest_reduced_distances` into this function
|
510
520
|
|
511
521
|
Args:
|
512
522
|
atoms:
|
@@ -600,3 +610,176 @@ def get_primitive_genpos_ops(sgnum: Union[int, str]) -> List[Dict]:
|
|
600
610
|
_check_space_group(sgnum)
|
601
611
|
with open(os.path.join(DATA_DIR, "primitive_GENPOS_ops.json")) as f:
|
602
612
|
return np.asarray(json.load(f)[str(sgnum)])
|
613
|
+
|
614
|
+
|
615
|
+
def reduce_and_avg(
|
616
|
+
atoms: Atoms, repeat: Tuple[int, int, int]
|
617
|
+
) -> Tuple[Atoms, npt.NDArray]:
|
618
|
+
"""
|
619
|
+
TODO: Upgrade :func:`change_of_basis_atoms` to provide the distances
|
620
|
+
array, obviating this function
|
621
|
+
|
622
|
+
Function to reduce all atoms to the original unit cell position.
|
623
|
+
|
624
|
+
Args:
|
625
|
+
atoms:
|
626
|
+
The supercell to reduce
|
627
|
+
repeat:
|
628
|
+
The number of repeats of each unit cell vector in the
|
629
|
+
provided supercell
|
630
|
+
|
631
|
+
Returns:
|
632
|
+
* The reduced unit cell
|
633
|
+
* An array of displacement vectors. First dimension: index of reference atom
|
634
|
+
in reduced cell. Second dimension: index of atom in provided supercell.
|
635
|
+
Third dimension: x, y, z
|
636
|
+
"""
|
637
|
+
new_atoms = atoms.copy()
|
638
|
+
|
639
|
+
cell = new_atoms.get_cell()
|
640
|
+
|
641
|
+
# Divide each unit vector by its number of repeats.
|
642
|
+
# See
|
643
|
+
# https://stackoverflow.com/questions/19602187/numpy-divide-each-row-by-a-vector-element.
|
644
|
+
cell = cell / np.array(repeat)[:, None]
|
645
|
+
|
646
|
+
# Decrease size of cell in the atoms object.
|
647
|
+
new_atoms.set_cell(cell)
|
648
|
+
new_atoms.set_pbc((True, True, True))
|
649
|
+
|
650
|
+
# Set averaging factor
|
651
|
+
M = np.prod(repeat)
|
652
|
+
|
653
|
+
# Wrap back the repeated atoms on top of
|
654
|
+
# the reference atoms in the original unit cell.
|
655
|
+
positions = new_atoms.get_positions(wrap=True)
|
656
|
+
|
657
|
+
number_atoms = len(new_atoms)
|
658
|
+
original_number_atoms = number_atoms // M
|
659
|
+
assert number_atoms == original_number_atoms * M
|
660
|
+
avg_positions_in_prim_cell = np.zeros((original_number_atoms, 3))
|
661
|
+
positions_in_prim_cell = np.zeros((number_atoms, 3))
|
662
|
+
|
663
|
+
# Start from end of the atoms
|
664
|
+
# because we will remove all atoms except the reference ones.
|
665
|
+
for i in reversed(range(number_atoms)):
|
666
|
+
if i >= original_number_atoms:
|
667
|
+
# Get the distance to the reference atom in the original unit cell with the
|
668
|
+
# minimum image convention.
|
669
|
+
distance = new_atoms.get_distance(
|
670
|
+
i % original_number_atoms, i, mic=True, vector=True
|
671
|
+
)
|
672
|
+
# Get the position that has the closest distance to
|
673
|
+
# the reference atom in the original unit cell.
|
674
|
+
position_i = positions[i % original_number_atoms] + distance
|
675
|
+
# Remove atom from atoms object.
|
676
|
+
new_atoms.pop()
|
677
|
+
else:
|
678
|
+
# Atom was part of the original unit cell.
|
679
|
+
position_i = positions[i]
|
680
|
+
# Average
|
681
|
+
avg_positions_in_prim_cell[i % original_number_atoms] += position_i / M
|
682
|
+
positions_in_prim_cell[i] = position_i
|
683
|
+
|
684
|
+
new_atoms.set_positions(avg_positions_in_prim_cell)
|
685
|
+
|
686
|
+
# Calculate the distances.
|
687
|
+
distances = np.empty((original_number_atoms, M, 3))
|
688
|
+
for i in range(number_atoms):
|
689
|
+
dr, _ = get_distances(
|
690
|
+
positions_in_prim_cell[i],
|
691
|
+
avg_positions_in_prim_cell[i % original_number_atoms],
|
692
|
+
cell=new_atoms.get_cell(),
|
693
|
+
pbc=True,
|
694
|
+
)
|
695
|
+
# dr is a distance matrix, here we only have one distance
|
696
|
+
assert dr.shape == (1, 1, 3)
|
697
|
+
distances[i % original_number_atoms, i // original_number_atoms] = dr[0][0]
|
698
|
+
|
699
|
+
return new_atoms, distances
|
700
|
+
|
701
|
+
|
702
|
+
def kstest_reduced_distances(
|
703
|
+
reduced_distances: npt.NDArray,
|
704
|
+
significance_level: float = 0.05,
|
705
|
+
plot_filename: Optional[str] = None,
|
706
|
+
number_bins: Optional[int] = None,
|
707
|
+
) -> None:
|
708
|
+
"""
|
709
|
+
TODO: Incorporate this into :func:`change_of_basis_atoms`
|
710
|
+
|
711
|
+
Function to test whether the reduced atom positions are normally distributed
|
712
|
+
around their average.
|
713
|
+
|
714
|
+
Args:
|
715
|
+
reduced_distances:
|
716
|
+
Distance array provided by :func:`reduce_and_avg`
|
717
|
+
significance_level:
|
718
|
+
Significance level for Kolmogorov-Smirnov
|
719
|
+
plot_filename:
|
720
|
+
number_bins:
|
721
|
+
Number of bins for plot
|
722
|
+
|
723
|
+
Raises:
|
724
|
+
PeriodExtensionException:
|
725
|
+
If a non-normal distribution is detected
|
726
|
+
"""
|
727
|
+
assert len(reduced_distances.shape) == 3
|
728
|
+
assert reduced_distances.shape[2] == 3
|
729
|
+
|
730
|
+
if plot_filename is not None:
|
731
|
+
if number_bins is None:
|
732
|
+
raise ValueError(
|
733
|
+
"number_bins must be specified if plot_filename is specified"
|
734
|
+
)
|
735
|
+
if not plot_filename.endswith(".pdf"):
|
736
|
+
raise ValueError(f"{plot_filename} is not a PDF file")
|
737
|
+
with PdfPages(plot_filename) as pdf:
|
738
|
+
for i in range(reduced_distances.shape[0]):
|
739
|
+
fig, axs = plt.subplots(1, 3, figsize=(10.0, 4.0))
|
740
|
+
for j in range(reduced_distances.shape[2]):
|
741
|
+
axs[j].hist(reduced_distances[i, :, j], bins=number_bins)
|
742
|
+
axs[j].set_xlabel(f"$x_{j}$")
|
743
|
+
axs[0].set_ylabel("Counts")
|
744
|
+
fig.suptitle(f"Atom {i}")
|
745
|
+
pdf.savefig()
|
746
|
+
else:
|
747
|
+
if number_bins is not None:
|
748
|
+
raise ValueError(
|
749
|
+
"number_bins must not be specified if plot_filename is not specified"
|
750
|
+
)
|
751
|
+
|
752
|
+
p_values = np.empty((reduced_distances.shape[0], reduced_distances.shape[2]))
|
753
|
+
for i in range(reduced_distances.shape[0]):
|
754
|
+
atom_distances = reduced_distances[i]
|
755
|
+
|
756
|
+
# Perform PCA on the xyz distribution.
|
757
|
+
pca = PCA(n_components=atom_distances.shape[1])
|
758
|
+
pca_components = pca.fit_transform(atom_distances)
|
759
|
+
assert (
|
760
|
+
pca_components.shape == atom_distances.shape == reduced_distances.shape[1:]
|
761
|
+
)
|
762
|
+
|
763
|
+
# Test each component with a KS test.
|
764
|
+
for j in range(pca_components.shape[1]):
|
765
|
+
component = pca_components[:, j]
|
766
|
+
component_mean = np.mean(component)
|
767
|
+
assert abs(component_mean) < 1.0e-7
|
768
|
+
component_std = np.std(component)
|
769
|
+
# Normalize component
|
770
|
+
normalized_component = (component - component_mean) / component_std
|
771
|
+
assert abs(np.mean(normalized_component)) < 1.0e-7
|
772
|
+
assert abs(np.std(normalized_component) - 1.0) < 1.0e-7
|
773
|
+
res = kstest(normalized_component, "norm")
|
774
|
+
p_values[i, j] = res.pvalue
|
775
|
+
|
776
|
+
if np.any(p_values <= significance_level):
|
777
|
+
raise PeriodExtensionException(
|
778
|
+
"Detected non-normal distribution of reduced atom positions around their "
|
779
|
+
f"average (smallest p value {np.min(p_values)})."
|
780
|
+
)
|
781
|
+
else:
|
782
|
+
print(
|
783
|
+
"Detected normal distribution or reduced atom positions around their "
|
784
|
+
f"average (smallest p value {np.min(p_values)})."
|
785
|
+
)
|
@@ -65,6 +65,7 @@ from ..aflow_util import (
|
|
65
65
|
get_space_group_number_from_prototype,
|
66
66
|
prototype_labels_are_equivalent,
|
67
67
|
)
|
68
|
+
from ..aflow_util.core import AFLOW_EXECUTABLE
|
68
69
|
from ..kimunits import convert_list, convert_units
|
69
70
|
from ..symmetry_util import (
|
70
71
|
cartesian_rotation_is_in_point_group,
|
@@ -464,7 +465,7 @@ class KIMTestDriver(ABC):
|
|
464
465
|
to be useful to for most KIM tests
|
465
466
|
|
466
467
|
Attributes:
|
467
|
-
__kim_model_name (str
|
468
|
+
__kim_model_name (Optional[str]):
|
468
469
|
KIM model name, absent if a non-KIM ASE calculator was provided
|
469
470
|
__calc (:obj:`~ase.calculators.calculator.Calculator`):
|
470
471
|
ASE calculator
|
@@ -563,7 +564,8 @@ class KIMTestDriver(ABC):
|
|
563
564
|
) -> None:
|
564
565
|
"""
|
565
566
|
Add a key to the most recent property instance added with
|
566
|
-
:func:`~kim_tools.KIMTestDriver._add_property_instance`.
|
567
|
+
:func:`~kim_tools.test_driver.core.KIMTestDriver._add_property_instance`.
|
568
|
+
If the value is an
|
567
569
|
array, this function will assume you want to write to the beginning of the array
|
568
570
|
in every dimension. This function is intended to write entire keys in one go,
|
569
571
|
and should not be used for modifying existing keys.
|
@@ -721,15 +723,16 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
|
|
721
723
|
prototype_label: str,
|
722
724
|
stoichiometric_species: List[str],
|
723
725
|
a: float,
|
726
|
+
a_unit: str,
|
724
727
|
parameter_values: Optional[List[float]] = None,
|
725
728
|
library_prototype_label: Optional[Union[List[str], str]] = None,
|
726
729
|
short_name: Optional[Union[List[str], str]] = None,
|
727
730
|
cell_cauchy_stress: Optional[List[float]] = None,
|
731
|
+
cell_cauchy_stress_unit: Optional[str] = None,
|
728
732
|
temperature: Optional[float] = None,
|
733
|
+
temperature_unit: Optional[str] = "K",
|
729
734
|
crystal_genome_source_structure_id: Optional[List[List[str]]] = None,
|
730
|
-
|
731
|
-
cell_cauchy_stress_unit: str = "eV/angstrom^3",
|
732
|
-
temperature_unit: str = "K",
|
735
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
733
736
|
) -> str:
|
734
737
|
"""
|
735
738
|
Write common Crystal Genome keys to the last element of ``property_instances``. See
|
@@ -741,6 +744,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
|
|
741
744
|
property_instances:
|
742
745
|
An EDN-serialized list of dictionaries representing KIM Property Instances.
|
743
746
|
The key will be added to the last dictionary in the list
|
747
|
+
aflow_executable:
|
748
|
+
Path to the AFLOW executable
|
744
749
|
|
745
750
|
Returns:
|
746
751
|
Updated EDN-serialized list of property instances
|
@@ -756,7 +761,7 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
|
|
756
761
|
)
|
757
762
|
|
758
763
|
# get parameter names
|
759
|
-
aflow = AFLOW()
|
764
|
+
aflow = AFLOW(aflow_executable=aflow_executable)
|
760
765
|
aflow_parameter_names = aflow.get_param_names_from_prototype(prototype_label)
|
761
766
|
if parameter_values is None:
|
762
767
|
if len(aflow_parameter_names) > 1:
|
@@ -795,6 +800,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
|
|
795
800
|
"Please specify the Cauchy stress as a 6-dimensional vector in Voigt "
|
796
801
|
"order [xx, yy, zz, yz, xz, xy]"
|
797
802
|
)
|
803
|
+
if cell_cauchy_stress_unit is None:
|
804
|
+
raise KIMTestDriver("Please provide a `cell_cauchy_stress_unit`")
|
798
805
|
property_instances = _add_key_to_current_property_instance(
|
799
806
|
property_instances,
|
800
807
|
"cell-cauchy-stress",
|
@@ -803,6 +810,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
|
|
803
810
|
)
|
804
811
|
|
805
812
|
if temperature is not None:
|
813
|
+
if temperature_unit is None:
|
814
|
+
raise KIMTestDriver("Please provide a `temperature_unit`")
|
806
815
|
property_instances = _add_key_to_current_property_instance(
|
807
816
|
property_instances, "temperature", temperature, temperature_unit
|
808
817
|
)
|
@@ -822,17 +831,18 @@ def _add_property_instance_and_common_crystal_genome_keys(
|
|
822
831
|
prototype_label: str,
|
823
832
|
stoichiometric_species: List[str],
|
824
833
|
a: float,
|
834
|
+
a_unit: str,
|
825
835
|
parameter_values: Optional[List[float]] = None,
|
826
836
|
library_prototype_label: Optional[Union[List[str], str]] = None,
|
827
837
|
short_name: Optional[Union[List[str], str]] = None,
|
828
838
|
cell_cauchy_stress: Optional[List[float]] = None,
|
839
|
+
cell_cauchy_stress_unit: Optional[str] = None,
|
829
840
|
temperature: Optional[float] = None,
|
841
|
+
temperature_unit: Optional[str] = "K",
|
830
842
|
crystal_genome_source_structure_id: Optional[List[List[str]]] = None,
|
831
|
-
a_unit: str = "angstrom",
|
832
|
-
cell_cauchy_stress_unit: str = "eV/angstrom^3",
|
833
|
-
temperature_unit: str = "K",
|
834
843
|
disclaimer: Optional[str] = None,
|
835
844
|
property_instances: Optional[str] = None,
|
845
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
836
846
|
) -> str:
|
837
847
|
"""
|
838
848
|
Initialize a new property instance to ``property_instances`` (an empty
|
@@ -853,6 +863,8 @@ def _add_property_instance_and_common_crystal_genome_keys(
|
|
853
863
|
"This relaxation did not reach the desired tolerance."
|
854
864
|
property_instances:
|
855
865
|
A pre-existing EDN-serialized list of KIM Property instances to add to
|
866
|
+
aflow_executable:
|
867
|
+
Path to the AFLOW executable
|
856
868
|
|
857
869
|
Returns:
|
858
870
|
Updated EDN-serialized list of property instances
|
@@ -861,24 +873,29 @@ def _add_property_instance_and_common_crystal_genome_keys(
|
|
861
873
|
property_name, disclaimer, property_instances
|
862
874
|
)
|
863
875
|
return _add_common_crystal_genome_keys_to_current_property_instance(
|
864
|
-
property_instances,
|
865
|
-
prototype_label,
|
866
|
-
stoichiometric_species,
|
867
|
-
a,
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
temperature,
|
873
|
-
|
874
|
-
|
875
|
-
cell_cauchy_stress_unit,
|
876
|
-
|
876
|
+
property_instances=property_instances,
|
877
|
+
prototype_label=prototype_label,
|
878
|
+
stoichiometric_species=stoichiometric_species,
|
879
|
+
a=a,
|
880
|
+
a_unit=a_unit,
|
881
|
+
parameter_values=parameter_values,
|
882
|
+
library_prototype_label=library_prototype_label,
|
883
|
+
short_name=short_name,
|
884
|
+
temperature=temperature,
|
885
|
+
temperature_unit=temperature_unit,
|
886
|
+
crystal_genome_source_structure_id=crystal_genome_source_structure_id,
|
887
|
+
cell_cauchy_stress_unit=cell_cauchy_stress_unit,
|
888
|
+
cell_cauchy_stress=cell_cauchy_stress,
|
889
|
+
aflow_executable=aflow_executable,
|
877
890
|
)
|
878
891
|
|
879
892
|
|
880
893
|
def get_crystal_structure_from_atoms(
|
881
|
-
atoms: Atoms,
|
894
|
+
atoms: Atoms,
|
895
|
+
get_short_name: bool = True,
|
896
|
+
prim: bool = True,
|
897
|
+
aflow_np: int = 4,
|
898
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
882
899
|
) -> Dict:
|
883
900
|
"""
|
884
901
|
By performing a symmetry analysis on an :class:`~ase.Atoms` object, generate a
|
@@ -896,6 +913,8 @@ def get_crystal_structure_from_atoms(
|
|
896
913
|
whether to compare against AFLOW prototype library to obtain short-name
|
897
914
|
prim: whether to primitivize the atoms object first
|
898
915
|
aflow_np: Number of processors to use with AFLOW executable
|
916
|
+
aflow_executable:
|
917
|
+
Path to the AFLOW executable
|
899
918
|
|
900
919
|
Returns:
|
901
920
|
A dictionary that has the following Property Keys (possibly optionally) defined.
|
@@ -912,7 +931,7 @@ def get_crystal_structure_from_atoms(
|
|
912
931
|
- "short-name"
|
913
932
|
|
914
933
|
"""
|
915
|
-
aflow = AFLOW(np=aflow_np)
|
934
|
+
aflow = AFLOW(aflow_executable=aflow_executable, np=aflow_np)
|
916
935
|
|
917
936
|
proto_des = aflow.get_prototype_designation_from_atoms(atoms, prim=prim)
|
918
937
|
library_prototype_label, short_name = (
|
@@ -933,16 +952,21 @@ def get_crystal_structure_from_atoms(
|
|
933
952
|
prototype_label=proto_des["aflow_prototype_label"],
|
934
953
|
stoichiometric_species=sorted(list(set(atoms.get_chemical_symbols()))),
|
935
954
|
a=a,
|
955
|
+
a_unit="angstrom",
|
936
956
|
parameter_values=parameter_values,
|
937
957
|
library_prototype_label=library_prototype_label,
|
938
958
|
short_name=short_name,
|
959
|
+
aflow_executable=aflow_executable,
|
939
960
|
)
|
940
961
|
|
941
962
|
return kim_edn.loads(property_instances)[0]
|
942
963
|
|
943
964
|
|
944
965
|
def get_poscar_from_crystal_structure(
|
945
|
-
crystal_structure: Dict,
|
966
|
+
crystal_structure: Dict,
|
967
|
+
output_file: Optional[str] = None,
|
968
|
+
flat: bool = False,
|
969
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
946
970
|
) -> Optional[str]:
|
947
971
|
"""
|
948
972
|
Write a POSCAR coordinate file (or output it as a multiline string) from the AFLOW
|
@@ -968,13 +992,15 @@ def get_poscar_from_crystal_structure(
|
|
968
992
|
Name of the output file. If not provided, the output is returned as a string
|
969
993
|
flat:
|
970
994
|
whether the input dictionary is flattened
|
995
|
+
aflow_executable:
|
996
|
+
path to AFLOW executable
|
971
997
|
Returns:
|
972
998
|
If ``output_file`` is not provided, a string in POSCAR format containg the
|
973
999
|
primitive unit cell of the crystal as defined in
|
974
1000
|
http://doi.org/10.1016/j.commatsci.2017.01.017. Lengths are always in angstrom.
|
975
1001
|
Raises:
|
976
|
-
AFLOW.ChangedSymmetryException:
|
977
|
-
|
1002
|
+
AFLOW.ChangedSymmetryException:
|
1003
|
+
if the symmetry of the atoms object is different from ``prototype_label``
|
978
1004
|
"""
|
979
1005
|
if flat:
|
980
1006
|
prototype_label = crystal_structure["prototype-label.source-value"]
|
@@ -993,7 +1019,7 @@ def get_poscar_from_crystal_structure(
|
|
993
1019
|
"source-value"
|
994
1020
|
]
|
995
1021
|
|
996
|
-
aflow = AFLOW()
|
1022
|
+
aflow = AFLOW(aflow_executable=aflow_executable)
|
997
1023
|
aflow_parameter_names = aflow.get_param_names_from_prototype(prototype_label)
|
998
1024
|
|
999
1025
|
# Atoms objects are always in angstrom
|
@@ -1035,7 +1061,9 @@ def get_poscar_from_crystal_structure(
|
|
1035
1061
|
|
1036
1062
|
|
1037
1063
|
def get_atoms_from_crystal_structure(
|
1038
|
-
crystal_structure: Dict,
|
1064
|
+
crystal_structure: Dict,
|
1065
|
+
flat: bool = False,
|
1066
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
1039
1067
|
) -> Atoms:
|
1040
1068
|
"""
|
1041
1069
|
Generate an :class:`~ase.Atoms` object from the AFLOW Prototype Designation obtained
|
@@ -1059,6 +1087,8 @@ def get_atoms_from_crystal_structure(
|
|
1059
1087
|
Dictionary containing the required keys in KIM Property Instance format
|
1060
1088
|
flat:
|
1061
1089
|
whether the dictionary is flattened
|
1090
|
+
aflow_executable:
|
1091
|
+
path to AFLOW executable
|
1062
1092
|
|
1063
1093
|
Returns:
|
1064
1094
|
Primitive unit cell of the crystal as defined in the
|
@@ -1070,7 +1100,9 @@ def get_atoms_from_crystal_structure(
|
|
1070
1100
|
if the symmetry of the atoms object is different from ``prototype_label``
|
1071
1101
|
"""
|
1072
1102
|
try:
|
1073
|
-
poscar_string = get_poscar_from_crystal_structure(
|
1103
|
+
poscar_string = get_poscar_from_crystal_structure(
|
1104
|
+
crystal_structure, flat=flat, aflow_executable=aflow_executable
|
1105
|
+
)
|
1074
1106
|
except AFLOW.ChangedSymmetryException as e:
|
1075
1107
|
# re-raise, just indicating that this function knows about this exception
|
1076
1108
|
raise e
|
@@ -1094,8 +1126,23 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1094
1126
|
<https://openkim.org/properties/show/crystal-structure-npt>`_
|
1095
1127
|
property representing the nominal crystal structure and conditions of the
|
1096
1128
|
current call to the Test Driver.
|
1129
|
+
aflow_executable [str]:
|
1130
|
+
Path to the AFLOW executable
|
1097
1131
|
"""
|
1098
1132
|
|
1133
|
+
def __init__(
|
1134
|
+
self, model: Union[str, Calculator], aflow_executable: str = AFLOW_EXECUTABLE
|
1135
|
+
) -> None:
|
1136
|
+
"""
|
1137
|
+
Args:
|
1138
|
+
model:
|
1139
|
+
ASE calculator or KIM model name to use
|
1140
|
+
aflow_executable:
|
1141
|
+
Path to AFLOW executable
|
1142
|
+
"""
|
1143
|
+
self.aflow_executable = aflow_executable
|
1144
|
+
super().__init__(model)
|
1145
|
+
|
1099
1146
|
def _setup(
|
1100
1147
|
self,
|
1101
1148
|
material: Union[Atoms, Dict],
|
@@ -1156,11 +1203,14 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1156
1203
|
simply provides recordkeeping of it. It is up to derived classes to
|
1157
1204
|
implement actually setting the temperature of the system.
|
1158
1205
|
"""
|
1206
|
+
|
1159
1207
|
if cell_cauchy_stress_eV_angstrom3 is None:
|
1160
1208
|
cell_cauchy_stress_eV_angstrom3 = [0, 0, 0, 0, 0, 0]
|
1161
1209
|
|
1162
1210
|
if isinstance(material, Atoms):
|
1163
|
-
crystal_structure = get_crystal_structure_from_atoms(
|
1211
|
+
crystal_structure = get_crystal_structure_from_atoms(
|
1212
|
+
atoms=material, aflow_executable=self.aflow_executable
|
1213
|
+
)
|
1164
1214
|
msg = (
|
1165
1215
|
"Rebuilding atoms object in a standard setting defined by "
|
1166
1216
|
"doi.org/10.1016/j.commatsci.2017.01.017. See log file or computed "
|
@@ -1243,7 +1293,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1243
1293
|
In practical terms, this means that this function is designed to take as input a
|
1244
1294
|
relaxed or time-averaged from MD (and folded back into the original primitive
|
1245
1295
|
cell) copy of the :class:`~ase.Atoms` object originally obtained from
|
1246
|
-
:func:`~kim_tools.SingleCrystalTestDriver._get_atoms()`.
|
1296
|
+
:func:`~kim_tools.test_driver.core.SingleCrystalTestDriver._get_atoms()`.
|
1247
1297
|
|
1248
1298
|
If finding the parameter fails, this function will raise an exception. This
|
1249
1299
|
probably indicates a phase transition to a different symmetry, which is a normal
|
@@ -1279,9 +1329,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1279
1329
|
If a more definitive error indicating a phase transformation is
|
1280
1330
|
encountered
|
1281
1331
|
"""
|
1282
|
-
|
1332
|
+
aflow = AFLOW(aflow_executable=self.aflow_executable)
|
1283
1333
|
try:
|
1284
|
-
aflow_parameter_values =
|
1334
|
+
aflow_parameter_values = aflow.solve_for_params_of_known_prototype(
|
1285
1335
|
atoms=atoms,
|
1286
1336
|
prototype_label=self.__nominal_crystal_structure_npt["prototype-label"][
|
1287
1337
|
"source-value"
|
@@ -1324,10 +1374,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1324
1374
|
Whether or not the symmetry is unchanged
|
1325
1375
|
|
1326
1376
|
"""
|
1377
|
+
aflow = AFLOW(aflow_executable=self.aflow_executable)
|
1327
1378
|
return prototype_labels_are_equivalent(
|
1328
|
-
|
1329
|
-
"aflow_prototype_label"
|
1330
|
-
],
|
1379
|
+
aflow.get_prototype_designation_from_atoms(atoms)["aflow_prototype_label"],
|
1331
1380
|
self.__nominal_crystal_structure_npt["prototype-label"]["source-value"],
|
1332
1381
|
)
|
1333
1382
|
|
@@ -1343,7 +1392,8 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1343
1392
|
|
1344
1393
|
Args:
|
1345
1394
|
change_of_basis:
|
1346
|
-
Passed to
|
1395
|
+
Passed to
|
1396
|
+
:meth:`kim_tools.test_driver.core.SingleCrystalTestDriver._get_atoms`
|
1347
1397
|
filename:
|
1348
1398
|
File to save to. Will be automatically moved and renamed,
|
1349
1399
|
e.g. 'instance.poscar' -> 'output/instance-1.poscar'
|
@@ -1362,8 +1412,10 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1362
1412
|
def _add_property_instance_and_common_crystal_genome_keys(
|
1363
1413
|
self,
|
1364
1414
|
property_name: str,
|
1365
|
-
write_stress: bool = False,
|
1366
|
-
write_temp: bool = False,
|
1415
|
+
write_stress: Union[bool, List[float]] = False,
|
1416
|
+
write_temp: Union[bool, float] = False,
|
1417
|
+
stress_unit: Optional[str] = None,
|
1418
|
+
temp_unit: str = "K",
|
1367
1419
|
disclaimer: Optional[str] = None,
|
1368
1420
|
) -> None:
|
1369
1421
|
"""
|
@@ -1378,9 +1430,20 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1378
1430
|
"tag:staff@noreply.openkim.org,2023-02-21:property/binding-energy-crystal"
|
1379
1431
|
or "binding-energy-crystal"
|
1380
1432
|
write_stress:
|
1381
|
-
|
1433
|
+
What (if any) to write to the "cell-cauchy-stress" key.
|
1434
|
+
If True, write the nominal stress the Test Driver was initialized with.
|
1435
|
+
If a list of floats is given, write that value (it must be a length 6
|
1436
|
+
list representing a stress tensor in Voigt order xx,yy,zz,yz,xz,xy).
|
1437
|
+
If a list is specified, you must also specify `stress_unit`.
|
1382
1438
|
write_temp:
|
1383
|
-
|
1439
|
+
What (if any) to write to the "temperature" key.
|
1440
|
+
If True, write the nominal temperature the Test Driver was initialized
|
1441
|
+
with. If float is given, write that value.
|
1442
|
+
stress_unit:
|
1443
|
+
Unit of stress. Required if a stress tensor is specified in
|
1444
|
+
`write_stress`.
|
1445
|
+
temp_unit:
|
1446
|
+
Unit of temperature. Defaults to K.
|
1384
1447
|
disclaimer:
|
1385
1448
|
An optional disclaimer commenting on the applicability of this result,
|
1386
1449
|
e.g. "This relaxation did not reach the desired tolerance."
|
@@ -1407,20 +1470,37 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1407
1470
|
|
1408
1471
|
short_name = _get_optional_source_value(crystal_structure, "short-name")
|
1409
1472
|
|
1410
|
-
|
1411
|
-
|
1473
|
+
if write_stress is False:
|
1474
|
+
cell_cauchy_stress = None
|
1475
|
+
cell_cauchy_stress_unit = None
|
1476
|
+
elif write_stress is True:
|
1477
|
+
if stress_unit is not None:
|
1478
|
+
raise KIMTestDriverError(
|
1479
|
+
"Setting write_stress=True indicates that you wish to use the "
|
1480
|
+
"nominal stress and stress unit stored in this object. Do not "
|
1481
|
+
"specify a stress_unit in this case"
|
1482
|
+
)
|
1412
1483
|
cell_cauchy_stress = crystal_structure["cell-cauchy-stress"]["source-value"]
|
1484
|
+
cell_cauchy_stress_unit = crystal_structure["cell-cauchy-stress"][
|
1485
|
+
"source-unit"
|
1486
|
+
]
|
1413
1487
|
else:
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1488
|
+
if len(write_stress) != 6:
|
1489
|
+
raise KIMTestDriverError(
|
1490
|
+
"`write_stress` must be a boolean or an array of length 6"
|
1491
|
+
)
|
1492
|
+
cell_cauchy_stress = write_stress
|
1493
|
+
cell_cauchy_stress_unit = stress_unit
|
1417
1494
|
|
1418
|
-
if write_temp:
|
1495
|
+
if write_temp is False:
|
1496
|
+
temperature = None
|
1497
|
+
temperature_unit = None
|
1498
|
+
elif write_temp is True:
|
1419
1499
|
temperature = crystal_structure["temperature"]["source-value"]
|
1500
|
+
temperature_unit = crystal_structure["temperature"]["source-unit"]
|
1420
1501
|
else:
|
1421
|
-
temperature =
|
1422
|
-
|
1423
|
-
temperature_unit = crystal_structure["temperature"]["source-unit"]
|
1502
|
+
temperature = write_temp
|
1503
|
+
temperature_unit = temp_unit
|
1424
1504
|
|
1425
1505
|
crystal_genome_source_structure_id = _get_optional_source_value(
|
1426
1506
|
crystal_structure, "crystal-genome-source-structure-id"
|
@@ -1432,17 +1512,18 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1432
1512
|
prototype_label=prototype_label,
|
1433
1513
|
stoichiometric_species=stoichiometric_species,
|
1434
1514
|
a=a,
|
1515
|
+
a_unit=a_unit,
|
1435
1516
|
parameter_values=parameter_values,
|
1436
1517
|
library_prototype_label=library_prototype_label,
|
1437
1518
|
short_name=short_name,
|
1438
1519
|
cell_cauchy_stress=cell_cauchy_stress,
|
1439
|
-
temperature=temperature,
|
1440
|
-
crystal_genome_source_structure_id=crystal_genome_source_structure_id,
|
1441
|
-
a_unit=a_unit,
|
1442
1520
|
cell_cauchy_stress_unit=cell_cauchy_stress_unit,
|
1521
|
+
temperature=temperature,
|
1443
1522
|
temperature_unit=temperature_unit,
|
1523
|
+
crystal_genome_source_structure_id=crystal_genome_source_structure_id,
|
1444
1524
|
disclaimer=disclaimer,
|
1445
1525
|
property_instances=super()._get_serialized_property_instances(),
|
1526
|
+
aflow_executable=self.aflow_executable,
|
1446
1527
|
)
|
1447
1528
|
)
|
1448
1529
|
|
@@ -1460,8 +1541,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1460
1541
|
Get the nominal temperature
|
1461
1542
|
|
1462
1543
|
Args:
|
1463
|
-
unit:
|
1464
|
-
|
1544
|
+
unit:
|
1545
|
+
The requested unit for the output. Must be understood by the GNU
|
1546
|
+
``units`` utility
|
1465
1547
|
"""
|
1466
1548
|
source_value = self.__nominal_crystal_structure_npt["temperature"][
|
1467
1549
|
"source-value"
|
@@ -1478,8 +1560,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1478
1560
|
Get the nominal stress
|
1479
1561
|
|
1480
1562
|
Args:
|
1481
|
-
unit:
|
1482
|
-
|
1563
|
+
unit:
|
1564
|
+
The requested unit for the output. Must be understood by the GNU
|
1565
|
+
``units`` utility
|
1483
1566
|
"""
|
1484
1567
|
source_value = self.__nominal_crystal_structure_npt["cell-cauchy-stress"][
|
1485
1568
|
"source-value"
|
@@ -1578,6 +1661,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1578
1661
|
aflow_np=aflow_np,
|
1579
1662
|
rot_rtol=rot_rtol,
|
1580
1663
|
rot_atol=rot_atol,
|
1664
|
+
aflow_executable=self.aflow_executable,
|
1581
1665
|
)
|
1582
1666
|
logger.info(
|
1583
1667
|
f"Deduplicated {len(self.property_instances)} Property Instances "
|
@@ -1639,7 +1723,8 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1639
1723
|
corresponding to the "old basis" and the returned ``Atoms`` object being
|
1640
1724
|
in the "new basis".
|
1641
1725
|
|
1642
|
-
See the docstring for
|
1726
|
+
See the docstring for
|
1727
|
+
:func:`kim_tools.symmetry_util.core.change_of_basis_atoms` for
|
1643
1728
|
more information on how to define the change of basis.
|
1644
1729
|
|
1645
1730
|
Returns:
|
@@ -1647,7 +1732,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
|
|
1647
1732
|
Lengths are always in angstrom
|
1648
1733
|
"""
|
1649
1734
|
crystal_structure = self.__nominal_crystal_structure_npt
|
1650
|
-
atoms_prim = get_atoms_from_crystal_structure(
|
1735
|
+
atoms_prim = get_atoms_from_crystal_structure(
|
1736
|
+
crystal_structure, aflow_executable=self.aflow_executable
|
1737
|
+
)
|
1651
1738
|
if isinstance(change_of_basis, str):
|
1652
1739
|
if change_of_basis.lower() == "primitive":
|
1653
1740
|
change_of_basis_matrix = None
|
@@ -1702,7 +1789,8 @@ def query_crystal_structures(
|
|
1702
1789
|
The list of possible shortnames is taken by postprocessing README_PROTO.TXT
|
1703
1790
|
from the AFLOW software and packaged with kim-tools for reproducibility. To
|
1704
1791
|
see the exact list of possible short names, call
|
1705
|
-
:func:`kim_tools.read_shortnames` and inspect the values of
|
1792
|
+
:func:`kim_tools.aflow_util.core.read_shortnames` and inspect the values of
|
1793
|
+
the returned
|
1706
1794
|
dictionary. Note that a given short name corresponds to an exact set of
|
1707
1795
|
parameters (with some tolerance), except the overall scale of the crystal.
|
1708
1796
|
For example, "Hexagonal Close Packed" will return only structures with a
|
@@ -1785,6 +1873,7 @@ def detect_unique_crystal_structures(
|
|
1785
1873
|
aflow_np: int = 4,
|
1786
1874
|
rot_rtol: float = 0.01,
|
1787
1875
|
rot_atol: float = 0.01,
|
1876
|
+
aflow_executable=AFLOW_EXECUTABLE,
|
1788
1877
|
) -> Dict:
|
1789
1878
|
"""
|
1790
1879
|
Detect which of the provided crystal structures is unique
|
@@ -1813,6 +1902,8 @@ def detect_unique_crystal_structures(
|
|
1813
1902
|
rotations. Default value chosen to be commensurate with AFLOW
|
1814
1903
|
default distance tolerance of 0.01*(NN distance). Used only if
|
1815
1904
|
`allow_rotation` is False
|
1905
|
+
aflow_executable:
|
1906
|
+
Path to AFLOW executable
|
1816
1907
|
Returns:
|
1817
1908
|
Dictionary with keys corresponding to indices of unique structures and values
|
1818
1909
|
being lists of indices of their duplicates
|
@@ -1820,7 +1911,7 @@ def detect_unique_crystal_structures(
|
|
1820
1911
|
if len(crystal_structures) == 0:
|
1821
1912
|
return []
|
1822
1913
|
|
1823
|
-
aflow = AFLOW(np=aflow_np)
|
1914
|
+
aflow = AFLOW(aflow_executable=aflow_executable, np=aflow_np)
|
1824
1915
|
|
1825
1916
|
with TemporaryDirectory() as tmpdirname:
|
1826
1917
|
# I don't know if crystal_structurs is a list or a dict with integer keys
|
@@ -1832,7 +1923,9 @@ def detect_unique_crystal_structures(
|
|
1832
1923
|
structure = crystal_structures[i]
|
1833
1924
|
try:
|
1834
1925
|
get_poscar_from_crystal_structure(
|
1835
|
-
structure,
|
1926
|
+
structure,
|
1927
|
+
os.path.join(tmpdirname, str(i)),
|
1928
|
+
aflow_executable=aflow_executable,
|
1836
1929
|
)
|
1837
1930
|
except AFLOW.ChangedSymmetryException:
|
1838
1931
|
logger.info(
|
@@ -1888,6 +1981,7 @@ def detect_unique_crystal_structures(
|
|
1888
1981
|
aflow_np=aflow_np,
|
1889
1982
|
rot_rtol=rot_rtol,
|
1890
1983
|
rot_atol=rot_atol,
|
1984
|
+
aflow_executable=aflow_executable,
|
1891
1985
|
)
|
1892
1986
|
)
|
1893
1987
|
|
@@ -1901,6 +1995,7 @@ def get_deduplicated_property_instances(
|
|
1901
1995
|
aflow_np: int = 4,
|
1902
1996
|
rot_rtol: float = 0.01,
|
1903
1997
|
rot_atol: float = 0.01,
|
1998
|
+
aflow_executable: str = AFLOW_EXECUTABLE,
|
1904
1999
|
) -> List[Dict]:
|
1905
2000
|
"""
|
1906
2001
|
Given a list of dictionaries constituting KIM Property instances,
|
@@ -1935,6 +2030,8 @@ def get_deduplicated_property_instances(
|
|
1935
2030
|
rotations. Default value chosen to be commensurate with AFLOW
|
1936
2031
|
default distance tolerance of 0.01*(NN distance). Used only if
|
1937
2032
|
`allow_rotation` is False
|
2033
|
+
aflow_executable:
|
2034
|
+
Path to aflow executable
|
1938
2035
|
|
1939
2036
|
Returns:
|
1940
2037
|
The deduplicated property instances
|
@@ -1969,6 +2066,7 @@ def get_deduplicated_property_instances(
|
|
1969
2066
|
aflow_np=aflow_np,
|
1970
2067
|
rot_rtol=rot_rtol,
|
1971
2068
|
rot_atol=rot_atol,
|
2069
|
+
aflow_executable=aflow_executable,
|
1972
2070
|
)
|
1973
2071
|
|
1974
2072
|
# Put together the list of unique instances for the current
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: kim-tools
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5
|
4
4
|
Summary: Base classes and helper routines for writing KIM Tests
|
5
|
-
Author-email: ilia Nikiforov <nikif002@umn.edu>, Ellad Tadmor <tadmor@umn.edu>, Claire Waters <bwaters@umn.edu>, "Daniel S. Karls" <karl0100umn@gmail.com>, Matt Bierbaum <matt.bierbaum@gmail.com>, Eric Fuemmeler <efuemmel@umn.edu>
|
5
|
+
Author-email: ilia Nikiforov <nikif002@umn.edu>, Ellad Tadmor <tadmor@umn.edu>, Claire Waters <bwaters@umn.edu>, "Daniel S. Karls" <karl0100umn@gmail.com>, Matt Bierbaum <matt.bierbaum@gmail.com>, Eric Fuemmeler <efuemmel@umn.edu>, Philipp Hoellmer <ph2484@nyu.edu>, Guanming Zhang <gz2241@nyu.edu>
|
6
6
|
Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
|
7
7
|
Project-URL: Homepage, https://kim-tools.readthedocs.io
|
8
8
|
Project-URL: Issues, https://github.com/openkim/kim-tools/issues
|
@@ -24,9 +24,16 @@ Requires-Dist: sympy>=1.13.2
|
|
24
24
|
Requires-Dist: numpy>=1.13.1
|
25
25
|
Requires-Dist: scipy>=1.3.0
|
26
26
|
Requires-Dist: jinja2>=2.7.2
|
27
|
+
Requires-Dist: matplotlib
|
28
|
+
Requires-Dist: scipy
|
29
|
+
Requires-Dist: scikit-learn
|
27
30
|
Dynamic: license-file
|
28
31
|
|
29
32
|
# kim-tools
|
30
33
|
|
34
|
+

|
35
|
+
[](https://kim-tools.readthedocs.io/en/latest/)
|
36
|
+
[](https://pypi.org/project/kim-tools/)
|
37
|
+
|
31
38
|
KIMTestDriver and SingleCrystalTestDriver classes for creating OpenKIM Test Drivers, and helper routines for writing
|
32
39
|
KIM Tests and Verification Checks. Documentation at https://kim-tools.readthedocs.io.
|
@@ -14,7 +14,10 @@ dependencies = [
|
|
14
14
|
"sympy >= 1.13.2",
|
15
15
|
"numpy >= 1.13.1",
|
16
16
|
"scipy >= 1.3.0",
|
17
|
-
"jinja2 >= 2.7.2"
|
17
|
+
"jinja2 >= 2.7.2",
|
18
|
+
"matplotlib",
|
19
|
+
"scipy",
|
20
|
+
"scikit-learn"
|
18
21
|
]
|
19
22
|
authors = [
|
20
23
|
{ name = "ilia Nikiforov", email = "nikif002@umn.edu" },
|
@@ -22,7 +25,9 @@ authors = [
|
|
22
25
|
{ name = "Claire Waters", email = "bwaters@umn.edu" },
|
23
26
|
{ name = "Daniel S. Karls", email = "karl0100umn@gmail.com" },
|
24
27
|
{ name = "Matt Bierbaum", email = "matt.bierbaum@gmail.com" },
|
25
|
-
{ name = "Eric Fuemmeler", email = "efuemmel@umn.edu"}
|
28
|
+
{ name = "Eric Fuemmeler", email = "efuemmel@umn.edu" },
|
29
|
+
{ name = "Philipp Hoellmer", email = "ph2484@nyu.edu" },
|
30
|
+
{ name = "Guanming Zhang", email = "gz2241@nyu.edu" }
|
26
31
|
]
|
27
32
|
maintainers = [
|
28
33
|
{ name = "ilia Nikiforov", email = "nikif002@umn.edu" },
|
@@ -32,6 +32,7 @@ logging.basicConfig(filename="kim-tools.log", level=logging.INFO, force=True)
|
|
32
32
|
|
33
33
|
TEST_CASES = [572, 365, 1729, 1194, 1473, 166, 1205, 1357, 915, 212, 641, 22]
|
34
34
|
MATERIALS_FILE = "test_structures.json"
|
35
|
+
QUERY_DUMP = "output/query_result.json"
|
35
36
|
|
36
37
|
|
37
38
|
def shuffle_atoms(atoms: Atoms) -> Atoms:
|
@@ -205,6 +206,9 @@ def get_test_crystal_structures(
|
|
205
206
|
indices_to_test = [0]
|
206
207
|
test_crystal_structures += [query_result[i] for i in indices_to_test]
|
207
208
|
|
209
|
+
with open(QUERY_DUMP, "w") as f:
|
210
|
+
json.dump(test_crystal_structures, f)
|
211
|
+
|
208
212
|
return test_crystal_structures
|
209
213
|
|
210
214
|
|
@@ -441,4 +445,6 @@ def test_solve_for_params_of_known_prototype(input_crystal_structures):
|
|
441
445
|
|
442
446
|
|
443
447
|
if __name__ == "__main__":
|
444
|
-
|
448
|
+
test_solve_for_params_of_known_prototype(
|
449
|
+
get_test_crystal_structures(test_cases=None)
|
450
|
+
)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
import numpy as np
|
4
4
|
from ase.build import bulk
|
5
5
|
from ase.calculators.kim.kim import KIM
|
6
|
+
from ase.io import read
|
6
7
|
|
7
8
|
from kim_tools import (
|
8
9
|
CENTERING_DIVISORS,
|
@@ -12,6 +13,11 @@ from kim_tools import (
|
|
12
13
|
get_formal_bravais_lattice_from_space_group,
|
13
14
|
get_space_group_number_from_prototype,
|
14
15
|
)
|
16
|
+
from kim_tools.symmetry_util.core import (
|
17
|
+
PeriodExtensionException,
|
18
|
+
kstest_reduced_distances,
|
19
|
+
reduce_and_avg,
|
20
|
+
)
|
15
21
|
|
16
22
|
|
17
23
|
def test_change_of_basis_atoms(
|
@@ -51,5 +57,22 @@ def test_change_of_basis_atoms(
|
|
51
57
|
assert np.isclose(conventional_energy, conventional_rebuilt_energy)
|
52
58
|
|
53
59
|
|
60
|
+
def test_test_reduced_distances():
|
61
|
+
data_file_has_period_extension = {
|
62
|
+
"structures/FeP_period_extended_phase_transition.data": True,
|
63
|
+
"structures/FeP_stable.data": False,
|
64
|
+
}
|
65
|
+
repeat = [11, 11, 11]
|
66
|
+
for data_file in data_file_has_period_extension:
|
67
|
+
has_period_extension = data_file_has_period_extension[data_file]
|
68
|
+
atoms = read(data_file, format="lammps-data")
|
69
|
+
_, reduced_distances = reduce_and_avg(atoms, repeat)
|
70
|
+
try:
|
71
|
+
kstest_reduced_distances(reduced_distances)
|
72
|
+
assert not has_period_extension
|
73
|
+
except PeriodExtensionException:
|
74
|
+
assert has_period_extension
|
75
|
+
|
76
|
+
|
54
77
|
if __name__ == "__main__":
|
55
78
|
test_change_of_basis_atoms()
|
kim_tools-0.2.3/README.md
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/possible_primitive_shifts.json
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{kim_tools-0.2.3 → kim_tools-0.2.5}/kim_tools/symmetry_util/data/wyckoff_multiplicities.json
RENAMED
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
|