rdkit-cli 0.3.1__tar.gz → 0.3.2__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.
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/CHANGELOG.md +25 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/PKG-INFO +5 -2
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/README.md +4 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/pyproject.toml +1 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/__init__.py +1 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/cli.py +6 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/conformers.py +174 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/depict.py +111 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/descriptors.py +22 -3
- rdkit_cli-0.3.2/src/rdkit_cli/commands/energy.py +151 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/filter.py +47 -8
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/fingerprints.py +4 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/fragment.py +103 -1
- rdkit_cli-0.3.2/src/rdkit_cli/commands/pharmacophore.py +151 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/props.py +163 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/reactions.py +158 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/scaffold.py +103 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/similarity.py +143 -4
- rdkit_cli-0.3.2/src/rdkit_cli/commands/stereo.py +236 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/conformers.py +146 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/descriptors.py +80 -4
- rdkit_cli-0.3.2/src/rdkit_cli/core/energy.py +118 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/filters.py +33 -4
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/fingerprints.py +37 -1
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/fragment.py +38 -0
- rdkit_cli-0.3.2/src/rdkit_cli/core/pharmacophore.py +115 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/reactions.py +78 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/scaffold.py +54 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/similarity.py +134 -13
- rdkit_cli-0.3.2/src/rdkit_cli/core/stereo.py +106 -0
- rdkit_cli-0.3.2/tests/integration/test_new_features.py +490 -0
- rdkit_cli-0.3.2/tests/unit/test_extended_features.py +457 -0
- rdkit_cli-0.3.2/tests/unit/test_shape_constrained_network.py +391 -0
- rdkit_cli-0.3.2/tests/unit/test_stereo_energy_pharmacophore.py +401 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/.github/workflows/publish.yml +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/LICENSE +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/docs/commands.md +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/__main__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/align.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/convert.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/deduplicate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/diversity.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/enumerate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/info.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/mcs.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/merge.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/mmp.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/protonate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/rgroup.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/rings.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/rmsd.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/sample.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/sascorer.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/split.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/standardize.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/stats.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/commands/validate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/align.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/deduplicate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/depict.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/diversity.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/enumerate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/info.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/mcs.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/merge.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/mmp.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/protonate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/rgroup.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/rings.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/rmsd.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/sample.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/sascorer.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/split.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/standardizer.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/stats.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/core/validate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/io/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/io/formats.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/io/readers.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/io/writers.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/parallel/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/parallel/batch.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/parallel/executor.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/progress/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/progress/ninja.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/utils/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/src/rdkit_cli/utils/logging.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/conftest.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/fixtures/sample.csv +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/fixtures/sample.smi +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/integration/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/integration/test_cli.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/integration/test_interop.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/integration/test_new_commands.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/__init__.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_align.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_deduplicate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_depict.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_descriptors.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_diversity.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_enumerate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_filters.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_fingerprints.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_fragment.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_info.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_io.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_mcs.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_merge.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_mmp.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_protonate.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_reactions.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_rgroup.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_rings.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_rmsd.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_sample.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_sascorer.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_scaffold.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_similarity.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_split.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_standardizer.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_stats.py +0 -0
- {rdkit_cli-0.3.1 → rdkit_cli-0.3.2}/tests/unit/test_validate.py +0 -0
|
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.2] - 2026-04-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **stereo**: New command for stereochemistry analysis — CIP label assignment (R/S, E/Z), stereocenter perception, enhanced stereo group inspection, and stereo cleanup/canonicalization
|
|
13
|
+
- **energy**: New command for force field energy calculations — single-point MMFF/UFF energy and structure minimization with convergence reporting
|
|
14
|
+
- **pharmacophore**: New command for pharmacophore feature perception (Donor, Acceptor, Aromatic, etc.) and 2D pharmacophore fingerprint similarity search
|
|
15
|
+
- **fingerprints**: Added Avalon, MHFP (MinHash), and 2D pharmacophore (Gobbi) fingerprint types
|
|
16
|
+
- **descriptors**: Added 42 Molecular Quantum Numbers (MQN) and 10 3D shape descriptors (PMI, NPR, Asphericity, Eccentricity, SpherocityIndex, PBF); new `--mqn`, `--3d`, and `--generate-conformers` flags
|
|
17
|
+
- **similarity**: Added 8 metrics (AllBit, Asymmetric, BraunBlanquet, Kulczynski, McConnaughey, OnBit, RogotGoldberg, Tversky with alpha/beta); new `shape` subcommand for 3D shape similarity (Tanimoto, Protrude, Tversky)
|
|
18
|
+
- **filter**: Expanded structural alert catalogs — `--catalog` option supports PAINS, PAINS_A/B/C, Brenk, NIH, ZINC, and all combined; added `alerts` subcommand as alias
|
|
19
|
+
- **conformers**: Added `constrained` subcommand for template-constrained 3D embedding and `torsion` subcommand for dihedral angle scanning with energy profiles
|
|
20
|
+
- **reactions**: Added `map` subcommand for atom-atom mapping inspection (text/JSON) and `fingerprint` subcommand for reaction difference/structural fingerprints
|
|
21
|
+
- **scaffold**: Added `network` subcommand for scaffold network construction (CSV/JSON output) using rdScaffoldNetwork
|
|
22
|
+
- **props**: Added `charges` subcommand for Gasteiger partial charges and `crippen` subcommand for per-atom LogP/MR contributions
|
|
23
|
+
- **fragment**: Added `brics-build` subcommand for recombining BRICS fragments into new molecules
|
|
24
|
+
- **depict**: Added `highlight` subcommand for SMARTS-based atom/bond highlighting with custom RGB colors
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Total command count increased from 29 to 32 (stereo, energy, pharmacophore)
|
|
29
|
+
- Total fingerprint types increased from 6 to 9
|
|
30
|
+
- Total descriptor count increased from ~133 to ~185
|
|
31
|
+
- Similarity metrics increased from 5 to 13
|
|
32
|
+
|
|
8
33
|
## [0.3.1] - 2026-03-14
|
|
9
34
|
|
|
10
35
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rdkit-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A comprehensive CLI tool for RDKit cheminformatics operations
|
|
5
5
|
Project-URL: Homepage, https://github.com/vitruves/rdkit-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/vitruves/rdkit-cli
|
|
@@ -41,7 +41,7 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
|
|
42
42
|
A high-performance CLI for cheminformatics workflows, powered by native RDKit (C++ under the hood).
|
|
43
43
|
|
|
44
|
-
**
|
|
44
|
+
**32 commands** | **5 I/O formats** (CSV, TSV, SMI, SDF, Parquet) | **multi-core parallel processing** | **~80ms startup**
|
|
45
45
|
|
|
46
46
|
## Installation
|
|
47
47
|
|
|
@@ -81,6 +81,7 @@ Commands:
|
|
|
81
81
|
depict Generate molecular depictions (SVG/PNG)
|
|
82
82
|
descriptors Compute molecular descriptors
|
|
83
83
|
diversity Analyze and select diverse molecules
|
|
84
|
+
energy Force field energy calculations
|
|
84
85
|
enumerate Enumerate stereoisomers and tautomers
|
|
85
86
|
filter Filter by substructure, properties, drug-likeness, PAINS
|
|
86
87
|
fingerprints Compute fingerprints (Morgan, MACCS, RDKit, AtomPair, Torsion)
|
|
@@ -89,6 +90,7 @@ Commands:
|
|
|
89
90
|
mcs Find Maximum Common Substructure
|
|
90
91
|
merge Merge multiple molecule files
|
|
91
92
|
mmp Matched Molecular Pairs analysis
|
|
93
|
+
pharmacophore Pharmacophore feature analysis
|
|
92
94
|
props Property column operations (add, rename, drop, keep)
|
|
93
95
|
protonate Enumerate protonation states
|
|
94
96
|
reactions Apply SMIRKS transformations and enumerate products
|
|
@@ -102,6 +104,7 @@ Commands:
|
|
|
102
104
|
split Split files into smaller chunks
|
|
103
105
|
standardize Standardize and canonicalize molecules
|
|
104
106
|
stats Calculate dataset statistics
|
|
107
|
+
stereo Analyze and manipulate stereochemistry
|
|
105
108
|
validate Validate molecular structures
|
|
106
109
|
|
|
107
110
|
Use 'rdkit-cli <command> --help' for command-specific options.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
A high-performance CLI for cheminformatics workflows, powered by native RDKit (C++ under the hood).
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**32 commands** | **5 I/O formats** (CSV, TSV, SMI, SDF, Parquet) | **multi-core parallel processing** | **~80ms startup**
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -47,6 +47,7 @@ Commands:
|
|
|
47
47
|
depict Generate molecular depictions (SVG/PNG)
|
|
48
48
|
descriptors Compute molecular descriptors
|
|
49
49
|
diversity Analyze and select diverse molecules
|
|
50
|
+
energy Force field energy calculations
|
|
50
51
|
enumerate Enumerate stereoisomers and tautomers
|
|
51
52
|
filter Filter by substructure, properties, drug-likeness, PAINS
|
|
52
53
|
fingerprints Compute fingerprints (Morgan, MACCS, RDKit, AtomPair, Torsion)
|
|
@@ -55,6 +56,7 @@ Commands:
|
|
|
55
56
|
mcs Find Maximum Common Substructure
|
|
56
57
|
merge Merge multiple molecule files
|
|
57
58
|
mmp Matched Molecular Pairs analysis
|
|
59
|
+
pharmacophore Pharmacophore feature analysis
|
|
58
60
|
props Property column operations (add, rename, drop, keep)
|
|
59
61
|
protonate Enumerate protonation states
|
|
60
62
|
reactions Apply SMIRKS transformations and enumerate products
|
|
@@ -68,6 +70,7 @@ Commands:
|
|
|
68
70
|
split Split files into smaller chunks
|
|
69
71
|
standardize Standardize and canonicalize molecules
|
|
70
72
|
stats Calculate dataset statistics
|
|
73
|
+
stereo Analyze and manipulate stereochemistry
|
|
71
74
|
validate Validate molecular structures
|
|
72
75
|
|
|
73
76
|
Use 'rdkit-cli <command> --help' for command-specific options.
|
|
@@ -146,6 +146,7 @@ def _register_commands(subparsers):
|
|
|
146
146
|
depict,
|
|
147
147
|
descriptors,
|
|
148
148
|
diversity,
|
|
149
|
+
energy,
|
|
149
150
|
enumerate,
|
|
150
151
|
filter,
|
|
151
152
|
fingerprints,
|
|
@@ -154,6 +155,7 @@ def _register_commands(subparsers):
|
|
|
154
155
|
mcs,
|
|
155
156
|
merge,
|
|
156
157
|
mmp,
|
|
158
|
+
pharmacophore,
|
|
157
159
|
props,
|
|
158
160
|
protonate,
|
|
159
161
|
reactions,
|
|
@@ -167,6 +169,7 @@ def _register_commands(subparsers):
|
|
|
167
169
|
split,
|
|
168
170
|
standardize,
|
|
169
171
|
stats,
|
|
172
|
+
stereo,
|
|
170
173
|
validate,
|
|
171
174
|
)
|
|
172
175
|
|
|
@@ -178,6 +181,7 @@ def _register_commands(subparsers):
|
|
|
178
181
|
depict.register_parser(subparsers)
|
|
179
182
|
descriptors.register_parser(subparsers)
|
|
180
183
|
diversity.register_parser(subparsers)
|
|
184
|
+
energy.register_parser(subparsers)
|
|
181
185
|
enumerate.register_parser(subparsers)
|
|
182
186
|
filter.register_parser(subparsers)
|
|
183
187
|
fingerprints.register_parser(subparsers)
|
|
@@ -186,6 +190,7 @@ def _register_commands(subparsers):
|
|
|
186
190
|
mcs.register_parser(subparsers)
|
|
187
191
|
merge.register_parser(subparsers)
|
|
188
192
|
mmp.register_parser(subparsers)
|
|
193
|
+
pharmacophore.register_parser(subparsers)
|
|
189
194
|
props.register_parser(subparsers)
|
|
190
195
|
protonate.register_parser(subparsers)
|
|
191
196
|
reactions.register_parser(subparsers)
|
|
@@ -199,6 +204,7 @@ def _register_commands(subparsers):
|
|
|
199
204
|
split.register_parser(subparsers)
|
|
200
205
|
standardize.register_parser(subparsers)
|
|
201
206
|
stats.register_parser(subparsers)
|
|
207
|
+
stereo.register_parser(subparsers)
|
|
202
208
|
validate.register_parser(subparsers)
|
|
203
209
|
|
|
204
210
|
|
|
@@ -120,6 +120,74 @@ def register_parser(subparsers):
|
|
|
120
120
|
)
|
|
121
121
|
opt_parser.set_defaults(func=run_optimize)
|
|
122
122
|
|
|
123
|
+
# conformers constrained
|
|
124
|
+
const_parser = conf_subparsers.add_parser(
|
|
125
|
+
"constrained",
|
|
126
|
+
help="Embed molecules constrained to a reference template",
|
|
127
|
+
formatter_class=RdkitHelpFormatter,
|
|
128
|
+
)
|
|
129
|
+
add_common_io_options(const_parser)
|
|
130
|
+
add_common_processing_options(const_parser)
|
|
131
|
+
const_parser.add_argument(
|
|
132
|
+
"-r", "--reference",
|
|
133
|
+
required=True,
|
|
134
|
+
metavar="FILE",
|
|
135
|
+
help="Reference molecule file with 3D coords (SDF, MOL, PDB)",
|
|
136
|
+
)
|
|
137
|
+
const_parser.add_argument(
|
|
138
|
+
"-f", "--force-field",
|
|
139
|
+
choices=["mmff", "uff"],
|
|
140
|
+
default="mmff",
|
|
141
|
+
help="Force field for optimization (default: mmff)",
|
|
142
|
+
)
|
|
143
|
+
const_parser.add_argument(
|
|
144
|
+
"--seed",
|
|
145
|
+
type=int,
|
|
146
|
+
default=42,
|
|
147
|
+
help="Random seed (default: 42)",
|
|
148
|
+
)
|
|
149
|
+
const_parser.set_defaults(func=run_constrained)
|
|
150
|
+
|
|
151
|
+
# conformers torsion
|
|
152
|
+
torsion_parser = conf_subparsers.add_parser(
|
|
153
|
+
"torsion",
|
|
154
|
+
help="Scan torsion angles and compute energy profile",
|
|
155
|
+
formatter_class=RdkitHelpFormatter,
|
|
156
|
+
)
|
|
157
|
+
add_common_io_options(torsion_parser)
|
|
158
|
+
add_common_processing_options(torsion_parser)
|
|
159
|
+
torsion_parser.add_argument(
|
|
160
|
+
"--atoms",
|
|
161
|
+
required=True,
|
|
162
|
+
metavar="I,J,K,L",
|
|
163
|
+
help="Comma-separated atom indices for the dihedral",
|
|
164
|
+
)
|
|
165
|
+
torsion_parser.add_argument(
|
|
166
|
+
"--start",
|
|
167
|
+
type=float,
|
|
168
|
+
default=-180.0,
|
|
169
|
+
help="Start angle in degrees (default: -180)",
|
|
170
|
+
)
|
|
171
|
+
torsion_parser.add_argument(
|
|
172
|
+
"--end",
|
|
173
|
+
type=float,
|
|
174
|
+
default=180.0,
|
|
175
|
+
help="End angle in degrees (default: 180)",
|
|
176
|
+
)
|
|
177
|
+
torsion_parser.add_argument(
|
|
178
|
+
"--step",
|
|
179
|
+
type=float,
|
|
180
|
+
default=10.0,
|
|
181
|
+
help="Step size in degrees (default: 10)",
|
|
182
|
+
)
|
|
183
|
+
torsion_parser.add_argument(
|
|
184
|
+
"-f", "--force-field",
|
|
185
|
+
choices=["mmff", "uff"],
|
|
186
|
+
default="mmff",
|
|
187
|
+
help="Force field (default: mmff)",
|
|
188
|
+
)
|
|
189
|
+
torsion_parser.set_defaults(func=run_torsion)
|
|
190
|
+
|
|
123
191
|
# Set default for main parser
|
|
124
192
|
parser.set_defaults(func=lambda args: parser.print_help() or 1)
|
|
125
193
|
|
|
@@ -218,3 +286,109 @@ def run_optimize(args) -> int:
|
|
|
218
286
|
)
|
|
219
287
|
|
|
220
288
|
return 0 if result.failed == 0 else 1
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def run_constrained(args) -> int:
|
|
292
|
+
"""Run constrained embedding."""
|
|
293
|
+
from rdkit_cli.core.conformers import ConstrainedEmbedder
|
|
294
|
+
from rdkit_cli.io import create_reader, create_writer, FileFormat
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
embedder = ConstrainedEmbedder(
|
|
298
|
+
reference_file=args.reference,
|
|
299
|
+
force_field=args.force_field,
|
|
300
|
+
random_seed=args.seed,
|
|
301
|
+
)
|
|
302
|
+
except ValueError as e:
|
|
303
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
304
|
+
return 1
|
|
305
|
+
|
|
306
|
+
input_path = Path(args.input)
|
|
307
|
+
if not input_path.exists():
|
|
308
|
+
print(f"Error: Input file not found: {input_path}", file=sys.stderr)
|
|
309
|
+
return 1
|
|
310
|
+
|
|
311
|
+
reader = create_reader(
|
|
312
|
+
input_path,
|
|
313
|
+
smiles_column=args.smiles_column,
|
|
314
|
+
name_column=args.name_column,
|
|
315
|
+
has_header=not args.no_header,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
output_path = Path(args.output)
|
|
319
|
+
writer = create_writer(output_path, format_override=FileFormat.SDF)
|
|
320
|
+
|
|
321
|
+
# Single-threaded: ConstrainedEmbed not picklable
|
|
322
|
+
records = list(reader)
|
|
323
|
+
succeeded = 0
|
|
324
|
+
failed = 0
|
|
325
|
+
with writer:
|
|
326
|
+
for record in records:
|
|
327
|
+
result = embedder.embed(record)
|
|
328
|
+
if result is not None:
|
|
329
|
+
writer.write_row(result)
|
|
330
|
+
succeeded += 1
|
|
331
|
+
else:
|
|
332
|
+
failed += 1
|
|
333
|
+
|
|
334
|
+
if not args.quiet:
|
|
335
|
+
total = succeeded + failed
|
|
336
|
+
print(
|
|
337
|
+
f"Embedded {succeeded}/{total} molecules "
|
|
338
|
+
f"({failed} failed)",
|
|
339
|
+
file=sys.stderr,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return 0
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def run_torsion(args) -> int:
|
|
346
|
+
"""Run torsion angle scan."""
|
|
347
|
+
from rdkit_cli.core.conformers import TorsionScanner
|
|
348
|
+
from rdkit_cli.io import create_reader, create_writer
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
indices = tuple(int(x) for x in args.atoms.split(","))
|
|
352
|
+
if len(indices) != 4:
|
|
353
|
+
raise ValueError("Need exactly 4 atom indices")
|
|
354
|
+
except ValueError as e:
|
|
355
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
356
|
+
return 1
|
|
357
|
+
|
|
358
|
+
scanner = TorsionScanner(
|
|
359
|
+
atom_indices=indices,
|
|
360
|
+
start_angle=args.start,
|
|
361
|
+
end_angle=args.end,
|
|
362
|
+
step=args.step,
|
|
363
|
+
force_field=args.force_field,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
input_path = Path(args.input)
|
|
367
|
+
if not input_path.exists():
|
|
368
|
+
print(f"Error: Input file not found: {input_path}", file=sys.stderr)
|
|
369
|
+
return 1
|
|
370
|
+
|
|
371
|
+
reader = create_reader(
|
|
372
|
+
input_path,
|
|
373
|
+
smiles_column=args.smiles_column,
|
|
374
|
+
name_column=getattr(args, "name_column", None),
|
|
375
|
+
has_header=not args.no_header,
|
|
376
|
+
)
|
|
377
|
+
output_path = Path(args.output)
|
|
378
|
+
writer = create_writer(output_path)
|
|
379
|
+
|
|
380
|
+
count = 0
|
|
381
|
+
with writer:
|
|
382
|
+
for record in reader:
|
|
383
|
+
result = scanner.scan(record)
|
|
384
|
+
if result is not None:
|
|
385
|
+
writer.write_row(result)
|
|
386
|
+
count += 1
|
|
387
|
+
|
|
388
|
+
if not args.quiet:
|
|
389
|
+
print(
|
|
390
|
+
f"Scanned torsion for {count} molecules",
|
|
391
|
+
file=sys.stderr,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
return 0
|
|
@@ -274,6 +274,48 @@ def register_parser(subparsers):
|
|
|
274
274
|
)
|
|
275
275
|
grid_parser.set_defaults(func=run_grid)
|
|
276
276
|
|
|
277
|
+
# depict highlight
|
|
278
|
+
hl_parser = depict_subparsers.add_parser(
|
|
279
|
+
"highlight",
|
|
280
|
+
help="Depict with SMARTS-based highlighting",
|
|
281
|
+
formatter_class=RdkitHelpFormatter,
|
|
282
|
+
)
|
|
283
|
+
hl_parser.add_argument(
|
|
284
|
+
"smiles",
|
|
285
|
+
metavar="SMILES",
|
|
286
|
+
help="SMILES string of molecule to depict",
|
|
287
|
+
)
|
|
288
|
+
hl_parser.add_argument(
|
|
289
|
+
"-s", "--smarts",
|
|
290
|
+
required=True,
|
|
291
|
+
metavar="PATTERN",
|
|
292
|
+
help="SMARTS pattern to highlight",
|
|
293
|
+
)
|
|
294
|
+
hl_parser.add_argument(
|
|
295
|
+
"-o", "--output",
|
|
296
|
+
required=True,
|
|
297
|
+
metavar="FILE",
|
|
298
|
+
help="Output image file (SVG or PNG)",
|
|
299
|
+
)
|
|
300
|
+
hl_parser.add_argument(
|
|
301
|
+
"--width",
|
|
302
|
+
type=int,
|
|
303
|
+
default=400,
|
|
304
|
+
help="Image width (default: 400)",
|
|
305
|
+
)
|
|
306
|
+
hl_parser.add_argument(
|
|
307
|
+
"--height",
|
|
308
|
+
type=int,
|
|
309
|
+
default=300,
|
|
310
|
+
help="Image height (default: 300)",
|
|
311
|
+
)
|
|
312
|
+
hl_parser.add_argument(
|
|
313
|
+
"--color",
|
|
314
|
+
default="1.0,0.0,0.0",
|
|
315
|
+
help="Highlight color as R,G,B floats (default: 1.0,0.0,0.0)",
|
|
316
|
+
)
|
|
317
|
+
hl_parser.set_defaults(func=run_highlight)
|
|
318
|
+
|
|
277
319
|
# Set default for main parser
|
|
278
320
|
parser.set_defaults(func=lambda args: parser.print_help() or 1)
|
|
279
321
|
|
|
@@ -431,3 +473,72 @@ def run_grid(args) -> int:
|
|
|
431
473
|
print(f"Wrote grid image to {output_path}", file=sys.stderr)
|
|
432
474
|
|
|
433
475
|
return 0
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def run_highlight(args) -> int:
|
|
479
|
+
"""Depict molecule with SMARTS-based highlighting."""
|
|
480
|
+
from rdkit import Chem
|
|
481
|
+
from rdkit.Chem.Draw import rdMolDraw2D
|
|
482
|
+
|
|
483
|
+
mol = Chem.MolFromSmiles(args.smiles)
|
|
484
|
+
if mol is None:
|
|
485
|
+
print(f"Error: Invalid SMILES: {args.smiles}", file=sys.stderr)
|
|
486
|
+
return 1
|
|
487
|
+
|
|
488
|
+
pattern = Chem.MolFromSmarts(args.smarts)
|
|
489
|
+
if pattern is None:
|
|
490
|
+
print(f"Error: Invalid SMARTS: {args.smarts}", file=sys.stderr)
|
|
491
|
+
return 1
|
|
492
|
+
|
|
493
|
+
# Find matching atoms/bonds
|
|
494
|
+
matches = mol.GetSubstructMatches(pattern)
|
|
495
|
+
if not matches:
|
|
496
|
+
print("Warning: No substructure match found", file=sys.stderr)
|
|
497
|
+
|
|
498
|
+
highlight_atoms = []
|
|
499
|
+
highlight_bonds = []
|
|
500
|
+
for match in matches:
|
|
501
|
+
highlight_atoms.extend(match)
|
|
502
|
+
for i in range(len(match)):
|
|
503
|
+
for j in range(i + 1, len(match)):
|
|
504
|
+
bond = mol.GetBondBetweenAtoms(match[i], match[j])
|
|
505
|
+
if bond is not None:
|
|
506
|
+
highlight_bonds.append(bond.GetIdx())
|
|
507
|
+
|
|
508
|
+
# Parse color
|
|
509
|
+
try:
|
|
510
|
+
rgb = tuple(float(c) for c in args.color.split(","))
|
|
511
|
+
except (ValueError, TypeError):
|
|
512
|
+
rgb = (1.0, 0.0, 0.0)
|
|
513
|
+
|
|
514
|
+
atom_colors = {a: rgb for a in highlight_atoms}
|
|
515
|
+
bond_colors = {b: rgb for b in highlight_bonds}
|
|
516
|
+
|
|
517
|
+
output_path = Path(args.output)
|
|
518
|
+
fmt = output_path.suffix.lower().lstrip(".")
|
|
519
|
+
|
|
520
|
+
if fmt == "svg":
|
|
521
|
+
drawer = rdMolDraw2D.MolDraw2DSVG(args.width, args.height)
|
|
522
|
+
else:
|
|
523
|
+
drawer = rdMolDraw2D.MolDraw2DCairo(args.width, args.height)
|
|
524
|
+
|
|
525
|
+
drawer.DrawMolecule(
|
|
526
|
+
mol,
|
|
527
|
+
highlightAtoms=highlight_atoms,
|
|
528
|
+
highlightBonds=highlight_bonds,
|
|
529
|
+
highlightAtomColors=atom_colors,
|
|
530
|
+
highlightBondColors=bond_colors,
|
|
531
|
+
)
|
|
532
|
+
drawer.FinishDrawing()
|
|
533
|
+
|
|
534
|
+
data = drawer.GetDrawingText()
|
|
535
|
+
mode = "w" if fmt == "svg" else "wb"
|
|
536
|
+
with open(output_path, mode) as f:
|
|
537
|
+
f.write(data)
|
|
538
|
+
|
|
539
|
+
print(
|
|
540
|
+
f"Wrote highlighted image to {output_path} "
|
|
541
|
+
f"({len(highlight_atoms)} atoms highlighted)",
|
|
542
|
+
file=sys.stderr,
|
|
543
|
+
)
|
|
544
|
+
return 0
|
|
@@ -17,6 +17,8 @@ DESCRIPTOR_CATEGORIES = [
|
|
|
17
17
|
"electronic",
|
|
18
18
|
"geometric",
|
|
19
19
|
"molecular",
|
|
20
|
+
"mqn",
|
|
21
|
+
"3d",
|
|
20
22
|
]
|
|
21
23
|
|
|
22
24
|
|
|
@@ -90,6 +92,17 @@ def register_parser(subparsers):
|
|
|
90
92
|
action="store_true",
|
|
91
93
|
help="Compute drug-likeness descriptors",
|
|
92
94
|
)
|
|
95
|
+
desc_group.add_argument(
|
|
96
|
+
"--mqn",
|
|
97
|
+
action="store_true",
|
|
98
|
+
help="Compute 42 Molecular Quantum Numbers",
|
|
99
|
+
)
|
|
100
|
+
desc_group.add_argument(
|
|
101
|
+
"--3d",
|
|
102
|
+
action="store_true",
|
|
103
|
+
dest="compute_3d_set",
|
|
104
|
+
help="Compute 3D shape descriptors (PMI, NPR, Asphericity, etc.)",
|
|
105
|
+
)
|
|
93
106
|
desc_group.add_argument(
|
|
94
107
|
"--category",
|
|
95
108
|
choices=DESCRIPTOR_CATEGORIES,
|
|
@@ -117,10 +130,9 @@ def register_parser(subparsers):
|
|
|
117
130
|
help="Value to use for failed calculations (default: NaN)",
|
|
118
131
|
)
|
|
119
132
|
compute_parser.add_argument(
|
|
120
|
-
"--
|
|
133
|
+
"--generate-conformers",
|
|
121
134
|
action="store_true",
|
|
122
|
-
|
|
123
|
-
help="Include 3D descriptors (requires 3D coordinates)",
|
|
135
|
+
help="Auto-generate 3D coordinates for 3D descriptors",
|
|
124
136
|
)
|
|
125
137
|
compute_parser.add_argument(
|
|
126
138
|
"--no-smiles",
|
|
@@ -209,6 +221,8 @@ def run_compute(args) -> int:
|
|
|
209
221
|
COMMON_DESCRIPTORS,
|
|
210
222
|
LIPINSKI_DESCRIPTORS,
|
|
211
223
|
DRUGLIKE_DESCRIPTORS,
|
|
224
|
+
MQN_DESCRIPTORS,
|
|
225
|
+
THREE_D_DESCRIPTORS,
|
|
212
226
|
)
|
|
213
227
|
from rdkit_cli.io import create_reader, create_writer
|
|
214
228
|
from rdkit_cli.parallel.batch import process_molecules
|
|
@@ -224,6 +238,10 @@ def run_compute(args) -> int:
|
|
|
224
238
|
descriptor_names = LIPINSKI_DESCRIPTORS
|
|
225
239
|
elif args.druglike:
|
|
226
240
|
descriptor_names = DRUGLIKE_DESCRIPTORS
|
|
241
|
+
elif args.mqn:
|
|
242
|
+
descriptor_names = MQN_DESCRIPTORS
|
|
243
|
+
elif args.compute_3d_set:
|
|
244
|
+
descriptor_names = THREE_D_DESCRIPTORS
|
|
227
245
|
elif args.compute_category:
|
|
228
246
|
descs = list_descriptors(category=args.compute_category)
|
|
229
247
|
descriptor_names = [d.name for d in descs]
|
|
@@ -249,6 +267,7 @@ def run_compute(args) -> int:
|
|
|
249
267
|
include_name=not args.no_name,
|
|
250
268
|
precision=args.precision,
|
|
251
269
|
error_value=args.error_value,
|
|
270
|
+
generate_conformers=getattr(args, "generate_conformers", False),
|
|
252
271
|
)
|
|
253
272
|
except ValueError as e:
|
|
254
273
|
print(f"Error: {e}", file=sys.stderr)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Energy command implementation."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from rdkit_cli.cli import (
|
|
7
|
+
RdkitHelpFormatter,
|
|
8
|
+
add_common_io_options,
|
|
9
|
+
add_common_processing_options,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def register_parser(subparsers):
|
|
14
|
+
"""Register the energy command and subcommands."""
|
|
15
|
+
parser = subparsers.add_parser(
|
|
16
|
+
"energy",
|
|
17
|
+
help="Force field energy calculations",
|
|
18
|
+
description="Compute MMFF/UFF energies and minimize structures.",
|
|
19
|
+
formatter_class=RdkitHelpFormatter,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
energy_sub = parser.add_subparsers(
|
|
23
|
+
title="Subcommands",
|
|
24
|
+
dest="subcommand",
|
|
25
|
+
metavar="<subcommand>",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# energy compute
|
|
29
|
+
compute_parser = energy_sub.add_parser(
|
|
30
|
+
"compute",
|
|
31
|
+
help="Compute single-point energy",
|
|
32
|
+
formatter_class=RdkitHelpFormatter,
|
|
33
|
+
)
|
|
34
|
+
add_common_io_options(compute_parser)
|
|
35
|
+
add_common_processing_options(compute_parser)
|
|
36
|
+
compute_parser.add_argument(
|
|
37
|
+
"-f", "--force-field",
|
|
38
|
+
choices=["mmff", "uff"],
|
|
39
|
+
default="mmff",
|
|
40
|
+
help="Force field (default: mmff)",
|
|
41
|
+
)
|
|
42
|
+
compute_parser.set_defaults(func=run_compute)
|
|
43
|
+
|
|
44
|
+
# energy minimize
|
|
45
|
+
minimize_parser = energy_sub.add_parser(
|
|
46
|
+
"minimize",
|
|
47
|
+
help="Minimize and report energy",
|
|
48
|
+
formatter_class=RdkitHelpFormatter,
|
|
49
|
+
)
|
|
50
|
+
add_common_io_options(minimize_parser)
|
|
51
|
+
add_common_processing_options(minimize_parser)
|
|
52
|
+
minimize_parser.add_argument(
|
|
53
|
+
"-f", "--force-field",
|
|
54
|
+
choices=["mmff", "uff"],
|
|
55
|
+
default="mmff",
|
|
56
|
+
help="Force field (default: mmff)",
|
|
57
|
+
)
|
|
58
|
+
minimize_parser.add_argument(
|
|
59
|
+
"--max-iter",
|
|
60
|
+
type=int,
|
|
61
|
+
default=500,
|
|
62
|
+
help="Maximum iterations (default: 500)",
|
|
63
|
+
)
|
|
64
|
+
minimize_parser.set_defaults(func=run_minimize)
|
|
65
|
+
|
|
66
|
+
parser.set_defaults(func=lambda args: parser.print_help() or 1)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def run_compute(args) -> int:
|
|
70
|
+
"""Compute single-point energies."""
|
|
71
|
+
from rdkit_cli.core.energy import EnergyCalculator
|
|
72
|
+
from rdkit_cli.io import create_reader, create_writer
|
|
73
|
+
from rdkit_cli.parallel.batch import process_molecules
|
|
74
|
+
|
|
75
|
+
calculator = EnergyCalculator(force_field=args.force_field)
|
|
76
|
+
|
|
77
|
+
input_path = Path(args.input)
|
|
78
|
+
if not input_path.exists():
|
|
79
|
+
print(f"Error: Input file not found: {input_path}", file=sys.stderr)
|
|
80
|
+
return 1
|
|
81
|
+
|
|
82
|
+
reader = create_reader(
|
|
83
|
+
input_path,
|
|
84
|
+
smiles_column=args.smiles_column,
|
|
85
|
+
name_column=getattr(args, "name_column", None),
|
|
86
|
+
has_header=not args.no_header,
|
|
87
|
+
)
|
|
88
|
+
output_path = Path(args.output)
|
|
89
|
+
writer = create_writer(output_path)
|
|
90
|
+
|
|
91
|
+
with reader, writer:
|
|
92
|
+
result = process_molecules(
|
|
93
|
+
reader=reader,
|
|
94
|
+
writer=writer,
|
|
95
|
+
processor=calculator.compute,
|
|
96
|
+
n_workers=1, # 3D generation not picklable
|
|
97
|
+
quiet=args.quiet,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if not args.quiet:
|
|
101
|
+
print(
|
|
102
|
+
f"Computed energy for "
|
|
103
|
+
f"{result.successful}/{result.total_processed} molecules "
|
|
104
|
+
f"in {result.elapsed_time:.1f}s",
|
|
105
|
+
file=sys.stderr,
|
|
106
|
+
)
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def run_minimize(args) -> int:
|
|
111
|
+
"""Minimize structures."""
|
|
112
|
+
from rdkit_cli.core.energy import EnergyMinimizer
|
|
113
|
+
from rdkit_cli.io import create_reader, create_writer
|
|
114
|
+
from rdkit_cli.parallel.batch import process_molecules
|
|
115
|
+
|
|
116
|
+
minimizer = EnergyMinimizer(
|
|
117
|
+
force_field=args.force_field,
|
|
118
|
+
max_iterations=args.max_iter,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
input_path = Path(args.input)
|
|
122
|
+
if not input_path.exists():
|
|
123
|
+
print(f"Error: Input file not found: {input_path}", file=sys.stderr)
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
reader = create_reader(
|
|
127
|
+
input_path,
|
|
128
|
+
smiles_column=args.smiles_column,
|
|
129
|
+
name_column=getattr(args, "name_column", None),
|
|
130
|
+
has_header=not args.no_header,
|
|
131
|
+
)
|
|
132
|
+
output_path = Path(args.output)
|
|
133
|
+
writer = create_writer(output_path)
|
|
134
|
+
|
|
135
|
+
with reader, writer:
|
|
136
|
+
result = process_molecules(
|
|
137
|
+
reader=reader,
|
|
138
|
+
writer=writer,
|
|
139
|
+
processor=minimizer.minimize,
|
|
140
|
+
n_workers=1,
|
|
141
|
+
quiet=args.quiet,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if not args.quiet:
|
|
145
|
+
print(
|
|
146
|
+
f"Minimized "
|
|
147
|
+
f"{result.successful}/{result.total_processed} molecules "
|
|
148
|
+
f"in {result.elapsed_time:.1f}s",
|
|
149
|
+
file=sys.stderr,
|
|
150
|
+
)
|
|
151
|
+
return 0
|