kim-tools 0.2.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {kim_tools-0.2.3/kim_tools.egg-info → kim_tools-0.2.4}/PKG-INFO +1 -1
  2. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/__init__.py +1 -1
  3. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/aflow_util/core.py +11 -4
  4. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/test_driver/core.py +86 -29
  5. {kim_tools-0.2.3 → kim_tools-0.2.4/kim_tools.egg-info}/PKG-INFO +1 -1
  6. {kim_tools-0.2.3 → kim_tools-0.2.4}/LICENSE.CDDL +0 -0
  7. {kim_tools-0.2.3 → kim_tools-0.2.4}/MANIFEST.in +0 -0
  8. {kim_tools-0.2.3 → kim_tools-0.2.4}/README.md +0 -0
  9. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/aflow_util/__init__.py +0 -0
  10. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/aflow_util/data/README_PROTO.TXT +0 -0
  11. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/ase/__init__.py +0 -0
  12. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/ase/core.py +0 -0
  13. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/kimunits.py +0 -0
  14. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/__init__.py +0 -0
  15. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/core.py +0 -0
  16. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/possible_primitive_shifts.json +0 -0
  17. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/primitive_GENPOS_ops.json +0 -0
  18. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/space_groups_for_each_bravais_lattice.json +0 -0
  19. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json +0 -0
  20. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/wyckoff_multiplicities.json +0 -0
  21. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/symmetry_util/data/wyckoff_sets.json +0 -0
  22. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/test_driver/__init__.py +0 -0
  23. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/vc/__init__.py +0 -0
  24. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools/vc/core.py +0 -0
  25. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools.egg-info/SOURCES.txt +0 -0
  26. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools.egg-info/dependency_links.txt +0 -0
  27. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools.egg-info/requires.txt +0 -0
  28. {kim_tools-0.2.3 → kim_tools-0.2.4}/kim_tools.egg-info/top_level.txt +0 -0
  29. {kim_tools-0.2.3 → kim_tools-0.2.4}/pyproject.toml +0 -0
  30. {kim_tools-0.2.3 → kim_tools-0.2.4}/setup.cfg +0 -0
  31. {kim_tools-0.2.3 → kim_tools-0.2.4}/setup.py +0 -0
  32. {kim_tools-0.2.3 → kim_tools-0.2.4}/tests/test_aflow_util.py +0 -0
  33. {kim_tools-0.2.3 → kim_tools-0.2.4}/tests/test_symmetry_util.py +0 -0
  34. {kim_tools-0.2.3 → kim_tools-0.2.4}/tests/test_test_driver.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kim-tools
3
- Version: 0.2.3
3
+ Version: 0.2.4
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>
6
6
  Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.3"
1
+ __version__ = "0.2.4"
2
2
 
3
3
  from .aflow_util import *
4
4
  from .aflow_util import __all__ as aflow_all
@@ -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
  """
@@ -741,7 +743,10 @@ class AFLOW:
741
743
  """
742
744
 
743
745
  def __init__(
744
- self, aflow_executable: str = "aflow", aflow_work_dir: str = "", np: int = 4
746
+ self,
747
+ aflow_executable: str = AFLOW_EXECUTABLE,
748
+ aflow_work_dir: str = "",
749
+ np: int = 4,
745
750
  ):
746
751
  """
747
752
  Args:
@@ -752,7 +757,7 @@ class AFLOW:
752
757
  self.aflow_executable = aflow_executable
753
758
 
754
759
  try:
755
- subprocess.check_output(["aflow", "--proto=A_cF4_225_a"])
760
+ subprocess.check_output([aflow_executable, "--proto=A_cF4_225_a"])
756
761
  except Exception:
757
762
  raise self.AFLOWNotFoundException(
758
763
  "Failed to run an AFLOW test command. It is likely "
@@ -1494,9 +1499,11 @@ class AFLOW:
1494
1499
  nl_len = nl.size
1495
1500
  cov_mult += 1
1496
1501
  # set the maximum error to 1% of NN distance to follow AFLOW convention
1497
- max_resid = nl.min() * 0.01
1502
+ # rescale by cube root of cell volume to get rough conversion from
1503
+ # cartesian to fractional
1504
+ max_resid = nl.min() * 0.01 * atoms.get_volume() ** (-1 / 3)
1498
1505
  logger.info(
1499
- "Automatically set max residual for solving position "
1506
+ "Automatically set max fractional residual for solving position "
1500
1507
  f"equations to {max_resid}"
1501
1508
  )
1502
1509
 
@@ -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,
@@ -730,6 +731,7 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
730
731
  a_unit: str = "angstrom",
731
732
  cell_cauchy_stress_unit: str = "eV/angstrom^3",
732
733
  temperature_unit: str = "K",
734
+ aflow_executable: str = AFLOW_EXECUTABLE,
733
735
  ) -> str:
734
736
  """
735
737
  Write common Crystal Genome keys to the last element of ``property_instances``. See
@@ -741,6 +743,8 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
741
743
  property_instances:
742
744
  An EDN-serialized list of dictionaries representing KIM Property Instances.
743
745
  The key will be added to the last dictionary in the list
746
+ aflow_executable:
747
+ Path to the AFLOW executable
744
748
 
745
749
  Returns:
746
750
  Updated EDN-serialized list of property instances
@@ -756,7 +760,7 @@ def _add_common_crystal_genome_keys_to_current_property_instance(
756
760
  )
757
761
 
758
762
  # get parameter names
759
- aflow = AFLOW()
763
+ aflow = AFLOW(aflow_executable=aflow_executable)
760
764
  aflow_parameter_names = aflow.get_param_names_from_prototype(prototype_label)
761
765
  if parameter_values is None:
762
766
  if len(aflow_parameter_names) > 1:
@@ -833,6 +837,7 @@ def _add_property_instance_and_common_crystal_genome_keys(
833
837
  temperature_unit: str = "K",
834
838
  disclaimer: Optional[str] = None,
835
839
  property_instances: Optional[str] = None,
840
+ aflow_executable: str = AFLOW_EXECUTABLE,
836
841
  ) -> str:
837
842
  """
838
843
  Initialize a new property instance to ``property_instances`` (an empty
@@ -853,6 +858,8 @@ def _add_property_instance_and_common_crystal_genome_keys(
853
858
  "This relaxation did not reach the desired tolerance."
854
859
  property_instances:
855
860
  A pre-existing EDN-serialized list of KIM Property instances to add to
861
+ aflow_executable:
862
+ Path to the AFLOW executable
856
863
 
857
864
  Returns:
858
865
  Updated EDN-serialized list of property instances
@@ -861,24 +868,29 @@ def _add_property_instance_and_common_crystal_genome_keys(
861
868
  property_name, disclaimer, property_instances
862
869
  )
863
870
  return _add_common_crystal_genome_keys_to_current_property_instance(
864
- property_instances,
865
- prototype_label,
866
- stoichiometric_species,
867
- a,
868
- parameter_values,
869
- library_prototype_label,
870
- short_name,
871
- cell_cauchy_stress,
872
- temperature,
873
- crystal_genome_source_structure_id,
874
- a_unit,
875
- cell_cauchy_stress_unit,
876
- temperature_unit,
871
+ property_instances=property_instances,
872
+ prototype_label=prototype_label,
873
+ stoichiometric_species=stoichiometric_species,
874
+ a=a,
875
+ parameter_values=parameter_values,
876
+ library_prototype_label=library_prototype_label,
877
+ short_name=short_name,
878
+ cell_cauchy_stress=cell_cauchy_stress,
879
+ temperature=temperature,
880
+ crystal_genome_source_structure_id=crystal_genome_source_structure_id,
881
+ a_unit=a_unit,
882
+ cell_cauchy_stress_unit=cell_cauchy_stress_unit,
883
+ temperature_unit=temperature_unit,
884
+ aflow_executable=aflow_executable,
877
885
  )
878
886
 
879
887
 
880
888
  def get_crystal_structure_from_atoms(
881
- atoms: Atoms, get_short_name: bool = True, prim: bool = True, aflow_np: int = 4
889
+ atoms: Atoms,
890
+ get_short_name: bool = True,
891
+ prim: bool = True,
892
+ aflow_np: int = 4,
893
+ aflow_executable: str = AFLOW_EXECUTABLE,
882
894
  ) -> Dict:
883
895
  """
884
896
  By performing a symmetry analysis on an :class:`~ase.Atoms` object, generate a
@@ -896,6 +908,8 @@ def get_crystal_structure_from_atoms(
896
908
  whether to compare against AFLOW prototype library to obtain short-name
897
909
  prim: whether to primitivize the atoms object first
898
910
  aflow_np: Number of processors to use with AFLOW executable
911
+ aflow_executable:
912
+ Path to the AFLOW executable
899
913
 
900
914
  Returns:
901
915
  A dictionary that has the following Property Keys (possibly optionally) defined.
@@ -912,7 +926,7 @@ def get_crystal_structure_from_atoms(
912
926
  - "short-name"
913
927
 
914
928
  """
915
- aflow = AFLOW(np=aflow_np)
929
+ aflow = AFLOW(aflow_executable=aflow_executable, np=aflow_np)
916
930
 
917
931
  proto_des = aflow.get_prototype_designation_from_atoms(atoms, prim=prim)
918
932
  library_prototype_label, short_name = (
@@ -936,13 +950,17 @@ def get_crystal_structure_from_atoms(
936
950
  parameter_values=parameter_values,
937
951
  library_prototype_label=library_prototype_label,
938
952
  short_name=short_name,
953
+ aflow_executable=aflow_executable,
939
954
  )
940
955
 
941
956
  return kim_edn.loads(property_instances)[0]
942
957
 
943
958
 
944
959
  def get_poscar_from_crystal_structure(
945
- crystal_structure: Dict, output_file: Optional[str] = None, flat: bool = False
960
+ crystal_structure: Dict,
961
+ output_file: Optional[str] = None,
962
+ flat: bool = False,
963
+ aflow_executable: str = AFLOW_EXECUTABLE,
946
964
  ) -> Optional[str]:
947
965
  """
948
966
  Write a POSCAR coordinate file (or output it as a multiline string) from the AFLOW
@@ -968,6 +986,8 @@ def get_poscar_from_crystal_structure(
968
986
  Name of the output file. If not provided, the output is returned as a string
969
987
  flat:
970
988
  whether the input dictionary is flattened
989
+ aflow_executable:
990
+ path to AFLOW executable
971
991
  Returns:
972
992
  If ``output_file`` is not provided, a string in POSCAR format containg the
973
993
  primitive unit cell of the crystal as defined in
@@ -993,7 +1013,7 @@ def get_poscar_from_crystal_structure(
993
1013
  "source-value"
994
1014
  ]
995
1015
 
996
- aflow = AFLOW()
1016
+ aflow = AFLOW(aflow_executable=aflow_executable)
997
1017
  aflow_parameter_names = aflow.get_param_names_from_prototype(prototype_label)
998
1018
 
999
1019
  # Atoms objects are always in angstrom
@@ -1035,7 +1055,9 @@ def get_poscar_from_crystal_structure(
1035
1055
 
1036
1056
 
1037
1057
  def get_atoms_from_crystal_structure(
1038
- crystal_structure: Dict, flat: bool = False
1058
+ crystal_structure: Dict,
1059
+ flat: bool = False,
1060
+ aflow_executable: str = AFLOW_EXECUTABLE,
1039
1061
  ) -> Atoms:
1040
1062
  """
1041
1063
  Generate an :class:`~ase.Atoms` object from the AFLOW Prototype Designation obtained
@@ -1059,6 +1081,8 @@ def get_atoms_from_crystal_structure(
1059
1081
  Dictionary containing the required keys in KIM Property Instance format
1060
1082
  flat:
1061
1083
  whether the dictionary is flattened
1084
+ aflow_executable:
1085
+ path to AFLOW executable
1062
1086
 
1063
1087
  Returns:
1064
1088
  Primitive unit cell of the crystal as defined in the
@@ -1070,7 +1094,9 @@ def get_atoms_from_crystal_structure(
1070
1094
  if the symmetry of the atoms object is different from ``prototype_label``
1071
1095
  """
1072
1096
  try:
1073
- poscar_string = get_poscar_from_crystal_structure(crystal_structure, flat=flat)
1097
+ poscar_string = get_poscar_from_crystal_structure(
1098
+ crystal_structure, flat=flat, aflow_executable=aflow_executable
1099
+ )
1074
1100
  except AFLOW.ChangedSymmetryException as e:
1075
1101
  # re-raise, just indicating that this function knows about this exception
1076
1102
  raise e
@@ -1094,8 +1120,23 @@ class SingleCrystalTestDriver(KIMTestDriver):
1094
1120
  <https://openkim.org/properties/show/crystal-structure-npt>`_
1095
1121
  property representing the nominal crystal structure and conditions of the
1096
1122
  current call to the Test Driver.
1123
+ aflow_executable [str]:
1124
+ Path to the AFLOW executable
1097
1125
  """
1098
1126
 
1127
+ def __init__(
1128
+ self, model: Union[str, Calculator], aflow_executable: str = AFLOW_EXECUTABLE
1129
+ ) -> None:
1130
+ """
1131
+ Args:
1132
+ model:
1133
+ ASE calculator or KIM model name to use
1134
+ aflow_executable:
1135
+ Path to AFLOW executable
1136
+ """
1137
+ self.aflow_executable = aflow_executable
1138
+ super().__init__(model)
1139
+
1099
1140
  def _setup(
1100
1141
  self,
1101
1142
  material: Union[Atoms, Dict],
@@ -1156,11 +1197,14 @@ class SingleCrystalTestDriver(KIMTestDriver):
1156
1197
  simply provides recordkeeping of it. It is up to derived classes to
1157
1198
  implement actually setting the temperature of the system.
1158
1199
  """
1200
+
1159
1201
  if cell_cauchy_stress_eV_angstrom3 is None:
1160
1202
  cell_cauchy_stress_eV_angstrom3 = [0, 0, 0, 0, 0, 0]
1161
1203
 
1162
1204
  if isinstance(material, Atoms):
1163
- crystal_structure = get_crystal_structure_from_atoms(material)
1205
+ crystal_structure = get_crystal_structure_from_atoms(
1206
+ atoms=material, aflow_executable=self.aflow_executable
1207
+ )
1164
1208
  msg = (
1165
1209
  "Rebuilding atoms object in a standard setting defined by "
1166
1210
  "doi.org/10.1016/j.commatsci.2017.01.017. See log file or computed "
@@ -1279,9 +1323,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
1279
1323
  If a more definitive error indicating a phase transformation is
1280
1324
  encountered
1281
1325
  """
1282
-
1326
+ aflow = AFLOW(aflow_executable=self.aflow_executable)
1283
1327
  try:
1284
- aflow_parameter_values = AFLOW().solve_for_params_of_known_prototype(
1328
+ aflow_parameter_values = aflow.solve_for_params_of_known_prototype(
1285
1329
  atoms=atoms,
1286
1330
  prototype_label=self.__nominal_crystal_structure_npt["prototype-label"][
1287
1331
  "source-value"
@@ -1324,10 +1368,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
1324
1368
  Whether or not the symmetry is unchanged
1325
1369
 
1326
1370
  """
1371
+ aflow = AFLOW(aflow_executable=self.aflow_executable)
1327
1372
  return prototype_labels_are_equivalent(
1328
- AFLOW().get_prototype_designation_from_atoms(atoms)[
1329
- "aflow_prototype_label"
1330
- ],
1373
+ aflow.get_prototype_designation_from_atoms(atoms)["aflow_prototype_label"],
1331
1374
  self.__nominal_crystal_structure_npt["prototype-label"]["source-value"],
1332
1375
  )
1333
1376
 
@@ -1443,6 +1486,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
1443
1486
  temperature_unit=temperature_unit,
1444
1487
  disclaimer=disclaimer,
1445
1488
  property_instances=super()._get_serialized_property_instances(),
1489
+ aflow_executable=self.aflow_executable,
1446
1490
  )
1447
1491
  )
1448
1492
 
@@ -1578,6 +1622,7 @@ class SingleCrystalTestDriver(KIMTestDriver):
1578
1622
  aflow_np=aflow_np,
1579
1623
  rot_rtol=rot_rtol,
1580
1624
  rot_atol=rot_atol,
1625
+ aflow_executable=self.aflow_executable,
1581
1626
  )
1582
1627
  logger.info(
1583
1628
  f"Deduplicated {len(self.property_instances)} Property Instances "
@@ -1647,7 +1692,9 @@ class SingleCrystalTestDriver(KIMTestDriver):
1647
1692
  Lengths are always in angstrom
1648
1693
  """
1649
1694
  crystal_structure = self.__nominal_crystal_structure_npt
1650
- atoms_prim = get_atoms_from_crystal_structure(crystal_structure)
1695
+ atoms_prim = get_atoms_from_crystal_structure(
1696
+ crystal_structure, aflow_executable=self.aflow_executable
1697
+ )
1651
1698
  if isinstance(change_of_basis, str):
1652
1699
  if change_of_basis.lower() == "primitive":
1653
1700
  change_of_basis_matrix = None
@@ -1785,6 +1832,7 @@ def detect_unique_crystal_structures(
1785
1832
  aflow_np: int = 4,
1786
1833
  rot_rtol: float = 0.01,
1787
1834
  rot_atol: float = 0.01,
1835
+ aflow_executable=AFLOW_EXECUTABLE,
1788
1836
  ) -> Dict:
1789
1837
  """
1790
1838
  Detect which of the provided crystal structures is unique
@@ -1813,6 +1861,8 @@ def detect_unique_crystal_structures(
1813
1861
  rotations. Default value chosen to be commensurate with AFLOW
1814
1862
  default distance tolerance of 0.01*(NN distance). Used only if
1815
1863
  `allow_rotation` is False
1864
+ aflow_executable:
1865
+ Path to AFLOW executable
1816
1866
  Returns:
1817
1867
  Dictionary with keys corresponding to indices of unique structures and values
1818
1868
  being lists of indices of their duplicates
@@ -1820,7 +1870,7 @@ def detect_unique_crystal_structures(
1820
1870
  if len(crystal_structures) == 0:
1821
1871
  return []
1822
1872
 
1823
- aflow = AFLOW(np=aflow_np)
1873
+ aflow = AFLOW(aflow_executable=aflow_executable, np=aflow_np)
1824
1874
 
1825
1875
  with TemporaryDirectory() as tmpdirname:
1826
1876
  # I don't know if crystal_structurs is a list or a dict with integer keys
@@ -1832,7 +1882,9 @@ def detect_unique_crystal_structures(
1832
1882
  structure = crystal_structures[i]
1833
1883
  try:
1834
1884
  get_poscar_from_crystal_structure(
1835
- structure, os.path.join(tmpdirname, str(i))
1885
+ structure,
1886
+ os.path.join(tmpdirname, str(i)),
1887
+ aflow_executable=aflow_executable,
1836
1888
  )
1837
1889
  except AFLOW.ChangedSymmetryException:
1838
1890
  logger.info(
@@ -1888,6 +1940,7 @@ def detect_unique_crystal_structures(
1888
1940
  aflow_np=aflow_np,
1889
1941
  rot_rtol=rot_rtol,
1890
1942
  rot_atol=rot_atol,
1943
+ aflow_executable=aflow_executable,
1891
1944
  )
1892
1945
  )
1893
1946
 
@@ -1901,6 +1954,7 @@ def get_deduplicated_property_instances(
1901
1954
  aflow_np: int = 4,
1902
1955
  rot_rtol: float = 0.01,
1903
1956
  rot_atol: float = 0.01,
1957
+ aflow_executable: str = AFLOW_EXECUTABLE,
1904
1958
  ) -> List[Dict]:
1905
1959
  """
1906
1960
  Given a list of dictionaries constituting KIM Property instances,
@@ -1935,6 +1989,8 @@ def get_deduplicated_property_instances(
1935
1989
  rotations. Default value chosen to be commensurate with AFLOW
1936
1990
  default distance tolerance of 0.01*(NN distance). Used only if
1937
1991
  `allow_rotation` is False
1992
+ aflow_executable:
1993
+ Path to aflow executable
1938
1994
 
1939
1995
  Returns:
1940
1996
  The deduplicated property instances
@@ -1969,6 +2025,7 @@ def get_deduplicated_property_instances(
1969
2025
  aflow_np=aflow_np,
1970
2026
  rot_rtol=rot_rtol,
1971
2027
  rot_atol=rot_atol,
2028
+ aflow_executable=aflow_executable,
1972
2029
  )
1973
2030
 
1974
2031
  # Put together the list of unique instances for the current
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kim-tools
3
- Version: 0.2.3
3
+ Version: 0.2.4
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>
6
6
  Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes