Query language
Describe molecular subsets with MolFrame's textual, typed, and reusable selection language.
A MolFrame query describes atoms by their structural identity, chemistry, or geometry. Compile it once when you will reuse it; evaluate it with Structure.select() whenever you need the matching atoms.
Structure.select() returns a structure-bound Selection. It is cheap to pass to another MolFrame operation; call to_coordinates() only when an API requires a gathered coordinate array.
Start with structural selectors
The built-in chemistry terms are the quickest way to state common molecular sets:
| Query | Selects |
|---|---|
protein | Protein atoms. |
ligand | Ligand atoms. |
water / ion | Water or ion atoms. |
polymer | Polymer atoms. |
backbone / sidechain | Protein backbone or side-chain atoms. |
nucleic | Nucleic-acid atoms. |
heavy / hydrogen | Non-hydrogen or hydrogen atoms. |
polar_hydrogen | Hydrogen bonded to N, O or S. |
nonpolar_hydrogen | All other hydrogen, including unbonded H. |
helix | All helices, including PPII and deposited other helices. |
strand / sheet | Beta ladders only / ladders plus isolated bridges. |
alpha_helix / helix_310 / pi_helix / polyproline | One exact helix state. |
bridge / turn / bend / coil | One exact state; coil excludes unassigned residues. |
Hydrogen polarity uses the full recorded bond topology even when a preceding
filter selects only hydrogen. A hydrogen with any N/O/S neighbour is polar;
hydrogen bound only to carbon, phosphorus, halogens or other elements is
nonpolar. These are complementary display-selection classes, not a
hydrogen-bond donor/acceptor analysis. No bonds are inferred by the query:
unavailable connectivity raises MOLFRAME-E4003, whereas a known-empty graph
makes all explicit hydrogen nonpolar.
Text, compiled queries and typed expressions share this classification:
polar = structure.select("polar_hydrogen")
nonpolar = molframe.Query("nonpolar_hydrogen").select(structure)
display_atoms = structure.select(~molframe.sel.nonpolar_hydrogen())Secondary selectors read the authoritative per-residue column without recomputing
analysis. Rust col and Python molframe.sel expose the same no-argument
names. Python Structure.secondary_structure returns typed
SecondaryStructure values: stable codes 0–9 are unchanged and
PolyProline appends code 10. molframe.analysis.dssp(structure)
computes a native DSSP table with residue and kind code columns.
Canonical CIF and PDB writers project helix classes 1, 3, 5 and 10 and sheet
ranges from that column. Other deposited helices remain OtherHelix,
not alpha helices. Canonical output uses class 2 for that unspecified class;
document-preserving CIF output retains the original deposited class number.
CIF also writes turns. Coil, bend and isolated bridge states have no faithful
legacy HELIX/SHEET encoding and are not relabelled as helices or strands.
PDB output regenerates secondary ranges after chain remapping and selection.
PDB endpoints include the deposited insertion code and decimal or hybrid-36
residue number, including negative numbers and blank chains. Both endpoints
must resolve uniquely inside one chain; ranges follow deposited residue order,
never a numeric approximation. Model-local PDB annotations stay with their
model; a dense shared topology uses the first model's local annotations.
Canonical writers refuse ambiguous projected residue identities before emitting
bytes. CIF secondary endpoints use label identities only, not author aliases.
Independent ragged-model secondary annotations require writing a model snapshot:
global secondary categories cannot encode their model-specific identity.
Columns constrain structural identifiers. A column can accept one or more values:
chain A
resname HEM
name CA
(element N or element O) and proteinUse chain, resid, resname, name, and element for ordinary structure work. MolFrame also exposes label and author variants such as label_chain / auth_chain and label_resid / auth_resid when a format carries both identifier systems.
A plain chain, resname or name follows the policy's identifier namespace (author names by default). A value can also name its namespace itself with a label: or auth: prefix, so chain label:A selects the same atoms as label_chain A, and chain label:A auth:C is their union. analysis.chain_interface reads its chain names the same way: a bare name that means different chains as a label and as an author id is refused, and "label:A" or "auth:A" resolves it.
Combine terms deliberately
not binds most tightly, then and, then or. Parenthesize a mixed expression so its scientific meaning remains obvious.
protein and name CA
(chain A or chain B) and not water
protein and (name CA or name CB)all and none are explicit complete and empty selections. Query keywords are case-insensitive; identifier values retain the meaning supplied by the structure.
Expand a molecular neighborhood
Spatial predicates take distances in Å. within matches atoms near a target; byres expands those atoms to their complete residues.
A spatial predicate reads its target and neighbours from the whole structure (or the view the query runs in), never from what the other side of an and has already kept, so chain A and within 4 of chain B and within 4 of chain B and chain A select the same atoms: the atoms of A that lie within 4 Å of B. Use global only to escape an enclosing view.
pocket = structure.select("byres (within 5 of resname HEM) and protein")Other useful structural expansions are same residue as QUERY, same chain as QUERY, and bonded QUERY (or bonded DEPTH QUERY). Keep the radius and the target in reports: a spatial selection is defined by both.
Filter numeric properties
Numeric columns support <, <=, >, >=, =, and !=. Common properties include bfactor, occupancy, charge, mass, plddt, and Cartesian x, y, z coordinates.
bfactor > 40
occupancy >= 0.5 and not altloc B
plddt < 50 and proteinExact floating-point equality is rarely a useful scientific criterion. Prefer a range or threshold when a property originated in a calculation.
Select a copy in a materialised assembly
A materialised biological assembly tags every atom with the 0-based instance
id of the chain copy it belongs to. assembly selects by that id and accepts
the same numbers and ranges as the other numeric columns:
assembly 1
assembly 0:3 and protein
same assembly as (resname HEM)On a structure that was not materialised from an assembly there is no instance
id to read, so assembly fails with MOLFRAME-E4003 (annotation
molframe.instance_id) instead of returning an empty selection.
Reuse a compiled query
Query validates text once and has a stable fingerprint. It is the right form for a filter used repeatedly across one or more structures.
import molframe
pocket_query = molframe.Query("byres (within 5 of resname HEM) and protein")
pocket = pocket_query.select(structure)
# The equivalent convenience call is structure.select(pocket_query).The typed builders in molframe.sel describe the same query model without parsing a string:
from molframe import sel
heme = sel.residue("HEM")
pocket_query = sel.residues_within(5.0, heme) & sel.protein()
pocket = structure.select(pocket_query)Name a query definition
Use QueryAliases when one query should refer to another. References use $name; resolution returns a closed query, detects unknown names and cycles, and never depends on atoms selected by an earlier evaluation.
aliases = molframe.QueryAliases()
aliases.define("heme", molframe.Query("resname HEM"))
aliases.define("pocket", molframe.Query("byres (within 5 of $heme) and protein"))
pocket_query = aliases.resolve(molframe.Query("$pocket"))
pocket = structure.select(pocket_query)Completion and byte offsets
The native completion API uses UTF-8 byte offsets, not Unicode character or code-point indexes. Pass a cursor in the range 0..=source.len(). A cursor in the middle of a UTF-8 scalar is rejected; returned replacement spans are byte ranges into the original source. This keeps editor replacements aligned with parser diagnostics and Rust string slices. Python and notebook adapters should convert their editor cursor to a UTF-8 byte offset before calling the native API and convert the returned start/end offsets back to their editor convention only at the UI boundary.
After a value column such as chain, resname, or name, query.complete takes candidates from an optional structure argument instead of any client-maintained keyword list. The native layer lends identifiers from the structure's own indexes, so no second vocabulary is built and duplicate identifiers are collapsed. Items typed value are structure-derived; keyword, column, macro, and alias come from the language registry.
Handle a query failure
Query("…") and Structure.select("…") raise when the expression cannot compile. Preserve the original source text beside the exception: it identifies exactly which scientific subset needs correction.
Visualize a query in MolGFX
MolGFX uses this exact language for representation targets and Workbench selections. See Query-driven visualization for the select → show → color → focus workflow.
Implementation and verification status
The parser, typed builders, alias resolver, logical and geometric selectors, and UTF-8 byte-offset completion contract are implemented at the MolFrame query boundary. A dollar-prefixed name is a query alias; an at-prefixed layer name is a separate MolGFX command-language concept. Invalid input remains a diagnostic rather than silently selecting an alternate parser.
The original query examples in this page are documentation fixtures for the structural, boolean, spatial, numeric, alias, and builder forms. They are preserved verbatim; successful browser, Python, and Workbench execution has not been established by this documentation update.
The required verification commands are listed in molframe/AGENTS.md. No formatter, linter, or test command was run for this documentation-only change. Completion replacement spans (including non-ASCII cursor conversion), W4003 unmatched-span diagnostics, dynamic selection invalidation, concurrent native query-result ownership, and every original example still require an observed end-to-end run.