Skip to main content
Docs

Search guides and API endpoints, for example “Idempotency-Key” or “submit job”.

    Tools · Computational Biology

    Protein Structure Ensemble Comparator

    Compare predicted or designed protein structures by TM-score and RMSD, cluster them, and keep one representative per cluster.

    Updated October 20, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    The Protein Structure Ensemble Comparator triages a set of structures of one protein or complex: design candidates, prediction samples, or conformers. It scores every pair by TM-score and RMSD, groups near-identical structures into clusters, and keeps the most central member of each cluster as its representative. Use it to remove redundant designs before interface analysis or MD, or to see how much a set of predictions disagrees. It runs on CPU with Biotite.

    How it works

    1. Selection: each structure contributes the protein chains being compared. Monomer mode uses one chain (the first protein chain unless you name one); multichain mode joins several chains in a fixed order, and every structure must have them. Waters, ligands, and other hetero groups are ignored for scoring but kept in the written structures.
    2. Alignment: every pair is superimposed by sequence-independent structural alignment, so designs with different sequences can be compared.
    3. Scores: TM-score is normalized by each structure's length, and the pair keeps the lower of the two, so a short fragment does not look like a match to a full-length protein. RMSD is over the aligned residues, and coverage is aligned residues over the longer chain. An optional reference structure, such as a target or crystal structure, is also scored against every member.
    4. Clustering: structures are taken in order of their mean TM-score to the rest of the set (ties by input order). Each one not yet assigned starts a cluster and claims every unassigned structure at or above the TM-score threshold. The same input always gives the same clusters.
    5. Representatives: each cluster keeps its medoid, the member with the highest mean TM-score to the other members. The whole set's medoid is reported too, and every structure is written superposed onto it.

    Choosing a threshold

    TM-score thresholdGroups together
    0.5Structures with the same fold
    0.8 (default)Close variants of one structure, such as near-duplicate designs
    0.9 and aboveNear-identical copies

    TM-score is on a 0–1 scale and, unlike RMSD, does not grow with protein size, so one threshold works across ensembles of different proteins.

    Inputs

    PDB or CIF files, or a structure ensemble artifact from an upstream workflow step such as Boltz-2, BoltzGen, FreeBindCraft, OpenDDE, RFAntibody, or the MD Trajectory Analyzer. Only the first model of a multi-model file is compared. B-factors are kept, so predicted structures keep their pLDDT.

    Outputs

    FileContents
    pairs.csvEvery pair: TM-score (both normalizations and the pair score), RMSD, aligned residues, and coverage
    clusters.csvEvery structure: cluster, cluster size, whether it is the representative, scores to its representative and to the reference, and its cluster's mean TM-score and RMSD
    representatives/One representative structure per cluster, as PDB
    medoid.pdbThe medoid of the whole set
    aligned/Every input superposed onto the medoid
    summary.jsonCounts, chains, threshold, cluster membership, and warnings

    A structure that PDB cannot hold (an mmCIF with chain IDs longer than one character, or more than 99,999 atoms) is still compared, but its structure files are written as mmCIF (.cif), with a warning. Those files are download only and are not passed to later workflow steps.

    In a workflow, the representatives and the medoid carry TM-score and cluster-size metrics, so a filter step can keep, for example, only representatives of clusters with several members.

    Not covered

    • Clustering is greedy, so it depends on the threshold and is not a global optimum.
    • Multichain mode joins chains in the order given; it does not try chain permutations, so swapped copies in a homo-oligomer score lower.
    • A very short fragment compared with a much longer chain can fail to align and scores 0.
    • No database-wide structure search, no MD-frame clustering (use the MD Trajectory Analyzer), and no small-molecule comparison (use the RMSD Calculator).

    Representatives go on to the Interaction Profiler, MM-GBSA Rescoring, or OpenMM MD.

    Run it from the API

    Submit with Submit a job and the job_type below. Price it first with Estimate job reservation cost: submitting reserves that amount from your wallet, and the charge settles at the actual runtime.

    Protein Structure Ensemble Comparator protein-structure-cluster

    Job type
    protein-structure-cluster
    Hardware
    cpu (default)
    Typical runtime
    1 h on CPU

    Payload

    Payload fields
    FieldTypeDescription
    structuresrequiredfile_object[] | file_object
    referencefile_object
    tm_thresholdnumber

    Default: 0.8Limits: ≥ 0.3, ≤ 1

    modestring

    Default: "monomer"One of: "monomer", "multichain"

    chainstring

    Limits: pattern ^[A-Za-z0-9]{1,4}$

    chains[]string[]

    Limits: min items 1, max items 26

    File fields take {"name": "x.pdb", "content_b64": "…"} or a stored artifact, {"$artifact": {"id": "art-…", "port": "…"}}. Files are up to 25 MiB each.

    Example

    from cognichem_client import CogniChem
    
    client = CogniChem.from_env()  # reads COGNICHEM_API_KEY
    payload = {
        "structures": [
            {
                "name": "design_01.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "design_02.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "design_03.pdb",
                "content_b64": "<file contents, base64>",
            },
        ],
        "tm_threshold": 0.5,
        "mode": "monomer",
    }
    
    estimate = client.jobs.estimate(job_type="protein-structure-cluster", payload=payload, resource="cpu")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-protein-structure-cluster-run",
        job_type="protein-structure-cluster",
        payload=payload,
        resource="cpu",
    )
    status = client.jobs.wait(job.process_id)
    if status.status == "completed":
        client.jobs.result(job.process_id, save_path=".")

    Sample data from the job catalog; long values are shortened here. Each job_name must be unique among your jobs.

    Workflow inputs

    • Protein structure · optionalPDB, CIF
    • Structures (list)PDB, CIF

    Workflow outputs

    • ArchiveZIP
    • TableCSV
    • Protein structurePDB
    • TableCSV
    • StructuresPDB