diff --git a/tools/rdkit/.shed.yml b/tools/rdkit/.shed.yml
new file mode 100644
index 000000000..193e16b4d
--- /dev/null
+++ b/tools/rdkit/.shed.yml
@@ -0,0 +1,19 @@
+name: rdkit
+owner: recetox
+remote_repository_url: "https://github.com/RECETOX/galaxytools/tree/master/tools/rdkit"
+homepage_url: "https://www.rdkit.org"
+categories:
+ - Computational chemistry
+description: Calculate structural similarity using RDKit fingerprints from SMILES/InChI/SDF files
+long_description: |
+ This tool calculates structural similarity between compounds using RDKit molecular
+ fingerprints. It accepts SMILES (.smi), InChI (.inchi), or SDF (.sdf) files and
+ supports multiple fingerprint types (Morgan, RDKit, MACCS) and similarity metrics
+ (Tanimoto, Dice, Cosine, Soergel, Kulczynski, McConnaughey).
+auto_tool_repositories:
+ name_template: "{{ tool_id }}"
+ description_template: "{{ tool_name }} tool from the RDKit package"
+suite:
+ name: suite_rdkit
+ description: Calculate structural similarity using RDKit fingerprints from SMILES/InChI/SDF files
+ type: repository_suite_definition
diff --git a/tools/rdkit/macros.xml b/tools/rdkit/macros.xml
new file mode 100644
index 000000000..1451c883e
--- /dev/null
+++ b/tools/rdkit/macros.xml
@@ -0,0 +1,65 @@
+
+ 2026.03.3
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ RDKit
+
+
+
+
+
+ topic_2258
+ topic_0091
+
+
+ operation_2483
+
+
+
+
+
+
+@article{RDKit_2024,
+ author = {Landrum, Gregory and others},
+ title = {{RDKit: Open-source cheminformatics}},
+ year = {2024},
+ url = {https://www.rdkit.org}
+}
+
+ 10.1186/1758-2946-3-33
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/rdkit/rdkit_structsim.xml b/tools/rdkit/rdkit_structsim.xml
new file mode 100644
index 000000000..0855dbbf9
--- /dev/null
+++ b/tools/rdkit/rdkit_structsim.xml
@@ -0,0 +1,184 @@
+
+ calculate structural similarity using RDKit fingerprints from SMILES/InChI/SDF files
+
+ macros.xml
+
+
+
+
+
+
+
+
+ rdkit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/rdkit/rdkit_structsim_wrapper.py b/tools/rdkit/rdkit_structsim_wrapper.py
new file mode 100644
index 000000000..e7b1bc3a6
--- /dev/null
+++ b/tools/rdkit/rdkit_structsim_wrapper.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python
+"""
+RDKit Structural Similarity Calculator
+
+This script calculates structural similarity between compounds using RDKit fingerprints.
+It accepts SMILES, InChI, or SDF files and outputs a table with similarity scores
+and the input structures.
+"""
+
+import argparse
+import logging
+import re
+import sys
+from typing import Callable, List, Optional, Tuple
+
+from rdkit import Chem, DataStructs
+from rdkit.Chem import AllChem, MACCSkeys, rdFingerprintGenerator
+
+logger = logging.getLogger(__name__)
+
+
+# Morgan fingerprint generator (ECFP-like) - using new API
+_morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
+
+
+def get_morgan_fingerprint(mol: Chem.Mol):
+ """Generate Morgan fingerprint for a molecule."""
+ return _morgan_gen.GetFingerprint(mol)
+
+
+def get_rdkit_fingerprint(mol: Chem.Mol):
+ """Generate RDKit fingerprint for a molecule."""
+ return AllChem.RDKFingerprint(mol, maxPath=7, fpSize=2048)
+
+
+def get_maccs_fingerprint(mol: Chem.Mol):
+ """Generate MACCS keys fingerprint for a molecule."""
+ return MACCSkeys.GenMACCSKeys(mol)
+
+
+def get_fingerprint(mol: Chem.Mol, fingerprint_type: str):
+ """
+ Generate fingerprint for a molecule based on specified type.
+
+ Args:
+ mol: RDKit Mol object
+ fingerprint_type: Type of fingerprint ("Morgan", "RDKit", or "MACCS")
+
+ Returns:
+ Fingerprint bit vector or None if molecule is invalid
+ """
+ if mol is None:
+ return None
+
+ if fingerprint_type == "Morgan":
+ return get_morgan_fingerprint(mol)
+ elif fingerprint_type == "RDKit":
+ return get_rdkit_fingerprint(mol)
+ elif fingerprint_type == "MACCS":
+ return get_maccs_fingerprint(mol)
+ else:
+ raise ValueError(f"Unknown fingerprint type: {fingerprint_type}")
+
+
+def detect_structure_type(structure_str: str) -> Optional[str]:
+ """
+ Detect whether a string is a SMILES or InChI representation.
+
+ Args:
+ structure_str: Structure string to analyze
+
+ Returns:
+ 'SMILES', 'InChI', or None if undetectable
+ """
+ if not structure_str:
+ return None
+
+ structure_str = str(structure_str).strip()
+
+ # Check for InChI prefix
+ if structure_str.startswith("InChI=") or structure_str.startswith("InChI"):
+ return "InChI"
+
+ # Simple heuristic for SMILES: contains common organic element symbols
+ # and doesn't start with InChI
+ smiles_pattern = r'^[CNOcSsNnPpFxClBrIa-zA-Z0-9@+\-\[\]()\\/=]+$'
+ if re.match(smiles_pattern, structure_str) and len(structure_str) > 1:
+ return "SMILES"
+
+ return None
+
+
+def parse_structure(structure_str: str) -> Optional[Tuple[Chem.Mol, str]]:
+ """
+ Parse a SMILES or InChI string into an RDKit Mol object.
+ Auto-detects the structure type.
+
+ Args:
+ structure_str: SMILES or InChI string
+
+ Returns:
+ Tuple of (RDKit Mol object, detected type) or None if parsing fails
+ """
+ if not structure_str:
+ return None
+
+ structure_str = str(structure_str).strip()
+
+ # Try to detect type
+ detected_type = detect_structure_type(structure_str)
+
+ if detected_type == "InChI":
+ mol = Chem.MolFromInchi(structure_str)
+ if mol is not None:
+ return (mol, "InChI")
+ elif detected_type == "SMILES":
+ mol = Chem.MolFromSmiles(structure_str)
+ if mol is not None:
+ return (mol, "SMILES")
+ else:
+ # Try SMILES first, then InChI
+ mol = Chem.MolFromSmiles(structure_str)
+ if mol is not None:
+ return (mol, "SMILES")
+
+ mol = Chem.MolFromInchi(structure_str)
+ if mol is not None:
+ return (mol, "InChI")
+
+ logger.warning(f"Could not parse structure: {structure_str[:50]}...")
+ return None
+
+
+def calculate_similarity(fp1, fp2, metric: str) -> float:
+ """
+ Calculate similarity between two fingerprints using the specified metric.
+
+ Args:
+ fp1: First fingerprint
+ fp2: Second fingerprint
+ metric: Similarity metric name
+
+ Returns:
+ Similarity score (0-1)
+ """
+ if metric == "tanimoto":
+ return DataStructs.TanimotoSimilarity(fp1, fp2)
+ elif metric == "dice":
+ return DataStructs.DiceSimilarity(fp1, fp2)
+ elif metric == "cosine":
+ return DataStructs.CosineSimilarity(fp1, fp2)
+ elif metric == "soergel":
+ return DataStructs.SoergelSimilarity(fp1, fp2)
+ elif metric == "kulczynski":
+ return DataStructs.KulczynskiSimilarity(fp1, fp2)
+ elif metric == "mcconnaughey":
+ return DataStructs.McConnaugheySimilarity(fp1, fp2)
+ else:
+ raise ValueError(f"Unknown similarity metric: {metric}")
+
+
+def load_compounds_from_smi_inchi(filepath: str) -> Tuple[List[Tuple[str, Chem.Mol]], str]:
+ """
+ Load compounds from SMI or INCHI file.
+
+ Args:
+ filepath: Path to input file
+
+ Returns:
+ Tuple of (list of (structure_string, Mol) tuples, detected structure type)
+ """
+ compounds = []
+ detected_type = None
+
+ with open(filepath, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue # Skip empty lines and comments
+
+ result = parse_structure(line)
+ if result:
+ mol, struct_type = result
+ if detected_type is None:
+ detected_type = struct_type
+ compounds.append((line, mol))
+
+ if not compounds:
+ raise ValueError(f"No valid compounds found in {filepath}!")
+
+ logger.info(f"Loaded {len(compounds)} compounds from {filepath} ({detected_type})")
+ return compounds, detected_type
+
+
+def load_compounds_from_sdf(filepath: str) -> Tuple[List[Tuple[str, Chem.Mol]], str]:
+ """
+ Load compounds from SDF file. Extracts SMILES from the data block.
+
+ Args:
+ filepath: Path to SDF file
+
+ Returns:
+ Tuple of (list of (structure_string, Mol) tuples, detected structure type)
+ """
+ compounds = []
+ detected_type = None
+
+ suppl = Chem.SDMolSupplier(filepath, removeHs=False)
+
+ for mol in suppl:
+ if mol is None:
+ continue
+
+ # Try to get SMILES from the molecule
+ smiles = Chem.MolToSmiles(mol)
+ if smiles:
+ compounds.append((smiles, mol))
+ if detected_type is None:
+ detected_type = "SMILES"
+
+ if not compounds:
+ raise ValueError(f"No valid compounds found in {filepath}!")
+
+ logger.info(f"Loaded {len(compounds)} compounds from {filepath} ({detected_type})")
+ return compounds, detected_type
+
+
+# Mapping from file type to loader function
+FILE_LOADERS: dict[str, Callable[[str], Tuple[List[Tuple[str, Chem.Mol]], str]]] = {
+ "smi": load_compounds_from_smi_inchi,
+ "inchi": load_compounds_from_smi_inchi,
+ "sdf": load_compounds_from_sdf,
+}
+
+
+def main(argv):
+ parser = argparse.ArgumentParser(
+ description="Calculate structural similarity between compounds using RDKit"
+ )
+
+ parser.add_argument(
+ "--queries", type=str, required=True,
+ help="Path to query compounds file"
+ )
+ parser.add_argument(
+ "--queries-type", type=str, required=True, choices=["smi", "inchi", "sdf"],
+ help="Format of query compounds file (smi, inchi, or sdf)"
+ )
+ parser.add_argument(
+ "--references", type=str, required=True,
+ help="Path to reference compounds file"
+ )
+ parser.add_argument(
+ "--references-type", type=str, required=True, choices=["smi", "inchi", "sdf"],
+ help="Format of reference compounds file (smi, inchi, or sdf)"
+ )
+ parser.add_argument(
+ "--similarity-metric", type=str, default="tanimoto",
+ choices=["tanimoto", "dice", "cosine", "soergel", "kulczynski", "mcconnaughey"],
+ help="Similarity metric to use (default: tanimoto)"
+ )
+ parser.add_argument(
+ "--fingerprint-type", type=str, default="Morgan",
+ choices=["Morgan", "RDKit", "MACCS"],
+ help="Type of fingerprint to use (default: Morgan)"
+ )
+ parser.add_argument(
+ "--output", type=str, required=True,
+ help="Output TSV file path for similarity results"
+ )
+
+ args = parser.parse_args(argv)
+
+ try:
+ # Get loader functions based on file types
+ query_loader = FILE_LOADERS.get(args.queries_type)
+ ref_loader = FILE_LOADERS.get(args.references_type)
+
+ if query_loader is None:
+ raise ValueError(f"Unsupported query file type: {args.queries_type}")
+ if ref_loader is None:
+ raise ValueError(f"Unsupported reference file type: {args.references_type}")
+
+ # Load compounds using the specified loaders (ignoring file extension)
+ logger.info("Loading query compounds...")
+ query_compounds, query_type = query_loader(args.queries)
+
+ logger.info("Loading reference compounds...")
+ ref_compounds, ref_type = ref_loader(args.references)
+
+ # Determine output structure type preference
+ output_type = query_type if query_type else ref_type
+ logger.info(f"Using structure type: {output_type}")
+
+ # Generate fingerprints
+ logger.info(f"Generating {args.fingerprint_type} fingerprints...")
+ query_fps = [get_fingerprint(mol, args.fingerprint_type) for _, mol in query_compounds]
+ ref_fps = [get_fingerprint(mol, args.fingerprint_type) for _, mol in ref_compounds]
+
+ # Remove invalid entries
+ valid_queries = [(comp[0], fp) for comp, fp in zip(query_compounds, query_fps) if fp is not None]
+ valid_refs = [(comp[0], fp) for comp, fp in zip(ref_compounds, ref_fps) if fp is not None]
+
+ if not valid_queries:
+ raise ValueError("No valid query compounds with usable fingerprints!")
+ if not valid_refs:
+ raise ValueError("No valid reference compounds with usable fingerprints!")
+
+ logger.info(
+ f"Valid compounds - Queries: {len(valid_queries)}, References: {len(valid_refs)}"
+ )
+
+ logger.info(f"Calculating {args.similarity_metric} similarity...")
+
+ # Calculate all pairwise similarities and build output
+ results = []
+ for q_struct, q_fp in valid_queries:
+ for r_struct, r_fp in valid_refs:
+ sim = calculate_similarity(q_fp, r_fp, args.similarity_metric)
+ results.append({
+ "query_structure": q_struct,
+ "reference_structure": r_struct,
+ "similarity": sim
+ })
+
+ # Create output - write directly to avoid pandas dependency
+ with open(args.output, 'w') as f:
+ f.write("similarity\tquery_structure\treference_structure\n")
+ for r in results:
+ f.write(f"{r['similarity']}\t{r['query_structure']}\t{r['reference_structure']}\n")
+
+ logger.info(f"Similarity results written to {args.output}")
+ logger.info(f"Total comparisons: {len(results)}")
+
+ except Exception as e:
+ logger.error(f"Error: {e}")
+ raise
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/rdkit/test-data/output_cosine_maccs.tsv b/tools/rdkit/test-data/output_cosine_maccs.tsv
new file mode 100644
index 000000000..2e4062638
--- /dev/null
+++ b/tools/rdkit/test-data/output_cosine_maccs.tsv
@@ -0,0 +1,11 @@
+similarity query_structure reference_structure
+1.0 CC(=O)Oc1ccccc1C(=O)O CC(=O)Oc1ccccc1C(O)=O
+0.6900655593423543 CC(=O)Oc1ccccc1C(=O)O Cc1ccc(cc1)C(=O)O
+0.6900655593423543 Cc1ccc(C(=O)O)cc1 CC(=O)Oc1ccccc1C(O)=O
+1.0 Cc1ccc(C(=O)O)cc1 Cc1ccc(cc1)C(=O)O
+0.6172133998483676 CC(C)Cc1ccccc1C(C)C(=O)O CC(=O)Oc1ccccc1C(O)=O
+0.7453559924999299 CC(C)Cc1ccccc1C(C)C(=O)O Cc1ccc(cc1)C(=O)O
+0.4504426164614508 Cn1c(=O)c2c(ncn2C)n(C)c1=O CC(=O)Oc1ccccc1C(O)=O
+0.3263766828841098 Cn1c(=O)c2c(ncn2C)n(C)c1=O Cc1ccc(cc1)C(=O)O
+0.6900655593423543 Oc1ccccc1 CC(=O)Oc1ccccc1C(O)=O
+0.6 Oc1ccccc1 Cc1ccc(cc1)C(=O)O
diff --git a/tools/rdkit/test-data/output_dice_rdkit.tsv b/tools/rdkit/test-data/output_dice_rdkit.tsv
new file mode 100644
index 000000000..79af9fa65
--- /dev/null
+++ b/tools/rdkit/test-data/output_dice_rdkit.tsv
@@ -0,0 +1,6 @@
+similarity query_structure reference_structure
+1.0 InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12) CC(=O)Oc1ccccc1C(=O)O
+0.44258872651356995 InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12) Cc1ccc(C(=O)O)cc1
+0.44145873320537427 InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12) O=C(O)c1cccc(C(=O)O)c1
+0.2978723404255319 InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12) CN(C)C(=O)c1ccccc1
+0.45414847161572053 InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12) O=C(O)c1ccccc1
diff --git a/tools/rdkit/test-data/output_tanimoto_morgan.tsv b/tools/rdkit/test-data/output_tanimoto_morgan.tsv
new file mode 100644
index 000000000..dd75371f3
--- /dev/null
+++ b/tools/rdkit/test-data/output_tanimoto_morgan.tsv
@@ -0,0 +1,26 @@
+similarity query_structure reference_structure
+1.0 CC(=O)Oc1ccccc1C(=O)O CC(=O)Oc1ccccc1C(=O)O
+0.28125 CC(=O)Oc1ccccc1C(=O)O Cc1ccc(C(=O)O)cc1
+0.2903225806451613 CC(=O)Oc1ccccc1C(=O)O O=C(O)c1cccc(C(=O)O)c1
+0.30303030303030304 CC(=O)Oc1ccccc1C(=O)O CN(C)C(=O)c1ccccc1
+0.35714285714285715 CC(=O)Oc1ccccc1C(=O)O O=C(O)c1ccccc1
+0.28125 Cc1ccc(C(=O)O)cc1 CC(=O)Oc1ccccc1C(=O)O
+1.0 Cc1ccc(C(=O)O)cc1 Cc1ccc(C(=O)O)cc1
+0.43478260869565216 Cc1ccc(C(=O)O)cc1 O=C(O)c1cccc(C(=O)O)c1
+0.3333333333333333 Cc1ccc(C(=O)O)cc1 CN(C)C(=O)c1ccccc1
+0.55 Cc1ccc(C(=O)O)cc1 O=C(O)c1ccccc1
+0.24390243902439024 CC(C)Cc1ccccc1C(C)C(=O)O CC(=O)Oc1ccccc1C(=O)O
+0.2222222222222222 CC(C)Cc1ccccc1C(C)C(=O)O Cc1ccc(C(=O)O)cc1
+0.22857142857142856 CC(C)Cc1ccccc1C(C)C(=O)O O=C(O)c1cccc(C(=O)O)c1
+0.2777777777777778 CC(C)Cc1ccccc1C(C)C(=O)O CN(C)C(=O)c1ccccc1
+0.28125 CC(C)Cc1ccccc1C(C)C(=O)O O=C(O)c1ccccc1
+0.08888888888888889 Cn1c(=O)c2c(ncn2C)n(C)c1=O CC(=O)Oc1ccccc1C(=O)O
+0.10526315789473684 Cn1c(=O)c2c(ncn2C)n(C)c1=O Cc1ccc(C(=O)O)cc1
+0.07894736842105263 Cn1c(=O)c2c(ncn2C)n(C)c1=O O=C(O)c1cccc(C(=O)O)c1
+0.1 Cn1c(=O)c2c(ncn2C)n(C)c1=O CN(C)C(=O)c1ccccc1
+0.08333333333333333 Cn1c(=O)c2c(ncn2C)n(C)c1=O O=C(O)c1ccccc1
+0.25 Oc1ccccc1 CC(=O)Oc1ccccc1C(=O)O
+0.21739130434782608 Oc1ccccc1 Cc1ccc(C(=O)O)cc1
+0.2857142857142857 Oc1ccccc1 O=C(O)c1cccc(C(=O)O)c1
+0.30434782608695654 Oc1ccccc1 CN(C)C(=O)c1ccccc1
+0.3888888888888889 Oc1ccccc1 O=C(O)c1ccccc1
diff --git a/tools/rdkit/test-data/queries.inchi b/tools/rdkit/test-data/queries.inchi
new file mode 100644
index 000000000..efe1f68a5
--- /dev/null
+++ b/tools/rdkit/test-data/queries.inchi
@@ -0,0 +1,2 @@
+InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12)
+InChI=1S/C8H8O2/c1-7(9)5-6-8(2)10/h5-6H,1H3,(H,9,10)
\ No newline at end of file
diff --git a/tools/rdkit/test-data/queries.sdf b/tools/rdkit/test-data/queries.sdf
new file mode 100644
index 000000000..86d9cce93
--- /dev/null
+++ b/tools/rdkit/test-data/queries.sdf
@@ -0,0 +1,149 @@
+Aspirin
+ RDKit 2D
+
+ 13 13 0 0 0 0 0 0 0 0999 V2000
+ 5.2500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 -2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 3.8971 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 2 0
+ 2 4 1 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 7 8 2 0
+ 8 9 1 0
+ 9 10 2 0
+ 10 11 1 0
+ 11 12 1 0
+ 11 13 2 0
+ 10 5 1 0
+M END
+$$$$
+p-Toluenecarboxylic acid
+ RDKit 2D
+
+ 10 10 0 0 0 0 0 0 0 0999 V2000
+ 3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.7500 -1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.7500 1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 1 0
+ 3 4 2 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 5 8 1 0
+ 8 9 2 0
+ 8 10 1 0
+ 7 2 2 0
+M END
+$$$$
+Ibuprofen
+ RDKit 2D
+
+ 15 15 0 0 0 0 0 0 0 0999 V2000
+ 5.2500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 -2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 3.8971 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 3.8971 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 1 0
+ 2 4 1 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 7 8 2 0
+ 8 9 1 0
+ 9 10 2 0
+ 10 11 1 0
+ 11 12 1 0
+ 11 13 1 0
+ 13 14 1 0
+ 13 15 2 0
+ 10 5 1 0
+M END
+$$$$
+Caffeine
+ RDKit 2D
+
+ 14 15 0 0 0 0 0 0 0 0999 V2000
+ 2.7760 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.2760 0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.3943 1.2135 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.0323 0.7500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.0323 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.3943 -1.2135 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7062 -2.6807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 2.1328 -3.1443 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.4086 -3.6844 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.8351 -3.2209 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -2.9499 -4.2246 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -2.1470 -1.7537 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.5736 -1.2902 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.0967 -5.1517 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 1 0
+ 3 4 2 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 7 8 2 0
+ 7 9 1 0
+ 9 10 1 0
+ 10 11 2 0
+ 10 12 1 0
+ 12 13 1 0
+ 9 14 1 0
+ 6 2 1 0
+ 12 5 1 0
+M END
+$$$$
+Phenol
+ RDKit 2D
+
+ 7 7 0 0 0 0 0 0 0 0999 V2000
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.0000 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 2 0
+ 2 3 1 0
+ 3 4 2 0
+ 4 5 1 0
+ 5 6 2 0
+ 4 7 1 0
+ 6 1 1 0
+M END
+$$$$
diff --git a/tools/rdkit/test-data/references.sdf b/tools/rdkit/test-data/references.sdf
new file mode 100644
index 000000000..74ee0f005
--- /dev/null
+++ b/tools/rdkit/test-data/references.sdf
@@ -0,0 +1,140 @@
+Aspirin
+ RDKit 2D
+
+ 13 13 0 0 0 0 0 0 0 0999 V2000
+ 5.2500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 -2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 3.8971 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 2 0
+ 2 4 1 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 7 8 2 0
+ 8 9 1 0
+ 9 10 2 0
+ 10 11 1 0
+ 11 12 1 0
+ 11 13 2 0
+ 10 5 1 0
+M END
+$$$$
+p-Toluenecarboxylic acid
+ RDKit 2D
+
+ 10 10 0 0 0 0 0 0 0 0999 V2000
+ 3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.7500 -1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.7500 1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 1 0
+ 3 4 2 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 5 8 1 0
+ 8 9 2 0
+ 8 10 1 0
+ 7 2 2 0
+M END
+$$$$
+Phthalic acid
+ RDKit 2D
+
+ 12 12 0 0 0 0 0 0 0 0999 V2000
+ 3.7500 1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 -1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.0000 2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 3.8971 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 2 0
+ 2 3 1 0
+ 2 4 1 0
+ 4 5 1 0
+ 5 6 2 0
+ 6 7 1 0
+ 7 8 2 0
+ 8 9 1 0
+ 8 10 1 0
+ 10 11 2 0
+ 10 12 1 0
+ 9 4 2 0
+M END
+$$$$
+N,N-Dimethylbenzamide
+ RDKit 2D
+
+ 11 11 0 0 0 0 0 0 0 0999 V2000
+ 5.2500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 -1.2990 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 -2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 3.7500 1.2990 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 1 0
+ 2 4 1 0
+ 4 5 2 0
+ 4 6 1 0
+ 6 7 2 0
+ 7 8 1 0
+ 8 9 2 0
+ 9 10 1 0
+ 10 11 2 0
+ 11 6 1 0
+M END
+$$$$
+Benzoic acid
+ RDKit 2D
+
+ 9 9 0 0 0 0 0 0 0 0999 V2000
+ 1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ 0.7500 1.2990 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -1.5000 -2.5981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
+ -0.7500 -3.8971 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ -3.0000 -2.5981 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
+ 1 2 1 0
+ 2 3 2 0
+ 3 4 1 0
+ 4 5 2 0
+ 5 6 1 0
+ 3 7 1 0
+ 7 8 2 0
+ 7 9 1 0
+ 6 1 2 0
+M END
+$$$$
diff --git a/tools/rdkit/test-data/references.smi b/tools/rdkit/test-data/references.smi
new file mode 100644
index 000000000..fc06e3e88
--- /dev/null
+++ b/tools/rdkit/test-data/references.smi
@@ -0,0 +1,2 @@
+CC(=O)Oc1ccccc1C(O)=O
+Cc1ccc(cc1)C(=O)O
\ No newline at end of file