kim-tools 0.3.4__py3-none-any.whl → 0.3.6__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.3.4"
1
+ __version__ = "0.3.6"
2
2
 
3
3
  from .aflow_util import *
4
4
  from .aflow_util import __all__ as aflow_all
@@ -392,6 +392,30 @@ def get_wyckoff_lists_from_prototype(prototype_label: str) -> List[str]:
392
392
  return expanded_wyckoff_letters
393
393
 
394
394
 
395
+ def get_atom_indices_for_each_wyckoff_orb(prototype_label: str) -> List[Dict]:
396
+ """
397
+ Get a list of dictionaries containing the atom indices of each Wyckoff
398
+ orbit.
399
+
400
+ Returns:
401
+ The information is in this format:
402
+
403
+ [{"letter":"a", "indices":[0,1]}, ... ]
404
+ """
405
+ return_list = []
406
+ wyck_lists = get_wyckoff_lists_from_prototype(prototype_label)
407
+ sgnum = get_space_group_number_from_prototype(prototype_label)
408
+ range_start = 0
409
+ for letter in "".join(wyck_lists):
410
+ multiplicity = get_primitive_wyckoff_multiplicity(sgnum, letter)
411
+ range_end = range_start + multiplicity
412
+ return_list.append(
413
+ {"letter": letter, "indices": list(range(range_start, range_end))}
414
+ )
415
+ range_start = range_end
416
+ return return_list
417
+
418
+
395
419
  def prototype_labels_are_equivalent(
396
420
  prototype_label_1: str,
397
421
  prototype_label_2: str,
@@ -841,9 +865,8 @@ class AFLOW:
841
865
  "that the AFLOW executable was not found."
842
866
  )
843
867
  # I am fine with allowing prereleases
844
- aflow_ver_no_prerelease = Version.parse(ver_str)
845
- aflow_ver_no_prerelease.replace(prerelease=None)
846
- if aflow_ver_no_prerelease < Version.parse(REQUIRED_AFLOW):
868
+ aflow_ver = Version.parse(ver_str)
869
+ if aflow_ver.replace(prerelease=None) < Version.parse(REQUIRED_AFLOW):
847
870
  raise self.AFLOWNotFoundException(
848
871
  f"Your AFLOW version {ver_str} is less "
849
872
  f"than the required {REQUIRED_AFLOW}"
kim_tools/ase/core.py CHANGED
@@ -208,7 +208,7 @@ def randomize_positions(atoms, pert_amp, seed=None):
208
208
 
209
209
 
210
210
  ################################################################################
211
- def get_isolated_energy_per_atom(model: Union[str, Calculator], symbol):
211
+ def get_isolated_energy_per_atom(model: Union[str, Calculator], symbol: str) -> float:
212
212
  """
213
213
  Construct a non-periodic cell containing a single atom and compute its energy.
214
214
 
@@ -238,10 +238,13 @@ def get_isolated_energy_per_atom(model: Union[str, Calculator], symbol):
238
238
  )
239
239
  single_atom.calc = calc
240
240
  energy_per_atom = single_atom.get_potential_energy()
241
- if hasattr(calc, "clean"):
242
- calc.clean()
243
- if hasattr(calc, "__del__"):
244
- calc.__del__()
241
+ # if we are attaching an existing LAMMPS calculator to an atoms object,
242
+ # we can't delete it. Only do so if we are making a new one from a KIM ID.
243
+ if isinstance(model, str):
244
+ if hasattr(calc, "clean"):
245
+ calc.clean()
246
+ if hasattr(calc, "__del__"):
247
+ calc.__del__()
245
248
  del single_atom
246
249
  return energy_per_atom
247
250
 
@@ -14,6 +14,7 @@ import numpy as np
14
14
  import numpy.typing as npt
15
15
  from ase import Atoms
16
16
  from ase.cell import Cell
17
+ from ase.constraints import FixSymmetry
17
18
  from ase.geometry import get_distances, get_duplicate_atoms
18
19
  from matplotlib.backends.backend_pdf import PdfPages
19
20
  from pymatgen.core.operations import SymmOp
@@ -617,6 +618,20 @@ def get_primitive_genpos_ops(sgnum: Union[int, str]) -> List[Dict]:
617
618
  return np.asarray(json.load(f)[str(sgnum)])
618
619
 
619
620
 
621
+ def transform_atoms(atoms: Atoms, op: Dict) -> Atoms:
622
+ """
623
+ Transform atoms by an operation defined by a dictionary containing a matrix 'W' and
624
+ translation 'w' defined as fractional operations in the unit cell. 'W' should be
625
+ oriented to operate on column vectors
626
+ """
627
+ frac_pos_columns = atoms.get_scaled_positions().T
628
+ frac_pos_cols_xform = op["W"] @ frac_pos_columns + np.reshape(op["w"], (3, 1))
629
+ atoms_transformed = atoms.copy()
630
+ atoms_transformed.set_scaled_positions(frac_pos_cols_xform.T)
631
+ atoms_transformed.wrap()
632
+ return atoms_transformed
633
+
634
+
620
635
  def reduce_and_avg(
621
636
  atoms: Atoms, repeat: Tuple[int, int, int]
622
637
  ) -> Tuple[Atoms, npt.ArrayLike]:
@@ -832,3 +847,78 @@ def fit_voigt_tensor_to_cell_and_space_group(
832
847
  t_symmetrized = sum(t_rotated_list) / len(t_rotated_list)
833
848
 
834
849
  return t_symmetrized.voigt
850
+
851
+
852
+ class FixProvidedSymmetry(FixSymmetry):
853
+ """
854
+ A modification of :obj:`~ase.constraints.FixSymmetry` that takes
855
+ a prescribed symmetry instead of analyzing the atoms object on the fly
856
+ """
857
+
858
+ def __init__(
859
+ self,
860
+ atoms: Atoms,
861
+ symmetry: Union[str, int, List[Dict]],
862
+ adjust_positions=True,
863
+ adjust_cell=True,
864
+ ):
865
+ """
866
+ Args:
867
+ symmetry:
868
+ Either the space group number, or a list of operations
869
+ as dictionaries with keys "W": (fractional rotation matrix),
870
+ "w": (fractional translation). The space group number input
871
+ will not work correctly unless this contraint is applied to
872
+ a primitive unit cell as defined in
873
+ http://doi.org/10.1016/j.commatsci.2017.01.017
874
+ """
875
+ self.atoms = atoms.copy()
876
+ self.symmetry = symmetry
877
+
878
+ if isinstance(symmetry, str) or isinstance(symmetry, int):
879
+ primitive_genpos_ops = get_primitive_genpos_ops(symmetry)
880
+ else:
881
+ try:
882
+ for op in symmetry:
883
+ assert np.asarray(op["W"]).shape == (3, 3)
884
+ assert np.asarray(op["w"]).shape == (3,)
885
+ primitive_genpos_ops = symmetry
886
+ except Exception:
887
+ raise RuntimeError("Incorrect input provided to FixProvidedSymmetry")
888
+
889
+ self.rotations = []
890
+ self.translations = []
891
+ for op in primitive_genpos_ops:
892
+ self.rotations.append(np.asarray(op["W"]))
893
+ self.translations.append(np.asarray(op["w"]))
894
+ self.prep_symm_map()
895
+
896
+ self.do_adjust_positions = adjust_positions
897
+ self.do_adjust_cell = adjust_cell
898
+
899
+ def prep_symm_map(self) -> None:
900
+ """
901
+ Prepare self.symm_map using provided symmetries
902
+ """
903
+ self.symm_map = []
904
+ scaled_pos = self.atoms.get_scaled_positions()
905
+ for rot, trans in zip(self.rotations, self.translations):
906
+ this_op_map = [-1] * len(self.atoms)
907
+ for i_at in range(len(self.atoms)):
908
+ new_p = rot @ scaled_pos[i_at, :] + trans
909
+ dp = scaled_pos - new_p
910
+ dp -= np.round(dp)
911
+ i_at_map = np.argmin(np.linalg.norm(dp, axis=1))
912
+ this_op_map[i_at] = i_at_map
913
+ self.symm_map.append(this_op_map)
914
+
915
+ def todict(self):
916
+ return {
917
+ "name": "FixProvidedSymmetry",
918
+ "kwargs": {
919
+ "atoms": self.atoms,
920
+ "symmetry": self.symmetry,
921
+ "adjust_positions": self.do_adjust_positions,
922
+ "adjust_cell": self.do_adjust_cell,
923
+ },
924
+ }
@@ -65,7 +65,8 @@ 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
+ from ..aflow_util.core import AFLOW_EXECUTABLE, get_atom_indices_for_each_wyckoff_orb
69
+ from ..ase import get_isolated_energy_per_atom
69
70
  from ..kimunits import convert_list, convert_units
70
71
  from ..symmetry_util import (
71
72
  cartesian_rotation_is_in_point_group,
@@ -138,7 +139,7 @@ def minimize_wrapper(
138
139
  logfile: Optional[Union[str, IO]] = "kim-tools.log",
139
140
  algorithm: Optimizer = LBFGSLineSearch,
140
141
  cell_filter: UnitCellFilter = FrechetCellFilter,
141
- fix_symmetry: bool = False,
142
+ fix_symmetry: Union[bool, FixSymmetry] = False,
142
143
  opt_kwargs: Dict = {},
143
144
  flt_kwargs: Dict = {},
144
145
  ) -> bool:
@@ -178,7 +179,8 @@ def minimize_wrapper(
178
179
  CellFilter:
179
180
  Filter to use if variable_cell is requested
180
181
  fix_symmetry:
181
- Whether to fix the crystallographic symmetry
182
+ Whether to fix the crystallographic symmetry. Can provide
183
+ a FixSymmetry class here instead of detecting it on the fly
182
184
  opt_kwargs:
183
185
  Dictionary of kwargs to pass to optimizer
184
186
  flt_kwargs:
@@ -187,8 +189,11 @@ def minimize_wrapper(
187
189
  Returns:
188
190
  Whether the minimization succeeded
189
191
  """
190
- if fix_symmetry:
191
- symmetry = FixSymmetry(atoms)
192
+ if fix_symmetry is not False:
193
+ if fix_symmetry is True:
194
+ symmetry = FixSymmetry(atoms)
195
+ else:
196
+ symmetry = fix_symmetry
192
197
  atoms.set_constraint(symmetry)
193
198
  if variable_cell:
194
199
  supercell_wrapped = cell_filter(atoms, **flt_kwargs)
@@ -224,11 +229,18 @@ def minimize_wrapper(
224
229
  del atoms.constraints
225
230
 
226
231
  if minimization_stalled or iteration_limits_reached:
227
- logger.info("Final forces:")
228
- logger.info(atoms.get_forces())
229
- logger.info("Final stress:")
230
- logger.info(atoms.get_stress())
231
- return False
232
+ try:
233
+ logger.info("Final forces:")
234
+ logger.info(atoms.get_forces())
235
+ logger.info("Final stress:")
236
+ logger.info(atoms.get_stress())
237
+ except Exception as e:
238
+ logger.info(
239
+ "The following exception was caught "
240
+ "trying to evaluate final forces and stress:"
241
+ )
242
+ logger.info(repr(e))
243
+ return False
232
244
  else:
233
245
  return True
234
246
 
@@ -746,6 +758,23 @@ class KIMTestDriver(ABC):
746
758
  self.__output_property_instances, f
747
759
  ) # serialize the dictionary to string first
748
760
 
761
+ def get_isolated_energy_per_atom(self, symbol: str) -> float:
762
+ """
763
+ Construct a non-periodic cell containing a single atom and compute its energy.
764
+
765
+ Args
766
+ symbol:
767
+ The chemical species
768
+
769
+ Returns:
770
+ The isolated energy of a single atom
771
+ """
772
+ try:
773
+ model = self.kim_model_name
774
+ except self.NonKIMModelError:
775
+ model = self._calc
776
+ return get_isolated_energy_per_atom(model=model, symbol=symbol)
777
+
749
778
 
750
779
  def _add_common_crystal_genome_keys_to_current_property_instance(
751
780
  property_instances: str,
@@ -1289,7 +1318,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
1289
1318
  )
1290
1319
  print(f"\nNOTE: {msg}\n")
1291
1320
  logger.info(msg)
1292
- if cell_cauchy_stress_eV_angstrom3 != [0, 0, 0, 0, 0, 0]:
1321
+ if cell_cauchy_stress_eV_angstrom3 == [0, 0, 0, 0, 0, 0]:
1293
1322
  stress_max = np.max(atoms_tmp.get_stress())
1294
1323
  if stress_max > FMAX_INITIAL:
1295
1324
  msg = (
@@ -1838,6 +1867,23 @@ class SingleCrystalTestDriver(KIMTestDriver):
1838
1867
  atoms_tmp.calc = self._calc
1839
1868
  return atoms_tmp
1840
1869
 
1870
+ def get_nominal_prototype_label(self) -> str:
1871
+ return self._get_nominal_crystal_structure_npt()["prototype-label"][
1872
+ "source-value"
1873
+ ]
1874
+
1875
+ def get_atom_indices_for_each_wyckoff_orb(self) -> List[Dict]:
1876
+ """
1877
+ Get a list of dictionaries containing the atom indices of each Wyckoff
1878
+ orbit.
1879
+
1880
+ Returns:
1881
+ The information is in this format:
1882
+
1883
+ [{"letter":"a", "indices":[0,1]}, ... ]
1884
+ """
1885
+ return get_atom_indices_for_each_wyckoff_orb(self.get_nominal_prototype_label())
1886
+
1841
1887
 
1842
1888
  def query_crystal_structures(
1843
1889
  stoichiometric_species: List[str],
@@ -1937,7 +1983,7 @@ def query_crystal_structures(
1937
1983
  logger.info(len_msg)
1938
1984
  logger.debug(f"Query result (length={len(query_result)}):\n{query_result}")
1939
1985
 
1940
- print(f"!!! {len_msg} !!!")
1986
+ print(f"\n!!! {len_msg} !!!\n")
1941
1987
 
1942
1988
  return query_result
1943
1989
 
@@ -2190,8 +2236,3 @@ def get_deduplicated_property_instances(
2190
2236
  property_instances_deduplicated.sort(key=lambda a: a["instance-id"])
2191
2237
 
2192
2238
  return property_instances_deduplicated
2193
-
2194
-
2195
- # If called directly, do nothing
2196
- if __name__ == "__main__":
2197
- pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kim-tools
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Base classes and helper routines for writing KIM Tests
5
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>, Tom Egg <tje3676@nyu.edu>
6
6
  Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
@@ -1,7 +1,7 @@
1
- kim_tools/__init__.py,sha256=CIQpbBUyJLgWZum2kUjz-jAOWw54_hO5_hHgtBV2DEM,433
1
+ kim_tools/__init__.py,sha256=kYbIfF9bmWajsk_oLkaGok3kpnHeo1rlLBUAranse8o,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=zi69wMcgkcYBTKbc-OvKlMIzUtgphO57yBA8LB7_5wc,77484
4
+ kim_tools/aflow_util/core.py,sha256=YH1KCMQjXiKMqhT1TAuoafpsGqPlybsykRRMaa8wWqc,78219
5
5
  kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A108B24C11D24_cP334_222_h4i_i_bf_i-001/info.json,sha256=IsFiO9X2Ko7yoq2QkDurUVP7k1BE4WFgblu7oxl6iZs,2013
6
6
  kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A10B11_tI84_139_dehim_eh2n-001/info.json,sha256=f1EdtouuSL2y9NNw40Rvz2J9ZZcsqQBcyEmlHj6XoW8,1186
7
7
  kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A10B2C_hP39_171_5c_c_a-001/info.json,sha256=vD1xjZKWShL0E6XNsSlmIhilGcGNefl56oQDLQlHO1M,1596
@@ -2002,9 +2002,9 @@ kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A_tP4_129_ac-001/info.jso
2002
2002
  kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A_tP4_136_f-001/info.json,sha256=5_xlFGOov7VoFwzhp7JtltRnWiAFfgpwF5qc3kMSAjQ,1278
2003
2003
  kim_tools/aflow_util/aflow_prototype_encyclopedia/data/A_tP50_134_a2m2n-001/info.json,sha256=K601zsKLpvPLIaK17bEiNGIQJYJDvIby1lIJ3P9Ze6E,1258
2004
2004
  kim_tools/ase/__init__.py,sha256=1i6ko5tNr0VZC3T7hoEzq4fnSU0DdxNpxXcSaWMcJWc,76
2005
- kim_tools/ase/core.py,sha256=ByiwAUBZzbhbXyfdnea5XTIbK6LSOUMCOy20_AFgbAQ,30991
2005
+ kim_tools/ase/core.py,sha256=odRuHQGZl-pcRtkdYvG31_3kW6nt3qwHdKvAOJ-ifwM,31207
2006
2006
  kim_tools/symmetry_util/__init__.py,sha256=uu-ZSUDUTe2P81rkAS3tXverx31s_uZ3wL4SD_dn5aI,86
2007
- kim_tools/symmetry_util/core.py,sha256=HfDy1CwNWUZIkb4cs3ebUDgWjtt4jRSkBKgGEnlkjuI,30888
2007
+ kim_tools/symmetry_util/core.py,sha256=aDaeYmtwfkNlNDcYQ3mAkez7e9_Lk84xQxwQ24cir38,34231
2008
2008
  kim_tools/symmetry_util/data/possible_primitive_shifts.json,sha256=4OVNgn3NnykgGlYiAcecERmVWiIZFrmP2LZI3ml_Sh0,25010
2009
2009
  kim_tools/symmetry_util/data/primitive_GENPOS_ops.json,sha256=FDu4H4PosOpK9yKwOPy3SxbH7xLMOmZfKZ4ItKPMjoQ,224498
2010
2010
  kim_tools/symmetry_util/data/space_groups_for_each_bravais_lattice.json,sha256=wSNu6d5pH72lJ6Zj5MZ64qzwS_6Fn5WOs0ts7E9uPC4,2507
@@ -2012,11 +2012,11 @@ kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json,sha256=6g1YuYh
2012
2012
  kim_tools/symmetry_util/data/wyckoff_multiplicities.json,sha256=qG2RPBd_-ejDIfz-E4ZhkHyRpIboxRy7oiXkdDf5Eg8,32270
2013
2013
  kim_tools/symmetry_util/data/wyckoff_sets.json,sha256=f5ZpHKDHo6_JWki1b7KUGoYLlhU-44Qikw_-PtbLssw,9248
2014
2014
  kim_tools/test_driver/__init__.py,sha256=KOiceeZNqkfrgZ66CiRiUdniceDrCmmDXQkOw0wXaCQ,92
2015
- kim_tools/test_driver/core.py,sha256=Q388GSKFVvwNIMElt4dHmLd_rJOxQeUG0QIbXmrhYHc,89763
2015
+ kim_tools/test_driver/core.py,sha256=DseMkfdb-b116rRQQR0N7HKhsMsZGq8ZxtKoCBlw8X8,91307
2016
2016
  kim_tools/vc/__init__.py,sha256=zXjhxXCKVMLBMXXWYG3if7VOpBnsFrn_RjVpnohDm5c,74
2017
2017
  kim_tools/vc/core.py,sha256=BIjzEExnQAL2S90a_npptRm3ACqAo4fZBtvTDBMWMdw,13963
2018
- kim_tools-0.3.4.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
2019
- kim_tools-0.3.4.dist-info/METADATA,sha256=SHsn9LcUrsa7Yg8huy7EfOTJ1MT7c0t4z0EMGvzP9YY,2032
2020
- kim_tools-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2021
- kim_tools-0.3.4.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
2022
- kim_tools-0.3.4.dist-info/RECORD,,
2018
+ kim_tools-0.3.6.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
2019
+ kim_tools-0.3.6.dist-info/METADATA,sha256=Q_aemQPU34z7rSPK04WnMP6WzUnS845Frlr3YWofcjw,2032
2020
+ kim_tools-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2021
+ kim_tools-0.3.6.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
2022
+ kim_tools-0.3.6.dist-info/RECORD,,