diff --git a/src/mol-math/geometry/symmetry-operator.ts b/src/mol-math/geometry/symmetry-operator.ts
index 3f848728976d022c1199434f64f17237d9d77db4..cbc2a5eda01f3d26d0f059e3e496d6743f8a4de9 100644
--- a/src/mol-math/geometry/symmetry-operator.ts
+++ b/src/mol-math/geometry/symmetry-operator.ts
@@ -1,11 +1,11 @@
 /**
- * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { lerp as scalar_lerp } from '../../mol-math/interpolate';
-import { defaults } from '../../mol-util';
 import { Mat3 } from '../linear-algebra/3d/mat3';
 import { Mat4 } from '../linear-algebra/3d/mat4';
 import { Quat } from '../linear-algebra/3d/quat';
@@ -29,11 +29,13 @@ interface SymmetryOperator {
     readonly hkl: Vec3,
     /** spacegroup symmetry operator index, -1 if not applicable */
     readonly spgrOp: number,
+    /** unique (external) key, -1 if not available */
+    readonly key: number,
 
     readonly matrix: Mat4,
-    // cache the inverse of the transform
+    /** cache the inverse of the transform */
     readonly inverse: Mat4,
-    // optimize the identity case
+    /** optimize the identity case */
     readonly isIdentity: boolean,
 
     /**
@@ -51,19 +53,20 @@ namespace SymmetryOperator {
 
     export const RotationTranslationEpsilon = 0.005;
 
-    export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number }
+    export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number, key?: number }
     export function create(name: string, matrix: Mat4, info?: CreateInfo | SymmetryOperator): SymmetryOperator {
-        let { assembly, ncsId, hkl, spgrOp } = info || { };
+        let { assembly, ncsId, hkl, spgrOp, key } = info || { };
         const _hkl = hkl ? Vec3.clone(hkl) : Vec3();
-        spgrOp = defaults(spgrOp, -1);
+        spgrOp = spgrOp ?? -1;
+        key = key ?? -1;
         ncsId = ncsId || -1;
         const isIdentity = Mat4.isIdentity(matrix);
         const suffix = getSuffix(info, isIdentity);
-        if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix };
+        if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix, key };
         if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) {
             console.warn(`Symmetry operator (${name}) should be a composition of rotation and translation.`);
         }
-        return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, ncsId, suffix };
+        return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, key, ncsId, suffix };
     }
 
     function isSymmetryOperator(x: any): x is SymmetryOperator {
diff --git a/src/mol-model-formats/structure/property/bonds/index-pair.ts b/src/mol-model-formats/structure/property/bonds/index-pair.ts
index c6021eb47183f67a6fee53a8bda06a2183f2e60a..99eb988e7e185d8297d2c71f66e4733dbdec5075 100644
--- a/src/mol-model-formats/structure/property/bonds/index-pair.ts
+++ b/src/mol-model-formats/structure/property/bonds/index-pair.ts
@@ -14,6 +14,8 @@ import { ElementIndex } from '../../../../mol-model/structure';
 
 export type IndexPairsProps = {
     readonly key: ArrayLike<number>
+    /** Sorted contor-paired operator keys */
+    readonly operator: ArrayLike<number>
     readonly order: ArrayLike<number>
     readonly distance: ArrayLike<number>
     readonly flag: ArrayLike<BondType.Flag>
@@ -24,18 +26,20 @@ export type IndexPairBonds = { bonds: IndexPairs, maxDistance: number }
 function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementIndex>, props: Partial<IndexPairsProps>, count: number): IndexPairs {
     const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB);
     const key = new Int32Array(builder.slotCount);
+    const operator = new Array(builder.slotCount);
     const order = new Int8Array(builder.slotCount);
     const distance = new Array(builder.slotCount);
     const flag = new Array(builder.slotCount);
     for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
         builder.addNextEdge();
         builder.assignProperty(key, props.key ? props.key[i] : -1);
+        builder.assignProperty(operator, props.operator ? props.operator[i] : -1);
         builder.assignProperty(order, props.order ? props.order[i] : 1);
         builder.assignProperty(distance, props.distance ? props.distance[i] : -1);
         builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent);
     }
 
-    return builder.createGraph({ key, order, distance, flag });
+    return builder.createGraph({ key, operator, order, distance, flag });
 }
 
 export namespace IndexPairBonds {
@@ -50,6 +54,10 @@ export namespace IndexPairBonds {
             indexA: Column<number>,
             indexB: Column<number>,
             key?: Column<number>,
+            /**
+             * Sorted contor-paired operator keys. Used in bond computation.
+             */
+            operator?: Column<number>,
             order?: Column<number>,
             /**
              * Useful for bonds in periodic cells. That is, only bonds within the given
@@ -83,11 +91,12 @@ export namespace IndexPairBonds {
         const indexA = pairs.indexA.toArray() as ArrayLike<ElementIndex>;
         const indexB = pairs.indexB.toArray() as ArrayLike<ElementIndex>;
         const key = pairs.key && pairs.key.toArray();
+        const operator = pairs.operator && pairs.operator.toArray();
         const order = pairs.order && pairs.order.toArray();
         const distance = pairs.distance && pairs.distance.toArray();
         const flag = pairs.flag && pairs.flag.toArray();
         return {
-            bonds: getGraph(indexA, indexB, { key, order, distance, flag }, count),
+            bonds: getGraph(indexA, indexB, { key, operator, order, distance, flag }, count),
             maxDistance: p.maxDistance
         };
     }
diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts
index c51b441a77da6cfffd623877b0734df8e87fc757..7cf7070a41db2e78ce0fbef36e706b873b872b98 100644
--- a/src/mol-model/structure/query/context.ts
+++ b/src/mol-model/structure/query/context.ts
@@ -155,6 +155,6 @@ class QueryContextBondInfo<U extends Unit = Unit> {
     }
 
     get length() {
-        return StructureElement.Location.distance(this.a, this. b);
+        return StructureElement.Location.distance(this.a, this.b);
     }
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts
index 1ee3110f99cea729f8561224c2056042774763ac..71e003b232d9dd679a624d9162f792b683dc993c 100644
--- a/src/mol-model/structure/structure/properties.ts
+++ b/src/mol-model/structure/structure/properties.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -173,6 +173,7 @@ const unit = {
     multiChain: p(l => Unit.Traits.is(l.unit.traits, Unit.Trait.MultiChain)),
     object_primitive: p(l => l.unit.objectPrimitive),
     operator_name: p(l => l.unit.conformation.operator.name),
+    operator_key: p(l => l.unit.conformation.operator.key),
     model_index: p(l => l.unit.model.modelNum),
     model_label: p(l => l.unit.model.label),
     model_entry_id: p(l => l.unit.model.entryId),
diff --git a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
index 74f5127df14aabdc3ba3ec71e6ca40ba1c92804c..2585e4c850423cf0558aeba61d46ce7956e30b80 100644
--- a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
+++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
@@ -20,6 +20,7 @@ import { InterUnitGraph } from '../../../../../mol-math/graph/inter-unit-graph';
 import { StructConn } from '../../../../../mol-model-formats/structure/property/bonds/struct_conn';
 import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common';
 import { Model } from '../../../model';
+import { sortedCantorPairing } from '../../../../../mol-data/util';
 
 // avoiding namespace lookup improved performance in Chrome (Aug 2020)
 const v3distance = Vec3.distance;
@@ -71,6 +72,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
     const testDistanceSq = (bRadius + maxRadius) * (bRadius + maxRadius);
 
     builder.startUnitPair(unitA.id, unitB.id);
+    const opPairKey = sortedCantorPairing(unitA.conformation.operator.key, unitB.conformation.operator.key);
 
     for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atomsA[_aI];
@@ -80,7 +82,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
 
         if (!props.forceCompute && indexPairs) {
             const { maxDistance } = indexPairs;
-            const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
+            const { offset, b, edgeProps: { order, distance, flag, key, operator } } = indexPairs.bonds;
 
             const srcA = sourceIndex.value(aI);
             const aeI = getElementIdx(type_symbolA.value(aI));
@@ -90,6 +92,9 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
                 const _bI = SortedArray.indexOf(unitB.elements, bI) as StructureElement.UnitIndex;
                 if (_bI < 0) continue;
 
+                const op = operator[i];
+                if (op >= 0 && opPairKey !== op) continue;
+
                 const beI = getElementIdx(type_symbolA.value(bI));
 
                 const d = distance[i];
diff --git a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
index 29fd505381b21fd4791c49718f3900b8eeb001cb..67d89caac6753787379ea4bb84b6fe1860634f29 100644
--- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
+++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
@@ -20,6 +20,7 @@ import { Vec3 } from '../../../../../mol-math/linear-algebra';
 import { ElementIndex } from '../../../model/indexing';
 import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common';
 import { Model } from '../../../model/model';
+import { sortedCantorPairing } from '../../../../../mol-data/util';
 
 // avoiding namespace lookup improved performance in Chrome (Aug 2020)
 const v3distance = Vec3.distance;
@@ -55,7 +56,7 @@ function findIndexPairBonds(unit: Unit.Atomic) {
     const { type_symbol } = unit.model.atomicHierarchy.atoms;
     const atomCount = unit.elements.length;
     const { maxDistance } = indexPairs;
-    const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
+    const { offset, b, edgeProps: { order, distance, flag, key, operator } } = indexPairs.bonds;
 
     const { atomSourceIndex: sourceIndex } = unit.model.atomicHierarchy;
     const { invertedIndex } = Model.getInvertedAtomSourceIndex(unit.model);
@@ -66,6 +67,8 @@ function findIndexPairBonds(unit: Unit.Atomic) {
     const orders: number[] = [];
     const keys: number[] = [];
 
+    const opPairKey = sortedCantorPairing(unit.conformation.operator.key, unit.conformation.operator.key);
+
     for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atoms[_aI];
         const aeI = getElementIdx(type_symbol.value(aI));
@@ -80,6 +83,9 @@ function findIndexPairBonds(unit: Unit.Atomic) {
             const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex;
             if (_bI < 0) continue;
 
+            const op = operator[i];
+            if (op >= 0 && opPairKey !== op) continue;
+
             const beI = getElementIdx(type_symbol.value(bI));
 
             const d = distance[i];
diff --git a/src/mol-script/language/symbol-table/structure-query.ts b/src/mol-script/language/symbol-table/structure-query.ts
index 209da4376e81c413caada96b2a2b544146e3de48..e7125a35814c22831970ee0571f198158e9df89a 100644
--- a/src/mol-script/language/symbol-table/structure-query.ts
+++ b/src/mol-script/language/symbol-table/structure-query.ts
@@ -273,6 +273,7 @@ const atomProperty = {
 
         sourceIndex: atomProp(Type.Num, 'Index of the atom/element in the input file.'),
         operatorName: atomProp(Type.Str, 'Name of the symmetry operator applied to this element.'),
+        operatorKey: atomProp(Type.Num, 'Key of the symmetry operator applied to this element.'),
         modelIndex: atomProp(Type.Num, 'Index of the model in the input file.'),
         modelLabel: atomProp(Type.Str, 'Label/header of the model in the input file.')
     },
diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts
index deab2d4baa0c10ab7fc3641b258ef8a1c7a0cce2..a87b23c7e6509c4324585904548578e9f8f10dd6 100644
--- a/src/mol-script/runtime/query/table.ts
+++ b/src/mol-script/runtime/query/table.ts
@@ -299,6 +299,7 @@ const symbols = [
     D(MolScript.structureQuery.atomProperty.core.z, atomProp(StructureProperties.atom.z)),
     D(MolScript.structureQuery.atomProperty.core.sourceIndex, atomProp(StructureProperties.atom.sourceIndex)),
     D(MolScript.structureQuery.atomProperty.core.operatorName, atomProp(StructureProperties.unit.operator_name)),
+    D(MolScript.structureQuery.atomProperty.core.operatorKey, atomProp(StructureProperties.unit.operator_key)),
     D(MolScript.structureQuery.atomProperty.core.modelIndex, atomProp(StructureProperties.unit.model_index)),
     D(MolScript.structureQuery.atomProperty.core.modelLabel, atomProp(StructureProperties.unit.model_label)),
     D(MolScript.structureQuery.atomProperty.core.atomKey, (ctx, xs) => {
diff --git a/src/mol-script/script/mol-script/symbols.ts b/src/mol-script/script/mol-script/symbols.ts
index c4bd1cfe251bba567bfdd89eabc319cea26ece99..feb891928cd41da4977ee58ffee8132a15ed0601 100644
--- a/src/mol-script/script/mol-script/symbols.ts
+++ b/src/mol-script/script/mol-script/symbols.ts
@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { UniqueArray } from '../../../mol-data/generic';
@@ -205,6 +206,7 @@ export const SymbolTable = [
             Alias(MolScript.structureQuery.atomProperty.core.z, 'atom.z'),
             Alias(MolScript.structureQuery.atomProperty.core.sourceIndex, 'atom.src-index'),
             Alias(MolScript.structureQuery.atomProperty.core.operatorName, 'atom.op-name'),
+            Alias(MolScript.structureQuery.atomProperty.core.operatorKey, 'atom.op-key'),
             Alias(MolScript.structureQuery.atomProperty.core.modelIndex, 'atom.model-index'),
             Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'),
             Alias(MolScript.structureQuery.atomProperty.core.atomKey, 'atom.key'),
@@ -253,6 +255,7 @@ export const SymbolTable = [
             'Bond Properties',
             Alias(MolScript.structureQuery.bondProperty.order, 'bond.order'),
             Alias(MolScript.structureQuery.bondProperty.length, 'bond.length'),
+            Alias(MolScript.structureQuery.bondProperty.key, 'bond.key'),
             Alias(MolScript.structureQuery.bondProperty.atomA, 'bond.atom-a'),
             Alias(MolScript.structureQuery.bondProperty.atomB, 'bond.atom-b'),
             Macro(MSymbol('bond.is', Arguments.List(StructureQueryTypes.BondFlag), Type.Bool,