Data structures for representing molecular coordinates and annotations.
This module provides a single-model Structure, immutable hierarchy
views (Chain, Residue, and Atom), an ordered
multi-model container (StructureEnsemble), and a shared-annotation
multi-model fast path (StructureStack).
The universal length unit is Å.
- class neurosnap.structure.structure.Atom(x, y, z, chain_id, res_id, ins_code, res_name, hetero, atom_name, element, annotations=<factory>)[source]#
Bases:
objectImmutable atom-level hierarchy view.
-
annotations:
Mapping[str,Any]#
-
atom_name:
str#
-
chain_id:
str#
- property coord: ndarray#
Return the atom coordinates as a length-3 NumPy array.
-
element:
str#
-
hetero:
bool#
-
ins_code:
str#
-
res_id:
int#
-
res_name:
str#
-
x:
float#
-
y:
float#
-
z:
float#
-
annotations:
- class neurosnap.structure.structure.BondType(*values)[source]#
Bases:
IntEnumStable categorical labels for topology-level bonds.
- COVALENT = 0#
- DISULFIDE = 1#
- METAL_COORDINATION = 2#
- OTHER = 127#
- class neurosnap.structure.structure.Chain(chain_id, _residues)[source]#
Bases:
objectImmutable chain-level hierarchy view.
A
Chainis a read-only hierarchy view over the residues associated with one chain identifier in a singleStructure. It provides chain- level traversal plus convenience helpers for sequence extraction and simple residue-number gap detection.- Variables:
chain_id – Chain identifier represented by this view.
- __getitem__(res_id)[source]#
Return a residue view by residue ID, not by positional index.
- Parameters:
res_id (
int) – Residue sequence number to retrieve.- Return type:
- Returns:
The first
Residuein this chain with the requested residue ID.- Raises:
TypeError – If
res_idis not an integer residue ID.KeyError – If no residue with the requested ID is present in the chain.
Notes
This method looks up residues by their residue ID rather than by list position. If multiple residues share the same residue ID, such as inserted residues distinguished by insertion codes, the first matching residue is returned and a warning is emitted.
-
chain_id:
str#
- missing_residue_ids()[source]#
Return missing residue numbers inferred from gaps in the chain.
Hetero residues are ignored so ligand or solvent numbering does not create artificial gaps in the polymer residue sequence.
- Return type:
List[int]- Returns:
Sorted list of integer residue IDs that are absent between observed non-hetero residue numbers.
- sequence(polymer_type='auto', include_modifications=False, modification_mode='inline', on_unknown_modified='raise')[source]#
Return the polymer sequence for this chain.
Protein, DNA, and RNA sequences are supported. Small molecules and other non-polymer residues in the chain are ignored. Modified residues can either be skipped, emitted inline as
(CCD), or mapped to their parent sequence code when available. Modified protein residues are recognized by their CCD peptide-linking or peptide-terminus type. Parent mode uses the closest canonical amino acid from CCD chemical similarity. CCD lookups use the local cache and may download or refresh it when needed.- Parameters:
polymer_type (
Literal['auto','protein','dna','rna','nucleotide']) – Polymer family to extract."auto"infers the family from the chain contents."nucleotide"accepts either DNA or RNA, but raises if both are present.include_modifications (
bool) – Whether modified residues should contribute to the sequence. IfFalse, modified residues are skipped entirely.modification_mode (
Literal['inline','parent']) – How included modifications are emitted."inline"inserts(CCD)tokens, while"parent"uses the closest canonical amino-acid code for proteins or an inferred parent code for nucleotides.on_unknown_modified (
Literal['raise','unknown']) – Behavior whenmodification_mode="parent"is requested but no parent code can be inferred."raise"raises aValueError;"unknown"inserts"X".
- Return type:
str- Returns:
Sequence string for the selected polymer family. Returns an empty string if the chain contains no residues from the requested polymer family.
- Raises:
ValueError – If the chain mixes polymer families in a way that conflicts with
polymer_typeor if an unknown modified residue cannot be mapped in"parent"mode.
- class neurosnap.structure.structure.InteractionType(*values)[source]#
Bases:
IntEnumStable categorical labels for noncovalent interactions.
- HYDROGEN_BOND = 1#
- IONIC = 0#
- OTHER_NONCOVALENT = 127#
- SALT_BRIDGE = 2#
- class neurosnap.structure.structure.Residue(chain_id, res_id, ins_code, res_name, hetero, _atoms, _atom_indices)[source]#
Bases:
objectImmutable residue-level hierarchy view.
A
Residuegroups atoms that share the same chain identifier, residue number, insertion code, residue name, and hetero flag. The object is a lightweight read-only view over the parsed atom table, intended for traversal and analysis rather than in-place editing.- Variables:
chain_id – Chain identifier containing the residue.
res_id – Residue sequence number.
ins_code – PDB insertion code for the residue.
res_name – Residue name / CCD code.
hetero –
Truefor heterogens andFalsefor polymerATOMrecords.
- atom_indices()[source]#
Return atom-table indices for the atoms in this residue.
- Return type:
List[int]- Returns:
List of integer atom indices in atom-table order.
-
chain_id:
str#
-
hetero:
bool#
-
ins_code:
str#
- key()[source]#
Return a stable residue identity tuple.
The returned key is suitable for dictionary/set membership when residue identity needs to be tracked outside the object itself.
- Return type:
Tuple[str,int,str,str,bool]- Returns:
(chain_id, res_id, ins_code, res_name, hetero)
-
res_id:
int#
-
res_name:
str#
- class neurosnap.structure.structure.Structure(*, remove_annotations=True)[source]#
Bases:
objectSingle-model molecular structure container.
Coordinates are stored separately from per-atom annotations so geometry-heavy operations can work on compact numeric arrays while annotation schemas remain flexible.
- Parameters:
remove_annotations (
bool) – IfTrue, optional annotation columns that only contain default values are removed after initialization.
- add_annotation(name, dtype, values=None, *, fill_value=None, overwrite=False)[source]#
Add a new per-atom annotation column.
- Parameters:
name (
str) – Annotation name to add.dtype (
Any) – NumPy-compatible scalar dtype for the annotation values.values (
Any) – Optional per-atom values for the annotation.fill_value (
Any) – Optional default value used whenvaluesis not supplied.overwrite (
bool) – Whether to replace an existing optional annotation of the same name.
- Raises:
ValueError – If the name is invalid, reserved, already present, or the supplied values do not match the atom count.
TypeError – If the supplied dtype is not a scalar per-atom dtype.
- add_atom(x, y, z, *, annotations=None, **annotation_values)[source]#
Append one atom and return its new atom index.
Missing annotation fields are filled from the structure defaults. Unknown annotation names are rejected so callers cannot silently lose metadata.
- Return type:
int
- add_atoms(coords, annotations)[source]#
Append multiple atoms and return their new atom indices.
- Return type:
List[int]
- add_bond(atom_i, atom_j, bond_order=1, bond_type=BondType.COVALENT)[source]#
Append one bond row and return its new bond index.
- Return type:
int
- add_bonds(bonds)[source]#
Append multiple bond rows and return their new bond indices.
- Return type:
List[int]
- add_interaction(atom_i, atom_j, interaction_type)[source]#
Append one noncovalent interaction row and return its new row index.
- Return type:
int
- add_interactions(interactions)[source]#
Append multiple noncovalent interaction rows and return their indices.
- Return type:
List[int]
- property annotation_names: Tuple[str, ...]#
Return the per-atom annotation column names.
- calculate_center_of_mass(chains=None)[source]#
Calculate the center of mass for the selected atoms.
- Parameters:
chains (
Optional[List[str]]) – Optional chain IDs to include. IfNone, all atoms are used.- Return type:
ndarray- Returns:
A length-3 NumPy array containing the center of mass in Å.
- Raises:
ValueError – If no atoms are found in the selected structure or if any selected atom has an unknown element mass.
- calculate_geometric_center(chains=None)[source]#
Calculate the geometric center for the selected atoms.
- Parameters:
chains (
Optional[List[str]]) – Optional chain IDs to include. IfNone, all atoms are used.- Return type:
ndarray- Returns:
A length-3 NumPy array containing the arithmetic mean of the selected atom coordinates in Å.
- Raises:
ValueError – If no atoms are found in the selected structure.
- calculate_rog(chains=None, center=None)[source]#
Calculate the radius of gyration for the selected atoms.
- Parameters:
chains (
Optional[List[str]]) – Optional chain IDs to include. IfNone, all atoms are used.center (
Optional[ndarray]) – Optional reference point. IfNone, the center of mass is used.
- Return type:
float- Returns:
Radius of gyration in Å.
- center_at(x=0.0, y=0.0, z=0.0, chains=None)[source]#
Translate selected atoms so their center of mass matches a target point.
- Parameters:
x (
float) – Target x-coordinate for the center of mass.y (
float) – Target y-coordinate for the center of mass.z (
float) – Target z-coordinate for the center of mass.chains (
Optional[List[str]]) – Optional chain IDs to center. IfNone, all atoms are used.
- chain_ids()[source]#
Return all chains IDs found in the structure.
- Return type:
List[str]- Returns:
List of strings for each chain.
- detect_coordination_centers(*, metal_coordination_cutoff=2.8, include_candidates=False)[source]#
Analyze and return coordination centers as a pandas DataFrame.
Only the metal coordination rule is evaluated. Requesting every rule here would inherit the aligned-RDKit-molecule requirement of the chemistry-typed rules, which would make this method unusable on any structure containing a ligand even when no ligand participates in a coordination shell.
- Parameters:
metal_coordination_cutoff (
float) – Maximum metal-donor distance in Å.include_candidates (
bool) – Whether to keep untypedcandidatedonors.
- Return type:
DataFrame- Returns:
One row per coordination center, with its donors and classified geometry.
- detect_interactions(interaction_types=None, *, contact_cutoff_a=4.5, vdw_tolerance_a=0.5, clash_overlap_a=0.4, include_hydrogens=False, covalent_candidates=False, covalent_lower_factor=0.8, covalent_upper_factor=1.2, disulfide_cutoff=2.2, salt_bridge_cutoff=4.0, hbond_donor_acceptor_cutoff=3.5, hbond_angle_cutoff=130.0, metal_coordination_cutoff=2.8, include_candidates=False)[source]#
Detect structural interactions and return them as a pandas DataFrame.
Named
detect_rather thanget_because this computes interactions from geometry and chemistry; it does not read the declaredinteractionstable, though declared rows raise an observation’s evidence toexplicit.- Return type:
DataFrame
- distances_from(point, chains=None)[source]#
Calculate distances from a point for the selected atoms.
- Parameters:
point (
ndarray) – Reference point as an array-like object with shape(3,).chains (
Optional[List[str]]) – Optional chain IDs to include. IfNone, all atoms are used.
- Return type:
ndarray- Returns:
A 1D NumPy array containing Euclidean distances in atom-table order.
- classmethod empty_like(template, *, copy_metadata=False)[source]#
Return an empty structure with the same array schemas as
template.- Return type:
- property entities: List[Any]#
Return the list of InteractionEntity objects defined on this structure.
- get_raw_contacts(cutoff=12.0)[source]#
Return all pairwise atom-atom contacts within a distance cutoff.
- Return type:
DataFrame
- property num_atoms: int#
Return the number of atoms in the structure.
- remove_annotation(name)[source]#
Remove a non-mandatory annotation column and return its values.
- Parameters:
name (
str) – Annotation name to remove.- Returns:
Copy of the removed annotation values.
- Raises:
KeyError – If the annotation does not exist.
ValueError – If the name is invalid or refers to a mandatory annotation.
- renumber(chain=None, start=1)[source]#
Renumber residues in-place.
- Parameters:
chain (
Optional[str]) – Chain ID to renumber. IfNone, all chains are renumbered in chain order using one continuous counter.start (
int) – Starting residue number.
Notes
Renumbering treats inserted residues as ordinary sequential residues and clears their insertion codes. For example, residues
10,10A, and10Bbecome1,2, and3(with empty insertion codes) when renumbered withstart=1.
- save_cif(cif, *, minimal=False)[source]#
Write the structure directly as an mmCIF file.
This is a convenience wrapper around
neurosnap.io.mmcif.save_cif(). It preserves the current atom table and metadata exactly as stored on the structure.- Parameters:
cif – Output filepath or open writable file handle.
minimal (
bool) – IfTrue, emit compact atom-site-only mmCIF output. IfFalse(default), include entity/polymer/subchain metadata.
- save_pdb(pdb)[source]#
Write the structure directly as a PDB file.
This is a convenience wrapper around
neurosnap.io.pdb.save_pdb(). It is especially useful afterselect(), because the selected structure can be exported without rebuilding a new container manually.- Parameters:
pdb – Output filepath or open writable file handle.
- select(*, chains=None, residues=None, predicate=None)[source]#
Return an independent atom-level subset of the structure.
The returned structure preserves the selected atoms exactly as parsed: coordinates, atom serials, residue identifiers, optional annotations, and any bonds whose endpoints remain in the subset. Bond indices are remapped onto the new atom table automatically so the subset can be exported directly with
save_pdb()orsave_cif().- Parameters:
chains (
Optional[Sequence[str]]) – Optional chain IDs to keep. IfNone, atoms from all chains remain eligible for selection.residues (
Optional[Sequence[Union[int,Residue,Tuple[Any,...]]]]) – Optional residue selectors to keep. Supported selector forms are integer residue IDs matched across the selected chains,Residueobjects,(chain_id, res_id)tuples,(chain_id, res_id, ins_code)tuples, and full residue-key tuples(chain_id, res_id, ins_code, res_name, hetero).predicate (
Optional[Callable[[Atom],bool]]) – Optional atom-level predicate. When provided, each atom is exposed as an immutableAtomview and kept only if the predicate returns a truthy value.
- Returns:
A new
Structurecontaining only atoms that satisfy every provided filter.- Return type:
- Raises:
ValueError – If a requested chain or residue selector is not present in the structure.
TypeError – If
predicateis not callable or a residue selector has an unsupported type or shape.
- to_dataframe()[source]#
Export the structure as a pandas dataframe.
This dataframe is derived on demand from the current atom table and is never cached on the structure.
- Return type:
DataFrame
- translate(x=0.0, y=0.0, z=0.0, chains=None)[source]#
Translate selected atoms in-place by a fixed vector.
- Parameters:
x (
float) – Translation along the x-axis.y (
float) – Translation along the y-axis.z (
float) – Translation along the z-axis.chains (
Optional[List[str]]) – Optional chain IDs to translate. IfNone, all atoms are translated.
- class neurosnap.structure.structure.StructureEnsemble(models=None, *, model_ids=None, metadata=None)[source]#
Bases:
objectOrdered collection of independent
Structuremodels.Unlike
StructureStack, models in an ensemble do not need to have the same atoms, annotations, or bonds.- Parameters:
models (
Optional[List[Structure]]) – Optional initial list of models.model_ids (
Optional[List[int]]) – Optional identifiers corresponding tomodels.metadata (
Optional[Mapping[str,Any]]) – Optional ensemble-level metadata dictionary.
- __getitem__(index)[source]#
Return a model by model ID or a sliced sub-ensemble by position.
Integer access uses
model_idlookup rather than positional indexing, soensemble[5]returns the model whose ID is5. Slice access keeps normal positional semantics to preserve standard Python iteration and slicing behavior.- Raises:
KeyError – If an integer model ID is requested but not present.
- __init__(models=None, *, model_ids=None, metadata=None)[source]#
Initialize an ordered collection of independent models.
- append(model, *, model_id=None)[source]#
Append a validated model to the ensemble.
- Parameters:
model (
Structure) – Model to append.model_id (
Optional[int]) – Optional model identifier. Defaults to the next sequential model ID starting at1.
- renumber(start=1)[source]#
Renumber model identifiers in-place.
- Parameters:
start (
int) – Starting model ID. Defaults to1.
- save_cif(cif, *, minimal=False)[source]#
Write the ensemble directly as an mmCIF file.
- Parameters:
cif – Output filepath or open writable file handle.
minimal (
bool) – IfTrue, emit compact atom-site-only mmCIF output. IfFalse(default), include entity/polymer/subchain metadata.
- save_pdb(pdb)[source]#
Write the ensemble directly as a PDB file.
- Parameters:
pdb – Output filepath or open writable file handle.
- select(*, models=None, chains=None, residues=None, predicate=None)[source]#
Return a filtered ensemble of independently subsetted models.
- Parameters:
models (
Optional[Sequence[int]]) – Optional model IDs to keep. IfNone, all models are considered.chains (
Optional[Sequence[str]]) – Optional chain IDs to keep within each selected model.residues (
Optional[Sequence[Union[int,Residue,Tuple[Any,...]]]]) – Optional residue selectors to keep within each selected model.predicate (
Optional[Callable[[Atom],bool]]) – Optional atom-level predicate applied independently inside each selected model.
- Return type:
- Returns:
A new
StructureEnsemblewhose model IDs match the selected source models and whose per-model contents are the correspondingStructure.select()subsets.- Raises:
ValueError – If any requested model ID, chain, or residue selector is not present.
TypeError – If
predicateis not callable or a residue selector is malformed.
- to_dataframe()[source]#
Export the ensemble as a pandas dataframe with a
modelcolumn.This dataframe is derived on demand from the current models and is never cached on the ensemble.
- Return type:
DataFrame
- class neurosnap.structure.structure.StructureStack(models=None, *, model_ids=None, metadata=None)[source]#
Bases:
objectShared-annotation, shared-topology multi-model fast path.
All models in a stack must share the same atom ordering, per-atom annotations, and bonds, because those describe persistent topology. Coordinates and interactions vary per model: interactions are geometry-dependent, so each model owns its own table and
interactionsis a list indexed in parallel withmodel_ids.- Parameters:
models (
Optional[List[Structure]]) – Optional initial list of stack-compatible models.model_ids (
Optional[List[int]]) – Optional identifiers corresponding tomodels.metadata (
Optional[Mapping[str,Any]]) – Optional stack-level metadata dictionary.
- __getitem__(index)[source]#
Return a materialized model by model ID or a sliced sub-stack by position.
Integer access uses
model_idlookup rather than positional indexing, sostack[5]returns the model whose ID is5. Slice access keeps normal positional semantics to preserve standard Python slicing behavior.- Raises:
KeyError – If an integer model ID is requested but not present.
- __init__(models=None, *, model_ids=None, metadata=None)[source]#
Initialize an empty or pre-populated stack of compatible models.
- __iter__()[source]#
Iterate over the stack as materialized
Structuremodels.- Return type:
Iterator[Structure]
- append(model, *, model_id=None)[source]#
Append a stack-compatible model.
- Parameters:
model (
Structure) – Model to append.model_id (
Optional[int]) – Optional model identifier. Defaults to the next sequential model ID starting at1.
- Raises:
ValueError – If the candidate model is not compatible with the existing stack.
- property atom_count: int#
Return the number of shared atoms per model.
- classmethod from_ensemble(ensemble)[source]#
Build a stack from an ensemble of compatible models.
- Return type:
- renumber(start=1)[source]#
Renumber model identifiers in-place.
- Parameters:
start (
int) – Starting model ID. Defaults to1.
- save_cif(cif, *, minimal=False)[source]#
Write the stack directly as an mmCIF file.
- Parameters:
cif – Output filepath or open writable file handle.
minimal (
bool) – IfTrue, emit compact atom-site-only mmCIF output. IfFalse(default), include entity/polymer/subchain metadata.
- save_pdb(pdb)[source]#
Write the stack directly as a PDB file.
- Parameters:
pdb – Output filepath or open writable file handle.
- select(*, models=None, chains=None, residues=None, predicate=None)[source]#
Return a filtered multi-model subset of the stack.
The selection is executed through the ensemble path so each chosen model is subsetted with the same semantics as
Structure.select(). If the resulting models still share identical atom annotations and bonds, the return value is aStructureStack; otherwise it falls back to aStructureEnsemble.- Parameters:
models (
Optional[Sequence[int]]) – Optional model IDs to keep. IfNone, all models are considered.chains (
Optional[Sequence[str]]) – Optional chain IDs to keep within each selected model.residues (
Optional[Sequence[Union[int,Residue,Tuple[Any,...]]]]) – Optional residue selectors to keep within each selected model.predicate (
Optional[Callable[[Atom],bool]]) – Optional atom-level predicate applied independently inside each selected model.
- Return type:
Union[StructureStack,StructureEnsemble]- Returns:
A
StructureStackwhen the subset remains stack-compatible, otherwise aStructureEnsemble.
- to_dataframe()[source]#
Export the stack as a pandas dataframe with a
modelcolumn.This dataframe is derived on demand from the current stack contents and is never cached on the stack.
- Return type:
DataFrame