chemparseplot 1.9.16__tar.gz → 1.9.17__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 (104) hide show
  1. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/PKG-INFO +3 -1
  2. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/_version.py +2 -2
  3. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/api.py +1 -2
  4. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/chemgp_hdf5.py +1 -0
  5. chemparseplot-1.9.17/chemparseplot/parse/eon/__init__.py +22 -0
  6. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/_trajectory_common.py +4 -1
  7. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/dimer_trajectory.py +2 -3
  8. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/frame_series.py +5 -3
  9. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/min_trajectory.py +2 -4
  10. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/saddle_search.py +1 -3
  11. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/grammar/__init__.py +7 -1
  12. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/grammar/orca_text.py +3 -4
  13. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/grammar/xyz.py +11 -8
  14. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/sella/saddle_search.py +4 -3
  15. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/types.py +1 -1
  16. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/geomscan.py +7 -1
  17. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/neb.py +13 -18
  18. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/optimization.py +2 -6
  19. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/structs.py +1 -3
  20. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/pyproject.toml +41 -11
  21. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/fixtures/orca_energy_coords_snippet.out +0 -1
  22. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/_con_fixtures.py +5 -6
  23. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/grammar/test_orca_text_grammar.py +2 -2
  24. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/grammar/test_xyz_grammar.py +1 -1
  25. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_con_io.py +1 -1
  26. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_eon_trajectory_common.py +17 -8
  27. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_frame_series.py +3 -1
  28. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_min_trajectory.py +1 -2
  29. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_neb_stitch.py +6 -4
  30. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_orca_public_api.py +9 -4
  31. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_readcon_boundary.py +3 -1
  32. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_neb_helpers.py +3 -2
  33. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_neb_renderers.py +7 -8
  34. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_optimization.py +7 -2
  35. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_strip_rendering.py +3 -1
  36. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/test_full_coverage.py +32 -5
  37. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/tutorials/test_chemparseplot.py +3 -2
  38. chemparseplot-1.9.16/chemparseplot/parse/eon/__init__.py +0 -8
  39. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/.gitignore +0 -0
  40. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/LICENSE +0 -0
  41. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/__init__.py +0 -0
  42. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/__init__.py +0 -0
  43. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/chemgp_jsonl.py +0 -0
  44. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/converter.py +0 -0
  45. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/con_io.py +0 -0
  46. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/gprd.py +0 -0
  47. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/minimization.py +0 -0
  48. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/neb.py +0 -0
  49. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/eon/stitch.py +0 -0
  50. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/file_.py +0 -0
  51. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/grammar/_deps.py +0 -0
  52. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/neb_utils.py +0 -0
  53. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/__init__.py +0 -0
  54. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/_opi.py +0 -0
  55. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/geomscan.py +0 -0
  56. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/neb/__init__.py +0 -0
  57. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/neb/interp.py +0 -0
  58. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/orca/neb/opi_parser.py +0 -0
  59. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/patterns.py +0 -0
  60. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/plumed.py +0 -0
  61. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/projection.py +0 -0
  62. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/trajectory/__init__.py +0 -0
  63. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/trajectory/hdf5.py +5 -5
  64. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/parse/trajectory/neb.py +0 -0
  65. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/__init__.py +0 -0
  66. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/chemgp.py +0 -0
  67. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/plumed.py +0 -0
  68. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/plot/theme.py +0 -0
  69. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/units.py +0 -0
  70. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/chemparseplot/util.py +0 -0
  71. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/readme.md +0 -0
  72. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/__init__.py +0 -0
  73. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/_optional_imports.py +0 -0
  74. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/conftest.py +0 -0
  75. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/fixtures/h2mol.xyz +0 -0
  76. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/fixtures_geomscan_snippet.txt +0 -0
  77. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/__init__.py +0 -0
  78. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/grammar/__init__.py +0 -0
  79. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/grammar/test_grammar_deps.py +1 -1
  80. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/orca/test_geomscan.py +0 -0
  81. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/orca/test_interp.py +0 -0
  82. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_chemgp_hdf5.py +0 -0
  83. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_chemgp_jsonl_types.py +0 -0
  84. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_converter.py +0 -0
  85. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_dimer_trajectory.py +0 -0
  86. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_eon_neb_types.py +0 -0
  87. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_minimization_result.py +0 -0
  88. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_neb_utils.py +0 -0
  89. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_parser_types.py +0 -0
  90. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_patterns.py +0 -0
  91. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_plumed.py +0 -0
  92. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_projection.py +0 -0
  93. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_saddle_results.py +0 -0
  94. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_sella_saddle.py +0 -0
  95. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_trajectory_hdf5.py +0 -0
  96. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/parse/test_trajectory_neb.py +0 -0
  97. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/__init__.py +0 -0
  98. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_chemgp_utils.py +0 -0
  99. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_orca_neb_payload.py +0 -0
  100. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_projection_refactor.py +0 -0
  101. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_structs_units.py +0 -0
  102. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/plot/test_surface_auto_thin.py +0 -0
  103. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/test_coverage_batch.py +2 -2
  104. {chemparseplot-1.9.16 → chemparseplot-1.9.17}/tests/test_public_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chemparseplot
3
- Version: 1.9.16
3
+ Version: 1.9.17
4
4
  Summary: Parsers and plotting tools for computational chemistry
5
5
  Project-URL: Documentation, https://chemparseplot.rgoswami.me
6
6
  Project-URL: Issues, https://github.com/HaoZeke/chemparseplot/issues
@@ -65,6 +65,7 @@ Requires-Dist: wheel>=0.40; extra == 'release'
65
65
  Provides-Extra: test
66
66
  Requires-Dist: ase>=3.22; extra == 'test'
67
67
  Requires-Dist: h5py>=3.0; extra == 'test'
68
+ Requires-Dist: jax[cpu]>=0.4; extra == 'test'
68
69
  Requires-Dist: matplotlib>=3.8.2; extra == 'test'
69
70
  Requires-Dist: parsimonious>=0.10.0; extra == 'test'
70
71
  Requires-Dist: polars>=0.20; extra == 'test'
@@ -73,6 +74,7 @@ Requires-Dist: pytest>=7.4.3; extra == 'test'
73
74
  Requires-Dist: readcon>=0.13.1; extra == 'test'
74
75
  Requires-Dist: rgpycrumbs; extra == 'test'
75
76
  Requires-Dist: scipy>=1.11; extra == 'test'
77
+ Requires-Dist: xyzrender>=0.1.3; extra == 'test'
76
78
  Provides-Extra: xyzrender
77
79
  Requires-Dist: xyzrender>=0.1.2; extra == 'xyzrender'
78
80
  Description-Content-Type: text/markdown
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.9.16'
22
- __version_tuple__ = version_tuple = (1, 9, 16)
21
+ __version__ = version = '1.9.17'
22
+ __version_tuple__ = version_tuple = (1, 9, 17)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -101,7 +101,6 @@ def parse_orca_final_energy(path_or_text: str):
101
101
  return summary.final_energy
102
102
 
103
103
 
104
-
105
104
  def extract_orca_geomscan_energy(
106
105
  data: str, energy_type: str = "Actual Energy"
107
106
  ) -> tuple[Q_, Q_]:
@@ -138,6 +137,6 @@ def suite_pins() -> dict[str, str]:
138
137
  try:
139
138
  cfg = load_config()
140
139
  pins.update(cfg.merged_package_pins_normalized())
141
- except Exception: # noqa: BLE001 — soft fail for consumers
140
+ except Exception:
142
141
  pass
143
142
  return pins
@@ -22,6 +22,7 @@ HDF5 Layout
22
22
  from typing import Any
23
23
 
24
24
  import numpy as np
25
+
25
26
  from chemparseplot.parse.types import ArrayGroup, ParserAttrs
26
27
 
27
28
 
@@ -0,0 +1,22 @@
1
+ # SPDX-FileCopyrightText: 2023-present Rohit Goswami <rog32@hi.is>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """eOn trajectory parsers.
5
+
6
+ ``con_io`` is always importable (readcon loads on first CON call).
7
+ ``frame_series`` needs the ``neb`` extra (readcon + polars + ase) and is
8
+ lazy so bare ``import chemparseplot.parse`` stays light.
9
+ """
10
+
11
+ from chemparseplot.parse.eon import con_io
12
+
13
+ __all__ = ["con_io", "frame_series"]
14
+
15
+
16
+ def __getattr__(name: str):
17
+ if name == "frame_series":
18
+ from chemparseplot.parse.eon import frame_series as _frame_series
19
+
20
+ return _frame_series
21
+ msg = f"module {__name__!r} has no attribute {name!r}"
22
+ raise AttributeError(msg)
@@ -141,7 +141,10 @@ def frame_rows_to_table(
141
141
  rows: list[dict[str, Any]] = []
142
142
  saw_complete_row = False
143
143
  for frame in frames:
144
- row = {column: coerce_metadata_value(column, metadata_value(frame, column)) for column in columns}
144
+ row = {
145
+ column: coerce_metadata_value(column, metadata_value(frame, column))
146
+ for column in columns
147
+ }
145
148
  if any(value is None for value in row.values()):
146
149
  if saw_complete_row or not allow_leading_incomplete:
147
150
  return pl.DataFrame()
@@ -16,11 +16,10 @@ from pathlib import Path
16
16
  import numpy as np
17
17
  import polars as pl
18
18
  import readcon
19
-
20
- from chemparseplot.parse.eon import con_io
21
19
  from ase import Atoms
22
20
 
23
- from ._trajectory_common import (
21
+ from chemparseplot.parse.eon import con_io
22
+ from chemparseplot.parse.eon._trajectory_common import (
24
23
  frame_rows_to_table,
25
24
  load_movie_and_table,
26
25
  load_optional_payload,
@@ -25,12 +25,10 @@ from chemparseplot.parse.eon._trajectory_common import (
25
25
  metadata_value,
26
26
  )
27
27
  from chemparseplot.parse.eon.dimer_trajectory import (
28
- DIMER_METADATA_COLUMNS,
29
28
  DimerTrajectoryData,
30
29
  table_from_dimer_metadata,
31
30
  )
32
31
  from chemparseplot.parse.eon.min_trajectory import (
33
- MIN_METADATA_COLUMNS,
34
32
  MinTrajectoryData,
35
33
  table_from_min_metadata,
36
34
  )
@@ -121,7 +119,11 @@ def neb_path_arrays(frames: Sequence[Any]) -> dict[str, Any]:
121
119
  ok = True
122
120
  for frame in frames:
123
121
  v = metadata_value(frame, key)
124
- if v is None and key in ("reaction_coordinate", "relative_energy", "parallel_force"):
122
+ if v is None and key in (
123
+ "reaction_coordinate",
124
+ "relative_energy",
125
+ "parallel_force",
126
+ ):
125
127
  # optional NEB stamps
126
128
  ok = False
127
129
  break
@@ -15,14 +15,12 @@ from pathlib import Path
15
15
 
16
16
  import polars as pl
17
17
  import readcon
18
-
19
- from chemparseplot.parse.eon import con_io
20
18
  from ase import Atoms
21
19
 
22
- from ._trajectory_common import (
20
+ from chemparseplot.parse.eon import con_io
21
+ from chemparseplot.parse.eon._trajectory_common import (
23
22
  frame_rows_to_table,
24
23
  load_movie_and_table,
25
- read_first_structure,
26
24
  read_optional_first,
27
25
  )
28
26
 
@@ -369,9 +369,7 @@ def parse_eon_saddle(eresp: Path, rloc: "SpinID") -> "SaddleMeasure":
369
369
  dimer_trans=meth.trans,
370
370
  init_energy=init_energy,
371
371
  barrier=(
372
- results_data.saddle_energy - init_energy
373
- if init_energy is not None
374
- else None
372
+ results_data.saddle_energy - init_energy if init_energy is not None else None
375
373
  ),
376
374
  mol_id=getattr(rloc, "mol_id", None),
377
375
  spin=getattr(rloc, "spin", None),
@@ -30,7 +30,13 @@ __all__ = [
30
30
 
31
31
 
32
32
  def __getattr__(name: str):
33
- if name in ("parse_xyz_text", "parse_xyz_file", "XyzFrame", "XyzAtom", "write_xyz_text"):
33
+ if name in (
34
+ "parse_xyz_text",
35
+ "parse_xyz_file",
36
+ "XyzFrame",
37
+ "XyzAtom",
38
+ "write_xyz_text",
39
+ ):
34
40
  from chemparseplot.parse.grammar import xyz as _xyz
35
41
 
36
42
  return getattr(_xyz, name)
@@ -151,7 +151,7 @@ def extract_final_energies_hartree(text: str) -> list[float]:
151
151
  line = m.group(0).strip()
152
152
  try:
153
153
  tree = g.parse(line)
154
- except Exception: # noqa: BLE001 — skip non-conforming noise
154
+ except Exception:
155
155
  continue
156
156
  v = EnergyVisitor()
157
157
  v.visit(tree)
@@ -192,14 +192,13 @@ def extract_cartesian_angstrom_blocks(text: str) -> list[list[OrcaAtomCoord]]:
192
192
  for m in _CART_BLOCK_RE.finditer(text):
193
193
  chunk = (
194
194
  "CARTESIAN COORDINATES (ANGSTROEM)\n"
195
- "---------------------------------\n"
196
- + m.group(1)
195
+ "---------------------------------\n" + m.group(1)
197
196
  )
198
197
  if not chunk.endswith("\n"):
199
198
  chunk += "\n"
200
199
  try:
201
200
  tree = g.parse(chunk)
202
- except Exception: # noqa: BLE001
201
+ except Exception:
203
202
  continue
204
203
  v = CartVisitor()
205
204
  v.visit(tree)
@@ -29,8 +29,8 @@ _XYZ_GRAMMAR_SRC = r"""
29
29
  meta = natoms newline comment_line newline coord_block ws?
30
30
  natoms = ~r"\d+"
31
31
  comment_line = ~r"[^\n]*"
32
- coord_block = (aline newline?)+
33
- aline = ws? atype ws float_ ws float_ ws float_ ws?
32
+ coord_block = (atom_line newline?)+
33
+ atom_line = ws? atype ws float_ ws float_ ws float_ ws?
34
34
  atype = ~r"[A-Za-z][a-zA-Z0-9]{0,2}"
35
35
  float_ = ~r"[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)?"
36
36
  newline = ~r"\n"
@@ -107,26 +107,29 @@ def _make_visitor() -> Any:
107
107
  self.comment: str = ""
108
108
  self.atoms: list[XyzAtom] = []
109
109
 
110
- def visit_natoms(self, node: Any, visited_children: Any) -> int:
110
+ def visit_natoms(self, node: Any, _visited_children: Any) -> int:
111
111
  self.natoms = int(node.text)
112
112
  return self.natoms
113
113
 
114
- def visit_comment_line(self, node: Any, visited_children: Any) -> str:
114
+ def visit_comment_line(self, node: Any, _visited_children: Any) -> str:
115
115
  self.comment = node.text
116
116
  return self.comment
117
117
 
118
- def visit_aline(self, node: Any, visited_children: Any) -> XyzAtom:
118
+ def visit_atom_line(self, node: Any, visited_children: Any) -> XyzAtom:
119
+ del node # structure from visited_children
119
120
  flat = _flatten(visited_children)
120
- symbol = next(x for x in flat if isinstance(x, str) and x.strip() and x[0].isalpha())
121
+ symbol = next(
122
+ x for x in flat if isinstance(x, str) and x.strip() and x[0].isalpha()
123
+ )
121
124
  nums = [x for x in flat if isinstance(x, float)]
122
125
  atom = XyzAtom(symbol=symbol, x=nums[0], y=nums[1], z=nums[2])
123
126
  self.atoms.append(atom)
124
127
  return atom
125
128
 
126
- def visit_atype(self, node: Any, visited_children: Any) -> str:
129
+ def visit_atype(self, node: Any, _visited_children: Any) -> str:
127
130
  return node.text
128
131
 
129
- def visit_float_(self, node: Any, visited_children: Any) -> float:
132
+ def visit_float_(self, node: Any, _visited_children: Any) -> float:
130
133
  return float(node.text)
131
134
 
132
135
  def generic_visit(self, node: Any, visited_children: Any) -> Any:
@@ -18,13 +18,14 @@ except ImportError:
18
18
 
19
19
  log = logging.getLogger(__name__)
20
20
 
21
+
21
22
  @dataclass(frozen=True, slots=True)
22
23
  class SellaLog:
23
24
  """Typed record for a single Sella geometry-step log entry.
24
25
 
25
- ```{versionadded} 0.0.3
26
- ```
27
- """
26
+ ```{versionadded} 0.0.3
27
+ ```
28
+ """
28
29
 
29
30
  step_id: int
30
31
  time_float: float
@@ -84,7 +84,7 @@ class OrcaNebResult(DataclassMapping):
84
84
 
85
85
  def __post_init__(self) -> None:
86
86
  if self.n_images is None:
87
- object.__setattr__(self, "n_images", int(len(self.energies)))
87
+ object.__setattr__(self, "n_images", len(self.energies))
88
88
 
89
89
  @classmethod
90
90
  def from_mapping(cls, data: Mapping[str, Any]) -> OrcaNebResult:
@@ -1,6 +1,12 @@
1
1
  import matplotlib.pyplot as plt
2
2
 
3
- from chemparseplot.plot.structs import AxisUnits, BasePlotter, EnergyPath, axis_label, to_magnitude
3
+ from chemparseplot.plot.structs import (
4
+ AxisUnits,
5
+ BasePlotter,
6
+ EnergyPath,
7
+ axis_label,
8
+ to_magnitude,
9
+ )
4
10
 
5
11
 
6
12
  def plot_energy_paths(
@@ -2,16 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  import io
4
4
  import logging
5
- import shutil
6
5
  import tempfile
6
+ from collections.abc import Mapping
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
9
  from typing import Any
10
10
 
11
- from collections.abc import Mapping
12
-
13
- import matplotlib.pyplot as plt
14
11
  import matplotlib.patheffects as path_effects
12
+ import matplotlib.pyplot as plt
15
13
  import numpy as np
16
14
  from ase.io import write as ase_write
17
15
  from matplotlib import tri
@@ -554,7 +552,7 @@ def plot_structure_strip(
554
552
  max_cols=6,
555
553
  renderer="xyzrender",
556
554
  col_spacing=1.5,
557
- show_dividers=False, # noqa: FBT002
555
+ show_dividers=False,
558
556
  xyzrender_config="paton",
559
557
  divider_color="gray",
560
558
  divider_style="--",
@@ -1044,15 +1042,15 @@ def plot_landscape_surface(
1044
1042
  method="grad_matern",
1045
1043
  rbf_smooth=None,
1046
1044
  cmap="viridis",
1047
- show_pts=True, # noqa: FBT002
1045
+ show_pts=True,
1048
1046
  variance_threshold=0.05,
1049
- project_path=True, # noqa: FBT002
1047
+ project_path=True,
1050
1048
  extra_points=None,
1051
1049
  n_inducing=None,
1052
1050
  xlim=None,
1053
1051
  ylim=None,
1054
1052
  basis=None,
1055
- auto_thin: bool = False, # noqa: FBT001, FBT002
1053
+ auto_thin: bool = False,
1056
1054
  max_surface_points: int = 64,
1057
1055
  surface_fit: SurfaceFitConfig | Mapping[str, Any] | None = None,
1058
1056
  ) -> Any:
@@ -1130,8 +1128,7 @@ def plot_landscape_surface(
1130
1128
  if auto_thin and n_obs > max_surface_points:
1131
1129
  fit_idx = surface_fit_indices(n_obs, max_surface_points)
1132
1130
  log.warning(
1133
- "auto_thin: fitting surface on %d of %d points "
1134
- "(max_surface_points=%d)",
1131
+ "auto_thin: fitting surface on %d of %d points (max_surface_points=%d)",
1135
1132
  len(fit_idx),
1136
1133
  n_obs,
1137
1134
  max_surface_points,
@@ -1428,7 +1425,7 @@ def plot_landscape_path_overlay(
1428
1425
  z,
1429
1426
  cmap,
1430
1427
  z_label,
1431
- project_path=True, # noqa: FBT002
1428
+ project_path=True,
1432
1429
  all_r=None,
1433
1430
  all_p=None,
1434
1431
  all_z=None,
@@ -1539,7 +1536,7 @@ def plot_mmf_peaks_overlay(
1539
1536
  peak_rmsd_r,
1540
1537
  peak_rmsd_p,
1541
1538
  peak_energies,
1542
- project_path=True, # noqa: FBT002
1539
+ project_path=True,
1543
1540
  path_rmsd_r=None,
1544
1541
  path_rmsd_p=None,
1545
1542
  ) -> None:
@@ -1613,7 +1610,7 @@ def plot_phase_points_overlay(
1613
1610
  rmsd_r,
1614
1611
  rmsd_p,
1615
1612
  *,
1616
- project_path=True, # noqa: FBT002
1613
+ project_path=True,
1617
1614
  path_rmsd_r=None,
1618
1615
  path_rmsd_p=None,
1619
1616
  phase_color="#FF8F00",
@@ -1667,7 +1664,7 @@ def plot_neb_evolution(
1667
1664
  ax,
1668
1665
  step_rmsd_r_list: list[np.ndarray],
1669
1666
  step_rmsd_p_list: list[np.ndarray],
1670
- project_path=True, # noqa: FBT002
1667
+ project_path=True,
1671
1668
  cmap="Blues",
1672
1669
  ) -> None:
1673
1670
  """Show NEB band evolution across optimization iterations.
@@ -1767,9 +1764,7 @@ def landscape_half_span(
1767
1764
  _s, _d = project_to_sd(np.asarray(final_r), np.asarray(final_p), basis)
1768
1765
  half_span = max(abs(float(_d.max())), abs(float(_d.min())), min_half) * path_pad
1769
1766
  for overlay in additional_atoms_data or []:
1770
- _, add_d = project_to_sd(
1771
- np.array([overlay.r]), np.array([overlay.p]), basis
1772
- )
1767
+ _, add_d = project_to_sd(np.array([overlay.r]), np.array([overlay.p]), basis)
1773
1768
  half_span = max(half_span, abs(float(add_d[0])) * path_pad)
1774
1769
  if equal_metric and x_limits is not None:
1775
1770
  s_half = 0.5 * max(float(x_limits[1] - x_limits[0]), 1e-9)
@@ -1934,7 +1929,7 @@ def plot_orca_neb_profile(
1934
1929
  image_indices = list(range(n_images))
1935
1930
  ax.plot(image_indices, energies, "o-", linewidth=2, markersize=8)
1936
1931
 
1937
- saddle_idx = _plot_orca_profile_keypoints(
1932
+ _plot_orca_profile_keypoints(
1938
1933
  ax,
1939
1934
  x_values=np.asarray(image_indices, dtype=float),
1940
1935
  energies=energies,
@@ -415,9 +415,7 @@ def render_single_ended_landscape(
415
415
  cap = float(np.min(display_e)) + energy_cap_window
416
416
  if cap is not None:
417
417
  factor = float(convert_energy(np.array([1.0]), energy_unit)[0])
418
- energies_plot = np.minimum(
419
- energies_plot, cap / factor if factor else cap
420
- )
418
+ energies_plot = np.minimum(energies_plot, cap / factor if factor else cap)
421
419
  display_e = convert_energy(energies_plot, energy_unit)
422
420
 
423
421
  f_para = -np.gradient(energies_plot)
@@ -466,9 +464,7 @@ def render_single_ended_landscape(
466
464
  )
467
465
  m = min(len(ra), len(atoms_ov))
468
466
  ra, rb = ra[:m], rb[:m]
469
- px, py, _ = project_landscape_path(
470
- ra, rb, project_path=project_path, basis=basis
471
- )
467
+ px, py, _ = project_landscape_path(ra, rb, project_path=project_path, basis=basis)
472
468
  color = OVERLAY_COLORS[idx % len(OVERLAY_COLORS)]
473
469
  if len(overlays) > 1:
474
470
  ax.plot(
@@ -135,9 +135,7 @@ class BasePlotter:
135
135
  ```
136
136
  """
137
137
 
138
- def __init__(
139
- self, figsize=(3.2, 2.5), dpi=200, pad=0.2, colormap=None, style="bmh"
140
- ):
138
+ def __init__(self, figsize=(3.2, 2.5), dpi=200, pad=0.2, colormap=None, style="bmh"):
141
139
  if colormap is None:
142
140
  from cmcrameri import cm # optional plot extra
143
141
 
@@ -55,15 +55,11 @@ opi = ["orca-pi>=0.1.0"]
55
55
  # Grammar/AST track (parsimonious) for XYZ and text-heavy ORCA sections.
56
56
  grammar = ["parsimonious>=0.10.0"]
57
57
  all = ["chemparseplot[plot,neb,xyzrender,opi,grammar]"]
58
- release = [
59
- "build>=0.10",
60
- "towncrier>=24.8.0",
61
- "twine>=4.0",
62
- "wheel>=0.40",
63
- ]
58
+ release = ["build>=0.10", "towncrier>=24.8.0", "twine>=4.0", "wheel>=0.40"]
64
59
  test = [
65
60
  "ase>=3.22",
66
61
  "h5py>=3.0",
62
+ "jax[cpu]>=0.4",
67
63
  "matplotlib>=3.8.2",
68
64
  "parsimonious>=0.10.0",
69
65
  "polars>=0.20",
@@ -72,6 +68,7 @@ test = [
72
68
  "readcon>=0.13.1",
73
69
  "rgpycrumbs",
74
70
  "scipy>=1.11",
71
+ "xyzrender>=0.1.3",
75
72
  ]
76
73
  lint = ["ruff>=0.1.6"]
77
74
  [project.urls]
@@ -172,9 +169,6 @@ ban-relative-imports = "all"
172
169
  "S603",
173
170
  "T201",
174
171
  ] # doctest scripts
175
- "chemparseplot/parse/**/*" = [
176
- "PLC0415",
177
- ] # deferred imports for optional deps (h5py, pandas, polars)
178
172
  "tests/**/*" = [
179
173
  "ARG001",
180
174
  "ARG002",
@@ -182,6 +176,8 @@ ban-relative-imports = "all"
182
176
  "B007",
183
177
  "B017",
184
178
  "E402",
179
+ "EM101",
180
+ "EM102",
185
181
  "F401",
186
182
  "ICN001",
187
183
  "N806",
@@ -193,8 +189,13 @@ ban-relative-imports = "all"
193
189
  "S101",
194
190
  "S108",
195
191
  "S110",
192
+ "S603",
196
193
  "T201",
197
194
  "TID252",
195
+ "UP031",
196
+ "RUF007",
197
+ "F841",
198
+ "RUF043",
198
199
  ]
199
200
  # __init__.py can import without use and use lazy __getattr__
200
201
  "__init__.py" = ["F401", "PLC0415"]
@@ -209,9 +210,38 @@ ban-relative-imports = "all"
209
210
  "FBT002",
210
211
  "PLW2901",
211
212
  ]
212
- "chemparseplot/plot/structs.py" = ["PLC0415"]
213
213
  "chemparseplot/plot/geomscan.py" = ["PLC0415"]
214
- "chemparseplot/plot/neb.py" = ["PLR2004", "PLC0415", "C408"]
214
+ "chemparseplot/units.py" = [
215
+ "F822", # lazy Q_/ureg via __getattr__
216
+ "EM102",
217
+ "N806", # pint Quantity alias Q
218
+ "PLW0603",
219
+ "PLC0415", # lazy pint import
220
+ "RUF059",
221
+ ]
222
+ "chemparseplot/__init__.py" = ["PLC0414", "EM102", "F401"]
223
+ "chemparseplot/api.py" = ["PLR2004", "S110", "PLC0415"]
224
+ "chemparseplot/util.py" = ["PLC0415"]
225
+ "chemparseplot/parse/**/*" = [
226
+ "PLC0415",
227
+ "EM102",
228
+ "N806",
229
+ "ARG002", # NodeVisitor signatures
230
+ "S112",
231
+ "PLW0603",
232
+ "UP047",
233
+ ]
234
+ "chemparseplot/plot/neb.py" = [
235
+ "PLR2004",
236
+ "PLC0415",
237
+ "C408",
238
+ "RUF046",
239
+ "FBT001",
240
+ "FBT002",
241
+ ]
242
+ "chemparseplot/plot/optimization.py" = ["N803", "PLC0415"]
243
+ "chemparseplot/plot/structs.py" = ["PLC0415", "F401", "N806"]
244
+ "chemparseplot/parse/orca/**/*" = ["PLW0603", "PLC0415", "N806"]
215
245
  "chemparseplot/parse/orca/neb/opi_parser.py" = [
216
246
  "PLC0415", # late imports for optional deps (ase)
217
247
  "PLW0603", # global for lazy-loaded OPI module
@@ -11,4 +11,3 @@ CARTESIAN COORDINATES (ANGSTROEM)
11
11
  C 0.253633 -0.326832 0.000000
12
12
  H -0.755037 0.031682 0.000000
13
13
  H 1.197464 0.178270 0.000000
14
-
@@ -8,7 +8,9 @@ from __future__ import annotations
8
8
  from pathlib import Path
9
9
 
10
10
 
11
- def band_text(frames: list[tuple[float, float]], *, extra_meta: dict | None = None) -> str:
11
+ def band_text(
12
+ frames: list[tuple[float, float]], *, extra_meta: dict | None = None
13
+ ) -> str:
12
14
  """Build multi-frame CON text. Each item is ``(energy_eV, z)`` for a single H."""
13
15
  blocks = []
14
16
  for idx, (energy, z) in enumerate(frames):
@@ -17,8 +19,7 @@ def band_text(frames: list[tuple[float, float]], *, extra_meta: dict | None = No
17
19
  meta.update(extra_meta)
18
20
  meta_json = (
19
21
  '{"con_spec_version":%(con_spec_version)d,'
20
- '"energy":%(energy)r,"frame_index":%(frame_index)d'
21
- % meta
22
+ '"energy":%(energy)r,"frame_index":%(frame_index)d' % meta
22
23
  )
23
24
  for key, value in meta.items():
24
25
  if key in {"con_spec_version", "energy", "frame_index"}:
@@ -51,9 +52,7 @@ def write_band(path: Path, frames: list[tuple[float, float]], **kwargs) -> Path:
51
52
  return path
52
53
 
53
54
 
54
- def min_metadata_band(
55
- path: Path, rows: list[tuple[float, float, float]]
56
- ) -> Path:
55
+ def min_metadata_band(path: Path, rows: list[tuple[float, float, float]]) -> Path:
57
56
  """Write minimization movie frames.
58
57
 
59
58
  Each row is ``(energy, step_size, convergence)`` with sequential frame_index.
@@ -11,8 +11,8 @@ import pytest
11
11
 
12
12
  pytest.importorskip("parsimonious")
13
13
 
14
- from chemparseplot.api import parse_orca_final_energy # noqa: E402
15
- from chemparseplot.parse.grammar.orca_text import ( # noqa: E402
14
+ from chemparseplot.api import parse_orca_final_energy
15
+ from chemparseplot.parse.grammar.orca_text import (
16
16
  extract_cartesian_angstrom_blocks,
17
17
  extract_final_energies_hartree,
18
18
  parse_orca_text_summary,
@@ -11,7 +11,7 @@ import pytest
11
11
 
12
12
  pytest.importorskip("parsimonious")
13
13
 
14
- from chemparseplot.parse.grammar.xyz import ( # noqa: E402
14
+ from chemparseplot.parse.grammar.xyz import (
15
15
  parse_xyz_file,
16
16
  parse_xyz_text,
17
17
  write_xyz_text,
@@ -117,7 +117,7 @@ def test_frame_with_energy_scalar_metadata_fallback(tmp_path: Path):
117
117
  return self._energy
118
118
 
119
119
  fake_readcon = SimpleNamespace(ConFrame=_RecordingConFrame)
120
- monkey_mod = SimpleNamespace(
120
+ SimpleNamespace(
121
121
  _readcon=lambda: fake_readcon,
122
122
  frame_with_energy=con_io_mod.frame_with_energy,
123
123
  )