This repository builds a pH-aware hydrogen dictionary from the Protein Data Bank Chemical Component Dictionary (CCD). The result is a compact SQLite database that can be queried by CCD compound ID and pH.
generate_json_from_ccd.pyreads a CCD file in CIF format and writes one JSON file per compound. For each compound, it selects the appropriate processing route, assigns a state at each sampled pH, and records the hydrogen geometry, charged atoms, and hydrogen-bond donors and acceptors. By default, it samples pH 0.0 to 14.0 in steps of 0.5. Some compounds have one fixed state across the full range.json_to_sql.pycollects those JSON files into one SQLite database. Consecutive pH values with the same state are stored as a single pH range, and hydrogen geometry and atom roles are compressed to keep the database small.
The second script should be run only after checking the report from the first. JSON files without usable states are not added to the database.
The latest CCD can be downloaded from the official wwPDB Chemical Component Dictionary page. Download the mmCIF file (components.cif.gz) and decompress it.
Run the commands from the repository directory in a Python environment containing the following packages: Gemmi, RDKit, molscrub, PyMOL and OpenBabel (these last two being optional but strongly recommended).
Generate JSON files from <dictionary>.cif (components.cif in the example) and save a detailed report:
python generate_json_from_ccd.py --ccd components.cif --outdir results --verboseUse --ph-step 0.1 to change the pH interval or --jobs 4 to use four worker processes. All options are listed by python generate_json_from_ccd.py --help.
Convert the generated JSON files into the database:
python json_to_sql.py --input-dir results --output db/ph-ccd.dbThe first script prints a summary at the end of the run. With --verbose, the same information and detailed processing messages are saved under reports/ as ccd_ph_dict_run_YYYYMMDD_HHMMSS.txt. The summary reports:
- Cleanly OK: completed without a recorded fallback or warning, grouped by processing route.
- OK with fallbacks: completed successfully using a recovery route.
- OK with warnings only: completed without a fallback, but with a diagnostic warning.
- Skipped / Errors: compounds that did not produce a usable result, with IDs and the main reasons.
- Diagnostic annotations: fallback and warning types, with the affected compound IDs. These groups can overlap.
After conversion, the second script reports the number of compounds and state/pH-range rows in the database, plus the total compressed sizes of the hydrogen-geometry and atom-role data.
The database contains one table, state_lookup. Query it with a CCD ID and a pH value from the grid used to build the database:
SELECT state_num, smiles, h_geom, atom_roles
FROM state_lookup
WHERE compound_id = ? AND ? BETWEEN ph_min AND ph_max
LIMIT 1;Python needs only its standard sqlite3, zlib, and json modules:
import json, sqlite3, zlib
with sqlite3.connect("db/ph-ccd.db") as db:
row = db.execute("""
SELECT state_num, smiles, h_geom, atom_roles
FROM state_lookup
WHERE compound_id = ? AND ? BETWEEN ph_min AND ph_max
LIMIT 1
""", ("ATP", 7.0)).fetchone()
if row:
state_num, smiles, h_blob, roles_blob = row
hydrogens = json.loads(zlib.decompress(h_blob))
atom_roles = json.loads(zlib.decompress(roles_blob))Each hydrogen entry is [name, bonded_atom, bond_length, bond_angle, angle_reference, dihedral, dihedral_reference]; some geometry values may be null. Atom roles are stored as {atom_name: bitmask}, where bits 1, 2, 4, and 8 mean donor, acceptor, cationic, and anionic, respectively. Use the atom names and geometry to place hydrogens, and test role bits with a bitwise AND. No returned row means that the compound or pH is not present.