Metadata-Version: 2.5
Name: caddforge-descriptors
Version: 0.11.0
Summary: Molecular descriptor workflows for the CADDForge toolbox.
Author-email: Manuel Sellner <manuel.sellner@idorsia.com>, Christoph Sager <christoph.sager@idorsia.com>
License: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: caddforge-core>=0.21.0
Requires-Dist: hdbscan>=0.8.33
Requires-Dist: numpy>=1.25
Requires-Dist: pandas>=2.1
Requires-Dist: pyocl-idor>=0.8.3
Requires-Dist: rdkit>=2022.9.5
Requires-Dist: scikit-learn-extra>=0.3
Requires-Dist: scikit-learn>=1.3
Requires-Dist: torchani>=2.7.9
Description-Content-Type: text/markdown

# CADDForge Descriptors Plugin

Install this plugin with `python -m pip install caddforge-descriptors`. It depends directly
on `caddforge-core>=0.21.0`; Python imports and CLI commands still use `caddforge`.
For the complete public toolbox, including user-facing extras, install
`python -m pip install caddforge`. When migrating an existing environment, uninstall
the former `caddforge` core distribution before installing renamed core and updated plugins.

`caddforge-descriptors` provides descriptor and clustering workflows as plugins for the CADDForge framework.

## Implemented Tools
- [`ani-psa`](#tool-ani-psa)
- [`ani-conformers`](#tool-ani-conformers)
- [`cluster-molecules`](#tool-cluster-molecules)
- [`phesa`](#tool-phesa)

## Tool: `ani-psa`

### What it does
The `ani-psa` tool computes ANI-weighted 3D polar surface area values from one molecule or a batch of molecules. The workflow:
- accepts one SMILES, an inline SMILES list, or a SMILES file
- generates conformers with RDKit, optimizes them with MMFF, clusters them, and re-ranks them with TorchANI
- computes 3D PSA on the selected conformers and reports the Boltzmann-weighted average
- writes standardized CADDForge outputs plus per-molecule JSON/CSV summaries

### External dependencies
No external command-line tools are required beyond the Python environment.

Runtime notes:
- TorchANI and PyTorch must be available in the environment.
- CUDA is optional; set `device: cuda` or `device: cuda:0` if you want GPU execution.

### CLI usage
```bash
caddforge run descriptors ani-psa --config ani_psa.yaml
```

### Python API usage
```python
from cadd_descriptors import AniPsaConfig, AniPsaWorkflow

cfg = AniPsaConfig(
    smiles="CC(=O)Oc1ccccc1C(=O)O",
    top_n=25,
    backend="local",
)

workflow = AniPsaWorkflow(cfg)
job = workflow.prepare_job()
result = workflow.run(job)
print(result.status.value, result.metrics)
```

### Example config
```yaml
smiles: "CCO"
num_confs: 100
top_n: 10
rmsd_cluster_thresh: 0.5
max_mmff_iters: 200
temperature: 298.15
model_name: ANI2x
device: cpu
```

### Tool-specific config options
Only one input source must be set:
- `smiles`
- `smiles_list`
- `smiles_file`

#### Input options
- `smiles` (`str | None`): Single SMILES string.
- `smiles_list` (`list[str] | None`): Inline batch of SMILES strings.
- `smiles_file` (`Path | None`): SMILES input file.
- `smiles_column` (`str`, default `smiles`): Column to read from CSV/TSV input.

#### Conformer and ANI options
- `num_confs` (`int`, default `500`): Number of conformers to generate.
- `rmsd_cluster_thresh` (`float`, default `0.5`): RMSD threshold for conformer clustering.
- `max_mmff_iters` (`int`, default `200`): MMFF optimization steps per conformer.
- `top_n` (`int`, default `50`): Number of ANI-ranked conformers kept for PSA averaging.
- `temperature` (`float`, default `298.15`): Boltzmann weighting temperature in Kelvin.
- `model_name` (`str`, default `ANI2x`): TorchANI model name.
- `device` (`str | None`, default `cpu`): Torch device string.

### Outputs
For each run, artifacts are written under `runs/<date>/<job-id>/`:
- `results/results.json`: per-molecule PSA summary payload
- `results/results.csv`: tabular per-molecule PSA summary
- `results/result_record.json`: standardized CADDForge `Result`
- `stdout.log`: workflow log

## Tool: `phesa`

### What it does
The `phesa` tool computes OpenChemLib PheSA shape/pharmacophore similarities between one reference
molecule and one or more candidates. It supports:
- a reference molecule from SMILES, SDF, or an encoded PheSA text file
- candidate molecules from either SMILES strings or single-/multi-compound SDF files
- configurable maximum conformer count and pharmacophore point weight
- aligned SDF outputs for the reference and candidates
- pipeline outputs for scores and aligned candidate collections

### External dependencies
PheSA imports `pyocl`, provided by the public `pyocl-idor>=0.8.3` distribution.
The OpenChemLib runtime requires Java 21 or newer with `JAVA_HOME` configured.

### CLI usage
```bash
caddforge run descriptors phesa --config phesa.yaml
```

### Python API usage
```python
from cadd_descriptors import PhesaConfig, PhesaWorkflow

cfg = PhesaConfig(
    reference_smiles="CCO",
    candidate_smiles=["CCCO", "CCCCO"],
    max_confs=200,
    pp_weight=0.5,
)

workflow = PhesaWorkflow(cfg)
job = workflow.prepare_job()
result = workflow.run(job)
print(result.status.value, result.metrics)
```

### Example configs
```yaml
reference_smiles: "CCO"
candidate_smiles:
  - "CCCO"
  - "CCCCO"
max_confs: 200
pp_weight: 0.5
```

```yaml
reference_sdf: reference.sdf
candidate_sdf_files:
  - candidates.sdf
  - extra_candidate.sdf
max_confs: 200
pp_weight: 0.5
write_merged_aligned_candidates_sdf: true
```

```yaml
reference_phesa_file: reference_phesa.txt
candidate_smiles:
  - "CCCO"
  - "CCCCO"
max_confs: 200
pp_weight: 0.5
```

### Tool-specific config options
Exactly one reference source must be set:
- `reference_smiles`
- `reference_sdf`
- `reference_phesa_file`

Exactly one candidate source must be set:
- `candidate_smiles`
- `candidate_sdf_files`

`candidate_sdf_files` may contain single- or multi-compound SDF files. Each valid SDF molecule
record is scored as one candidate; record names are used as candidate IDs when present, otherwise
IDs fall back to the source file stem plus the record index.

#### PheSA options
- `max_confs` (`int`, default `200`): Maximum number of conformers for generated PheSA descriptors.
- `pp_weight` (`float`, default `0.5`): Pharmacophore point weight.
- `scores_csv_name` (`str`, default `scores.csv`): Scores CSV filename under `results/`.
- `aligned_dir_name` (`str`, default `aligned`): Aligned SDF output directory under `results/`.
- `write_merged_aligned_candidates_sdf` (`bool`, default `false`): Write one SDF containing all
  successfully aligned candidate molecules.
- `merged_aligned_candidates_sdf_name` (`str`, default `aligned_candidates.sdf`): Filename for the
  merged aligned candidate SDF under the aligned output directory.

When `reference_smiles` is used, both the reference and candidates use OpenChemLib's generated-shape
handler with `max_confs` and `pp_weight`. When `reference_sdf` is used, the reference uses
OpenChemLib's one-conformation handler; candidates still use `max_confs` and `pp_weight`.

With `reference_phesa_file`, the entire UTF-8 text file must contain one encoded PheSA descriptor.
Surrounding whitespace is stripped before decoding with `DescriptorHandlerShape.decode()`.
The decoded reference is used directly, without generating reference conformers. Candidates still
use `max_confs` and `pp_weight`. Empty, unreadable, or invalid descriptor files fail the run.

**Reference coordinate frame:** With `reference_phesa_file`, the aligned reference and candidate
SDFs, including the optional merged candidate SDF, remain in the PheSA alignment coordinate frame.
They are not restored to the coordinates of the original molecule used to create the encoded
descriptor. If downstream calculations need alignment in an input reference SDF's coordinate frame,
use `reference_sdf`; coordinate restoration applies only to that reference mode.

### Outputs
For each run, artifacts are written under `runs/<date>/<job-id>/`:
- `results/results.json`: per-candidate PheSA similarity payload
- `results/scores.csv`: tabular candidate scores
- `results/aligned/reference.sdf`: aligned reference structure
- `results/aligned/candidates/*.sdf`: aligned candidate structures
- `results/aligned/aligned_candidates.sdf`: optional merged aligned candidate SDF
- `results/aligned/manifest.csv`: candidate scores and aligned SDF paths
- `results/result_record.json`: standardized CADDForge `Result`

### Pipeline artifacts
The plugin exposes:
- input `reference_sdf` mapped to config field `reference_sdf`
- input `reference_phesa_file` mapped to config field `reference_phesa_file`
- input `candidates` mapped to config field `candidate_sdf_files`
- output `scores` from `scores_csv`
- output `aligned_reference` from `aligned_reference_sdf`
- output `aligned_candidates` from `aligned_manifest_csv` using column `aligned_candidate_sdf`
- optional output `merged_aligned_candidates` from `merged_aligned_candidates_sdf`

## Tool: `ani-conformers`

### What it does
The `ani-conformers` tool generates ANI re-ranked conformers and writes the selected conformers to SDF. It supports:
- one SMILES, an inline SMILES list, a SMILES file, or an input SDF file
- RDKit conformer generation and MMFF optimization
- ANI re-ranking of clustered conformer representatives
- batch processing with per-molecule summaries and a combined ranked-conformer SDF

### External dependencies
No external command-line tools are required beyond the Python environment.

Runtime notes:
- TorchANI and PyTorch must be available in the environment.
- CUDA is optional.

### CLI usage
```bash
caddforge run descriptors ani-conformers --config ani_conformers.yaml
```

### Python API usage
```python
from cadd_descriptors import AniConformersConfig, AniConformersWorkflow

cfg = AniConformersConfig(
    smiles="CCO",
    top_n=10,
    num_confs=50,
    backend="local",
)

workflow = AniConformersWorkflow(cfg)
job = workflow.prepare_job()
result = workflow.run(job)
print(result.status.value, result.paths["ranked_conformers_sdf"])
```

### Example config
```yaml
smiles: "CCO"
num_confs: 100
top_n: 10
rmsd_cluster_thresh: 0.5
max_mmff_iters: 200
model_name: ANI2x
device: cpu
output_sdf_name: ranked_conformers.sdf
```

### Tool-specific config options
Only one input source must be set:
- `smiles`
- `smiles_list`
- `smiles_file`
- `structure_file`

#### Input options
- `smiles` (`str | None`): Single SMILES string.
- `smiles_list` (`list[str] | None`): Inline batch of SMILES strings.
- `smiles_file` (`Path | None`): SMILES input file.
- `structure_file` (`Path | None`): Input SDF file.
- `smiles_column` (`str`, default `smiles`): Column to read from CSV/TSV SMILES input.

#### Conformer and ANI options
- `num_confs` (`int`, default `500`): Number of conformers to generate.
- `rmsd_cluster_thresh` (`float`, default `0.5`): RMSD threshold for conformer clustering.
- `max_mmff_iters` (`int`, default `200`): MMFF optimization steps per conformer.
- `top_n` (`int`, default `50`): Number of ANI-ranked conformers to keep.
- `model_name` (`str`, default `ANI2x`): TorchANI model name.
- `device` (`str | None`, default `cpu`): Torch device string.
- `output_sdf_name` (`str`, default `ranked_conformers.sdf`): Output SDF filename under `results/`.

### Outputs
For each run, artifacts are written under `runs/<date>/<job-id>/`:
- `results/results.json`: per-molecule conformer summary payload
- `results/<output_sdf_name>`: ranked conformers SDF
- `results/result_record.json`: standardized CADDForge `Result`
- `stdout.log`: workflow log

## Tool: `cluster-molecules`

### What it does
The `cluster-molecules` tool clusters molecules from SMILES input using fingerprint-based and scaffold-based algorithms. It supports:
- SMILES input from `.smi`, `.smiles`, `.sdf`, `.csv`, or `.tsv`
- multiple clustering algorithms: Butina, agglomerative, DBSCAN, HDBSCAN, k-medoids, scaffold, and affinity propagation
- medoid/centroid reporting for each cluster
- automatic k-medoids planning that chooses exact in-memory, exact disk-backed, or approximate execution modes

### External dependencies
No external command-line tools are required beyond the Python environment.

Runtime notes:
- `hdbscan` is required only for `algorithm: hdbscan`.
- The approximate `kmedoids` fallback uses sparse `MiniBatchKMeans` and then snaps cluster centers back to actual medoids.

### CLI usage
```bash
caddforge run descriptors cluster-molecules --config cluster.yaml
```

### Python API usage
```python
from cadd_descriptors import ClusterMoleculesConfig, ClusterMoleculesWorkflow

cfg = ClusterMoleculesConfig(
    input_path="molecules.csv",
    smiles_column="smiles",
    algorithm="kmedoids",
    kmedoids_n_clusters=100,
    kmedoids_mode="auto",
    backend="local",
)

workflow = ClusterMoleculesWorkflow(cfg)
job = workflow.prepare_job()
result = workflow.run(job)
print(result.status.value, result.metrics)
```

### Example config
```yaml
input_path: molecules.csv
smiles_column: smiles
algorithm: kmedoids       # butina|agglomerative|dbscan|hdbscan|kmedoids|scaffold|affinity_propagation
fingerprint_radius: 2
fingerprint_bits: 2048
kmedoids_n_clusters: 100
kmedoids_mode: auto       # auto|exact_ram|exact_disk|approximate
kmedoids_memory_budget_gb: null
kmedoids_disk_budget_gb: null
kmedoids_max_exact_n: 5000
kmedoids_max_exact_pairs: 25000000
kmedoids_max_exact_disk_n: 20000
kmedoids_max_exact_disk_pairs: 100000000
kmedoids_batch_size: 1024
kmedoids_max_iter: 20
kmedoids_minibatch_size: 4096
kmedoids_local_refine_limit: 128
kmedoids_temp_dir: null
```

### Tool-specific config options
#### Input options
- `input_path` (`Path`): Input file containing molecules.
- `smiles_column` (`str`, default `smiles`): SMILES column for CSV/TSV input.
- `id_column` (`str | None`): Optional identifier column copied into outputs.

#### Fingerprint options
- `fingerprint_radius` (`int`, default `2`): Morgan fingerprint radius.
- `fingerprint_bits` (`int`, default `2048`): Morgan fingerprint size.

#### Algorithm selection
- `algorithm` (`butina | agglomerative | dbscan | hdbscan | kmedoids | scaffold | affinity_propagation`): Clustering algorithm.

#### Algorithm-specific options
- `butina_cutoff` (`float`, default `0.6`): Butina Tanimoto similarity cutoff.
- `agglomerative_n_clusters` (`int | None`): Agglomerative cluster count.
- `agglomerative_distance_threshold` (`float | None`): Agglomerative distance threshold.
- `dbscan_eps` (`float`, default `0.4`): DBSCAN epsilon in distance space.
- `dbscan_min_samples` (`int`, default `3`): DBSCAN minimum samples.
- `hdbscan_min_cluster_size` (`int`, default `5`): HDBSCAN minimum cluster size.
- `affinity_preference` (`float | None`): Affinity propagation preference.
- `scaffold_use_generic` (`bool`, default `true`): Use generic Bemis-Murcko scaffolds.

#### K-medoids planning options
- `kmedoids_n_clusters` (`int`, default `5`): Target number of medoid clusters.
- `kmedoids_mode` (`auto | exact_ram | exact_disk | approximate`, default `auto`): Force or auto-select the execution strategy.
- `kmedoids_memory_budget_gb` (`float | None`): Optional RAM budget for planning.
- `kmedoids_disk_budget_gb` (`float | None`): Optional temp-disk budget for planning.
- `kmedoids_max_exact_n` (`int`, default `5000`): Max size for exact in-memory planning.
- `kmedoids_max_exact_pairs` (`int`, default `25000000`): Max pair count for exact in-memory planning.
- `kmedoids_max_exact_disk_n` (`int`, default `20000`): Max size for exact disk-backed planning.
- `kmedoids_max_exact_disk_pairs` (`int`, default `100000000`): Max pair count for exact disk-backed planning.
- `kmedoids_batch_size` (`int`, default `1024`): Batch size for exact assignment/cost evaluation.
- `kmedoids_max_iter` (`int`, default `20`): PAM swap-iteration limit.
- `kmedoids_minibatch_size` (`int`, default `4096`): Mini-batch size for approximate mode.
- `kmedoids_local_refine_limit` (`int`, default `128`): Max cluster size for exact local medoid refinement in approximate mode.
- `kmedoids_temp_dir` (`Path | None`): Optional temp directory for disk-backed exact mode.

### Outputs
For each run, artifacts are written under `runs/<date>/<job-id>/`:
- `results/assignments.csv`: molecule-to-cluster assignments
- `results/centroids.csv`: per-cluster medoid/centroid summary
- `results/summary.json`: clustering summary
- `results/planning.json`: k-medoids planning summary when `algorithm: kmedoids`
- `results/result_record.json`: standardized CADDForge `Result`
- `stdout.log`: workflow log
## Compressed inputs

File-based SMILES, SDF, CSV, and TSV inputs may be supplied with a trailing `.gz`. CADDForge
expands these wrappers in job-local runtime scratch before invoking the descriptor workflow. Plain
inputs and native tool formats retain their existing behavior.

## Continuous integration and releases

`.github/workflows/release.yml` builds a wheel and source archive, validates
metadata, and tests the installed wheel on Linux with Python 3.10 and 3.12.
The lightweight test selection is recorded in `scripts/ci_tests.json`;
external scientific engines are validated separately.

Pushing a matching `v*` release tag publishes the tested artifacts to PyPI.
Branch pushes, pull requests and manual runs never publish. Configure a
PyPI Trusted Publisher for the `idorsia` repository, `release.yml` workflow
and `pypi` environment before pushing release tags. Runtime CADDForge
dependencies must already be available publicly; publish core first,
and publish docking/descriptors before MD.
