pymdkit 1.6.2__tar.gz → 1.6.3__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 (57) hide show
  1. {pymdkit-1.6.2 → pymdkit-1.6.3}/PKG-INFO +9 -3
  2. {pymdkit-1.6.2 → pymdkit-1.6.3}/README.md +8 -2
  3. {pymdkit-1.6.2 → pymdkit-1.6.3}/pyproject.toml +1 -1
  4. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/__init__.py +1 -1
  5. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_cifio.py +19 -0
  6. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_symmetry.py +1 -3
  7. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_vaspset.py +3 -5
  8. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/compute_rmsd.py +3 -4
  9. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/convert.py +2 -5
  10. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/ewald.py +6 -3
  11. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/realloc_occ.py +1 -3
  12. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/substitute.py +1 -3
  13. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/PKG-INFO +9 -3
  14. {pymdkit-1.6.2 → pymdkit-1.6.3}/tests/test_lattice_symmetry.py +34 -0
  15. {pymdkit-1.6.2 → pymdkit-1.6.3}/LICENSE +0 -0
  16. {pymdkit-1.6.2 → pymdkit-1.6.3}/setup.cfg +0 -0
  17. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/__init__.py +0 -0
  18. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_extxyz.py +0 -0
  19. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_fileio.py +0 -0
  20. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_geometry.py +0 -0
  21. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_gpumd.py +0 -0
  22. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_lattice.py +0 -0
  23. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_oxidation.py +0 -0
  24. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_precision.py +0 -0
  25. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_reduction.py +0 -0
  26. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_supercell.py +0 -0
  27. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/_vasp_output.py +0 -0
  28. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/add_config_type.py +0 -0
  29. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/chemsys_entry.py +0 -0
  30. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/compute_ehull.py +0 -0
  31. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gather_fs.py +0 -0
  32. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gpumd_group.py +0 -0
  33. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gpumd_msd.py +0 -0
  34. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gpumd_relax.py +0 -0
  35. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gpumd_thermo.py +0 -0
  36. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/gpumd_trjcat.py +0 -0
  37. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/nep_rmse.py +0 -0
  38. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/pca.py +0 -0
  39. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/perturb.py +0 -0
  40. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/submit_vasp.py +0 -0
  41. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/supercell.py +0 -0
  42. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/trj_extract.py +0 -0
  43. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/trj_sparse.py +0 -0
  44. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/umap.py +0 -0
  45. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/vasp2xyz.py +0 -0
  46. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/vasp_fe.py +0 -0
  47. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/vasp_relax.py +0 -0
  48. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/commands/vasp_static.py +0 -0
  49. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit/pymdkit_main.py +0 -0
  50. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/SOURCES.txt +0 -0
  51. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/dependency_links.txt +0 -0
  52. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/entry_points.txt +0 -0
  53. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/requires.txt +0 -0
  54. {pymdkit-1.6.2 → pymdkit-1.6.3}/src/pymdkit.egg-info/top_level.txt +0 -0
  55. {pymdkit-1.6.2 → pymdkit-1.6.3}/tests/test_convert.py +0 -0
  56. {pymdkit-1.6.2 → pymdkit-1.6.3}/tests/test_realloc_occ.py +0 -0
  57. {pymdkit-1.6.2 → pymdkit-1.6.3}/tests/test_vaspset.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymdkit
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: A unified command-line toolkit for atomistic / MD structure workflows.
5
5
  Author-email: Yueda Wang <ydwang0608@ustc.edu.cn>
6
6
  License-Expression: GPL-3.0-or-later
@@ -336,8 +336,14 @@ CIF input with pymatgen rather than ASE. ASE interprets
336
336
  `_space_group_crystal_system` as a setting hint only for rhombohedral groups
337
337
  and emits a potentially misleading warning for other crystal systems. Using
338
338
  pymatgen avoids that ambiguity while PMK continues to use ASE for non-CIF
339
- formats. A partially occupied CIF must first be ordered with `convert -d2o`
340
- before it can be represented as an explicit atom list.
339
+ formats. Pymatgen intentionally snaps fractional coordinates within its
340
+ default `1e-4` tolerance to ideal values before applying symmetry. PMK keeps
341
+ that corrective behavior but hides only its corresponding finite-precision
342
+ notice; composition, occupancy, malformed-CIF, and all other warnings remain
343
+ visible. See the
344
+ [pymatgen CifParser documentation](https://pymatgen.org/pymatgen.io.html#pymatgen.io.cif.CifParser).
345
+ A partially occupied CIF must first be ordered with `convert -d2o` before it
346
+ can be represented as an explicit atom list.
341
347
 
342
348
  CIF `_atom_type_symbol` and `_atom_site_label` values use per-element
343
349
  inequivalent-site identifiers such as `Li1`, `Y1`, `Cl1`, and `Cl2`.
@@ -309,8 +309,14 @@ CIF input with pymatgen rather than ASE. ASE interprets
309
309
  `_space_group_crystal_system` as a setting hint only for rhombohedral groups
310
310
  and emits a potentially misleading warning for other crystal systems. Using
311
311
  pymatgen avoids that ambiguity while PMK continues to use ASE for non-CIF
312
- formats. A partially occupied CIF must first be ordered with `convert -d2o`
313
- before it can be represented as an explicit atom list.
312
+ formats. Pymatgen intentionally snaps fractional coordinates within its
313
+ default `1e-4` tolerance to ideal values before applying symmetry. PMK keeps
314
+ that corrective behavior but hides only its corresponding finite-precision
315
+ notice; composition, occupancy, malformed-CIF, and all other warnings remain
316
+ visible. See the
317
+ [pymatgen CifParser documentation](https://pymatgen.org/pymatgen.io.html#pymatgen.io.cif.CifParser).
318
+ A partially occupied CIF must first be ordered with `convert -d2o` before it
319
+ can be represented as an explicit atom list.
314
320
 
315
321
  CIF `_atom_type_symbol` and `_atom_site_label` values use per-element
316
322
  inequivalent-site identifiers such as `Li1`, `Y1`, `Cl1`, and `Cl2`.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pymdkit"
7
- version = "1.6.2"
7
+ version = "1.6.3"
8
8
  description = "A unified command-line toolkit for atomistic / MD structure workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """pymdkit -- a unified CLI for atomistic / MD structure workflows."""
2
2
 
3
- __version__ = "1.6.2"
3
+ __version__ = "1.6.3"
@@ -41,12 +41,31 @@ CIF_FLOAT_TAGS = {
41
41
  _CIF_DECIMAL_RE = re.compile(
42
42
  r"[-+]?(?:(?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?|\d+[eE][-+]?\d+)"
43
43
  )
44
+ _BENIGN_FRACTIONAL_ROUNDING_WARNING = (
45
+ r"Issues encountered while parsing CIF:\s*\d+ fractional coordinates? "
46
+ r"rounded to ideal values to avoid issues with finite precision\.\s*$"
47
+ )
44
48
 
45
49
 
46
50
  def is_cif(path):
47
51
  return Path(path).suffix.lower() == ".cif"
48
52
 
49
53
 
54
+ def read_structure(path):
55
+ """Read with pymatgen, hiding only its benign CIF idealization notice."""
56
+ from pymatgen.core import Structure
57
+
58
+ if not is_cif(path):
59
+ return Structure.from_file(str(path))
60
+ with warnings.catch_warnings():
61
+ warnings.filterwarnings(
62
+ "ignore",
63
+ message=_BENIGN_FRACTIONAL_ROUNDING_WARNING,
64
+ category=UserWarning,
65
+ )
66
+ return Structure.from_file(str(path))
67
+
68
+
50
69
  def as_pymatgen_structure(structure):
51
70
  """Return a periodic pymatgen Structure from pymatgen or ASE input."""
52
71
  from pymatgen.core import Structure
@@ -36,11 +36,9 @@ DEFAULT_OXIDATION_STATES = {
36
36
 
37
37
 
38
38
  def load_structure(path):
39
- from pymatgen.core import Structure
40
-
41
39
  path = Path(path)
42
40
  try:
43
- return Structure.from_file(str(path))
41
+ return _cifio.read_structure(path)
44
42
  except Exception as pymatgen_error:
45
43
  if _cifio.is_cif(path):
46
44
  raise ValueError(
@@ -17,12 +17,12 @@ from __future__ import annotations
17
17
 
18
18
  import argparse
19
19
  import ast
20
- import warnings
21
20
  from pathlib import Path
22
21
 
23
22
  try:
24
- from . import _lattice, _precision, _supercell
23
+ from . import _cifio, _lattice, _precision, _supercell
25
24
  except ImportError:
25
+ import _cifio
26
26
  import _lattice
27
27
  import _precision
28
28
  import _supercell
@@ -216,8 +216,6 @@ def _run_trajectory(args, set_cls, settings):
216
216
  def run_vasp_set(args, set_cls, default_settings):
217
217
  """Write *set_cls* input files for one structure (-i), a folder (-if), or a
218
218
  trajectory (-it). Defaults can be overridden via ``-custom-setting FILE``."""
219
- warnings.filterwarnings("ignore", category=UserWarning, module="pymatgen")
220
- from pymatgen.core import Structure
221
219
 
222
220
  settings = dict(default_settings)
223
221
  if args.custom_setting:
@@ -244,7 +242,7 @@ def run_vasp_set(args, set_cls, default_settings):
244
242
  written = 0
245
243
  for struct_file, out_dir in jobs:
246
244
  try:
247
- structure = Structure.from_file(str(struct_file))
245
+ structure = _cifio.read_structure(struct_file)
248
246
  except Exception as e: # noqa: BLE001 - report and continue in batch
249
247
  print(f"{struct_file}: cannot read ({e}) -> skipping")
250
248
  continue
@@ -11,8 +11,9 @@ from itertools import combinations
11
11
  from pathlib import Path
12
12
 
13
13
  try:
14
- from . import _fileio
14
+ from . import _cifio, _fileio
15
15
  except ImportError:
16
+ import _cifio
16
17
  import _fileio
17
18
 
18
19
  COMMAND = "rmsd"
@@ -49,11 +50,9 @@ def create_matcher():
49
50
 
50
51
 
51
52
  def load_structure(path):
52
- from pymatgen.core import Structure
53
-
54
53
  path = Path(path)
55
54
  try:
56
- return Structure.from_file(str(path))
55
+ return _cifio.read_structure(path)
57
56
  except Exception as pymatgen_error:
58
57
  if path.suffix.lower() == ".cif":
59
58
  print(f" [warn] pymatgen could not read CIF {path}: {pymatgen_error}")
@@ -23,10 +23,9 @@ HELP = "Convert structures, refine CIF symmetry, or order disorder."
23
23
 
24
24
  def _read_cif(path):
25
25
  """Read CIF with pymatgen, avoiding ASE's setting-name ambiguity."""
26
- from pymatgen.core import Structure
27
26
  from pymatgen.io.ase import AseAtomsAdaptor
28
27
 
29
- structure = Structure.from_file(str(path))
28
+ structure = _cifio.read_structure(path)
30
29
  if not structure.is_ordered:
31
30
  raise ValueError(
32
31
  "partially occupied CIF cannot be converted to an explicit atom "
@@ -149,9 +148,7 @@ def _write(
149
148
  ) from pmg_error
150
149
 
151
150
  def _read_structure(path):
152
- from pymatgen.core import Structure
153
-
154
- return Structure.from_file(str(path))
151
+ return _cifio.read_structure(path)
155
152
 
156
153
 
157
154
  def _write_structure(
@@ -14,14 +14,17 @@ from __future__ import annotations
14
14
  import argparse
15
15
  from pathlib import Path
16
16
 
17
+ try:
18
+ from . import _cifio
19
+ except ImportError:
20
+ import _cifio
21
+
17
22
  COMMAND = "ewald"
18
23
  HELP = "Compute CIF electrostatic energy using pymatgen EwaldSummation."
19
24
 
20
25
 
21
26
  def read_charged_structure(path):
22
- from pymatgen.core import Structure
23
-
24
- structure = Structure.from_file(str(path))
27
+ structure = _cifio.read_structure(path)
25
28
  missing = []
26
29
  for site in structure:
27
30
  for specie in site.species:
@@ -580,9 +580,7 @@ def _write_candidate(
580
580
  temporary = output.with_name(f".{output.stem}.base.cif")
581
581
  try:
582
582
  render_candidate(parsed, occupancies, target, target_counts, z, temporary)
583
- from pymatgen.core import Structure
584
-
585
- structure = Structure.from_file(temporary)
583
+ structure = _cifio.read_structure(temporary)
586
584
  structure.make_supercell(list(matrix))
587
585
  _cifio.write_cif(structure, output, data_name=output.stem, refine=False)
588
586
  finally:
@@ -207,11 +207,9 @@ def read_cif_labels(path):
207
207
 
208
208
  def read_structure(path):
209
209
  """Read any pymatgen-readable structure, falling back to ASE."""
210
- from pymatgen.core import Structure
211
-
212
210
  path = Path(path)
213
211
  try:
214
- return Structure.from_file(str(path))
212
+ return _cifio.read_structure(path)
215
213
  except Exception as pymatgen_error:
216
214
  if _cifio.is_cif(path):
217
215
  raise ValueError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymdkit
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: A unified command-line toolkit for atomistic / MD structure workflows.
5
5
  Author-email: Yueda Wang <ydwang0608@ustc.edu.cn>
6
6
  License-Expression: GPL-3.0-or-later
@@ -336,8 +336,14 @@ CIF input with pymatgen rather than ASE. ASE interprets
336
336
  `_space_group_crystal_system` as a setting hint only for rhombohedral groups
337
337
  and emits a potentially misleading warning for other crystal systems. Using
338
338
  pymatgen avoids that ambiguity while PMK continues to use ASE for non-CIF
339
- formats. A partially occupied CIF must first be ordered with `convert -d2o`
340
- before it can be represented as an explicit atom list.
339
+ formats. Pymatgen intentionally snaps fractional coordinates within its
340
+ default `1e-4` tolerance to ideal values before applying symmetry. PMK keeps
341
+ that corrective behavior but hides only its corresponding finite-precision
342
+ notice; composition, occupancy, malformed-CIF, and all other warnings remain
343
+ visible. See the
344
+ [pymatgen CifParser documentation](https://pymatgen.org/pymatgen.io.html#pymatgen.io.cif.CifParser).
345
+ A partially occupied CIF must first be ordered with `convert -d2o` before it
346
+ can be represented as an explicit atom list.
341
347
 
342
348
  CIF `_atom_type_symbol` and `_atom_site_label` values use per-element
343
349
  inequivalent-site identifiers such as `Li1`, `Y1`, `Cl1`, and `Cl2`.
@@ -2,6 +2,7 @@ import math
2
2
  import sys
3
3
  import types
4
4
  import unittest
5
+ import warnings
5
6
  from pathlib import Path
6
7
  from unittest.mock import patch
7
8
 
@@ -38,6 +39,39 @@ class FakeLattice:
38
39
  return math.degrees(math.acos(float(np.clip(cosine, -1.0, 1.0))))
39
40
 
40
41
 
42
+ class CifReaderTests(unittest.TestCase):
43
+ def test_only_benign_fractional_rounding_warning_is_hidden(self):
44
+ structure = object()
45
+
46
+ class Structure:
47
+ @classmethod
48
+ def from_file(cls, path):
49
+ warnings.warn(
50
+ "Issues encountered while parsing CIF: 4 fractional "
51
+ "coordinates rounded to ideal values to avoid issues "
52
+ "with finite precision.",
53
+ UserWarning,
54
+ )
55
+ warnings.warn("CIF composition mismatch", UserWarning)
56
+ return structure
57
+
58
+ pymatgen = types.ModuleType("pymatgen")
59
+ core = types.ModuleType("pymatgen.core")
60
+ core.Structure = Structure
61
+ pymatgen.core = core
62
+ with patch.dict(sys.modules, {
63
+ "pymatgen": pymatgen,
64
+ "pymatgen.core": core,
65
+ }), warnings.catch_warnings(record=True) as caught:
66
+ warnings.simplefilter("always")
67
+ result = _cifio.read_structure("input.cif")
68
+
69
+ self.assertIs(result, structure)
70
+ self.assertEqual([str(item.message) for item in caught], [
71
+ "CIF composition mismatch"
72
+ ])
73
+
74
+
41
75
  class LatticeGeometryTests(unittest.TestCase):
42
76
  def test_gpumd_uses_pymatgen_lattice_row_vectors(self):
43
77
  pymatgen = types.ModuleType("pymatgen")
File without changes
File without changes
File without changes
File without changes