Browse Source

use only unit ids in inter unit graph

not units themselves, so the graph can be reused for structures with same topology but different coordinates
Alexander Rose 4 years ago
parent
commit
eb71e2c606

+ 6 - 4
src/cli/structure-info/model.ts

@@ -99,15 +99,17 @@ export function printBonds(structure: Structure, showIntra: boolean, showInter:
         for (const unit of structure.units) {
             if (!Unit.isAtomic(unit)) continue;
 
-            for (const pairBonds of bonds.getConnectedUnits(unit)) {
+            for (const pairBonds of bonds.getConnectedUnits(unit.id)) {
                 if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
 
-                const { unitA, unitB } = pairBonds;
-                console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
+                const { unitA, unitB, edgeCount } = pairBonds;
+                const uA = structure.unitMap.get(unitA);
+                const uB = structure.unitMap.get(unitB);
+                console.log(`${unitA} - ${unitB}: ${edgeCount} bond(s)`);
 
                 for (const aI of pairBonds.connectedIndices) {
                     for (const bond of pairBonds.getEdges(aI)) {
-                        console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[bond.indexB])}`);
+                        console.log(`${atomLabel(uA.model, uA.elements[aI])} -- ${atomLabel(uB.model, uB.elements[bond.indexB])}`);
                     }
                 }
             }

+ 3 - 3
src/extensions/rcsb/validation-report/prop.ts

@@ -187,7 +187,7 @@ type InterUnitClashesProps = {
 }
 
 export type IntraUnitClashes = IntAdjacencyGraph<UnitIndex, IntraUnitClashesProps>
-export type InterUnitClashes = InterUnitGraph<Unit.Atomic, UnitIndex, InterUnitClashesProps>
+export type InterUnitClashes = InterUnitGraph<number, UnitIndex, InterUnitClashesProps>
 
 export interface Clashes {
     readonly interUnit: InterUnitClashes
@@ -195,7 +195,7 @@ export interface Clashes {
 }
 
 function createInterUnitClashes(structure: Structure, clashes: ValidationReport['clashes']) {
-    const builder = new InterUnitGraph.Builder<Unit.Atomic, UnitIndex, InterUnitClashesProps>();
+    const builder = new InterUnitGraph.Builder<number, UnitIndex, InterUnitClashesProps>();
     const { a, b, edgeProps: { id, magnitude, distance } } = clashes;
 
     const pA = Vec3(), pB = Vec3();
@@ -204,7 +204,7 @@ function createInterUnitClashes(structure: Structure, clashes: ValidationReport[
         const elementsA = unitA.elements;
         const elementsB = unitB.elements;
 
-        builder.startUnitPair(unitA as Unit.Atomic, unitB as Unit.Atomic);
+        builder.startUnitPair(unitA.id, unitB.id);
 
         for (let i = 0, il = clashes.edgeCount * 2; i < il; ++i) {
             // TODO create lookup

+ 13 - 8
src/extensions/rcsb/validation-report/representation.ts

@@ -159,7 +159,8 @@ function createInterUnitClashCylinderMesh(ctx: VisualContext, structure: Structu
         linkCount: edgeCount,
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
             const b = edges[edgeIndex];
-            const uA = b.unitA, uB = b.unitB;
+            const uA = structure.unitMap.get(b.unitA);
+            const uB = structure.unitMap.get(b.unitB);
             uA.conformation.position(uA.elements[b.indexA], posA);
             uB.conformation.position(uB.elements[b.indexB], posB);
         },
@@ -197,11 +198,13 @@ export function InterUnitClashVisual(materialId: number): ComplexVisual<InterUni
     }, materialId);
 }
 
-function getInterClashBoundingSphere(clashes: InterUnitClashes, elements: number[], boundingSphere: Sphere3D) {
+function getInterClashBoundingSphere(structure: Structure, clashes: InterUnitClashes, elements: number[], boundingSphere: Sphere3D) {
     return CentroidHelper.fromPairProvider(elements.length, (i, pA, pB) => {
         const c = clashes.edges[elements[i]];
-        c.unitA.conformation.position(c.unitA.elements[c.indexA], pA);
-        c.unitB.conformation.position(c.unitB.elements[c.indexB], pB);
+        const uA = structure.unitMap.get(c.unitA);
+        const uB = structure.unitMap.get(c.unitB);
+        uA.conformation.position(uA.elements[c.indexA], pA);
+        uB.conformation.position(uB.elements[c.indexB], pB);
     }, boundingSphere);
 }
 
@@ -209,18 +212,20 @@ function getInterClashLabel(structure: Structure, clashes: InterUnitClashes, ele
     const idx = elements[0];
     if (idx === undefined) return '';
     const c = clashes.edges[idx];
+    const uA = structure.unitMap.get(c.unitA);
+    const uB = structure.unitMap.get(c.unitB);
     const mag = c.props.magnitude.toFixed(2);
     const dist = c.props.distance.toFixed(2);
 
     return [
         `Clash id: ${c.props.id} | Magnitude: ${mag} \u212B | Distance: ${dist} \u212B`,
-        bondLabel(Bond.Location(structure, c.unitA, c.indexA, structure, c.unitB, c.indexB))
+        bondLabel(Bond.Location(structure, uA, c.indexA, structure, uB, c.indexB))
     ].join('</br>');
 }
 
 function InterClashLoci(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
     return DataLoci('inter-clashes', clashes, elements,
-        (boundingSphere: Sphere3D) =>  getInterClashBoundingSphere(clashes, elements, boundingSphere),
+        (boundingSphere: Sphere3D) =>  getInterClashBoundingSphere(structure, clashes, elements, boundingSphere),
         () => getInterClashLabel(structure, clashes, elements));
 }
 
@@ -246,8 +251,8 @@ function createInterClashIterator(structure: Structure): LocationIterator {
     const location = StructureElement.Location.create(structure);
     const getLocation = (groupIndex: number) => {
         const clash = clashes.edges[groupIndex];
-        location.unit = clash.unitA;
-        location.element = clash.unitA.elements[clash.indexA];
+        location.unit = structure.unitMap.get(clash.unitA);
+        location.element = location.unit.elements[clash.indexA];
         return location;
     };
     return LocationIterator(groupCount, instanceCount, getLocation, true);

+ 30 - 31
src/mol-math/graph/inter-unit-graph.ts

@@ -9,46 +9,46 @@ import { UniqueArray } from '../../mol-data/generic';
 
 export { InterUnitGraph };
 
-class InterUnitGraph<Unit extends InterUnitGraph.UnitBase, VertexIndex extends number, EdgeProps extends InterUnitGraph.EdgePropsBase = {}> {
+class InterUnitGraph<UnitId extends number, VertexIndex extends number, EdgeProps extends InterUnitGraph.EdgePropsBase = {}> {
     /** Number of inter-unit edges */
     readonly edgeCount: number
     /** Array of inter-unit edges */
-    readonly edges: ReadonlyArray<InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps>>
+    readonly edges: ReadonlyArray<InterUnitGraph.Edge<UnitId, VertexIndex, EdgeProps>>
     private readonly edgeKeyIndex: Map<string, number>
     private readonly vertexKeyIndex: Map<string, number[]>
 
     /** Get an array of unit-pair-edges that are connected to the given unit */
-    getConnectedUnits(unit: Unit): ReadonlyArray<InterUnitGraph.UnitPairEdges<Unit, VertexIndex, EdgeProps>> {
-        if (!this.map.has(unit.id)) return emptyArray;
-        return this.map.get(unit.id)!;
+    getConnectedUnits(unit: UnitId): ReadonlyArray<InterUnitGraph.UnitPairEdges<UnitId, VertexIndex, EdgeProps>> {
+        if (!this.map.has(unit)) return emptyArray;
+        return this.map.get(unit)!;
     }
 
     /** Index into this.edges */
-    getEdgeIndex(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): number {
-        const edgeKey = InterUnitGraph.getEdgeKey<Unit, VertexIndex>(indexA, unitA, indexB, unitB);
+    getEdgeIndex(indexA: VertexIndex, unitA: UnitId, indexB: VertexIndex, unitB: UnitId): number {
+        const edgeKey = InterUnitGraph.getEdgeKey<UnitId, VertexIndex>(indexA, unitA, indexB, unitB);
         const index = this.edgeKeyIndex.get(edgeKey);
         return index !== undefined ? index : -1;
     }
 
     /** Check if edge exists */
-    hasEdge(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): boolean {
+    hasEdge(indexA: VertexIndex, unitA: UnitId, indexB: VertexIndex, unitB: UnitId): boolean {
         return this.getEdgeIndex(indexA, unitA, indexB, unitB) !== -1;
     }
 
     /** Get inter-unit edge given a pair of indices and units */
-    getEdge(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit): InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps> | undefined {
+    getEdge(indexA: VertexIndex, unitA: UnitId, indexB: VertexIndex, unitB: UnitId): InterUnitGraph.Edge<UnitId, VertexIndex, EdgeProps> | undefined {
         const index = this.getEdgeIndex(indexA, unitA, indexB, unitB);
         return index !== -1 ? this.edges[index] : undefined;
     }
 
     /** Indices into this.edges */
-    getEdgeIndices(index: VertexIndex, unit: Unit): ReadonlyArray<number> {
+    getEdgeIndices(index: VertexIndex, unit: UnitId): ReadonlyArray<number> {
         return this.vertexKeyIndex.get(InterUnitGraph.getVertexKey(index, unit)) || [];
     }
 
-    constructor(protected readonly map: Map<number, InterUnitGraph.UnitPairEdges<Unit, VertexIndex, EdgeProps>[]>) {
+    constructor(protected readonly map: Map<number, InterUnitGraph.UnitPairEdges<UnitId, VertexIndex, EdgeProps>[]>) {
         let count = 0;
-        const edges: (InterUnitGraph.Edge<Unit, VertexIndex, EdgeProps>)[] = [];
+        const edges: (InterUnitGraph.Edge<UnitId, VertexIndex, EdgeProps>)[] = [];
         const edgeKeyIndex = new Map<string, number>();
         const vertexKeyIndex = new Map<string, number[]>();
 
@@ -81,7 +81,7 @@ class InterUnitGraph<Unit extends InterUnitGraph.UnitBase, VertexIndex extends n
 }
 
 namespace InterUnitGraph {
-    export class UnitPairEdges<Unit extends UnitBase, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
+    export class UnitPairEdges<UnitId extends number, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
         hasEdges(indexA: VertexIndex) {
             return this.edgeMap.has(indexA);
         }
@@ -92,16 +92,15 @@ namespace InterUnitGraph {
         }
 
         get areUnitsOrdered() {
-            return this.unitA.id < this.unitB.id;
+            return this.unitA < this.unitB;
         }
 
-        constructor(public unitA: Unit, public unitB: Unit,
+        constructor(public unitA: UnitId, public unitB: UnitId,
             public edgeCount: number, public connectedIndices: ReadonlyArray<VertexIndex>,
             private edgeMap: Map<number, EdgeInfo<VertexIndex, EdgeProps>[]>) {
         }
     }
 
-    export type UnitBase = { id: number }
     export type EdgePropsBase = { [name: string]: any }
 
     export interface EdgeInfo<VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
@@ -110,20 +109,20 @@ namespace InterUnitGraph {
         readonly props: EdgeProps
     }
 
-    export interface Edge<Unit extends UnitBase, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
-        readonly unitA: Unit,
-        readonly unitB: Unit,
+    export interface Edge<UnitId extends number, VertexIndex extends number, EdgeProps extends EdgePropsBase = {}> {
+        readonly unitA: UnitId,
+        readonly unitB: UnitId,
         readonly indexA: VertexIndex,
         readonly indexB: VertexIndex,
         readonly props: EdgeProps
     }
 
-    export function getEdgeKey<Unit extends UnitBase, VertexIndex extends number>(indexA: VertexIndex, unitA: Unit, indexB: VertexIndex, unitB: Unit) {
-        return `${indexA}|${unitA.id}|${indexB}|${unitB.id}`;
+    export function getEdgeKey<UnitId extends number, VertexIndex extends number>(indexA: VertexIndex, unitA: UnitId, indexB: VertexIndex, unitB: UnitId) {
+        return `${indexA}|${unitA}|${indexB}|${unitB}`;
     }
 
-    export function getVertexKey<Unit extends UnitBase, VertexIndex extends number>(index: VertexIndex, unit: Unit) {
-        return `${index}|${unit.id}`;
+    export function getVertexKey<UnitId extends number, VertexIndex extends number>(index: VertexIndex, unit: UnitId) {
+        return `${index}|${unit}`;
     }
 
     //
@@ -134,18 +133,18 @@ namespace InterUnitGraph {
     }
 
 
-    export class Builder<Unit extends InterUnitGraph.UnitBase, VertexIndex extends number, EdgeProps extends InterUnitGraph.EdgePropsBase = {}> {
-        private uA: Unit
-        private uB: Unit
+    export class Builder<UnitId extends number, VertexIndex extends number, EdgeProps extends InterUnitGraph.EdgePropsBase = {}> {
+        private uA: UnitId
+        private uB: UnitId
         private mapAB: Map<number, EdgeInfo<VertexIndex, EdgeProps>[]>
         private mapBA: Map<number, EdgeInfo<VertexIndex, EdgeProps>[]>
         private linkedA: UniqueArray<VertexIndex, VertexIndex>
         private linkedB: UniqueArray<VertexIndex, VertexIndex>
         private linkCount: number
 
-        private map = new Map<number, UnitPairEdges<Unit, VertexIndex, EdgeProps>[]>();
+        private map = new Map<number, UnitPairEdges<UnitId, VertexIndex, EdgeProps>[]>();
 
-        startUnitPair(unitA: Unit, unitB: Unit) {
+        startUnitPair(unitA: UnitId, unitB: UnitId) {
             this.uA = unitA;
             this.uB = unitB;
             this.mapAB = new Map();
@@ -157,8 +156,8 @@ namespace InterUnitGraph {
 
         finishUnitPair() {
             if (this.linkCount === 0) return;
-            addMapEntry(this.map, this.uA.id, new UnitPairEdges(this.uA, this.uB, this.linkCount, this.linkedA.array, this.mapAB));
-            addMapEntry(this.map, this.uB.id, new UnitPairEdges(this.uB, this.uA, this.linkCount, this.linkedB.array, this.mapBA));
+            addMapEntry(this.map, this.uA, new UnitPairEdges(this.uA, this.uB, this.linkCount, this.linkedA.array, this.mapAB));
+            addMapEntry(this.map, this.uB, new UnitPairEdges(this.uB, this.uA, this.linkCount, this.linkedB.array, this.mapBA));
         }
 
         add(indexA: VertexIndex, indexB: VertexIndex, props: EdgeProps) {
@@ -169,7 +168,7 @@ namespace InterUnitGraph {
             this.linkCount += 1;
         }
 
-        getMap(): Map<number, InterUnitGraph.UnitPairEdges<Unit, VertexIndex, EdgeProps>[]> {
+        getMap(): Map<number, InterUnitGraph.UnitPairEdges<UnitId, VertexIndex, EdgeProps>[]> {
             return this.map;
         }
     }

+ 6 - 5
src/mol-model-props/computed/chemistry/util.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -34,7 +34,7 @@ export function compId(unit: Unit.Atomic, index: StructureElement.UnitIndex) {
 
 export function interBondCount(structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex): number {
     let count = 0;
-    const indices = structure.interUnitBonds.getEdgeIndices(index, unit);
+    const indices = structure.interUnitBonds.getEdgeIndices(index, unit.id);
     for (let i = 0, il = indices.length; i < il; ++i) {
         const b = structure.interUnitBonds.edges[indices[i]];
         if (BondType.isCovalent(b.props.flag)) count += 1;
@@ -75,7 +75,7 @@ export function intraConnectedTo(unit: Unit.Atomic, indexA: StructureElement.Uni
 }
 
 export function interConnectedTo(structure: Structure, unitA: Unit.Atomic, indexA: StructureElement.UnitIndex, unitB: Unit.Atomic, indexB: StructureElement.UnitIndex) {
-    const b = structure.interUnitBonds.getEdge(indexA, unitA, indexB, unitB);
+    const b = structure.interUnitBonds.getEdge(indexA, unitA.id, indexB, unitB.id);
     return b && BondType.isCovalent(b.props.flag);
 }
 
@@ -86,10 +86,11 @@ export function connectedTo(structure: Structure, unitA: Unit.Atomic, indexA: St
 //
 
 export function eachInterBondedAtom(structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex, cb: (unit: Unit.Atomic, index: StructureElement.UnitIndex) => void): void {
-    const indices = structure.interUnitBonds.getEdgeIndices(index, unit);
+    const indices = structure.interUnitBonds.getEdgeIndices(index, unit.id);
     for (let i = 0, il = indices.length; i < il; ++i) {
         const b = structure.interUnitBonds.edges[indices[i]];
-        if (BondType.isCovalent(b.props.flag)) cb(b.unitB, b.indexB);
+        const uB = structure.unitMap.get(b.unitB) as Unit.Atomic;
+        if (BondType.isCovalent(b.props.flag)) cb(uB, b.indexB);
     }
 }
 

+ 1 - 1
src/mol-model-props/computed/chemistry/valence-model.ts

@@ -73,7 +73,7 @@ export function explicitValence (structure: Structure, unit: Unit.Atomic, index:
         if (BondType.isCovalent(flags[i])) v += order[i];
     }
     // inter-unit bonds
-    structure.interUnitBonds.getEdgeIndices(index, unit).forEach(i => {
+    structure.interUnitBonds.getEdgeIndices(index, unit.id).forEach(i => {
         const b = structure.interUnitBonds.edges[i];
         if (BondType.isCovalent(b.props.flag)) v += b.props.order;
     });

+ 9 - 9
src/mol-model-props/computed/interactions/common.ts

@@ -84,25 +84,25 @@ namespace InteractionsIntraContacts {
 }
 
 export { InteractionsInterContacts };
-class InteractionsInterContacts extends InterUnitGraph<Unit, Features.FeatureIndex, InteractionsInterContacts.Props> {
+class InteractionsInterContacts extends InterUnitGraph<number, Features.FeatureIndex, InteractionsInterContacts.Props> {
     private readonly elementKeyIndex: Map<string, number[]>
 
     getContactIndicesForElement(index: StructureElement.UnitIndex, unit: Unit): ReadonlyArray<number> {
-        return this.elementKeyIndex.get(this.getElementKey(index, unit)) || [];
+        return this.elementKeyIndex.get(this.getElementKey(index, unit.id)) || [];
     }
 
-    private getElementKey(index: StructureElement.UnitIndex, unit: Unit): string {
-        return `${index}|${unit.id}`;
+    private getElementKey(index: StructureElement.UnitIndex, unitId: number): string {
+        return `${index}|${unitId}`;
     }
 
-    constructor(map: Map<number, InterUnitGraph.UnitPairEdges<Unit, Features.FeatureIndex, InteractionsInterContacts.Props>[]>, unitsFeatures: IntMap<Features>) {
+    constructor(map: Map<number, InterUnitGraph.UnitPairEdges<number, Features.FeatureIndex, InteractionsInterContacts.Props>[]>, unitsFeatures: IntMap<Features>) {
         super(map);
 
         let count = 0;
         const elementKeyIndex = new Map<string, number[]>();
 
-        const add = (index: StructureElement.UnitIndex, unit: Unit) => {
-            const vertexKey = this.getElementKey(index, unit);
+        const add = (index: StructureElement.UnitIndex, unitId: number) => {
+            const vertexKey = this.getElementKey(index, unitId);
             const e = elementKeyIndex.get(vertexKey);
             if (e === undefined) elementKeyIndex.set(vertexKey, [count]);
             else e.push(count);
@@ -114,13 +114,13 @@ class InteractionsInterContacts extends InterUnitGraph<Unit, Features.FeatureInd
                     pairEdges.getEdges(indexA).forEach(edgeInfo => {
                         const { unitA, unitB } = pairEdges;
 
-                        const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA.id);
+                        const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA);
                         for (let j = offsetsA[indexA], jl = offsetsA[indexA + 1]; j < jl; ++j) {
                             add(membersA[j], unitA);
                         }
 
                         const { indexB } = edgeInfo;
-                        const { offsets: offsetsB, members: membersB } = unitsFeatures.get(unitB.id);
+                        const { offsets: offsetsB, members: membersB } = unitsFeatures.get(unitB);
                         for (let j = offsetsB[indexB], jl = offsetsB[indexB + 1]; j < jl; ++j) {
                             add(membersB[j], unitB);
                         }

+ 2 - 2
src/mol-model-props/computed/interactions/contacts-builder.ts

@@ -64,11 +64,11 @@ interface InterContactsBuilder {
 
 namespace InterContactsBuilder {
     export function create(): InterContactsBuilder {
-        const builder = new InterUnitGraph.Builder<Unit, Features.FeatureIndex, InteractionsInterContacts.Props>();
+        const builder = new InterUnitGraph.Builder<number, Features.FeatureIndex, InteractionsInterContacts.Props>();
 
         return {
             startUnitPair(unitA: Unit, unitB: Unit) {
-                builder.startUnitPair(unitA, unitB);
+                builder.startUnitPair(unitA.id, unitB.id);
             },
             finishUnitPair() {
                 builder.finishUnitPair();

+ 1 - 1
src/mol-model-props/computed/interactions/interactions.ts

@@ -80,7 +80,7 @@ namespace Interactions {
             const idx = contacts.getDirectedEdgeIndex(indexA, indexB);
             return interactionTypeLabel(contacts.edgeProps.type[idx]);
         } else {
-            const idx = contacts.getEdgeIndex(indexA, unitA, indexB, unitB);
+            const idx = contacts.getEdgeIndex(indexA, unitA.id, indexB, unitB.id);
             return interactionTypeLabel(contacts.edges[idx].props.type);
         }
     }

+ 6 - 4
src/mol-model-props/computed/interactions/refine.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  *
@@ -31,10 +31,12 @@ export function refineInteractions(structure: Structure, interactions: Interacti
 
     for (let i = 0, il = contacts.edgeCount; i < il; ++i) {
         const e = contacts.edges[i];
+        const uA = structure.unitMap.get(e.unitA) as Unit.Atomic;
+        const uB = structure.unitMap.get(e.unitB) as Unit.Atomic;
 
-        const infoA = Features.Info(structure, e.unitA as Unit.Atomic, unitsFeatures.get(e.unitA.id));
+        const infoA = Features.Info(structure, uA, unitsFeatures.get(e.unitA));
         infoA.feature = e.indexA;
-        const infoB = Features.Info(structure, e.unitB as Unit.Atomic, unitsFeatures.get(e.unitB.id));
+        const infoB = Features.Info(structure, uB, unitsFeatures.get(e.unitB));
         infoB.feature = e.indexB;
 
         for (const refiner of contactRefiners) {
@@ -144,7 +146,7 @@ function weakHydrogenBondsRefiner(structure: Structure, interactions: Interactio
         }
 
         // check inter
-        const interIndices = contacts.getEdgeIndices(acc.feature, acc.unit);
+        const interIndices = contacts.getEdgeIndices(acc.feature, acc.unit.id);
         for (let i = 0, il = interIndices.length; i < il; ++i) {
             if (contacts.edges[interIndices[i]].props.type === InteractionType.HydrogenBond) return true;
         }

+ 19 - 15
src/mol-model-props/computed/representations/interactions-inter-unit-cylinder.ts

@@ -38,26 +38,28 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
         linkCount: edgeCount,
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
             const { unitA, indexA, unitB, indexB } = edges[edgeIndex];
-            const fA = unitsFeatures.get(unitA.id);
-            const fB = unitsFeatures.get(unitB.id);
+            const fA = unitsFeatures.get(unitA);
+            const fB = unitsFeatures.get(unitB);
+            const uA = structure.unitMap.get(unitA);
+            const uB = structure.unitMap.get(unitB);
 
             Vec3.set(posA, fA.x[indexA], fA.y[indexA], fA.z[indexA]);
-            Vec3.transformMat4(posA, posA, unitA.conformation.operator.matrix);
+            Vec3.transformMat4(posA, posA, uA.conformation.operator.matrix);
 
             Vec3.set(posB, fB.x[indexB], fB.y[indexB], fB.z[indexB]);
-            Vec3.transformMat4(posB, posB, unitB.conformation.operator.matrix);
+            Vec3.transformMat4(posB, posB, uB.conformation.operator.matrix);
         },
         style: (edgeIndex: number) => LinkStyle.Dashed,
         radius: (edgeIndex: number) => {
             const b = edges[edgeIndex];
-            const fA = unitsFeatures.get(b.unitA.id);
+            const fA = unitsFeatures.get(b.unitA);
             tmpLoc.structure = structure;
-            tmpLoc.unit = b.unitA;
-            tmpLoc.element = b.unitA.elements[fA.members[fA.offsets[b.indexA]]];
+            tmpLoc.unit = structure.unitMap.get(b.unitA);
+            tmpLoc.element = tmpLoc.unit.elements[fA.members[fA.offsets[b.indexA]]];
             const sizeA = theme.size.size(tmpLoc);
-            const fB = unitsFeatures.get(b.unitB.id);
-            tmpLoc.unit = b.unitB;
-            tmpLoc.element = b.unitB.elements[fB.members[fB.offsets[b.indexB]]];
+            const fB = unitsFeatures.get(b.unitB);
+            tmpLoc.unit = structure.unitMap.get(b.unitB);
+            tmpLoc.element = tmpLoc.unit.elements[fB.members[fB.offsets[b.indexB]]];
             const sizeB = theme.size.size(tmpLoc);
             return Math.min(sizeA, sizeB) * sizeFactor;
         },
@@ -102,9 +104,11 @@ function getInteractionLoci(pickingId: PickingId, structure: Structure, id: numb
     if (id === objectId) {
         const interactions = InteractionsProvider.get(structure).value!;
         const c = interactions.contacts.edges[groupId];
+        const unitA = structure.unitMap.get(c.unitA);
+        const unitB = structure.unitMap.get(c.unitB);
         return Interactions.Loci(structure, interactions, [
-            { unitA: c.unitA, indexA: c.indexA, unitB: c.unitB, indexB: c.indexB },
-            { unitA: c.unitB, indexA: c.indexB, unitB: c.unitA, indexB: c.indexA },
+            { unitA: unitA, indexA: c.indexA, unitB: unitB, indexB: c.indexB },
+            { unitA: unitB, indexA: c.indexB, unitB: unitA, indexB: c.indexA },
         ]);
     }
     return EmptyLoci;
@@ -119,7 +123,7 @@ function eachInteraction(loci: Loci, structure: Structure, apply: (interval: Int
         const { contacts } = interactions;
 
         for (const c of loci.elements) {
-            const idx = contacts.getEdgeIndex(c.indexA, c.unitA, c.indexB, c.unitB);
+            const idx = contacts.getEdgeIndex(c.indexA, c.unitA.id, c.indexB, c.unitB.id);
             if (idx !== -1) {
                 if (apply(Interval.ofSingleton(idx))) changed = true;
             }
@@ -137,9 +141,9 @@ function createInteractionsIterator(structure: Structure): LocationIterator {
     const { element } = location;
     const getLocation = (groupIndex: number) => {
         const c = contacts.edges[groupIndex];
-        element.unitA = c.unitA;
+        element.unitA = structure.unitMap.get(c.unitA);
         element.indexA = c.indexA;
-        element.unitB = c.unitB;
+        element.unitB = structure.unitMap.get(c.unitB);
         element.indexB = c.indexB;
         return location;
     };

+ 1 - 1
src/mol-model-props/computed/themes/interaction-type.ts

@@ -86,7 +86,7 @@ export function InteractionTypeColorTheme(ctx: ThemeDataContext, props: PD.Value
                     const idx = links.getDirectedEdgeIndex(indexA, indexB);
                     return typeColor(links.edgeProps.type[idx]);
                 } else {
-                    const idx = contacts.getEdgeIndex(indexA, unitA, indexB, unitB);
+                    const idx = contacts.getEdgeIndex(indexA, unitA.id, indexB, unitB.id);
                     return typeColor(contacts.edges[idx].props.type);
                 }
             }

+ 4 - 3
src/mol-model/structure/query/queries/filters.ts

@@ -249,7 +249,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
         const inputUnit = input.unitMap.get(unit.id) as Unit.Atomic;
 
         const { offset, b, edgeProps: { flags, order } } = inputUnit.bonds;
-        const bondedUnits = interBonds.getConnectedUnits(unit);
+        const bondedUnits = interBonds.getConnectedUnits(unit.id);
         const buCount = bondedUnits.length;
 
         const srcElements = unit.elements;
@@ -278,11 +278,12 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
 
             for (let li = 0; li < buCount; li++) {
                 const lu = bondedUnits[li];
-                const bElements = lu.unitB.elements;
+                const bUnit = structure.unitMap.get(lu.unitB) as Unit.Atomic;
+                const bElements = bUnit.elements;
                 const bonds = lu.getEdges(inputIndex);
                 for (let bi = 0, _bi = bonds.length; bi < _bi; bi++) {
                     const bond = bonds[bi];
-                    atomicBond.b.unit = lu.unitB;
+                    atomicBond.b.unit = bUnit;
                     atomicBond.b.element = bElements[bond.indexB];
                     if (!target.hasElement(atomicBond.b)) continue;
                     if (disjunct && structure.hasElement(atomicBond.b)) continue;

+ 4 - 4
src/mol-model/structure/query/queries/generators.ts

@@ -332,11 +332,11 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
 
         // Process inter unit bonds
         for (const bond of interBonds.edges) {
-            atomicBond.a.unit = bond.unitA;
-            atomicBond.a.element = bond.unitA.elements[bond.indexA];
+            atomicBond.a.unit = structure.unitMap.get(bond.unitA) as Unit.Atomic;
+            atomicBond.a.element = atomicBond.a.unit.elements[bond.indexA];
             atomicBond.aIndex = bond.indexA;
-            atomicBond.b.unit = bond.unitB;
-            atomicBond.b.element = bond.unitB.elements[bond.indexB];
+            atomicBond.b.unit = structure.unitMap.get(bond.unitA) as Unit.Atomic;
+            atomicBond.b.element = atomicBond.b.unit.elements[bond.indexB];
             atomicBond.bIndex = bond.indexB;
             atomicBond.order = bond.props.order;
             atomicBond.type = bond.props.flag;

+ 8 - 7
src/mol-model/structure/query/queries/modifiers.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  */
@@ -390,30 +390,31 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
         }
 
         // Process inter unit bonds
-        for (const bondedUnit of interBonds.getConnectedUnits(inputUnitA)) {
-            const currentUnitB = structure.unitMap.get(bondedUnit.unitB.id);
+        for (const bondedUnit of interBonds.getConnectedUnits(inputUnitA.id)) {
+            const currentUnitB = structure.unitMap.get(bondedUnit.unitB);
+            const inputUnitB = inputStructure.unitMap.get(bondedUnit.unitB) as Unit.Atomic;
 
             for (const aI of bondedUnit.connectedIndices) {
                 // check if the element is in the expanded structure
                 if (!SortedArray.has(unit.elements, inputUnitA.elements[aI])) continue;
 
                 for (const bond of bondedUnit.getEdges(aI)) {
-                    const bElement = bondedUnit.unitB.elements[bond.indexB];
+                    const bElement = inputUnitB.elements[bond.indexB];
 
                     // Check if the element is already present:
-                    if ((currentUnitB && SortedArray.has(currentUnitB.elements, bElement)) || builder.has(bondedUnit.unitB.id, bElement)) continue;
+                    if ((currentUnitB && SortedArray.has(currentUnitB.elements, bElement)) || builder.has(bondedUnit.unitB, bElement)) continue;
 
                     atomicBond.a.unit = inputUnitA;
                     atomicBond.aIndex = aI;
                     atomicBond.a.element = inputUnitA.elements[aI];
-                    atomicBond.b.unit = bondedUnit.unitB;
+                    atomicBond.b.unit = inputUnitB;
                     atomicBond.bIndex = bond.indexB;
                     atomicBond.b.element = bElement;
                     atomicBond.type = bond.props.flag;
                     atomicBond.order = bond.props.order;
 
                     if (atomicBond.test(ctx, true)) {
-                        builder.addToUnit(bondedUnit.unitB.id, bElement);
+                        builder.addToUnit(bondedUnit.unitB, bElement);
                     }
                 }
             }

+ 3 - 2
src/mol-model/structure/structure/carbohydrates/compute.ts

@@ -269,12 +269,13 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
         const unit = structure.units[i];
         if (!Unit.isAtomic(unit)) continue;
 
-        structure.interUnitBonds.getConnectedUnits(unit).forEach(pairBonds => {
+        structure.interUnitBonds.getConnectedUnits(unit.id).forEach(pairBonds => {
             pairBonds.connectedIndices.forEach(indexA => {
                 pairBonds.getEdges(indexA).forEach(({ props, indexB }) => {
                     if (!BondType.isCovalent(props.flag)) return;
 
-                    const { unitA, unitB } = pairBonds;
+                    const unitA = structure.unitMap.get(pairBonds.unitA) as Unit.Atomic;
+                    const unitB = structure.unitMap.get(pairBonds.unitB) as Unit.Atomic;
                     const ringElementIndicesA = getRingElementIndices(unitA, indexA);
                     const ringElementIndicesB = getRingElementIndices(unitB, indexB);
                     if (ringElementIndicesA.length > 0 && ringElementIndicesB.length > 0) {

+ 1 - 1
src/mol-model/structure/structure/structure.ts

@@ -1077,7 +1077,7 @@ namespace Structure {
         for (const unit of structure.units) {
             if (!validUnit(unit)) continue;
 
-            const bs = unit.lookup3d.boundary.sphere;
+            const bs = unit.boundary.sphere;
             Vec3.transformMat4(imageCenter, bs.center, unit.conformation.operator.matrix);
             const closeUnits = lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + maxRadius);
             for (let i = 0; i < closeUnits.count; i++) {

+ 2 - 2
src/mol-model/structure/structure/unit/bonds.ts

@@ -199,7 +199,7 @@ namespace Bond {
             this.unit = unit;
             this.index = index;
 
-            this.interBondIndices = structure.interUnitBonds.getEdgeIndices(index, unit);
+            this.interBondIndices = structure.interUnitBonds.getEdgeIndices(index, unit.id);
             this.interBondCount = this.interBondIndices.length;
             this.interBondIndex = 0;
 
@@ -218,7 +218,7 @@ namespace Bond {
                 this.intraBondIndex += 1;
             } else if (this.interBondIndex < this.interBondCount) {
                 const b = this.structure.interUnitBonds.edges[this.interBondIndex];
-                this.current.otherUnit = b.unitA !== this.unit ? b.unitA : b.unitB;
+                this.current.otherUnit = this.structure.unitMap.get(b.unitA !== this.unit.id ? b.unitA : b.unitB) as Unit.Atomic;
                 this.current.otherIndex = b.indexA !== this.index ? b.indexA : b.indexB;
                 this.current.type = b.props.flag;
                 this.current.order = b.props.order;

+ 5 - 5
src/mol-model/structure/structure/unit/bonds/data.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2020 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>
@@ -20,20 +20,20 @@ namespace IntraUnitBonds {
 
 type InterUnitEdgeProps = { readonly order: number, readonly flag: BondType.Flag }
 
-class InterUnitBonds extends InterUnitGraph<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps> {
+class InterUnitBonds extends InterUnitGraph<number, StructureElement.UnitIndex, InterUnitEdgeProps> {
     /** Get inter-unit bond given a bond-location */
     getBondFromLocation(l: Bond.Location) {
-        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdge(l.aIndex, l.aUnit, l.bIndex, l.bUnit) : undefined;
+        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdge(l.aIndex, l.aUnit.id, l.bIndex, l.bUnit.id) : undefined;
     }
 
     /** Get inter-unit bond index given a bond-location */
     getBondIndexFromLocation(l: Bond.Location) {
-        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdgeIndex(l.aIndex, l.aUnit, l.bIndex, l.bUnit) : -1;
+        return Unit.isAtomic(l.aUnit) && Unit.isAtomic(l.bUnit) ? this.getEdgeIndex(l.aIndex, l.aUnit.id, l.bIndex, l.bUnit.id) : -1;
     }
 }
 
 namespace InterUnitBonds {
-    export class UnitPairBonds extends InterUnitGraph.UnitPairEdges<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps> {}
+    export class UnitPairBonds extends InterUnitGraph.UnitPairEdges<number, StructureElement.UnitIndex, InterUnitEdgeProps> {}
     export type BondInfo = InterUnitGraph.EdgeInfo<StructureElement.UnitIndex, InterUnitEdgeProps>
 }
 

+ 3 - 3
src/mol-model/structure/structure/unit/bonds/inter-compute.ts

@@ -33,7 +33,7 @@ function getDistance(unitA: Unit.Atomic, indexA: ElementIndex, unitB: Unit.Atomi
 const _imageTransform = Mat4();
 const _imageA = Vec3();
 
-function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComputationProps, builder: InterUnitGraph.Builder<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps>) {
+function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComputationProps, builder: InterUnitGraph.Builder<number, StructureElement.UnitIndex, InterUnitEdgeProps>) {
     const { elements: atomsA, residueIndex: residueIndexA } = unitA;
     const { x: xA, y: yA, z: zA } = unitA.model.atomicConformation;
     const { elements: atomsB, residueIndex: residueIndexB } = unitB;
@@ -58,7 +58,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
     const { center: bCenter, radius: bRadius } = unitB.boundary.sphere;
     const testDistanceSq = (bRadius + MAX_RADIUS) * (bRadius + MAX_RADIUS);
 
-    builder.startUnitPair(unitA, unitB);
+    builder.startUnitPair(unitA.id, unitB.id);
 
     for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
         const aI = atomsA[_aI];
@@ -171,7 +171,7 @@ export interface InterBondComputationProps extends BondComputationProps {
 }
 
 function findBonds(structure: Structure, props: InterBondComputationProps) {
-    const builder = new InterUnitGraph.Builder<Unit.Atomic, StructureElement.UnitIndex, InterUnitEdgeProps>();
+    const builder = new InterUnitGraph.Builder<number, StructureElement.UnitIndex, InterUnitEdgeProps>();
 
     if (props.noCompute) {
         // TODO add function that only adds bonds defined in structConn and avoids using

+ 17 - 12
src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts

@@ -42,14 +42,18 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
         linkCount: edgeCount,
         referencePosition: (edgeIndex: number) => {
             const b = edges[edgeIndex];
-            let unitA: Unit, unitB: Unit;
+            let unitA: Unit.Atomic, unitB: Unit.Atomic;
             let indexA: StructureElement.UnitIndex, indexB: StructureElement.UnitIndex;
-            if (b.unitA.id < b.unitB.id) {
-                unitA = b.unitA, unitB = b.unitB;
-                indexA = b.indexA, indexB = b.indexB;
-            } else if (b.unitA.id > b.unitB.id) {
-                unitA = b.unitB, unitB = b.unitA;
-                indexA = b.indexB, indexB = b.indexA;
+            if (b.unitA < b.unitB) {
+                unitA = structure.unitMap.get(b.unitA) as Unit.Atomic;
+                unitB = structure.unitMap.get(b.unitB) as Unit.Atomic;
+                indexA = b.indexA;
+                indexB = b.indexB;
+            } else if (b.unitA > b.unitB) {
+                unitA = structure.unitMap.get(b.unitB) as Unit.Atomic;
+                unitB = structure.unitMap.get(b.unitA) as Unit.Atomic;
+                indexA = b.indexB;
+                indexB = b.indexA;
             } else {
                 throw new Error('same units in createInterUnitBondCylinderMesh');
             }
@@ -57,7 +61,8 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
         },
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
             const b = edges[edgeIndex];
-            const uA = b.unitA, uB = b.unitB;
+            const uA = structure.unitMap.get(b.unitA);
+            const uB = structure.unitMap.get(b.unitB);
             uA.conformation.position(uA.elements[b.indexA], posA);
             uB.conformation.position(uB.elements[b.indexB], posB);
         },
@@ -78,11 +83,11 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
         radius: (edgeIndex: number) => {
             const b = edges[edgeIndex];
             tmpLoc.structure = structure;
-            tmpLoc.unit = b.unitA;
-            tmpLoc.element = b.unitA.elements[b.indexA];
+            tmpLoc.unit = structure.unitMap.get(b.unitA);
+            tmpLoc.element = tmpLoc.unit.elements[b.indexA];
             const sizeA = theme.size.size(tmpLoc);
-            tmpLoc.unit = b.unitB;
-            tmpLoc.element = b.unitB.elements[b.indexB];
+            tmpLoc.unit = structure.unitMap.get(b.unitB);
+            tmpLoc.element = tmpLoc.unit.elements[b.indexB];
             const sizeB = theme.size.size(tmpLoc);
             return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio;
         },

+ 18 - 13
src/mol-repr/structure/visual/bond-inter-unit-line.ts

@@ -42,22 +42,27 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
         linkCount: edgeCount,
         referencePosition: (edgeIndex: number) => {
             const b = edges[edgeIndex];
-            let unitA: Unit, unitB: Unit;
+            let unitA: Unit.Atomic, unitB: Unit.Atomic;
             let indexA: StructureElement.UnitIndex, indexB: StructureElement.UnitIndex;
-            if (b.unitA.id < b.unitB.id) {
-                unitA = b.unitA, unitB = b.unitB;
-                indexA = b.indexA, indexB = b.indexB;
-            } else if (b.unitA.id > b.unitB.id) {
-                unitA = b.unitB, unitB = b.unitA;
-                indexA = b.indexB, indexB = b.indexA;
+            if (b.unitA < b.unitB) {
+                unitA = structure.unitMap.get(b.unitA) as Unit.Atomic;
+                unitB = structure.unitMap.get(b.unitB) as Unit.Atomic;
+                indexA = b.indexA;
+                indexB = b.indexB;
+            } else if (b.unitA > b.unitB) {
+                unitA = structure.unitMap.get(b.unitB) as Unit.Atomic;
+                unitB = structure.unitMap.get(b.unitA) as Unit.Atomic;
+                indexA = b.indexB;
+                indexB = b.indexA;
             } else {
-                throw new Error('same units in createInterUnitBondCylinderMesh');
+                throw new Error('same units in createInterUnitBondLines');
             }
             return setRefPosition(tmpRef, structure, unitA, indexA) || setRefPosition(tmpRef, structure, unitB, indexB);
         },
         position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
             const b = edges[edgeIndex];
-            const uA = b.unitA, uB = b.unitB;
+            const uA = structure.unitMap.get(b.unitA);
+            const uB = structure.unitMap.get(b.unitB);
             uA.conformation.position(uA.elements[b.indexA], posA);
             uB.conformation.position(uB.elements[b.indexB], posB);
         },
@@ -78,11 +83,11 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
         radius: (edgeIndex: number) => {
             const b = edges[edgeIndex];
             tmpLoc.structure = structure;
-            tmpLoc.unit = b.unitA;
-            tmpLoc.element = b.unitA.elements[b.indexA];
+            tmpLoc.unit = structure.unitMap.get(b.unitA);
+            tmpLoc.element = tmpLoc.unit.elements[b.indexA];
             const sizeA = theme.size.size(tmpLoc);
-            tmpLoc.unit = b.unitB;
-            tmpLoc.element = b.unitB.elements[b.indexB];
+            tmpLoc.unit = structure.unitMap.get(b.unitB);
+            tmpLoc.element = tmpLoc.unit.elements[b.indexB];
             const sizeB = theme.size.size(tmpLoc);
             return Math.min(sizeA, sizeB) * sizeFactor;
         },

+ 12 - 15
src/mol-repr/structure/visual/util/bond.ts

@@ -78,7 +78,8 @@ export function makeInterBondIgnoreTest(structure: Structure, props: BondProps):
 
     const ignoreHydrogen = (edgeIndex: number) => {
         const b = edges[edgeIndex];
-        const uA = b.unitA, uB = b.unitB;
+        const uA = structure.unitMap.get(b.unitA);
+        const uB = structure.unitMap.get(b.unitB);
         return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB]);
     };
 
@@ -113,8 +114,8 @@ export namespace BondIterator {
         const location = StructureElement.Location.create(structure);
         const getLocation = (groupIndex: number) => {
             const bond = structure.interUnitBonds.edges[groupIndex];
-            location.unit = bond.unitA;
-            location.element = bond.unitA.elements[bond.indexA];
+            location.unit = structure.unitMap.get(bond.unitA);
+            location.element = location.unit.elements[bond.indexA];
             return location;
         };
         return LocationIterator(groupCount, instanceCount, getLocation, true);
@@ -189,16 +190,12 @@ export function eachIntraBond(loci: Loci, structureGroup: StructureGroup, apply:
 export function getInterBondLoci(pickingId: PickingId, structure: Structure, id: number) {
     const { objectId, groupId } = pickingId;
     if (id === objectId) {
-        const bond = structure.interUnitBonds.edges[groupId];
+        const b = structure.interUnitBonds.edges[groupId];
+        const uA = structure.unitMap.get(b.unitA);
+        const uB = structure.unitMap.get(b.unitB);
         return Bond.Loci(structure, [
-            Bond.Location(
-                structure, bond.unitA, bond.indexA as StructureElement.UnitIndex,
-                structure, bond.unitB, bond.indexB as StructureElement.UnitIndex
-            ),
-            Bond.Location(
-                structure, bond.unitB, bond.indexB as StructureElement.UnitIndex,
-                structure, bond.unitA, bond.indexA as StructureElement.UnitIndex
-            )
+            Bond.Location(structure, uA, b.indexA, structure, uB, b.indexB),
+            Bond.Location(structure, uB, b.indexB, structure, uA, b.indexA)
         ]);
     }
     return EmptyLoci;
@@ -224,14 +221,14 @@ export function eachInterBond(loci: Loci, structure: Structure, apply: (interval
         for (const e of loci.elements) {
             const { unit } = e;
             if (!Unit.isAtomic(unit)) continue;
-            structure.interUnitBonds.getConnectedUnits(unit).forEach(b => {
-                const otherLociIndices = map.get(b.unitB.id);
+            structure.interUnitBonds.getConnectedUnits(unit.id).forEach(b => {
+                const otherLociIndices = map.get(b.unitB);
                 if (otherLociIndices) {
                     OrderedSet.forEach(e.indices, v => {
                         if (!b.connectedIndices.includes(v)) return;
                         b.getEdges(v).forEach(bi => {
                             if (!isMarking || OrderedSet.has(otherLociIndices, bi.indexB)) {
-                                const idx = structure.interUnitBonds.getEdgeIndex(v, unit, bi.indexB, b.unitB);
+                                const idx = structure.interUnitBonds.getEdgeIndex(v, unit.id, bi.indexB, b.unitB);
                                 if (apply(Interval.ofSingleton(idx))) changed = true;
                             }
                         });