Skip to content

NO_JIRA Added Pharmacophore generator script to create CrossMiner que… - #99

Open
CRingroseCCDC wants to merge 1 commit into
mainfrom
pharmacophore_generator
Open

NO_JIRA Added Pharmacophore generator script to create CrossMiner que…#99
CRingroseCCDC wants to merge 1 commit into
mainfrom
pharmacophore_generator

Conversation

@CRingroseCCDC

Copy link
Copy Markdown
Contributor

…ries from Ligand Overlay outputs. Includes basic tests and examples.

…ries from Ligand Overlay outputs. Includes basic tests and examples.

from ccdc.pharmacophore import Pharmacophore

def search(query_file: Path, database_file: Path):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <302> reported by reviewdog 🐶
expected 2 blank lines, found 1




@dataclass

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <303> reported by reviewdog 🐶
too many blank lines (3)

feature_definitions=tmp_path / "does_not_exist",
output_file=tmp_path / "query.cm",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [flake8] <391> reported by reviewdog 🐶
blank line at end of file

{}
FEATURE_SUBSTRUCTURE_END
"""
def __init__(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <301> reported by reviewdog 🐶
expected 1 blank line, found 0

return matches[0]


@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <303> reported by reviewdog 🐶
too many blank lines (2)


from ccdc.pharmacophore import Pharmacophore

def search(query_file: Path, database_file: Path):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <302> reported by reviewdog 🐶
expected 2 blank lines, found 1




@dataclass

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <303> reported by reviewdog 🐶
too many blank lines (3)

feature_definitions=tmp_path / "does_not_exist",
output_file=tmp_path / "query.cm",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [flake8] <391> reported by reviewdog 🐶
blank line at end of file

{}
FEATURE_SUBSTRUCTURE_END
"""
def __init__(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <301> reported by reviewdog 🐶
expected 1 blank line, found 0

return matches[0]


@staticmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [flake8] <303> reported by reviewdog 🐶
too many blank lines (2)

- Access to CSD CrossMiner and the feature definitions (`.cpf`) files.
- Access to the CSD Ligand Overlay Tool.

## Licensing Requirements

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
error MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1]

(either directly, or in `any`/`protein`/`small_molecule` subdirectories).
Usually, the location is `C:\users\<username>\CCDC\ccdc-software\csd-crossminer\feature_definitions`

```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]


### Options for Ligand Overlay Output

* `cluster`: Cluster the similar pharmacophore features based on proximity

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
error MD004/ul-style Unordered list style [Expected: dash; Actual: asterisk]

### Options for Ligand Overlay Output

* `cluster`: Cluster the similar pharmacophore features based on proximity
* `projected`: Treat pharmacophore features as projected when appropriate e.g. acceptors and donors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
error MD004/ul-style Unordered list style [Expected: dash; Actual: asterisk]


### Using the Queries Generated

If you would like to use the queries generated with this tool, they can be opened in CrossMiner to run a search.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
error MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1]

@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

- Access to CSD CrossMiner and the feature definitions (`.cpf`) files.
- Access to the CSD Ligand Overlay Tool.

## Licensing Requirements

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint-fix] reported by reviewdog 🐶

Suggested change
## Licensing Requirements
## Licensing Requirements

Comment on lines +36 to +37
* `cluster`: Cluster the similar pharmacophore features based on proximity
* `projected`: Treat pharmacophore features as projected when appropriate e.g. acceptors and donors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint-fix] reported by reviewdog 🐶

Suggested change
* `cluster`: Cluster the similar pharmacophore features based on proximity
* `projected`: Treat pharmacophore features as projected when appropriate e.g. acceptors and donors
- `cluster`: Cluster the similar pharmacophore features based on proximity
- `projected`: Treat pharmacophore features as projected when appropriate e.g. acceptors and donors


### Using the Queries Generated

If you would like to use the queries generated with this tool, they can be opened in CrossMiner to run a search.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint-fix] reported by reviewdog 🐶

Suggested change
If you would like to use the queries generated with this tool, they can be opened in CrossMiner to run a search.
If you would like to use the queries generated with this tool, they can be opened in CrossMiner to run a search.

raise FileNotFoundError(f"Feature definitions folder {feature_definitions} does not exist.")

output_folder = Path(args.output_folder) if args.output_folder else Path('queries')
output_folder.mkdir(parents=True, exist_ok=True)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new overlay_to_pharmacophore script package that converts CSD Ligand Overlay pharmacophore outputs into CrossMiner query (.cm) files, with clustering support plus bundled example data and pytest coverage.

Changes:

  • Implement pharmacophore feature extraction from .mol2 pharmacophore files and optional clustering.
  • Generate CrossMiner query files using external .cpf feature definitions.
  • Add basic pytest suite and bundled example Ligand Overlay/pharmacophore files, plus usage documentation.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/discovery/overlay_to_pharmacophore/main.py CLI entrypoint for producing query files from one or many overlay solutions.
scripts/discovery/overlay_to_pharmacophore/overlay.py Parses pharmacophore mol2 ATOM blocks into labeled feature points (with virtual points).
scripts/discovery/overlay_to_pharmacophore/cluster.py Clusters nearby features (including projected feature virtual-point distance checks).
scripts/discovery/overlay_to_pharmacophore/write_query.py Writes CrossMiner .cm query output, embedding referenced feature definition files.
scripts/discovery/overlay_to_pharmacophore/datastructures.py Defines core datatypes (OverlayData, PharmFeaturePoint, tolerances/weights).
scripts/discovery/overlay_to_pharmacophore/crossminer_search.py Example CrossMiner search helper using CSD Python API.
scripts/discovery/overlay_to_pharmacophore/ReadMe.md Usage instructions, requirements, and feature-definition setup guidance.
scripts/discovery/overlay_to_pharmacophore/tests/tests.py Pytest suite validating parsing, projection handling, clustering, and query writing.
scripts/discovery/overlay_to_pharmacophore/tests/job1/solution_01.mol2 Bundled example Ligand Overlay solution file for tests.
scripts/discovery/overlay_to_pharmacophore/tests/job1/pharmacophores/solution_pharm_01.mol2 Bundled example pharmacophore file used by tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +12
from pathlib import Path

import pytest

from cluster import cluster_features
from datastructures import OverlayData, PharmFeaturePoint
from overlay import OverlayToPharmFeatures
from write_query import FeaturesToCrossMinerQuery

TESTS_DIR = Path(__file__).parent
REPO_ROOT = TESTS_DIR.parent
JOB_FOLDER = TESTS_DIR / "job1"
Comment on lines +56 to +60
def __repr__(self):
return (
f"PharmFeaturePoint({self.x}, {self.y}, {self.z}, "
f"label={self.label}, virtual_points={self.virtual_point}), "
)
Comment on lines +83 to +86
elif prefix == 'NL':
if last_feature_point is None:
raise ValueError("Virtual point found before its parent feature point.")
last_feature_point.virtual_point = np.array([x, y, z], dtype=float)
parser = argparse.ArgumentParser(
description="Create Pharmacophore Features from a Ligand Overlay"
)
parser.add_argument('-i', '--input_folder', type=str, help='Input file(s) path.')
@@ -0,0 +1,76 @@
import argparse

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we put this header at the top of each oy and ipynb file which we publish on the open source:

'''
This script can be used for any purpose without limitation subject to the
conditions at https://www.ccdc.cam.ac.uk/Community/Pages/Licences/v2.aspx
This permission notice and the following statement of attribution must be
included in all copies or substantial portions of this script.

"date of creation": created by the Cambridge Crystallographic Data Centre
'''

@@ -0,0 +1,18 @@
from pathlib import Path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that it is called from anywhere. You can probs add a flag like --run_search so users could do that without any extra actions.

if last_feature_point is None:
raise ValueError("Virtual point found before its parent feature point.")
last_feature_point.virtual_point = np.array([x, y, z], dtype=float)
last_feature_point.label = 'ring_planar_projected'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line suggests that it is always a planar ring. I have an example where it sems to be just a ring, I'll share the example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants