Skip to main content
Docs

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

    Tools · Computational Biology

    Structure QC Report

    Grade protein model geometry (Ramachandran and rotamer outliers, clashes, cis-peptides, Cβ deviation, ligand bond geometry) before docking, MM-GBSA, or model training.

    Updated October 26, 2026

    On this page

    Prices, workflows, and method papersOpen in the app

    The Structure QC Report grades the geometry of protein models, one model at a time: predicted complexes, designs, MD frames, or crystal structures. It catches construction failures (a distorted helix, side chains driven into each other, a peptide bond flipped to cis, a mangled ligand) before those models reach expensive steps such as docking, MM-GBSA, or model training. It never changes coordinates. It runs on CPU with Biopython, SciPy, and RDKit.

    How it works

    Only heavy atoms are graded; hydrogens are not placed. Every definition below is CogniChem's own, so the numbers will not match MolProbity's.

    1. Ramachandran: each residue's φ / ψ pair is classed favored, allowed, or outlier against hand-drawn regions for four residue classes (general, glycine, proline, and the residue before a proline). The regions follow the published shapes of the favored and allowed areas; no reference contour data is used.
    2. Rotamers: each side-chain χ angle must sit within 40° of a canonical staggered position (or, for aromatic rings, a perpendicular or m−30 position). Angles with naturally broad distributions are not graded.
    3. Clashes: two heavy atoms clash when their van der Waals radii overlap by 0.4 Å or more. Atoms up to four bonds apart, N / O pairs at hydrogen-bond distance, and metal–ligand contacts are exempt. The clashscore is clashes per 1,000 heavy atoms.
    4. Peptide bonds: ω near 0° on a residue other than proline is a non-Pro cis-peptide; ω more than 30° from planar trans is a twisted peptide.
    5. Cβ deviation: the distance of each Cβ from its ideal position built from the backbone; more than 0.25 Å is an outlier.
    6. Ligands: each ligand's bonds and angles are compared with a copy of the same molecule minimized with the MMFF94 force field, as Z-scores. Bond orders are read from the ligand's own geometry. Ions, waters, common crystallization additives, DNA / RNA nucleotides, and modified amino acids (such as selenomethionine or phosphoserine, which are graded as protein instead) are not graded as ligands.

    Reading the grades

    Each model gets pass, warn, or fail per metric, plus a count of failed metrics (qc_fail_count) and an overall status. The default levels are versioned and recorded with every run; you can override any warn or fail level, or what counts as one outlier. For predicted models, treat B-factors as pLDDT to flag residues under a confidence cutoff instead of penalizing them: low-confidence regions are marked in the offender list, and the metrics still count them.

    Inputs

    PDB or CIF files, or structures from an upstream workflow step: a structure ensemble from Boltz-2, FreeBindCraft, or RFAntibody, or a single structure from ESMFold2 or OpenMM MD. Only the first model of a multi-model file is graded. A file that cannot be graded (unreadable, no protein in the selected chains, or over 5,000 residues) gets a failed row with the reason in error; the rest of the batch still runs.

    Outputs

    FileContents
    qc.csvOne row per model: size, every metric, each metric's status, warn and fail counts, the overall status, and error for a model that could not be graded
    offenders.csvOne row per flagged residue or ligand term: chain, residue, metric, value, detail, severity, and whether it is low-confidence, worst first
    structures/Each graded input passed through unchanged, every chain and model included (CIF inputs written as PDB)
    summary.jsonThresholds and their version, status counts, median and maximum per metric across the batch, software versions, and warnings

    In a workflow

    The graded models come out as a structure ensemble that carries their QC metrics, so a filter step can gate on them: for example, keep only models with no failed metric (qc_fail_count at most 0) before MM-GBSA Rescoring, or only models without non-Pro cis-peptides before the Protein Structure Ensemble Comparator.

    Not covered

    • Geometry quality is not binding plausibility: a model can pass every check and still bind the wrong way.
    • Heavy atoms only: some real hydrogen-bond contacts can read as clashes, and clashes that involve hydrogens are not seen.
    • Ligand Z-scores use each ligand's own geometry to assign bond orders, so a badly distorted ligand can be read with the wrong chemistry; its Z-scores are still large. This is not a comparison with the Chemical Component Dictionary.
    • Grading only: no side-chain rebuilding or repair (use the Protein Preparer), no RNA or DNA validation, and no docking-pose quality (use the Docking Protocol Validator).

    Models that pass go on to MM-GBSA Rescoring, the Interaction Profiler, AutoDock Vina, or GNINA; the Protein Preparer fixes structures before docking.

    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.

    Structure QC Report structure-qc

    Job type
    structure-qc
    Hardware
    cpu (default)
    Typical runtime
    30 min on CPU

    Payload

    Provide at least one of: structures, structure.

    Payload fields
    FieldTypeDescription
    structuresfile_object[] | file_object
    structurefile_object
    chains[]string[]

    Limits: min items 1, max items 26

    include_ligandsboolean

    Default: true

    bfactor_is_plddtboolean

    Default: false

    parametersobject
    parameters.clash_overlap_angstromnumber

    Default: 0.4Limits: ≥ 0.2, ≤ 1

    parameters.cbeta_deviation_angstromnumber

    Default: 0.25Limits: ≥ 0.1, ≤ 1

    parameters.cis_omega_degreesnumber

    Default: 30Limits: ≥ 10, ≤ 60

    parameters.omega_twist_degreesnumber

    Default: 30Limits: ≥ 10, ≤ 60

    parameters.ligand_z_outliernumber

    Default: 4Limits: ≥ 2, ≤ 10

    parameters.plddt_cutoffnumber

    Default: 50Limits: ≥ 0, ≤ 100

    thresholdsobject
    thresholds.clashscoreobject

    Default: {"warn":10,"fail":20}

    thresholds.clashscore.warnnumber

    Limits: ≥ 0

    thresholds.clashscore.failnumber

    Limits: ≥ 0

    thresholds.rama_outlier_pctobject

    Default: {"warn":0.5,"fail":2}

    thresholds.rama_outlier_pct.warnnumber

    Limits: ≥ 0, ≤ 100

    thresholds.rama_outlier_pct.failnumber

    Limits: ≥ 0, ≤ 100

    thresholds.rama_favored_pctobject

    Default: {"warn":95,"fail":90}

    thresholds.rama_favored_pct.warnnumber

    Limits: ≥ 0, ≤ 100

    thresholds.rama_favored_pct.failnumber

    Limits: ≥ 0, ≤ 100

    thresholds.rotamer_outlier_pctobject

    Default: {"warn":2,"fail":5}

    thresholds.rotamer_outlier_pct.warnnumber

    Limits: ≥ 0, ≤ 100

    thresholds.rotamer_outlier_pct.failnumber

    Limits: ≥ 0, ≤ 100

    thresholds.cbeta_outlier_countobject

    Default: {"warn":1,"fail":5}

    thresholds.cbeta_outlier_count.warnnumber

    Limits: ≥ 0

    thresholds.cbeta_outlier_count.failnumber

    Limits: ≥ 0

    thresholds.cis_nonpro_countobject

    Default: {"warn":1,"fail":3}

    thresholds.cis_nonpro_count.warnnumber

    Limits: ≥ 0

    thresholds.cis_nonpro_count.failnumber

    Limits: ≥ 0

    thresholds.omega_twist_countobject

    Default: {"warn":1,"fail":5}

    thresholds.omega_twist_count.warnnumber

    Limits: ≥ 0

    thresholds.omega_twist_count.failnumber

    Limits: ≥ 0

    thresholds.ligand_geometry_outlier_countobject

    Default: {"warn":1,"fail":3}

    thresholds.ligand_geometry_outlier_count.warnnumber

    Limits: ≥ 0

    thresholds.ligand_geometry_outlier_count.failnumber

    Limits: ≥ 0

    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": "crambin_clean.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "crambin_clash.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "crambin_cis_nonpro.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "crambin_rama_outlier.pdb",
                "content_b64": "<file contents, base64>",
            },
            {
                "name": "trypsin_benzamidine_distorted.pdb",
                "content_b64": "<file contents, base64>",
            },
        ],
    }
    
    estimate = client.jobs.estimate(job_type="structure-qc", payload=payload, resource="cpu")
    print(f"Reserves ${estimate.cost:.2f}")
    
    job = client.jobs.submit(
        job_name="my-structure-qc-run",
        job_type="structure-qc",
        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) · optionalPDB, CIF

    Workflow outputs

    • ArchiveZIP
    • TableCSV
    • TableCSV
    • StructuresPDB