kim-tools 0.2.4__py3-none-any.whl → 0.2.5__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.
kim_tools/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.4"
1
+ __version__ = "0.2.5"
2
2
 
3
3
  from .aflow_util import *
4
4
  from .aflow_util import __all__ as aflow_all
@@ -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
@@ -115,7 +115,7 @@ class EquivalentAtomSet:
115
115
 
116
116
 
117
117
  def write_tmp_poscar_from_atoms_and_run_function(
118
- atoms: Atoms, function: callable, *args, **kwargs
118
+ atoms: Atoms, function: Callable, *args, **kwargs
119
119
  ) -> Any:
120
120
  """
121
121
  Write the Atoms file to a NamedTemporaryFile and run 'function' on it.
@@ -143,7 +143,7 @@ def check_number_of_atoms(
143
143
  has the correct number of atoms according to prototype_label
144
144
 
145
145
  Raises:
146
- IncorrectNumAtomsException
146
+ IncorrectNumAtomsException
147
147
  """
148
148
  prototype_label_list = prototype_label.split("_")
149
149
  pearson = prototype_label_list[1]
@@ -269,8 +269,9 @@ def get_equivalent_atom_sets_from_prototype_and_atom_map(
269
269
  `atoms`, and `prototype_label` is the detected prototype label
270
270
 
271
271
  Args:
272
- sort_atoms: If `atom_map` was obtained by sorting `atoms` before writing it to
273
- POSCAR, set this to True
272
+ sort_atoms:
273
+ If `atom_map` was obtained by sorting `atoms` before writing it to
274
+ POSCAR, set this to True
274
275
  """
275
276
 
276
277
  if sort_atoms:
@@ -1214,9 +1215,10 @@ class AFLOW:
1214
1215
  sort_atoms2: Whether to sort atoms2 before comparing.
1215
1216
 
1216
1217
  Returns:
1217
- Tuple of arrays in the order:
1218
- basis transformation, rotation, origin shift, atom_map
1219
- atom_map[index_in_structure_1] = index_in_structure_2
1218
+ * basis transformation
1219
+ * rotation
1220
+ * origin shift
1221
+ * atom_map (atom_map[index_in_structure_1] = index_in_structure_2)
1220
1222
 
1221
1223
  Raises:
1222
1224
  AFLOW.FailedToMatchException: if AFLOW fails to match the crystals
@@ -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 numpy.typing import ArrayLike
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
- NOTE: This requires ASE >= 3.25 to delete atoms that are close across PBCs
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
+ )
@@ -465,7 +465,7 @@ class KIMTestDriver(ABC):
465
465
  to be useful to for most KIM tests
466
466
 
467
467
  Attributes:
468
- __kim_model_name (str, optional):
468
+ __kim_model_name (Optional[str]):
469
469
  KIM model name, absent if a non-KIM ASE calculator was provided
470
470
  __calc (:obj:`~ase.calculators.calculator.Calculator`):
471
471
  ASE calculator
@@ -564,7 +564,8 @@ class KIMTestDriver(ABC):
564
564
  ) -> None:
565
565
  """
566
566
  Add a key to the most recent property instance added with
567
- :func:`~kim_tools.KIMTestDriver._add_property_instance`. If the value is an
567
+ :func:`~kim_tools.test_driver.core.KIMTestDriver._add_property_instance`.
568
+ If the value is an
568
569
  array, this function will assume you want to write to the beginning of the array
569
570
  in every dimension. This function is intended to write entire keys in one go,
570
571
  and should not be used for modifying existing keys.
@@ -722,15 +723,15 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
722
723
  prototype_label: str,
723
724
  stoichiometric_species: List[str],
724
725
  a: float,
726
+ a_unit: str,
725
727
  parameter_values: Optional[List[float]] = None,
726
728
  library_prototype_label: Optional[Union[List[str], str]] = None,
727
729
  short_name: Optional[Union[List[str], str]] = None,
728
730
  cell_cauchy_stress: Optional[List[float]] = None,
731
+ cell_cauchy_stress_unit: Optional[str] = None,
729
732
  temperature: Optional[float] = None,
733
+ temperature_unit: Optional[str] = "K",
730
734
  crystal_genome_source_structure_id: Optional[List[List[str]]] = None,
731
- a_unit: str = "angstrom",
732
- cell_cauchy_stress_unit: str = "eV/angstrom^3",
733
- temperature_unit: str = "K",
734
735
  aflow_executable: str = AFLOW_EXECUTABLE,
735
736
  ) -> str:
736
737
  """
@@ -799,6 +800,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
799
800
  "Please specify the Cauchy stress as a 6-dimensional vector in Voigt "
800
801
  "order [xx, yy, zz, yz, xz, xy]"
801
802
  )
803
+ if cell_cauchy_stress_unit is None:
804
+ raise KIMTestDriver("Please provide a `cell_cauchy_stress_unit`")
802
805
  property_instances = _add_key_to_current_property_instance(
803
806
  property_instances,
804
807
  "cell-cauchy-stress",
@@ -807,6 +810,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
807
810
  )
808
811
 
809
812
  if temperature is not None:
813
+ if temperature_unit is None:
814
+ raise KIMTestDriver("Please provide a `temperature_unit`")
810
815
  property_instances = _add_key_to_current_property_instance(
811
816
  property_instances, "temperature", temperature, temperature_unit
812
817
  )
@@ -826,15 +831,15 @@ def _add_property_instance_and_common_crystal_genome_keys(
826
831
  prototype_label: str,
827
832
  stoichiometric_species: List[str],
828
833
  a: float,
834
+ a_unit: str,
829
835
  parameter_values: Optional[List[float]] = None,
830
836
  library_prototype_label: Optional[Union[List[str], str]] = None,
831
837
  short_name: Optional[Union[List[str], str]] = None,
832
838
  cell_cauchy_stress: Optional[List[float]] = None,
839
+ cell_cauchy_stress_unit: Optional[str] = None,
833
840
  temperature: Optional[float] = None,
841
+ temperature_unit: Optional[str] = "K",
834
842
  crystal_genome_source_structure_id: Optional[List[List[str]]] = None,
835
- a_unit: str = "angstrom",
836
- cell_cauchy_stress_unit: str = "eV/angstrom^3",
837
- temperature_unit: str = "K",
838
843
  disclaimer: Optional[str] = None,
839
844
  property_instances: Optional[str] = None,
840
845
  aflow_executable: str = AFLOW_EXECUTABLE,
@@ -872,15 +877,15 @@ def _add_property_instance_and_common_crystal_genome_keys(
872
877
  prototype_label=prototype_label,
873
878
  stoichiometric_species=stoichiometric_species,
874
879
  a=a,
880
+ a_unit=a_unit,
875
881
  parameter_values=parameter_values,
876
882
  library_prototype_label=library_prototype_label,
877
883
  short_name=short_name,
878
- cell_cauchy_stress=cell_cauchy_stress,
879
884
  temperature=temperature,
885
+ temperature_unit=temperature_unit,
880
886
  crystal_genome_source_structure_id=crystal_genome_source_structure_id,
881
- a_unit=a_unit,
882
887
  cell_cauchy_stress_unit=cell_cauchy_stress_unit,
883
- temperature_unit=temperature_unit,
888
+ cell_cauchy_stress=cell_cauchy_stress,
884
889
  aflow_executable=aflow_executable,
885
890
  )
886
891
 
@@ -947,6 +952,7 @@ def get_crystal_structure_from_atoms(
947
952
  prototype_label=proto_des["aflow_prototype_label"],
948
953
  stoichiometric_species=sorted(list(set(atoms.get_chemical_symbols()))),
949
954
  a=a,
955
+ a_unit="angstrom",
950
956
  parameter_values=parameter_values,
951
957
  library_prototype_label=library_prototype_label,
952
958
  short_name=short_name,
@@ -993,8 +999,8 @@ def get_poscar_from_crystal_structure(
993
999
  primitive unit cell of the crystal as defined in
994
1000
  http://doi.org/10.1016/j.commatsci.2017.01.017. Lengths are always in angstrom.
995
1001
  Raises:
996
- AFLOW.ChangedSymmetryException: if the symmetry of the atoms object is different
997
- from ``prototype_label``
1002
+ AFLOW.ChangedSymmetryException:
1003
+ if the symmetry of the atoms object is different from ``prototype_label``
998
1004
  """
999
1005
  if flat:
1000
1006
  prototype_label = crystal_structure["prototype-label.source-value"]
@@ -1287,7 +1293,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
1287
1293
  In practical terms, this means that this function is designed to take as input a
1288
1294
  relaxed or time-averaged from MD (and folded back into the original primitive
1289
1295
  cell) copy of the :class:`~ase.Atoms` object originally obtained from
1290
- :func:`~kim_tools.SingleCrystalTestDriver._get_atoms()`.
1296
+ :func:`~kim_tools.test_driver.core.SingleCrystalTestDriver._get_atoms()`.
1291
1297
 
1292
1298
  If finding the parameter fails, this function will raise an exception. This
1293
1299
  probably indicates a phase transition to a different symmetry, which is a normal
@@ -1386,7 +1392,8 @@ class SingleCrystalTestDriver(KIMTestDriver):
1386
1392
 
1387
1393
  Args:
1388
1394
  change_of_basis:
1389
- Passed to :meth:`kim_tools.SingleCrystalTestDriver._get_atoms`
1395
+ Passed to
1396
+ :meth:`kim_tools.test_driver.core.SingleCrystalTestDriver._get_atoms`
1390
1397
  filename:
1391
1398
  File to save to. Will be automatically moved and renamed,
1392
1399
  e.g. 'instance.poscar' -> 'output/instance-1.poscar'
@@ -1405,8 +1412,10 @@ class SingleCrystalTestDriver(KIMTestDriver):
1405
1412
  def _add_property_instance_and_common_crystal_genome_keys(
1406
1413
  self,
1407
1414
  property_name: str,
1408
- write_stress: bool = False,
1409
- 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",
1410
1419
  disclaimer: Optional[str] = None,
1411
1420
  ) -> None:
1412
1421
  """
@@ -1421,9 +1430,20 @@ class SingleCrystalTestDriver(KIMTestDriver):
1421
1430
  "tag:staff@noreply.openkim.org,2023-02-21:property/binding-energy-crystal"
1422
1431
  or "binding-energy-crystal"
1423
1432
  write_stress:
1424
- Write the "cell-cauchy-stress" key
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`.
1425
1438
  write_temp:
1426
- Write the "temperature" key
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.
1427
1447
  disclaimer:
1428
1448
  An optional disclaimer commenting on the applicability of this result,
1429
1449
  e.g. "This relaxation did not reach the desired tolerance."
@@ -1450,20 +1470,37 @@ class SingleCrystalTestDriver(KIMTestDriver):
1450
1470
 
1451
1471
  short_name = _get_optional_source_value(crystal_structure, "short-name")
1452
1472
 
1453
- # stress and temperature are always there (default 0), but we don't always write
1454
- if write_stress:
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
+ )
1455
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
+ ]
1456
1487
  else:
1457
- cell_cauchy_stress = None
1458
-
1459
- cell_cauchy_stress_unit = crystal_structure["cell-cauchy-stress"]["source-unit"]
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
1460
1494
 
1461
- if write_temp:
1495
+ if write_temp is False:
1496
+ temperature = None
1497
+ temperature_unit = None
1498
+ elif write_temp is True:
1462
1499
  temperature = crystal_structure["temperature"]["source-value"]
1500
+ temperature_unit = crystal_structure["temperature"]["source-unit"]
1463
1501
  else:
1464
- temperature = None
1465
-
1466
- temperature_unit = crystal_structure["temperature"]["source-unit"]
1502
+ temperature = write_temp
1503
+ temperature_unit = temp_unit
1467
1504
 
1468
1505
  crystal_genome_source_structure_id = _get_optional_source_value(
1469
1506
  crystal_structure, "crystal-genome-source-structure-id"
@@ -1475,15 +1512,15 @@ class SingleCrystalTestDriver(KIMTestDriver):
1475
1512
  prototype_label=prototype_label,
1476
1513
  stoichiometric_species=stoichiometric_species,
1477
1514
  a=a,
1515
+ a_unit=a_unit,
1478
1516
  parameter_values=parameter_values,
1479
1517
  library_prototype_label=library_prototype_label,
1480
1518
  short_name=short_name,
1481
1519
  cell_cauchy_stress=cell_cauchy_stress,
1482
- temperature=temperature,
1483
- crystal_genome_source_structure_id=crystal_genome_source_structure_id,
1484
- a_unit=a_unit,
1485
1520
  cell_cauchy_stress_unit=cell_cauchy_stress_unit,
1521
+ temperature=temperature,
1486
1522
  temperature_unit=temperature_unit,
1523
+ crystal_genome_source_structure_id=crystal_genome_source_structure_id,
1487
1524
  disclaimer=disclaimer,
1488
1525
  property_instances=super()._get_serialized_property_instances(),
1489
1526
  aflow_executable=self.aflow_executable,
@@ -1504,8 +1541,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
1504
1541
  Get the nominal temperature
1505
1542
 
1506
1543
  Args:
1507
- unit: The requested unit for the output. Must be understood by the GNU
1508
- ``units`` utility
1544
+ unit:
1545
+ The requested unit for the output. Must be understood by the GNU
1546
+ ``units`` utility
1509
1547
  """
1510
1548
  source_value = self.__nominal_crystal_structure_npt["temperature"][
1511
1549
  "source-value"
@@ -1522,8 +1560,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
1522
1560
  Get the nominal stress
1523
1561
 
1524
1562
  Args:
1525
- unit: The requested unit for the output. Must be understood by the GNU
1526
- ``units`` utility
1563
+ unit:
1564
+ The requested unit for the output. Must be understood by the GNU
1565
+ ``units`` utility
1527
1566
  """
1528
1567
  source_value = self.__nominal_crystal_structure_npt["cell-cauchy-stress"][
1529
1568
  "source-value"
@@ -1684,7 +1723,8 @@ class SingleCrystalTestDriver(KIMTestDriver):
1684
1723
  corresponding to the "old basis" and the returned ``Atoms`` object being
1685
1724
  in the "new basis".
1686
1725
 
1687
- See the docstring for :func:`kim_tools.change_of_basis_atoms` for
1726
+ See the docstring for
1727
+ :func:`kim_tools.symmetry_util.core.change_of_basis_atoms` for
1688
1728
  more information on how to define the change of basis.
1689
1729
 
1690
1730
  Returns:
@@ -1749,7 +1789,8 @@ def query_crystal_structures(
1749
1789
  The list of possible shortnames is taken by postprocessing README_PROTO.TXT
1750
1790
  from the AFLOW software and packaged with kim-tools for reproducibility. To
1751
1791
  see the exact list of possible short names, call
1752
- :func:`kim_tools.read_shortnames` and inspect the values of the returned
1792
+ :func:`kim_tools.aflow_util.core.read_shortnames` and inspect the values of
1793
+ the returned
1753
1794
  dictionary. Note that a given short name corresponds to an exact set of
1754
1795
  parameters (with some tolerance), except the overall scale of the crystal.
1755
1796
  For example, "Hexagonal Close Packed" will return only structures with a
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kim-tools
3
- Version: 0.2.4
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
+ ![testing](https://github.com/openkim/kim-tools/actions/workflows/testing.yml/badge.svg)
35
+ [![docs](https://app.readthedocs.org/projects/kim-tools/badge/?version=latest)](https://kim-tools.readthedocs.io/en/latest/)
36
+ [![PyPI](https://img.shields.io/pypi/v/kim-tools.svg)](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.
@@ -1,12 +1,12 @@
1
- kim_tools/__init__.py,sha256=YtOUsgsVDwHP4lYryk1YLNspyTqXCTLof033m3a4xCw,433
1
+ kim_tools/__init__.py,sha256=R9ao3IDvoNpX2rM78JclHnE-LLpCDqYzT5n-dqIlY5g,433
2
2
  kim_tools/kimunits.py,sha256=jOxBv9gRVhxPE6ygAIUxOzCAfPI6tT6sBaF_FNl9m-M,5387
3
3
  kim_tools/aflow_util/__init__.py,sha256=lJnQ8fZCma80QVRQeKvY4MQ87oCWu-9KATV3dKJfpDc,80
4
- kim_tools/aflow_util/core.py,sha256=6JAlTq7ciS1zWOTfwwA2Ms9sXHc5PoRKdCve-PwAsIs,75770
4
+ kim_tools/aflow_util/core.py,sha256=t12Gi7dNRhkYhFgBpWrFrJJPGVIdstAAcVaNoOtI7u0,75789
5
5
  kim_tools/aflow_util/data/README_PROTO.TXT,sha256=bTpcd8GHOkpcQn6YUZzqKhiTytwSDpkgu4boeoogT38,447851
6
6
  kim_tools/ase/__init__.py,sha256=1i6ko5tNr0VZC3T7hoEzq4fnSU0DdxNpxXcSaWMcJWc,76
7
7
  kim_tools/ase/core.py,sha256=d6eOu_HSxVr-ae0TSEbY4HKdePxhNu3yv8NN9VDl-BA,30256
8
8
  kim_tools/symmetry_util/__init__.py,sha256=uu-ZSUDUTe2P81rkAS3tXverx31s_uZ3wL4SD_dn5aI,86
9
- kim_tools/symmetry_util/core.py,sha256=EED_LYfgwf6mmFwTJlzIwTvf8RUiM29bkzEIQrOqwUw,22271
9
+ kim_tools/symmetry_util/core.py,sha256=lxg02QlDmfUtBxw0CZRpuX84cRhk9dLZv33-xpPHF3g,29042
10
10
  kim_tools/symmetry_util/data/possible_primitive_shifts.json,sha256=4OVNgn3NnykgGlYiAcecERmVWiIZFrmP2LZI3ml_Sh0,25010
11
11
  kim_tools/symmetry_util/data/primitive_GENPOS_ops.json,sha256=FDu4H4PosOpK9yKwOPy3SxbH7xLMOmZfKZ4ItKPMjoQ,224498
12
12
  kim_tools/symmetry_util/data/space_groups_for_each_bravais_lattice.json,sha256=wSNu6d5pH72lJ6Zj5MZ64qzwS_6Fn5WOs0ts7E9uPC4,2507
@@ -14,11 +14,11 @@ kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json,sha256=6g1YuYh
14
14
  kim_tools/symmetry_util/data/wyckoff_multiplicities.json,sha256=qG2RPBd_-ejDIfz-E4ZhkHyRpIboxRy7oiXkdDf5Eg8,32270
15
15
  kim_tools/symmetry_util/data/wyckoff_sets.json,sha256=f5ZpHKDHo6_JWki1b7KUGoYLlhU-44Qikw_-PtbLssw,9248
16
16
  kim_tools/test_driver/__init__.py,sha256=KOiceeZNqkfrgZ66CiRiUdniceDrCmmDXQkOw0wXaCQ,92
17
- kim_tools/test_driver/core.py,sha256=Pyg6zR4ZeulL26B1QefJUDWiPHhkw4ig770U8pWzYJg,84841
17
+ kim_tools/test_driver/core.py,sha256=Zo8rnuGChRBg-YhcSC69BYhVKlhNOTQpAROy5E6Ll48,86900
18
18
  kim_tools/vc/__init__.py,sha256=zXjhxXCKVMLBMXXWYG3if7VOpBnsFrn_RjVpnohDm5c,74
19
19
  kim_tools/vc/core.py,sha256=BIjzEExnQAL2S90a_npptRm3ACqAo4fZBtvTDBMWMdw,13963
20
- kim_tools-0.2.4.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
21
- kim_tools-0.2.4.dist-info/METADATA,sha256=ybguaUJRvdCNr2TJGckx2BGySv5FvPIko1C5ld9kp84,1460
22
- kim_tools-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- kim_tools-0.2.4.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
24
- kim_tools-0.2.4.dist-info/RECORD,,
20
+ kim_tools-0.2.5.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
21
+ kim_tools-0.2.5.dist-info/METADATA,sha256=l2u-Jdzex0bEIvtwyVCWTIH5qTCbrQoCZqy5j-AFqsg,1910
22
+ kim_tools-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ kim_tools-0.2.5.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
24
+ kim_tools-0.2.5.dist-info/RECORD,,