chemparseplot 1.10.2__tar.gz → 1.11.0__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.
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/PKG-INFO +1 -1
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/_version.py +2 -2
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/disconnectivity.py +6 -16
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/landscape.py +26 -17
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/neb.py +41 -12
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/pyproject.toml +1 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_landscape_disconnectivity.py +4 -12
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_strip_rendering.py +37 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/.gitignore +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/LICENSE +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/api.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/chemgp_hdf5.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/chemgp_jsonl.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/converter.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/_trajectory_common.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/con_io.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/dimer_trajectory.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/frame_series.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/gprd.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/min_trajectory.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/minimization.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/neb.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/saddle_search.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/eon/stitch.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/file_.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/grammar/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/grammar/_deps.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/grammar/orca_text.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/grammar/xyz.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/neb_utils.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/_opi.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/geomscan.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/neb/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/neb/interp.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/orca/neb/opi_parser.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/patterns.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/plumed.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/projection.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/sella/saddle_search.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/trajectory/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/trajectory/hdf5.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/trajectory/neb.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/parse/types.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/chemgp.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/geomscan.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/optimization.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/plumed.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/structs.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/plot/theme.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/units.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/chemparseplot/util.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/readme.md +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/_optional_imports.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/conftest.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/fixtures/h2mol.xyz +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/fixtures/orca_energy_coords_snippet.out +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/fixtures_geomscan_snippet.txt +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/_con_fixtures.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/grammar/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/grammar/test_grammar_deps.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/grammar/test_orca_text_grammar.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/grammar/test_xyz_grammar.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/orca/test_geomscan.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/orca/test_interp.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_chemgp_hdf5.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_chemgp_jsonl_types.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_con_io.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_converter.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_dimer_trajectory.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_eon_neb_types.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_eon_trajectory_common.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_frame_series.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_min_trajectory.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_minimization_result.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_neb_stitch.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_neb_utils.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_orca_public_api.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_parser_types.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_patterns.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_plumed.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_projection.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_readcon_boundary.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_saddle_results.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_sella_saddle.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_trajectory_hdf5.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/parse/test_trajectory_neb.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/__init__.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_chemgp_utils.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_neb_helpers.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_neb_renderers.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_optimization.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_orca_neb_payload.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_projection_refactor.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_structs_units.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/plot/test_surface_auto_thin.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/test_coverage_batch.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/test_full_coverage.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/test_public_api.py +0 -0
- {chemparseplot-1.10.2 → chemparseplot-1.11.0}/tests/tutorials/test_chemparseplot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chemparseplot
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
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
|
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.11.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 11, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -27,9 +27,7 @@ from scipy.sparse.csgraph import connected_components, laplacian
|
|
|
27
27
|
|
|
28
28
|
from chemparseplot.plot.theme import RUHI_COLORS
|
|
29
29
|
|
|
30
|
-
BASIN_PALETTE = [
|
|
31
|
-
RUHI_COLORS[c] for c in ["teal", "sky", "magenta", "coral", "sunshine"]
|
|
32
|
-
]
|
|
30
|
+
BASIN_PALETTE = [RUHI_COLORS[c] for c in ["teal", "sky", "magenta", "coral", "sunshine"]]
|
|
33
31
|
|
|
34
32
|
|
|
35
33
|
def dedup(matrix, energies, tol):
|
|
@@ -86,9 +84,7 @@ def bridge_components(adjacency, matrix, weight=0.1):
|
|
|
86
84
|
for b in range(a + 1, n_comp):
|
|
87
85
|
ia = np.where(labels == a)[0]
|
|
88
86
|
ib = np.where(labels == b)[0]
|
|
89
|
-
d = np.linalg.norm(
|
|
90
|
-
matrix[ia][:, None, :] - matrix[ib][None, :, :], axis=-1
|
|
91
|
-
)
|
|
87
|
+
d = np.linalg.norm(matrix[ia][:, None, :] - matrix[ib][None, :, :], axis=-1)
|
|
92
88
|
i, j = np.unravel_index(int(np.argmin(d)), d.shape)
|
|
93
89
|
adjacency[ia[i], ib[j]] = weight
|
|
94
90
|
adjacency[ib[j], ia[i]] = weight
|
|
@@ -119,9 +115,7 @@ def spectral_basins(adjacency, n_basins, rng, matrix=None):
|
|
|
119
115
|
first = int(rng.integers(len(x)))
|
|
120
116
|
centers = [x[first]]
|
|
121
117
|
while len(centers) < k:
|
|
122
|
-
d = np.min(
|
|
123
|
-
[np.linalg.norm(x - c, axis=1) for c in centers], axis=0
|
|
124
|
-
)
|
|
118
|
+
d = np.min([np.linalg.norm(x - c, axis=1) for c in centers], axis=0)
|
|
125
119
|
centers.append(x[int(np.argmax(d))])
|
|
126
120
|
centers = np.array(centers)
|
|
127
121
|
for _ in range(60):
|
|
@@ -147,7 +141,7 @@ class Node:
|
|
|
147
141
|
```
|
|
148
142
|
"""
|
|
149
143
|
|
|
150
|
-
__slots__ = ("
|
|
144
|
+
__slots__ = ("argmin", "children", "emin", "leaf", "level", "x")
|
|
151
145
|
|
|
152
146
|
def __init__(self, level, children=None, leaf=None):
|
|
153
147
|
self.level = level
|
|
@@ -177,9 +171,7 @@ def merge_tree(adjacency, energies, levels):
|
|
|
177
171
|
if not mask.any():
|
|
178
172
|
continue
|
|
179
173
|
sub = np.where(mask)[0]
|
|
180
|
-
_, labels = connected_components(
|
|
181
|
-
adjacency[sub][:, sub], directed=False
|
|
182
|
-
)
|
|
174
|
+
_, labels = connected_components(adjacency[sub][:, sub], directed=False)
|
|
183
175
|
comps = {}
|
|
184
176
|
for local, g in enumerate(sub):
|
|
185
177
|
comps.setdefault(labels[local], []).append(g)
|
|
@@ -196,9 +188,7 @@ def merge_tree(adjacency, energies, levels):
|
|
|
196
188
|
if not merged:
|
|
197
189
|
node = leaves[extra[0]]
|
|
198
190
|
for m in extra[1:]:
|
|
199
|
-
node = Node(
|
|
200
|
-
max(energies[m], node.level), children=[node, leaves[m]]
|
|
201
|
-
)
|
|
191
|
+
node = Node(max(energies[m], node.level), children=[node, leaves[m]])
|
|
202
192
|
else:
|
|
203
193
|
node = merged[0] if len(merged) == 1 else Node(lev, children=merged)
|
|
204
194
|
for m in extra:
|
|
@@ -28,7 +28,7 @@ import matplotlib as mpl
|
|
|
28
28
|
import matplotlib.pyplot as plt
|
|
29
29
|
import numpy as np
|
|
30
30
|
|
|
31
|
-
from chemparseplot.plot.theme import
|
|
31
|
+
from chemparseplot.plot.theme import RUHI_THEME
|
|
32
32
|
|
|
33
33
|
ARM_MARKERS = ["o", "^", "s", "P", "X"]
|
|
34
34
|
|
|
@@ -176,13 +176,10 @@ def run_sketchmap(
|
|
|
176
176
|
# point, which is exactly where a pinned reference's twin sits.
|
|
177
177
|
selected = [i for i in idx if i >= n_pinned]
|
|
178
178
|
keep = list(range(n_pinned)) + selected
|
|
179
|
-
keep = keep[:max(n_landmark, n_pinned)]
|
|
179
|
+
keep = keep[: max(n_landmark, n_pinned)]
|
|
180
180
|
idx = np.array(keep)
|
|
181
181
|
(workdir / "landmarks.dat").write_text(
|
|
182
|
-
"\n".join(
|
|
183
|
-
" ".join(f"{v:.8f}" for v in matrix[i]) for i in keep
|
|
184
|
-
)
|
|
185
|
-
+ "\n"
|
|
182
|
+
"\n".join(" ".join(f"{v:.8f}" for v in matrix[i]) for i in keep) + "\n"
|
|
186
183
|
)
|
|
187
184
|
proj = subprocess.run( # noqa: S603
|
|
188
185
|
[
|
|
@@ -242,14 +239,12 @@ def plot_landscape_surface(ax, coords, energies, cmap=None, levels=20):
|
|
|
242
239
|
np.linspace(coords[:, 0].min(), coords[:, 0].max(), 240),
|
|
243
240
|
np.linspace(coords[:, 1].min(), coords[:, 1].max(), 240),
|
|
244
241
|
)
|
|
245
|
-
surface = griddata(
|
|
246
|
-
coords, np.clip(energies, vmin, vmax), (gx, gy), method="linear"
|
|
247
|
-
)
|
|
242
|
+
surface = griddata(coords, np.clip(energies, vmin, vmax), (gx, gy), method="linear")
|
|
248
243
|
lv = np.linspace(vmin, vmax, levels)
|
|
249
|
-
cf = ax.contourf(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
244
|
+
cf = ax.contourf(
|
|
245
|
+
gx, gy, surface, levels=lv, cmap=cmap, vmin=vmin, vmax=vmax, alpha=0.85
|
|
246
|
+
)
|
|
247
|
+
ax.contour(gx, gy, surface, levels=lv, colors="black", linewidths=0.3, alpha=0.45)
|
|
253
248
|
ax.set_facecolor(RUHI_THEME.gridcolor)
|
|
254
249
|
return cf
|
|
255
250
|
|
|
@@ -260,8 +255,14 @@ def margin_inset_slots(n):
|
|
|
260
255
|
```{versionadded} 1.10.0
|
|
261
256
|
```
|
|
262
257
|
"""
|
|
263
|
-
slots = [
|
|
264
|
-
|
|
258
|
+
slots = [
|
|
259
|
+
(-0.26, 0.82),
|
|
260
|
+
(-0.26, 0.22),
|
|
261
|
+
(1.30, 0.82),
|
|
262
|
+
(1.30, 0.22),
|
|
263
|
+
(-0.26, 0.52),
|
|
264
|
+
(1.30, 0.52),
|
|
265
|
+
]
|
|
265
266
|
return slots[:n]
|
|
266
267
|
|
|
267
268
|
|
|
@@ -306,8 +307,16 @@ def place_margin_insets(fig, ax, entries, zoom=0.14, renderer="xyzrender"):
|
|
|
306
307
|
renderer=renderer,
|
|
307
308
|
xyzrender_config="paton",
|
|
308
309
|
)
|
|
309
|
-
ax.scatter(
|
|
310
|
-
|
|
310
|
+
ax.scatter(
|
|
311
|
+
[x],
|
|
312
|
+
[y],
|
|
313
|
+
marker="o",
|
|
314
|
+
s=120,
|
|
315
|
+
facecolors="none",
|
|
316
|
+
edgecolors="black",
|
|
317
|
+
linewidths=1.4,
|
|
318
|
+
zorder=7,
|
|
319
|
+
)
|
|
311
320
|
if entry.get("label"):
|
|
312
321
|
ax.annotate(
|
|
313
322
|
entry["label"],
|
|
@@ -560,6 +560,7 @@ def plot_structure_strip(
|
|
|
560
560
|
max_display_height_px: float | None = None,
|
|
561
561
|
width_fill_fraction=0.82,
|
|
562
562
|
prefer_single_row: bool = True,
|
|
563
|
+
label_ax=None,
|
|
563
564
|
) -> Any:
|
|
564
565
|
"""Renders a horizontal gallery of atomic structures.
|
|
565
566
|
|
|
@@ -580,6 +581,10 @@ def plot_structure_strip(
|
|
|
580
581
|
If True (default), pack up to 16 structures in one row. If False,
|
|
581
582
|
honour ``max_cols`` so e.g. 12 images with ``max_cols=6`` become two
|
|
582
583
|
rows of larger molecules.
|
|
584
|
+
label_ax : matplotlib.axes.Axes or None
|
|
585
|
+
Optional caption-only axis. When supplied, structure images use the
|
|
586
|
+
full height of ``ax`` and labels are placed in ``label_ax`` at matching
|
|
587
|
+
normalized column and row positions.
|
|
583
588
|
|
|
584
589
|
```{versionadded} 0.1.0
|
|
585
590
|
```
|
|
@@ -601,6 +606,8 @@ def plot_structure_strip(
|
|
|
601
606
|
labels = []
|
|
602
607
|
|
|
603
608
|
ax.axis("off")
|
|
609
|
+
if label_ax is not None:
|
|
610
|
+
label_ax.axis("off")
|
|
604
611
|
n_plot = len(atoms_list)
|
|
605
612
|
# Single-row packing is optional: landscape strips with many images often
|
|
606
613
|
# want max_cols=6 → two rows of larger structures.
|
|
@@ -623,13 +630,26 @@ def plot_structure_strip(
|
|
|
623
630
|
ax.set_xlim(0, ax_w_px)
|
|
624
631
|
ax.set_ylim(0, ax_h_px)
|
|
625
632
|
|
|
633
|
+
label_w_px = 0
|
|
634
|
+
label_h_px = 0
|
|
635
|
+
if label_ax is not None:
|
|
636
|
+
label_bbox = label_ax.get_window_extent(mpl_renderer)
|
|
637
|
+
label_w_px = max(1, int(round(label_bbox.width)))
|
|
638
|
+
label_h_px = max(1, int(round(label_bbox.height)))
|
|
639
|
+
label_ax.set_xlim(0, label_w_px)
|
|
640
|
+
label_ax.set_ylim(0, label_h_px)
|
|
641
|
+
|
|
626
642
|
per_col_px = ax_w_px / max(n_cols, 1)
|
|
627
643
|
# The band must hold the tallest label: multiline labels previously
|
|
628
644
|
# overflowed a fixed 1.8-line reservation into the structure images.
|
|
629
|
-
max_label_lines =
|
|
630
|
-
(str(lbl).count("\n") + 1 for lbl in labels), default=1
|
|
631
|
-
)
|
|
632
|
-
label_band_px =
|
|
645
|
+
max_label_lines = (
|
|
646
|
+
max((str(lbl).count("\n") + 1 for lbl in labels), default=1) if labels else 1
|
|
647
|
+
)
|
|
648
|
+
label_band_px = (
|
|
649
|
+
0.0
|
|
650
|
+
if label_ax is not None
|
|
651
|
+
else label_fontsize * fig.dpi / 72 * (0.9 + 1.35 * max_label_lines)
|
|
652
|
+
)
|
|
633
653
|
top_padding_px = 6.0
|
|
634
654
|
bottom_padding_px = 6.0
|
|
635
655
|
row_gap_px = 10.0
|
|
@@ -689,16 +709,25 @@ def plot_structure_strip(
|
|
|
689
709
|
_alpha_blit_rgba(canvas, resized, x0, y0)
|
|
690
710
|
|
|
691
711
|
if labels and i < len(labels):
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
712
|
+
text_ax = label_ax if label_ax is not None else ax
|
|
713
|
+
if label_ax is not None:
|
|
714
|
+
label_slot_width = label_w_px / max(n_cols, 1)
|
|
715
|
+
label_row_height = label_h_px / max(n_rows, 1)
|
|
716
|
+
label_x = (col + 0.5) * label_slot_width
|
|
717
|
+
label_y = label_h_px - (row + 0.5) * label_row_height
|
|
718
|
+
label_va = "center"
|
|
719
|
+
else:
|
|
720
|
+
# Each row owns its caption baseline so column mates cannot
|
|
721
|
+
# occupy the same text position.
|
|
722
|
+
label_x = slot_center_x
|
|
723
|
+
label_y = max(2.0, slot_bottom_y + 3.0)
|
|
724
|
+
label_va = "bottom"
|
|
725
|
+
text_ax.text(
|
|
726
|
+
x=label_x,
|
|
727
|
+
y=label_y,
|
|
699
728
|
s=labels[i],
|
|
700
729
|
ha="center",
|
|
701
|
-
va=
|
|
730
|
+
va=label_va,
|
|
702
731
|
fontsize=label_fontsize,
|
|
703
732
|
color=theme_color,
|
|
704
733
|
fontweight="bold",
|
|
@@ -211,6 +211,7 @@ ban-relative-imports = "all"
|
|
|
211
211
|
"PLW2901",
|
|
212
212
|
]
|
|
213
213
|
"chemparseplot/plot/geomscan.py" = ["PLC0415"]
|
|
214
|
+
"chemparseplot/plot/landscape.py" = ["PLC0415"] # optional plotting deps stay lazy
|
|
214
215
|
"chemparseplot/units.py" = [
|
|
215
216
|
"F822", # lazy Q_/ureg via __getattr__
|
|
216
217
|
"EM102",
|
|
@@ -48,9 +48,7 @@ def test_cn_matrix_species_restriction():
|
|
|
48
48
|
pos = np.zeros((4, 3))
|
|
49
49
|
pos[:, 0] = [0.0, 1.0, 5.0, 6.0]
|
|
50
50
|
full = cn_matrix([pos.ravel()], ["O", "H", "O", "H"], cutoff=1.5)
|
|
51
|
-
only_o = cn_matrix(
|
|
52
|
-
[pos.ravel()], ["O", "H", "O", "H"], cutoff=1.5, cn_species="O"
|
|
53
|
-
)
|
|
51
|
+
only_o = cn_matrix([pos.ravel()], ["O", "H", "O", "H"], cutoff=1.5, cn_species="O")
|
|
54
52
|
assert full.shape[1] == 4
|
|
55
53
|
assert only_o.shape[1] == 2
|
|
56
54
|
|
|
@@ -106,9 +104,7 @@ def test_merge_tree_deep_chain_no_recursion():
|
|
|
106
104
|
|
|
107
105
|
def test_spectral_basins_shapes():
|
|
108
106
|
rng = np.random.default_rng(7)
|
|
109
|
-
m = np.vstack(
|
|
110
|
-
[rng.normal(0, 0.1, size=(10, 2)), rng.normal(5, 0.1, size=(10, 2))]
|
|
111
|
-
)
|
|
107
|
+
m = np.vstack([rng.normal(0, 0.1, size=(10, 2)), rng.normal(5, 0.1, size=(10, 2))])
|
|
112
108
|
adjacency = knn_graph(m, k=3)
|
|
113
109
|
assign = spectral_basins(adjacency, 2, rng, matrix=m)
|
|
114
110
|
assert assign.shape == (20,)
|
|
@@ -119,12 +115,8 @@ def test_spectral_basins_shapes():
|
|
|
119
115
|
|
|
120
116
|
def test_plot_disconnectivity_end_to_end():
|
|
121
117
|
rng = np.random.default_rng(9)
|
|
122
|
-
m = np.vstack(
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
es = np.concatenate(
|
|
126
|
-
[rng.uniform(-10, -8, size=15), rng.uniform(-9, -7, size=15)]
|
|
127
|
-
)
|
|
118
|
+
m = np.vstack([rng.normal(0, 0.2, size=(15, 3)), rng.normal(4, 0.2, size=(15, 3))])
|
|
119
|
+
es = np.concatenate([rng.uniform(-10, -8, size=15), rng.uniform(-9, -7, size=15)])
|
|
128
120
|
fig, ax = plt.subplots()
|
|
129
121
|
reps, root = plot_disconnectivity(ax, m, es, dedup_tol=0.01, knn=3)
|
|
130
122
|
assert len(reps) <= 30
|
|
@@ -197,6 +197,43 @@ class TestPlotStructureStrip:
|
|
|
197
197
|
assert len(ax.artists) == 0
|
|
198
198
|
plt.close(fig)
|
|
199
199
|
|
|
200
|
+
def test_separate_label_axis_keeps_text_out_of_image_band(
|
|
201
|
+
self, monkeypatch, small_molecules
|
|
202
|
+
):
|
|
203
|
+
fig = plt.figure(figsize=(6, 2.4))
|
|
204
|
+
grid = fig.add_gridspec(2, 1, height_ratios=(4, 1), hspace=0.08)
|
|
205
|
+
image_ax = fig.add_subplot(grid[0])
|
|
206
|
+
label_ax = fig.add_subplot(grid[1])
|
|
207
|
+
|
|
208
|
+
def _fake_render(*_args, **_kwargs):
|
|
209
|
+
img = np.zeros((60, 80, 4), dtype=np.float32)
|
|
210
|
+
img[10:50, 20:60, :3] = 1.0
|
|
211
|
+
img[10:50, 20:60, 3] = 1.0
|
|
212
|
+
return img
|
|
213
|
+
|
|
214
|
+
labels = ["Cage\n0.00 eV", "Prism\n0.10 eV", "Book\n0.20 eV"]
|
|
215
|
+
monkeypatch.setattr("chemparseplot.plot.neb._render_atoms", _fake_render)
|
|
216
|
+
plot_structure_strip(
|
|
217
|
+
image_ax,
|
|
218
|
+
small_molecules,
|
|
219
|
+
labels,
|
|
220
|
+
label_ax=label_ax,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
fig.canvas.draw()
|
|
224
|
+
mpl_renderer = fig.canvas.get_renderer()
|
|
225
|
+
image_bbox = image_ax.get_window_extent(mpl_renderer)
|
|
226
|
+
label_bbox = label_ax.get_window_extent(mpl_renderer)
|
|
227
|
+
|
|
228
|
+
assert len(image_ax.texts) == 0
|
|
229
|
+
assert [text.get_text() for text in label_ax.texts] == labels
|
|
230
|
+
for text in label_ax.texts:
|
|
231
|
+
text_bbox = text.get_window_extent(mpl_renderer)
|
|
232
|
+
assert label_bbox.contains(text_bbox.x0, text_bbox.y0)
|
|
233
|
+
assert label_bbox.contains(text_bbox.x1, text_bbox.y1)
|
|
234
|
+
assert text_bbox.y1 <= image_bbox.y0
|
|
235
|
+
plt.close(fig)
|
|
236
|
+
|
|
200
237
|
|
|
201
238
|
class TestPlotStructureInset:
|
|
202
239
|
def test_basic_inset(self, h2o):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|