cavefiller 0.2.1__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cavefiller/__init__.py +1 -1
- cavefiller/cavity_finder.py +82 -22
- cavefiller/cli.py +34 -5
- cavefiller/water_filler.py +63 -7
- {cavefiller-0.2.1.dist-info → cavefiller-0.3.1.dist-info}/METADATA +10 -11
- cavefiller-0.3.1.dist-info/RECORD +11 -0
- cavefiller-0.2.1.dist-info/RECORD +0 -11
- {cavefiller-0.2.1.dist-info → cavefiller-0.3.1.dist-info}/WHEEL +0 -0
- {cavefiller-0.2.1.dist-info → cavefiller-0.3.1.dist-info}/entry_points.txt +0 -0
- {cavefiller-0.2.1.dist-info → cavefiller-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {cavefiller-0.2.1.dist-info → cavefiller-0.3.1.dist-info}/top_level.txt +0 -0
cavefiller/__init__.py
CHANGED
cavefiller/cavity_finder.py
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
"""Cavity detection using pyKVFinder."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from typing import List, Dict, Tuple, Any
|
|
5
4
|
import numpy as np
|
|
6
5
|
|
|
7
6
|
# Grid spacing for cavity detection (in Angstroms)
|
|
8
7
|
DEFAULT_GRID_STEP = 0.6
|
|
8
|
+
DEFAULT_PROBE_IN = 1.4
|
|
9
|
+
DEFAULT_PROBE_OUT = 4.0
|
|
10
|
+
DEFAULT_EXTERIOR_TRIM_DISTANCE = 2.4
|
|
11
|
+
DEFAULT_VOLUME_CUTOFF = 5.0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _map_volume_keys_to_grid_labels(cavity_data: Any, volume_keys: List[str]) -> Dict[str, int]:
|
|
15
|
+
"""
|
|
16
|
+
Map pyKVFinder cavity string IDs (KAA, KAB, ...) to integer labels in `cavity_data.cavities`.
|
|
17
|
+
|
|
18
|
+
pyKVFinder 0.9.0 often uses positive cavity labels starting at 2 (label 1 is reserved),
|
|
19
|
+
while volumes are reported by sequential string IDs. We map by ordered positive labels.
|
|
20
|
+
"""
|
|
21
|
+
labels = sorted(int(v) for v in np.unique(cavity_data.cavities) if int(v) > 0)
|
|
22
|
+
if not labels:
|
|
23
|
+
return {}
|
|
24
|
+
|
|
25
|
+
# Most pyKVFinder outputs are contiguous and aligned with volume-key order.
|
|
26
|
+
if len(labels) >= len(volume_keys):
|
|
27
|
+
ordered_labels = labels[: len(volume_keys)]
|
|
28
|
+
else:
|
|
29
|
+
ordered_labels = labels + list(range(labels[-1] + 1, labels[-1] + 1 + (len(volume_keys) - len(labels))))
|
|
30
|
+
|
|
31
|
+
return {key: int(ordered_labels[idx]) for idx, key in enumerate(volume_keys)}
|
|
9
32
|
|
|
10
33
|
|
|
11
34
|
def find_cavities(
|
|
12
35
|
protein_file: str,
|
|
13
|
-
probe_in: float =
|
|
14
|
-
probe_out: float =
|
|
15
|
-
|
|
36
|
+
probe_in: float = DEFAULT_PROBE_IN,
|
|
37
|
+
probe_out: float = DEFAULT_PROBE_OUT,
|
|
38
|
+
step: float = DEFAULT_GRID_STEP,
|
|
39
|
+
removal_distance: float = DEFAULT_EXTERIOR_TRIM_DISTANCE,
|
|
40
|
+
volume_cutoff: float = DEFAULT_VOLUME_CUTOFF,
|
|
16
41
|
output_dir: str = "./output",
|
|
17
42
|
) -> Tuple[List[Dict[str, Any]], Any]:
|
|
18
43
|
"""
|
|
@@ -22,6 +47,8 @@ def find_cavities(
|
|
|
22
47
|
protein_file: Path to the protein PDB file
|
|
23
48
|
probe_in: Probe In radius for cavity detection (Å)
|
|
24
49
|
probe_out: Probe Out radius for cavity detection (Å)
|
|
50
|
+
step: Grid spacing for cavity detection (Å)
|
|
51
|
+
removal_distance: Exterior trim distance for cavity detection (Å)
|
|
25
52
|
volume_cutoff: Minimum cavity volume to consider (Ų)
|
|
26
53
|
output_dir: Directory to save cavity detection results
|
|
27
54
|
|
|
@@ -40,7 +67,8 @@ def find_cavities(
|
|
|
40
67
|
input=protein_file,
|
|
41
68
|
probe_in=probe_in,
|
|
42
69
|
probe_out=probe_out,
|
|
43
|
-
step=
|
|
70
|
+
step=step,
|
|
71
|
+
removal_distance=removal_distance,
|
|
44
72
|
volume_cutoff=volume_cutoff,
|
|
45
73
|
)
|
|
46
74
|
|
|
@@ -52,17 +80,17 @@ def find_cavities(
|
|
|
52
80
|
volumes = cavity_data.volume
|
|
53
81
|
areas = cavity_data.area if hasattr(cavity_data, 'area') else {}
|
|
54
82
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
# Map string IDs to underlying integer cavity-grid labels.
|
|
84
|
+
# User-facing IDs remain sequential for compatibility.
|
|
85
|
+
volume_keys = list(volumes.keys())
|
|
86
|
+
cavity_grid_id_map = _map_volume_keys_to_grid_labels(cavity_data, volume_keys)
|
|
87
|
+
|
|
61
88
|
# Process each cavity
|
|
62
|
-
for cavity_str_id, volume in volumes.items():
|
|
89
|
+
for display_idx, (cavity_str_id, volume) in enumerate(volumes.items(), start=1):
|
|
63
90
|
if volume >= volume_cutoff:
|
|
64
91
|
cavity_info = {
|
|
65
|
-
"id":
|
|
92
|
+
"id": display_idx,
|
|
93
|
+
"grid_id": cavity_grid_id_map.get(cavity_str_id, display_idx),
|
|
66
94
|
"string_id": cavity_str_id,
|
|
67
95
|
"volume": volume,
|
|
68
96
|
"area": areas.get(cavity_str_id, 0.0) if areas else 0.0,
|
|
@@ -96,19 +124,51 @@ def get_cavity_grid_points(cavity_data: Any, cavity_id: int) -> np.ndarray:
|
|
|
96
124
|
# Note: KVFinder uses 1-indexed cavity IDs in the grid
|
|
97
125
|
points = np.argwhere(cavity_grid == cavity_id)
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
127
|
+
points = points.astype(float)
|
|
128
|
+
|
|
129
|
+
# Convert grid indices to real coordinates if metadata is available.
|
|
130
|
+
# pyKVFinder versions expose this either as public P1/P2/P3/P4 or private _vertices/_step.
|
|
131
|
+
step = float(getattr(cavity_data, "step", getattr(cavity_data, "_step", DEFAULT_GRID_STEP)))
|
|
132
|
+
vertices = None
|
|
103
133
|
|
|
104
134
|
if hasattr(cavity_data, "surface") and hasattr(cavity_data.surface, "P1"):
|
|
105
|
-
|
|
135
|
+
vertices = np.array(
|
|
136
|
+
[
|
|
137
|
+
[cavity_data.surface.P1[i] for i in range(3)],
|
|
138
|
+
[cavity_data.surface.P2[i] for i in range(3)],
|
|
139
|
+
[cavity_data.surface.P3[i] for i in range(3)],
|
|
140
|
+
[cavity_data.surface.P4[i] for i in range(3)],
|
|
141
|
+
],
|
|
142
|
+
dtype=float,
|
|
143
|
+
)
|
|
106
144
|
elif hasattr(cavity_data, "P1"):
|
|
107
|
-
|
|
145
|
+
vertices = np.array(
|
|
146
|
+
[
|
|
147
|
+
[cavity_data.P1[i] for i in range(3)],
|
|
148
|
+
[cavity_data.P2[i] for i in range(3)],
|
|
149
|
+
[cavity_data.P3[i] for i in range(3)],
|
|
150
|
+
[cavity_data.P4[i] for i in range(3)],
|
|
151
|
+
],
|
|
152
|
+
dtype=float,
|
|
153
|
+
)
|
|
154
|
+
elif hasattr(cavity_data, "_vertices"):
|
|
155
|
+
vertices = np.asarray(cavity_data._vertices, dtype=float)
|
|
108
156
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
157
|
+
if vertices is not None and vertices.shape[0] >= 4:
|
|
158
|
+
origin = vertices[0]
|
|
159
|
+
axes = [vertices[1] - origin, vertices[2] - origin, vertices[3] - origin]
|
|
160
|
+
unit_axes = []
|
|
161
|
+
for axis in axes:
|
|
162
|
+
norm = np.linalg.norm(axis)
|
|
163
|
+
unit_axes.append(axis / norm if norm > 1e-8 else np.zeros(3, dtype=float))
|
|
164
|
+
return (
|
|
165
|
+
origin
|
|
166
|
+
+ points[:, [0]] * (step * unit_axes[0])
|
|
167
|
+
+ points[:, [1]] * (step * unit_axes[1])
|
|
168
|
+
+ points[:, [2]] * (step * unit_axes[2])
|
|
169
|
+
)
|
|
170
|
+
if vertices is not None and vertices.shape[0] >= 1:
|
|
171
|
+
return vertices[0] + points * step
|
|
112
172
|
|
|
113
173
|
# Fallback: return index-space points; downstream code will align to protein frame.
|
|
114
174
|
return points
|
cavefiller/cli.py
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from cavefiller.cavity_finder import
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from cavefiller.cavity_finder import (
|
|
7
|
+
find_cavities,
|
|
8
|
+
DEFAULT_GRID_STEP,
|
|
9
|
+
DEFAULT_PROBE_IN,
|
|
10
|
+
DEFAULT_PROBE_OUT,
|
|
11
|
+
DEFAULT_EXTERIOR_TRIM_DISTANCE,
|
|
12
|
+
DEFAULT_VOLUME_CUTOFF,
|
|
13
|
+
)
|
|
7
14
|
from cavefiller.cavity_selector import select_cavities
|
|
8
15
|
from cavefiller.water_filler import fill_cavities_with_water
|
|
9
16
|
|
|
@@ -21,16 +28,26 @@ def run(
|
|
|
21
28
|
Path("./output"),
|
|
22
29
|
help="Directory to save output files",
|
|
23
30
|
),
|
|
31
|
+
grid_step: float = typer.Option(
|
|
32
|
+
DEFAULT_GRID_STEP,
|
|
33
|
+
"--grid-step",
|
|
34
|
+
help="Grid spacing for cavity detection (Å)",
|
|
35
|
+
),
|
|
24
36
|
probe_in: float = typer.Option(
|
|
25
|
-
|
|
37
|
+
DEFAULT_PROBE_IN,
|
|
26
38
|
help="Probe In radius for cavity detection (Å)",
|
|
27
39
|
),
|
|
28
40
|
probe_out: float = typer.Option(
|
|
29
|
-
|
|
41
|
+
DEFAULT_PROBE_OUT,
|
|
30
42
|
help="Probe Out radius for cavity detection (Å)",
|
|
31
43
|
),
|
|
44
|
+
exterior_trim_distance: float = typer.Option(
|
|
45
|
+
DEFAULT_EXTERIOR_TRIM_DISTANCE,
|
|
46
|
+
"--exterior-trim-distance",
|
|
47
|
+
help="Exterior trim distance (KVFinder removal distance) (Å)",
|
|
48
|
+
),
|
|
32
49
|
volume_cutoff: float = typer.Option(
|
|
33
|
-
|
|
50
|
+
DEFAULT_VOLUME_CUTOFF,
|
|
34
51
|
help="Minimum cavity volume to consider (ų)",
|
|
35
52
|
),
|
|
36
53
|
auto_select: bool = typer.Option(
|
|
@@ -54,6 +71,15 @@ def run(
|
|
|
54
71
|
300,
|
|
55
72
|
help="Maximum MMFF94 iterations when optimization is enabled.",
|
|
56
73
|
),
|
|
74
|
+
remove_after_optim: bool = typer.Option(
|
|
75
|
+
True,
|
|
76
|
+
"--remove-after-optim/--no-remove-after-optim",
|
|
77
|
+
"--remove_after_optim/--no_remove_after_optim",
|
|
78
|
+
help=(
|
|
79
|
+
"After MMFF94, remove waters that fail cavity/clash validation. "
|
|
80
|
+
"Disable to keep all optimized waters."
|
|
81
|
+
),
|
|
82
|
+
),
|
|
57
83
|
):
|
|
58
84
|
"""
|
|
59
85
|
Find cavities in a protein and fill them with explicit water molecules.
|
|
@@ -74,8 +100,10 @@ def run(
|
|
|
74
100
|
typer.echo("Step 1: Finding cavities with KVFinder...")
|
|
75
101
|
cavities, cavity_data = find_cavities(
|
|
76
102
|
str(protein_file),
|
|
103
|
+
step=grid_step,
|
|
77
104
|
probe_in=probe_in,
|
|
78
105
|
probe_out=probe_out,
|
|
106
|
+
removal_distance=exterior_trim_distance,
|
|
79
107
|
volume_cutoff=volume_cutoff,
|
|
80
108
|
output_dir=str(output_dir),
|
|
81
109
|
)
|
|
@@ -135,6 +163,7 @@ def run(
|
|
|
135
163
|
waters_per_cavity=waters_dict,
|
|
136
164
|
optimize_mmff94=optimize_mmff94,
|
|
137
165
|
mmff_max_iterations=mmff_max_iterations,
|
|
166
|
+
remove_after_optim=remove_after_optim,
|
|
138
167
|
)
|
|
139
168
|
|
|
140
169
|
typer.echo(f"\n✅ Success! Output saved to: {output_file}")
|
cavefiller/water_filler.py
CHANGED
|
@@ -30,6 +30,46 @@ HOH_ANGLE_DEG = 104.52
|
|
|
30
30
|
DEFAULT_MMFF_MAX_ITERS = 300
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _run_mmff_with_progress(ff: Any, max_iterations: int) -> None:
|
|
34
|
+
"""Run MMFF minimization while reporting per-step progress."""
|
|
35
|
+
if max_iterations <= 0:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from tqdm import tqdm # type: ignore
|
|
40
|
+
except Exception:
|
|
41
|
+
tqdm = None
|
|
42
|
+
|
|
43
|
+
converged = False
|
|
44
|
+
if tqdm is not None:
|
|
45
|
+
with tqdm(total=max_iterations, desc="MMFF94", unit="iter") as bar:
|
|
46
|
+
for _step in range(1, max_iterations + 1):
|
|
47
|
+
# RDKit returns 0 on convergence, non-zero otherwise.
|
|
48
|
+
status = ff.Minimize(maxIts=1)
|
|
49
|
+
bar.update(1)
|
|
50
|
+
if status == 0:
|
|
51
|
+
converged = True
|
|
52
|
+
break
|
|
53
|
+
if converged:
|
|
54
|
+
bar.set_postfix_str("converged")
|
|
55
|
+
else:
|
|
56
|
+
bar.set_postfix_str("max iters")
|
|
57
|
+
else:
|
|
58
|
+
print(f"MMFF94 optimization progress: 0/{max_iterations}")
|
|
59
|
+
for step in range(1, max_iterations + 1):
|
|
60
|
+
status = ff.Minimize(maxIts=1)
|
|
61
|
+
if step == 1 or step % 10 == 0 or step == max_iterations or status == 0:
|
|
62
|
+
print(f"MMFF94 optimization progress: {step}/{max_iterations}")
|
|
63
|
+
if status == 0:
|
|
64
|
+
converged = True
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
if converged:
|
|
68
|
+
print("MMFF94 optimization converged before max iterations")
|
|
69
|
+
else:
|
|
70
|
+
print("MMFF94 optimization reached max iterations")
|
|
71
|
+
|
|
72
|
+
|
|
33
73
|
def fill_cavities_with_water(
|
|
34
74
|
protein_file: str,
|
|
35
75
|
selected_cavities: List[Dict[str, Any]],
|
|
@@ -38,6 +78,7 @@ def fill_cavities_with_water(
|
|
|
38
78
|
waters_per_cavity: Dict[int, int] = None,
|
|
39
79
|
optimize_mmff94: bool = True,
|
|
40
80
|
mmff_max_iterations: int = DEFAULT_MMFF_MAX_ITERS,
|
|
81
|
+
remove_after_optim: bool = True,
|
|
41
82
|
) -> str:
|
|
42
83
|
"""Place explicit waters in selected cavities and write a combined PDB."""
|
|
43
84
|
base_name = os.path.splitext(os.path.basename(protein_file))[0]
|
|
@@ -51,6 +92,7 @@ def fill_cavities_with_water(
|
|
|
51
92
|
|
|
52
93
|
for cavity in selected_cavities:
|
|
53
94
|
cavity_id = cavity["id"]
|
|
95
|
+
cavity_grid_id = int(cavity.get("grid_id", cavity_id))
|
|
54
96
|
volume = cavity["volume"]
|
|
55
97
|
|
|
56
98
|
if waters_per_cavity and cavity_id in waters_per_cavity:
|
|
@@ -63,9 +105,12 @@ def fill_cavities_with_water(
|
|
|
63
105
|
f"(volume: {volume:.2f} A^3)..."
|
|
64
106
|
)
|
|
65
107
|
|
|
66
|
-
cavity_points = get_cavity_grid_points(cavity_data,
|
|
108
|
+
cavity_points = get_cavity_grid_points(cavity_data, cavity_grid_id)
|
|
67
109
|
if len(cavity_points) == 0:
|
|
68
|
-
print(
|
|
110
|
+
print(
|
|
111
|
+
f" Warning: no grid points found for cavity {cavity_id} "
|
|
112
|
+
f"(grid label {cavity_grid_id})"
|
|
113
|
+
)
|
|
69
114
|
continue
|
|
70
115
|
|
|
71
116
|
cavity_points = _ensure_cavity_points_near_protein(cavity_points, protein_atoms)
|
|
@@ -105,12 +150,19 @@ def fill_cavities_with_water(
|
|
|
105
150
|
water_origin_cavity_points=all_water_origin_cavity_points,
|
|
106
151
|
protein_atoms=protein_atoms,
|
|
107
152
|
max_iterations=mmff_max_iterations,
|
|
153
|
+
remove_after_optim=remove_after_optim,
|
|
108
154
|
)
|
|
109
155
|
if optimized_positions:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
156
|
+
if remove_after_optim:
|
|
157
|
+
print(
|
|
158
|
+
f"MMFF94 (fixed protein) kept {len(optimized_positions)}/"
|
|
159
|
+
f"{len(all_water_positions)} waters after filtering"
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
print(
|
|
163
|
+
f"MMFF94 (fixed protein) kept all {len(optimized_positions)} waters "
|
|
164
|
+
"without post-optimization filtering"
|
|
165
|
+
)
|
|
114
166
|
optimized_waters_mol = build_waters_mol(
|
|
115
167
|
water_positions=[],
|
|
116
168
|
chain_id="W",
|
|
@@ -278,6 +330,7 @@ def optimize_waters_mmff94_fixed_protein(
|
|
|
278
330
|
water_origin_cavity_points: List[np.ndarray],
|
|
279
331
|
protein_atoms: List[Tuple[str, np.ndarray]],
|
|
280
332
|
max_iterations: int = DEFAULT_MMFF_MAX_ITERS,
|
|
333
|
+
remove_after_optim: bool = True,
|
|
281
334
|
) -> Tuple[List[np.ndarray], List[Tuple[np.ndarray, np.ndarray, np.ndarray]]]:
|
|
282
335
|
"""MMFF94 optimize waters with protein atoms fixed in place."""
|
|
283
336
|
if not water_positions:
|
|
@@ -328,7 +381,7 @@ def optimize_waters_mmff94_fixed_protein(
|
|
|
328
381
|
ff.AddFixedPoint(atom_idx)
|
|
329
382
|
|
|
330
383
|
ff.Initialize()
|
|
331
|
-
ff
|
|
384
|
+
_run_mmff_with_progress(ff, max_iterations)
|
|
332
385
|
|
|
333
386
|
conf = work_mol.GetConformer()
|
|
334
387
|
optimized_geometries = []
|
|
@@ -347,6 +400,9 @@ def optimize_waters_mmff94_fixed_protein(
|
|
|
347
400
|
)
|
|
348
401
|
)
|
|
349
402
|
|
|
403
|
+
if not remove_after_optim:
|
|
404
|
+
return [geom[0] for geom in optimized_geometries], optimized_geometries
|
|
405
|
+
|
|
350
406
|
selected_geometries = _select_valid_geometries_after_mmff(
|
|
351
407
|
optimized_geometries=optimized_geometries,
|
|
352
408
|
original_geometries=original_geometries,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cavefiller
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A tool to find and fill protein cavities with water molecules using KVFinder and Packmol
|
|
5
5
|
Author: CaveFiller Contributors
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -11,6 +11,7 @@ Requires-Dist: pykvfinder>=0.6.0
|
|
|
11
11
|
Requires-Dist: rdkit>=2022.9.1
|
|
12
12
|
Requires-Dist: numpy>=1.20.0
|
|
13
13
|
Requires-Dist: biopython>=1.79
|
|
14
|
+
Requires-Dist: tqdm>=4.67.3
|
|
14
15
|
Provides-Extra: dev
|
|
15
16
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
16
17
|
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
@@ -74,18 +75,23 @@ cavefiller [PROTEIN_FILE] [OPTIONS]
|
|
|
74
75
|
|
|
75
76
|
**Options:**
|
|
76
77
|
- `--output-dir PATH`: Directory to save output files (default: `./output`)
|
|
78
|
+
- `--grid-step FLOAT`: Grid spacing for cavity detection in Ångströms (default: 0.6)
|
|
77
79
|
- `--probe-in FLOAT`: Probe In radius for cavity detection in Ångströms (default: 1.4)
|
|
78
80
|
- `--probe-out FLOAT`: Probe Out radius for cavity detection in Ångströms (default: 4.0)
|
|
81
|
+
- `--exterior-trim-distance FLOAT`: Exterior trim distance in Ångströms (default: 2.4)
|
|
79
82
|
- `--volume-cutoff FLOAT`: Minimum cavity volume to consider in Ų (default: 5.0)
|
|
80
83
|
- `--auto-select`: Automatically select all cavities without user interaction
|
|
81
84
|
- `--cavity-ids TEXT`: Comma-separated list of cavity IDs to fill (e.g., '1,2,3')
|
|
82
85
|
- `--waters-per-cavity TEXT`: Comma-separated list of water counts (e.g., '10,15,20'), must match cavity-ids order
|
|
83
86
|
- `--optimize-mmff94 / --no-optimize-mmff94`: Enable/disable MMFF94 with protein fixed (default: enabled)
|
|
84
87
|
- `--mmff-max-iterations INTEGER`: Max MMFF94 iterations (default: 300)
|
|
88
|
+
- `--remove-after-optim / --no-remove-after-optim`: After MMFF94, remove waters that fail post-checks (default: enabled)
|
|
89
|
+
- Also accepted: `--remove_after_optim / --no_remove_after_optim`
|
|
85
90
|
|
|
86
91
|
Recommended usage:
|
|
87
92
|
- Prefer interactive/manual cavity and water-count selection over `--auto-select`. Auto-selection often overfills cavities with too many waters.
|
|
88
93
|
- Keep `--optimize-mmff94` enabled (recommended) to refine water placement after Monte Carlo sampling.
|
|
94
|
+
- Use `--no-remove-after-optim` if you want to keep all waters after MMFF94, even if they clash or move out of cavity bounds.
|
|
89
95
|
|
|
90
96
|
### Examples
|
|
91
97
|
|
|
@@ -106,7 +112,7 @@ cavefiller protein.pdb --cavity-ids "1,3,5" --waters-per-cavity "10,15,20"
|
|
|
106
112
|
|
|
107
113
|
**Custom cavity detection parameters:**
|
|
108
114
|
```bash
|
|
109
|
-
cavefiller protein.pdb --probe-in 1.
|
|
115
|
+
cavefiller protein.pdb --grid-step 0.6 --probe-in 1.4 --probe-out 4.0 --exterior-trim-distance 2.4 --volume-cutoff 5.0
|
|
110
116
|
```
|
|
111
117
|
|
|
112
118
|
## Workflow
|
|
@@ -180,7 +186,7 @@ This repository includes GitHub Actions workflow at `.github/workflows/ci-cd.yml
|
|
|
180
186
|
- Runs `pytest` on every push to `main`
|
|
181
187
|
- Runs `pytest` on every pull request targeting `main`
|
|
182
188
|
- Builds package distributions after tests pass
|
|
183
|
-
- Publishes to PyPI only
|
|
189
|
+
- Publishes to PyPI only on pushes to `main` where `pyproject.toml` `project.version` changed
|
|
184
190
|
|
|
185
191
|
#### One-time setup for automatic PyPI publishing
|
|
186
192
|
|
|
@@ -200,14 +206,7 @@ No PyPI API token secret is needed when using Trusted Publishing.
|
|
|
200
206
|
- `pyproject.toml` (`project.version`)
|
|
201
207
|
- `cavefiller/__init__.py` (`__version__`)
|
|
202
208
|
2. Commit and push to `main`.
|
|
203
|
-
3.
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
git tag v0.1.1
|
|
207
|
-
git push origin v0.1.1
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The workflow validates that the tag matches `pyproject.toml` (for example, tag `v0.1.1` must match version `0.1.1`) before publishing.
|
|
209
|
+
3. CI will publish that pushed version to PyPI automatically, but only if `pyproject.toml` version changed versus the previous commit on `main`.
|
|
211
210
|
|
|
212
211
|
## License
|
|
213
212
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
cavefiller/__init__.py,sha256=jcIzkqy6uAePOJUzb9CH7fTuyV27k8IU1gvCvse7ac0,105
|
|
2
|
+
cavefiller/cavity_finder.py,sha256=vfORCpZpQbT1XiAA533LKGyvapCuJ3frnxDTkSFYOg0,6508
|
|
3
|
+
cavefiller/cavity_selector.py,sha256=B4nUcFg4YrQbzvtoHhd-JEs-d8TABllABwc-_TuOWnM,4923
|
|
4
|
+
cavefiller/cli.py,sha256=TAYnkq2VVNyN9c8TOySefAOXWmVPwJt1aWajZDFjGnY,6193
|
|
5
|
+
cavefiller/water_filler.py,sha256=graTfaNslyDer3ykZsK1ZI3nK7jEQ5vZvuyeCkZ6wD0,23824
|
|
6
|
+
cavefiller-0.3.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
7
|
+
cavefiller-0.3.1.dist-info/METADATA,sha256=28WwPH5Q1ayr5a80sIKDHpWd0z-Wl69m1eE-Yf9I1mI,7882
|
|
8
|
+
cavefiller-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
cavefiller-0.3.1.dist-info/entry_points.txt,sha256=w_9KoS9Km-h_WG_T5TObSM9oGAygpbsNdh9i_FRX4YM,50
|
|
10
|
+
cavefiller-0.3.1.dist-info/top_level.txt,sha256=YsdpUmwz7b7C53YyyA2tO0cBPcqHTQGAGday-ZMQ0c0,11
|
|
11
|
+
cavefiller-0.3.1.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
cavefiller/__init__.py,sha256=Ac6YpUglCiETp4zMEd9CS-7Y_--a5wC8oLBI-xdFB-Q,105
|
|
2
|
-
cavefiller/cavity_finder.py,sha256=Q6d5rWVksX9ZZVbeopHWXy0ohPdRq8kjKWnXi2h2VsM,3960
|
|
3
|
-
cavefiller/cavity_selector.py,sha256=B4nUcFg4YrQbzvtoHhd-JEs-d8TABllABwc-_TuOWnM,4923
|
|
4
|
-
cavefiller/cli.py,sha256=0pcMBhFiZZR7vjR9CGKvaOb--GyGGL5IluIYoU5SnEw,5206
|
|
5
|
-
cavefiller/water_filler.py,sha256=beNGVN8vV6xXD7QTnaiHbNSmOoMyvlrMCDCC7yniPOU,21720
|
|
6
|
-
cavefiller-0.2.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
7
|
-
cavefiller-0.2.1.dist-info/METADATA,sha256=wnDDY2DIEFQeq-v84DiNtS2p514_wzGR1KpoIUWBwZg,7369
|
|
8
|
-
cavefiller-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
-
cavefiller-0.2.1.dist-info/entry_points.txt,sha256=w_9KoS9Km-h_WG_T5TObSM9oGAygpbsNdh9i_FRX4YM,50
|
|
10
|
-
cavefiller-0.2.1.dist-info/top_level.txt,sha256=YsdpUmwz7b7C53YyyA2tO0cBPcqHTQGAGday-ZMQ0c0,11
|
|
11
|
-
cavefiller-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|