Bond Utilities#

Utility functions for the detection, and creation, of bonds in a structure.

atomworks.io.utils.bonds.LongBondPolicyType#

Type alias for long bond filtering policy options.

alias of Literal[‘keep’, ‘filter’, ‘warn’, ‘raise’, ‘filter_nonstandard_only’, ‘warn_nonstandard_only’, ‘raise_nonstandard_only’]

atomworks.io.utils.bonds.add_bonds_from_struct_conn(atom_array: AtomArray | AtomArrayStack, cif_block: CIFBlock, add_bond_types_from_struct_conn: tuple[str, ...] = ('covale',), struct_conn_distance_policy: Literal['keep', 'filter', 'warn', 'raise', 'filter_nonstandard_only', 'warn_nonstandard_only', 'raise_nonstandard_only'] = 'filter') AtomArray | AtomArrayStack[source]#

Convenience wrapper to add bonds to an AtomArray from a struct_conn CIF category.

See also

get_struct_conn_bonds() - For arguments and return types.

atomworks.io.utils.bonds.build_bond_dict_for_atom_array(atom_array: AtomArray, custom_bond_dict: dict[str, dict[tuple[str, str], int]] | None = None, ccd_mirror_path: str | None = None) dict[str, dict[tuple[str, str], int]] | None[source]#

Build a complete bond dictionary for an AtomArray by merging CCD bonds with custom overrides.

Fetches CCD bonds for all residues as the baseline, then overlays custom_bond_dict on top. This ensures that atoms added later (e.g., H atoms via add_missing_atoms) always have bonds available from CCD, while custom connectivity still takes precedence for the pairs it explicitly defines.

Parameters:
  • atom_array – Structure containing residues to get bonds for.

  • custom_bond_dict – Optional custom bonds (e.g., from CIF chem_comp_bond). Maps residue names to {(atom1_name, atom2_name): bond_type_int}. These override the CCD entries for matching atom pairs.

  • ccd_mirror_path – Path to local CCD mirror. Defaults to CCD_MIRROR_PATH.

Returns:

Complete bond dictionary with CCD bonds as baseline and custom bonds as overrides, or None if no bonds could be found for any residue.

Example

>>> # Custom bonds for VER override CCD heavy-atom pairs; CCD still provides H bonds
>>> custom_bonds = {"VER": {("C1", "C2"): 1, ("C2", "O1"): 2}}
>>> bond_dict = build_bond_dict_for_atom_array(atom_array, custom_bond_dict=custom_bonds, ccd_mirror_path="/path/to/ccd")
>>> # bond_dict contains VER bonds (CCD H-bonds + custom heavy-atom overrides) + ALA, GLY, etc. (CCD)
atomworks.io.utils.bonds.correct_charged_amide_nitrogens(atom_array: AtomArray, to_update: ndarray | None = None) AtomArray[source]#

Neutralize charged nitrogens that are part of an amide pattern.

When a charged nitrogen (charge=+1) is bonded to a carbon that is also bonded to an oxygen (N-C=O / N-C-O amide pattern), the nitrogen should be neutral.

Example: PDB ID 1qfe Lysine NZ becomes a component of an amide bond to small molecule DHS, and should NOT be charged.

Parameters:
  • atom_array – The AtomArray to fix.

  • to_update – Boolean mask of atoms to consider. If None, defaults to atoms involved in inter-residue bonds.

Returns:

The AtomArray with amide nitrogens corrected (modified in-place).

atomworks.io.utils.bonds.correct_formal_charges_for_specified_atoms(atom_array: AtomArray, to_update: ndarray) AtomArray[source]#

Fix formal charges for atoms in an AtomArray based on valence rules and current bonding pattern.

Parameters:
  • atom_array (AtomArray) – The AtomArray to fix.

  • to_update (np.ndarray) – A boolean mask of atoms whose formal charges should be fixed. These are normally the atoms for which bonds were manually added or modified (e.g., inter-residue bonds).

Returns:

The AtomArray with fixed formal charges.

Return type:

AtomArray

atomworks.io.utils.bonds.filter_bonds_by_distance(atom_array: AtomArray, policy: Literal['keep', 'filter', 'warn', 'raise', 'filter_nonstandard_only', 'warn_nonstandard_only', 'raise_nonstandard_only'] = 'filter', chno_threshold: float = 1.7, chnops_threshold: float = 2.4, other_threshold: float = 3.6) AtomArray[source]#

Handle unphysical bonds based on element-dependent distance thresholds.

The thresholds are: 1. Bonds involving ONLY C, H, N, O: flagged if distance > chno_threshold 2. Bonds involving ONLY C, H, N, O, P, S: flagged if distance > chnops_threshold 3. Any other bonds (metals, etc.): flagged if distance > other_threshold

Parameters:
  • atom_array – The AtomArray containing atoms and bonds to check.

  • policy

    How to handle long bonds. Options:

    • "keep": No checking, return atom_array unchanged.

    • "filter": Remove all bonds exceeding thresholds.

    • "warn": Log warning about all long bonds but keep them.

    • "raise": Raise ValueError if any long bonds detected.

    • "filter_nonstandard_only": Remove long bonds only in non-standard residues. Bonds where both atoms are in standard AA (20 canonical + UNK), RNA (A, C, G, U + N), or DNA (DA, DC, DG, DT + DN) are preserved.

    • "warn_nonstandard_only": Warn about long bonds in non-standard residues only.

    • "raise_nonstandard_only": Raise error if long bonds in non-standard residues.

    Defaults to "filter".

  • chno_threshold – Maximum distance for bonds between only C, H, N, O atoms. Defaults to 1.8 Angstroms.

  • chnops_threshold – Maximum distance for bonds between only C, H, N, O, P, S atoms. Defaults to 2.4 Angstroms.

  • other_threshold – Maximum distance for bonds involving any other elements. Defaults to 3.6 Angstroms.

Returns:

The input AtomArray, potentially with long bonds removed (if policy="filter").

Raises:

ValueError – If policy="raise" and long bonds are detected.

Note

Bonds involving atoms with NaN coordinates are preserved (not flagged).

atomworks.io.utils.bonds.generate_inter_level_bond_hash(atom_array: AtomArray, lower_level_id: str, lower_level_entity: str | None = None, exclude_bond_types: set[BondType] | None = None) str[source]#

Generates a hash string representing the inter-level bonds within an AtomArray.

When computing entities IDs, we must consider inter-level bonds at the atom- and residue-level to avoid ambiguity.

Parameters:
  • atom_array (AtomArray) – The array of atoms containing bond and annotation information.

  • lower_level_id (str) – The level which to find, and hash, the inter-level bonds. For example, when computing molecule entities, we’d consider the inter-PN Unit bonds.

  • lower_level_entity (str | None) – An additional entity annotation to use when computing the hash. Optional; if None, then only residue ID, residue name, and atom name are used.

Returns:

A hash string representing the inter-level bonds.

Return type:

str

atomworks.io.utils.bonds.get_coarse_graph_as_nodes_and_edges(atom_array: AtomArray, annotations: str | tuple[str], exclude_bond_types: set[BondType] | None = None) tuple[ndarray, ndarray][source]#

Returns the coarse-grained nodes and edges at the given annotation level based on the atom array’s bond connectivity.

Parameters:
  • atom_array (-) – The atom array containing atomic information and bonds.

  • annotations (-) – A single annotation or a tuple of annotations to be used for node identification.

  • exclude_bond_types (-) – Bond types to exclude from connectivity. For example, pass {BondType.COORDINATION} to prevent metal-ligand bonds from merging components.

Returns:

An array of unique nodes, each represented by a combination of annotations. - edges (np.ndarray): An array of edges, where each edge is a tuple of node indices representing a bond

between two nodes.

Return type:

  • nodes (np.ndarray)

Example

>>> atom_array = cached_parse("5ocm")["atom_array"]
>>> nodes, edges = get_coarse_graph(atom_array, ["chain_id", "transformation_id"])
>>> print(nodes)
array([('A', '1'), ('F', '1'), ('G', '1'), ('H', '1'), ('I', '1'),
       ('W', '1'), ('X', '1'), ('Y', '1')],
      dtype=[('chain_id', '<U4'), ('transformation_id', '<U1')])
>>> print(edges)
array([[0, 0],
       [1, 1],
       [2, 2],
       [3, 3],
       [5, 5],
       [6, 6]])
atomworks.io.utils.bonds.get_connected_nodes(nodes: ndarray, edges: ndarray) list[list[Any]][source]#

Returns connected nodes as a mapped list given corresponding arrays of nodes and edges.

Example

>>> nodes = np.array([("A", "1"), ("B", "1"), ("C", "1"), ("D", "1")])
>>> edges = np.array([[0, 1], [0, 2], [1, 2]])
>>> connected_nodes = get_connected_nodes(nodes, edges)
>>> print(connected_nodes)
[[("A", "1"), ("B", "1"), ("C", "1")], [("D", "1")]]
atomworks.io.utils.bonds.get_inter_pn_unit_bond_mask(atom_array: AtomArray) ndarray[source]#

Return a mask indicating which bonds are between two distinct PN units.

atomworks.io.utils.bonds.get_struct_conn_bonds(atom_array: AtomArray, struct_conn_dict: dict[str, ndarray], add_bond_types: tuple[str, ...] = ('covale',), raise_on_failure: bool = False, distance_policy: Literal['keep', 'filter', 'warn', 'raise', 'filter_nonstandard_only', 'warn_nonstandard_only', 'raise_nonstandard_only'] = 'filter') BondList[source]#

Find inter-residue bonds from the CIF struct_conn category.

Modified from biotite’s internal _get_struct_conn_bonds.

Parameters:
  • atom_array – The atom array used to look up atom indices.

  • struct_conn_dict – The struct_conn category of a CIF block as a dict of numpy arrays. Required keys: conn_type_id, ptnr{1,2}_label_asym_id, ptnr{1,2}_label_comp_id, ptnr{1,2}_label_seq_id, ptnr{1,2}_label_atom_id.

  • add_bond_types – Bond type IDs to include. Valid values are "covale", "disulf", and "metalc". Defaults to ["covale"].

  • raise_on_failure – If True, raise on missing atoms or residues. Defaults to False.

Returns:

A biotite.structure.BondList ready to merge into the atom array’s bond list.

Reference:

struct_conn.conn_type_id

atomworks.io.utils.bonds.hash_atom_array(atom_array: AtomArray, annotations: tuple[str] = ('element', 'atom_name'), bond_order: bool = True, cast_aromatic_bonds_to_same_type: bool = False, use_md5: bool = False, md5_length: int | None = None) str[source]#

Computes a hash for an AtomArray based on the bond connectivity and the selected node annotations.

Parameters:
  • atom_array (AtomArray) – The array of atoms to hash

  • annotations (tuple[str]) – The node annotations to include in the hash

  • bond_order (bool) – Whether to include bond order in the hash

  • cast_aromatic_bonds_to_same_type (bool) – Whether to treat all aromatic bonds as the same type

  • use_md5 (bool) – Whether to use MD5 hashing on the output

  • md5_length (int | None) – If using MD5, the number of characters to keep from the hash. If None, returns full hash.

Returns:

The computed hash

Return type:

str

atomworks.io.utils.bonds.hash_graph(graph: Graph, node_attr: str | None = None, edge_attr: str | None = None, iterations: int = 3, digest_size: int = 16) str[source]#

Computes a hash for a given graph using the Weisfeiler-Lehman (WL) graph hashing algorithm and additionally adds a node and edge attribute hash, if specified, to deal with common edge cases where WL fails (e.g. disconnected graphs).

Parameters:
  • graph (-) – The input graph to be hashed.

  • node_attr (-) – The node attribute to be used for hashing. If None, node attributes are ignored.

  • edge_attr (-) – The edge attribute to be used for hashing. If None, edge attributes are ignored.

  • iterations (-) – The number of iterations for the WL algorithm. Default is 3.

  • digest_size (-) – The size of the hash digest for WL. Default is 16.

Returns:

The computed hash of the graph.

Return type:

  • str

Example

>>> import networkx as nx
>>> G = nx.gnm_random_graph(10, 15)
>>> hash_graph(G)
'504894f49dd84b17c391b163af69624b'
atomworks.io.utils.bonds.remap_intra_residue_coordination_bonds(structure: AtomArray) AtomArray[source]#

Remap intra-residue COORDINATION bonds to SINGLE.

Biotite’s CIF writer routes intra-residue bonds to chem_comp_bond, which cannot represent COORDINATION; inter-residue bonds go to struct_conn (which can).