Python package to run the SIFTS (Structure Integration with Function, Taxonomy and Sequences) pipeline locally.
Developed at EMBL-EBI by the PDBe team.
SIFTS provides residue-level mappings between structures and sequences. This package automates the full pipeline:
- Build a reference sequence database (MMseqs2 or BLASTP)
- Align structure sequences against it to identify the best match per chain (≥ 90% identity) according to the SIFTS scoring function
- Generate precise residue- and segment-level structure-sequence mappings via local alignment (FASTA36
lalign36) - Store results in a DuckDB database and per-entry CSV files
- Export mappings back into annotated mmCIF files
The whole pipeline can work on non-UniProt or non-PDB entries. However, it will use only the adjusted score to rank the hits.
The quickstart notebook also walks through the installation.
The following binaries must be installed and available on PATH:
| Tool | Purpose | Install |
|---|---|---|
| MMseqs2 | Fast global sequence search | conda install -c conda-forge mmseqs2 |
FASTA36 (lalign36) |
Local pairwise alignment | conda install -c bioconda fasta3 |
| BLAST+ | Optional alternative to MMseqs2 | conda install -c bioconda blast |
# Clone the repository
git clone https://github.com/PDBeurope/SIFTS
cd SIFTS
# Create environment from file
conda env create -f environment.yml
# Activate environment
conda activate pdbe_sifts
# Use the checked-out source in editable mode. This replaces the published
# pdbe-sifts package installed by environment.yml.
pip install -e .pip install pdbe-siftsThe external MMseqs2 and FASTA36 binaries must still be installed separately.
BLAST+ is required only when using --tool blastp.
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# Alternative
pip install uvgit clone https://github.com/PDBeurope/SIFTS
cd SIFTS# Create .venv and uv.lock, and install this checkout in editable mode
uv sync# macOS/Linux
source .venv/bin/activate
# Windows
.venv\Scripts\activateThe external binaries listed above are not installed by uv sync and must be
installed separately.
Requirements: Python ≥ 3.10 · 16 GB RAM minimum (32 GB+ recommended for large datasets)
Install BLAST+:
brew install blast # MacOS
sudo apt install ncbi-blast+ # LinuxOn Windows, download BLAST+ from the NCBI distribution directory.
Install MMseqs2:
brew install mmseqs2 # MacOS or Linux
# Windows
# Not supported yet (it is recommanded to use the linux subsytem from Windows.)Install FASTA36 from source (Linux):
git clone https://github.com/wrpearson/fasta36.git
cd fasta36/src
make -f ../make/Makefile.linux_sse2 all
# lalign36 is generated in fasta36/bin; make it available on PATH
export PATH="$(pwd)/../bin:$PATH"
# Windows
# Not supported yet (use the Windows Subsystem for Linux).You first need a fasta file containing sequences and a taxonomic mapping file. For that you can use the command:
pdbe_sifts prepare_build_db --output-fasta /my/path/to/myfile.fasta.gz --output-tax-mapping /my/path/to/taxonomy_mapping.tsvYou can also specify an existing FASTA file using --input-fasta. Without it,
the command downloads the UniProtKB/Swiss-Prot FASTA file by default.
This command assumes that the FASTA headers follow UniProtKB conventions.
If the FASTA is already in its final location and only the taxonomy mapping is needed, create it without copying or downloading the FASTA:
pdbe_sifts create_tax_file \
--input-fasta /my/path/to/myfile.fasta.gz \
--output-tax-mapping /my/path/to/taxonomy_mapping.tsvBoth plain and gzip-compressed FASTA files are accepted. Headers must use the
UniProtKB sp|... or tr|... format and contain OX=<taxid>. The output has
no header and contains one ACCESSION<TAB>TAXID row per compatible header.
pdbe_sifts init
# → creates ~/.config/pdbe_sifts/config.yaml
# → downloads the NCBI taxonomy database (~70 MB, first run only)
# → builds the UniProt–PDB cross-reference index and CCD mapping cacheEdit the config to set your paths (base_dir, nobackup_dir, target_db (after building it), etc.). You can also setup different alignment parameters.
base_dir: path to where you want the results to be saved
nobackup_dir:path to where you want the tmp files to be saved
target_db: path to the database created using build_db
If you already have a UniProtKB FASTA file, the complete path from that FASTA to a searchable database is:
pdbe_sifts create_tax_file \
--input-fasta /my/path/to/myfile.fasta.gz \
--output-tax-mapping /my/path/to/taxonomy_mapping.tsv
pdbe_sifts build_db \
-i /my/path/to/myfile.fasta.gz \
-o ./my/folder/prefix \
-t /my/path/to/taxonomy_mapping.tsv \
--tool mmseqs \
--threads 8create_tax_file requires UniProtKB headers in the format described above. For
a custom FASTA header format, create the headerless taxonomy TSV yourself, with
one sequence_id<TAB>tax_id row per sequence, and pass it directly to
build_db with -t.
build_db accepts a gzip-compressed FASTA when using MMseqs2. It creates
several database files using the value of -o as their path prefix. MMseqs2 is
the default backend; use --tool blastp to build a BLAST database instead.
# Single CIF entry
pdbe_sifts sequence_match -i 1abc.cif -o ./results -d ./my/db/folder/prefix
# Batch (one mmCIF path per line)
pdbe_sifts sequence_match -i entries.txt -o ./results -d ./my/db/folder/prefix --threads 8At this step you can also provide a .csv file to faster the scoring function. This CSV file must contains per row: row_num, uniprot_accession, dataset (Swiss-Prot or TrEMBL), pdb cross-references, annotation score.
This command produces hits.duckdb and hits_<entry>.tsv — scored and raw
sequence candidates per structure entity. The files are written under
{output_dir}/{tool}_{input_name}/; for example,
./results/mmseqs_entries/hits.duckdb.
# With DuckDB hits (from structure to sequence matching step)
pdbe_sifts segments \
-i 1abc.cif.gz \
-o ./segments \
-d ./results/mmseqs_1abc/hits.duckdb
# Custom FASTA mapping (headers: >{structure_id}|{auth_asym_id}|{sequence_id})
pdbe_sifts segments -i 1abc.cif.gz -o ./segments -m custom_seqs.fastaProduces per-entry gzip-compressed CSV files under {output_dir}/.
pdbe_sifts db_load \
-i ./segments/ \
-d ./results/mmseqs_1abc/hits.duckdbBulk-loads the segment and residue CSVs produced in step 4 into the sifts_xref_segment and sifts_xref_residue tables of the DuckDB file.
If you run the db_load command, you can use the command below because the -d argument requires two tables created by the db_load
step.
# Reading from DuckDB (after step 5)
pdbe_sifts sifts2mmcif \
-i 1abc.cif.gz \
-o ./sifts_mmcif \
-d ./results/mmseqs_1abc/hits.duckdbIf not please use:
# Or reading segment CSVs directly (skip step 5)
pdbe_sifts sifts2mmcif \
-i 1abc.cif.gz \
-o ./sifts_mmcif \
-s ./segments/This integrates the computed SIFTS annotations into the source PDBx/mmCIF file by populating the _pdbx_sifts_unp_segments. Additionally, the atom_site category is extended to include the sequence accession and residue number from the best available mapping, enabling each atomic coordinate to be associated directly with the corresponding mapped residue.
| Command | Description |
|---|---|
pdbe_sifts init |
Create the config and initialise the taxonomy, UniProt–PDB, and CCD caches |
pdbe_sifts show |
Print the fully resolved configuration |
pdbe_sifts update_ncbi |
Force-update the local NCBI taxonomy database (ete4) |
pdbe_sifts prepare_build_db |
Prepare FASTA and taxonomy mapping inputs for target database creation |
pdbe_sifts create_tax_file |
Extract an accession-to-taxid TSV from a UniProtKB FASTA file |
pdbe_sifts build_db |
Build a reference sequence database (MMseqs2 or BLASTP) from a FASTA file |
pdbe_sifts fasta_build |
Extract entity sequences from mmCIF files and write a FASTA |
pdbe_sifts sequence_match |
Align structure sequences against the reference DB; score and store hits in DuckDB |
pdbe_sifts segments |
Generate SIFTS mappings for a single mmCIF entry |
pdbe_sifts db_load |
Bulk-load segment/residue CSVs from segments generation into DuckDB |
pdbe_sifts sifts2mmcif |
Inject SIFTS mappings into an annotated mmCIF file |
pdbe_sifts update_ccd_mapping |
Check whether the remote CCD file is newer than the cached three-to-one letter mapping CSV and regenerate it if so |
pdbe_sifts seq2seq |
Align canonical deposited sequence vs coordinate sequence |
The pipeline classes can be used directly in Python scripts without going through the CLI.
from pdbe_sifts.sequence_match.target_database import TargetDb
TargetDb(
input_path="uniprot_sprot.fasta",
output_path="./my_db/target_db",
tax_mapping_file="taxonomy.tsv",
tool="mmseqs", # or "blastp"
threads=8,
).run()from pdbe_sifts.sifts_fasta_builder import FastaBuilder
fasta_path = FastaBuilder(
input_path="1abc.cif", # or .cif.gz, or a .txt file listing CIF paths
out_dir="./fasta/",
threads=4,
).build()from pdbe_sifts.sifts_sequence_match import SiftsSequenceMatch
SiftsSequenceMatch(
input_file="1abc.cif", # or .fasta, or a .txt list of CIF paths
out_dir="./results/",
db_file="./my_db/target_db",
tool="mmseqs", # or "blastp"
threads=8,
).process()
# → writes hits.duckdb and hits_<entry>.tsv under out_dir/mmseqs_1abc/from pdbe_sifts.sifts_segments_generation import SiftsAlign
# Mode 1: use scored hits from sequence_match
sa = SiftsAlign(
cif_file="1abc.cif",
out_dir="./segments/",
db_conn_str="hits.duckdb",
)
# Mode 2: provide a custom FASTA mapping
sa = SiftsAlign(
cif_file="1abc.cif",
out_dir="./segments/",
mapping_fasta="custom_seqs.fasta",
)
sa.process_entry("1abc")
if sa.conn:
sa.conn.close()
# → writes {out_dir}/1abc_seg.csv.gz
# {out_dir}/1abc_res.csv.gzimport duckdb
from pdbe_sifts.database.sifts_db_wrapper import SiftsDB
conn = duckdb.connect("hits.duckdb")
SiftsDB(conn).bulk_load_from_entries("./segments/")
conn.close()| File | Format | Content |
|---|---|---|
hits.duckdb |
DuckDB | Scored sequence accession candidates per structure entity |
hits_<entry>.tsv |
TSV | Raw MMseqs2 / BLASTP alignment hits |
Per entry, under {output_dir}:
| File | Format | Content |
|---|---|---|
{entry}_seg.csv.gz |
CSV (gzip) | One row per contiguous aligned range (structure ↔ sequence positions, identity, conflicts, chimera flag) |
{entry}_res.csv.gz |
CSV (gzip) | One row per mapped structure residue (auth seq id, sequence position, one-letter codes, observed flag) |
{entry}_nf90_seg.csv.gz |
CSV (gzip) | NF90 variant of the segment file (written when applicable) |
After running db_load, results are available in DuckDB tables sifts_xref_segment and sifts_xref_residue.
| Variable | Default | Description |
|---|---|---|
PDBE_SIFTS_CONFIG |
platform config directory | Custom path to the user configuration file |
SIFTS_LOG_LEVEL |
INFO |
Logging verbosity: DEBUG, INFO, WARNING, ERROR, CRITICAL |
SIFTS_N_PROC |
auto | Number of internal threads per worker (lalign36 jobs). Override manually to cap CPU use. |
SIFTS_NO_CACHE_ALL |
unset | If set (any value), disables the UniProt pickle cache and always fetches from the REST API. |
SLURM_CPUS_PER_TASK |
unset | Detected automatically on SLURM clusters. Used by get_allocated_cpus() to set the thread count when running under a SLURM job allocation. |
src/pdbe_sifts/
├── cli.py # CLI entry point (pdbe_sifts command)
├── sifts_sequence_match.py # Global mapping pipeline (SiftsSequenceMatch)
├── sifts_segments_generation.py # Single-entry segment generation (SiftsAlign)
├── sifts_fasta_builder.py # Extract sequences from mmCIF → FASTA (FastaBuilder)
├── sifts_database_loader.py # Standalone bulk-loader script (wraps SiftsDB)
├── config/ # OmegaConf configuration loading — defines load_config()
├── base/
│ ├── paths.py # All configuration getters (imports load_config() from config/)
│ ├── utils.py # UniProt fetch, CPU helpers, SiftsAction
│ ├── log.py # Logging setup (StreamHandler, coloredlogs)
│ └── exceptions.py # All custom exceptions (centralised)
├── database/
│ └── sifts_db_wrapper.py # SiftsDB: DuckDB schema + bulk loader
├── mmcif/ # mmCIF parsing (Entry, Chain, Entity, Residue, ChemComp)
├── sequence_match/
│ ├── target_database.py # Build MMseqs2 / BLAST reference database (TargetDb)
│ ├── mmseqs_search.py # MMseqs2 easy-search wrapper
│ ├── blastp.py # BLASTP wrapper
│ └── sequence_match_parser.py # Parse TSV hits, score, store in DuckDB
├── segments_generation/
│ └── alignment/ # lalign36 wrapper, isoform alignment, residue mapping
├── sifts_to_mmcif/ # Inject SIFTS data back into mmCIF files
├── unp/
│ └── unp.py # UniProt REST client, pickle cache, isoform handling
└── data/
└── default_config.yaml # Default configuration template (all tuneable params)
EMBL-EBI PDBe team: Adam Bellaiche, Preeti Choudhary, Sreenath Sasidharan Nair, Jennifer Fleming, Sameer Velankar
Apache-2.0