diff --git a/src/mol-data/int/_spec/sorted-ranges.spec.ts b/src/mol-data/int/_spec/sorted-ranges.spec.ts
index 307b3c08b497b3f655e490e11c4112e3d8049cce..84075305edfe65c901c316a2b287bbc94f0f7314 100644
--- a/src/mol-data/int/_spec/sorted-ranges.spec.ts
+++ b/src/mol-data/int/_spec/sorted-ranges.spec.ts
@@ -17,7 +17,7 @@ describe('rangesArray', () => {
         it(`iterator, ${name}`, () => {
             const rangesIt = SortedRanges.transientSegments(ranges, set)
             const { index, start, end } = expectedValues
-    
+
             let i = 0
             while (rangesIt.hasNext) {
                 const segment = rangesIt.move()
@@ -41,7 +41,7 @@ describe('rangesArray', () => {
 
     testIterator('two ranges',
         SortedRanges.ofSortedRanges([1, 2, 3, 4]),
-        OrderedSet.ofBounds(1, 4),
+        OrderedSet.ofBounds(1, 5),
         { index: [0, 1], start: [0, 2], end: [2, 4] }
     )
     testIterator('first range',
@@ -62,7 +62,7 @@ describe('rangesArray', () => {
     testIterator('set in second range and beyond',
         SortedRanges.ofSortedRanges([1, 2, 3, 4]),
         SortedArray.ofSortedArray([3, 10]),
-        { index: [1], start: [0], end: [2] }
+        { index: [1], start: [0], end: [1] }
     )
     testIterator('length 1 range',
         SortedRanges.ofSortedRanges([1, 1, 3, 4]),
diff --git a/src/mol-data/int/impl/sorted-array.ts b/src/mol-data/int/impl/sorted-array.ts
index faa32048fca2e968542643436b8ca878dbfccc24..8c81e7940adde281f8950fdd78c71bbbb233cee0 100644
--- a/src/mol-data/int/impl/sorted-array.ts
+++ b/src/mol-data/int/impl/sorted-array.ts
@@ -65,6 +65,11 @@ export function areEqual(a: Nums, b: Nums) {
     return true;
 }
 
+/**
+ * Returns 0 if `v` is smaller or equal the first element of `xs`
+ * Returns length of `xs` if `v` is bigger than the last element of `xs`
+ * Otherwise returns the first index where the value of `xs` is equal or bigger than `v`
+ */
 export function findPredecessorIndex(xs: Nums, v: number) {
     const len = xs.length;
     if (v <= xs[0]) return 0;
diff --git a/src/mol-data/int/ordered-set.ts b/src/mol-data/int/ordered-set.ts
index 4ec45b17f185c1343a44e454c641879dbb58ae92..d8e7f705d5c0d7a16991f24c104970a6b1fbd80d 100644
--- a/src/mol-data/int/ordered-set.ts
+++ b/src/mol-data/int/ordered-set.ts
@@ -21,6 +21,7 @@ namespace OrderedSet {
     export const has: <T extends number = number>(set: OrderedSet<T>, x: T) => boolean = Base.has as any;
     /** Returns the index of `x` in `set` or -1 if not found. */
     export const indexOf: <T extends number = number>(set: OrderedSet<T>, x: T) => number = Base.indexOf as any;
+    /** Returns the value in `set` at index `i`. */
     export const getAt: <T extends number = number>(set: OrderedSet<T>, i: number) => T = Base.getAt as any;
 
     export const min: <T extends number = number>(set: OrderedSet<T>) => T = Base.min as any;
@@ -40,6 +41,11 @@ namespace OrderedSet {
     /** Returns elements of `a` that are not in `b`, i.e `a` - `b` */
     export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any;
 
+    /**
+     * Returns 0 if `x` is smaller or equal the first element of `set`
+     * Returns length of `set` if `x` is bigger than the last element of `set`
+     * Otherwise returns the first index where the value of `set` is equal or bigger than `x`
+     */
     export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any;
     export const findPredecessorIndexInInterval: <T extends number = number>(set: OrderedSet<T>, x: T, range: Interval) => number = Base.findPredecessorIndexInInterval as any;
     export const findRange: <T extends number = number>(set: OrderedSet<T>, min: T, max: T) => Interval = Base.findRange as any;
diff --git a/src/mol-data/int/segmentation.ts b/src/mol-data/int/segmentation.ts
index 7062733eeaf6ae039febf1767eba5b886a9018cf..8d26c5f1f67e023fbd225e9fe2e9084e741e0d50 100644
--- a/src/mol-data/int/segmentation.ts
+++ b/src/mol-data/int/segmentation.ts
@@ -18,7 +18,7 @@ namespace Segmentation {
     export const getSegment: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, value: T) => number = Impl.getSegment as any;
     export const projectValue: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any;
 
-    // Segment iterator that mutates a single segment object to mark all the segments.
+    /** Segment iterator that mutates a single segment object to mark all the segments. */
     export const transientSegments: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, segment?: Segment) => Impl.SegmentIterator<I> = Impl.segments as any;
 
     export type SegmentIterator<I extends number = number> = Impl.SegmentIterator<I>
diff --git a/src/mol-data/int/sorted-ranges.ts b/src/mol-data/int/sorted-ranges.ts
index 967595e0f670ba6840665ca03ebce519fcdde4bb..754946a5b9221a83bb2f9b5e6dff6d0c49c37a14 100644
--- a/src/mol-data/int/sorted-ranges.ts
+++ b/src/mol-data/int/sorted-ranges.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -23,6 +23,46 @@ namespace SortedRanges {
         }
         return size
     }
+    export function count<T extends number = number>(ranges: SortedRanges<T>) { return ranges.length / 2 }
+
+    export function startAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
+        return ranges[index * 2]
+    }
+    export function endAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
+        return ranges[index * 2 + 1] + 1
+    }
+
+    export function minAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
+        return ranges[index * 2]
+    }
+    export function maxAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
+        return ranges[index * 2 + 1]
+    }
+
+    /** Returns if a value of `set` is included in `ranges` */
+    export function has<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
+        return firstIntersectionIndex(ranges, set) !== -1
+    }
+
+    /** Returns if a value of `set` is included in `ranges` from given index */
+    export function hasFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number) {
+        return firstIntersectionIndexFrom(ranges, set, from) !== -1
+    }
+
+    export function firstIntersectionIndex<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>): number {
+        return firstIntersectionIndexFrom(ranges, set, 0)
+    }
+
+    export function firstIntersectionIndexFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number): number {
+        if (minAt(ranges, from) > OrderedSet.max(set) || max(ranges) < OrderedSet.min(set)) return -1
+
+        for (let i = from, il = count(ranges); i < il; ++i) {
+            const interval = Interval.ofRange(minAt(ranges, i), maxAt(ranges, i))
+            if (OrderedSet.areIntersecting(interval, set)) return i
+        }
+
+        return -1
+    }
 
     export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
         return new Iterator<T, I>(ranges, set)
@@ -32,53 +72,27 @@ namespace SortedRanges {
         private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T }
 
         private curIndex = 0
-        private maxIndex = 0
-        private interval: Interval<T>
-        private curMin: T = 0 as T
 
         hasNext: boolean = false;
 
-        updateInterval() {
-            this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1])
-        }
-
-        updateValue() {
-            this.value.index = this.curIndex / 2 as I
-            this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex])
-            this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1
+        private updateValue() {
+            this.value.index = this.curIndex as I
+            this.value.start = OrderedSet.findPredecessorIndex(this.set, startAt(this.ranges, this.curIndex))
+            this.value.end = OrderedSet.findPredecessorIndex(this.set, endAt(this.ranges, this.curIndex))
         }
 
         move() {
             if (this.hasNext) {
                 this.updateValue()
-                while (this.curIndex <= this.maxIndex) {
-                    this.curIndex += 2
-                    this.curMin = Interval.end(this.interval)
-                    this.updateInterval()
-                    if (Interval.min(this.interval) >= this.curMin && OrderedSet.areIntersecting(this.interval, this.set)) break
-                }
-                this.hasNext = this.curIndex <= this.maxIndex
+                this.curIndex = firstIntersectionIndexFrom(this.ranges, this.set, this.curIndex + 1)
+                this.hasNext = this.curIndex !== -1
             }
             return this.value;
         }
 
-        getRangeIndex(value: number) {
-            const index = SortedArray.findPredecessorIndex(this.ranges, value)
-            return (index % 2 === 1) ? index - 1 : index
-        }
-
         constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) {
-            // TODO cleanup, refactor to make it clearer
-            const min = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.min(set))
-            const max = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.max(set) + 1)
-            if (ranges.length && min !== max) {
-                this.curIndex = this.getRangeIndex(OrderedSet.min(set))
-                this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set)))
-                this.curMin = this.ranges[this.curIndex]
-                this.updateInterval()
-            }
-
-            this.hasNext = ranges.length > 0 && min !== max && this.curIndex <= this.maxIndex
+            this.curIndex = firstIntersectionIndex(ranges, set)
+            this.hasNext = this.curIndex !== -1
         }
     }
 }
diff --git a/src/mol-geo/geometry/overpaint-data.ts b/src/mol-geo/geometry/overpaint-data.ts
index b97e19a4abfcc6f8f09cb8c9df01cf58b8082781..08e753594ec85ced455e351a65efc360b316d7ac 100644
--- a/src/mol-geo/geometry/overpaint-data.ts
+++ b/src/mol-geo/geometry/overpaint-data.ts
@@ -25,6 +25,7 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe
 
 export function clearOverpaint(array: Uint8Array, start: number, end: number) {
     array.fill(0, start * 4, end * 4)
+    return true
 }
 
 export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData {
diff --git a/src/mol-model/structure/export/categories/modified-residues.ts b/src/mol-model/structure/export/categories/modified-residues.ts
index 2e9183f88784900b765cde496a00a8255d7df7a3..39eb324f85c6834fde2c6f01c8b11907abeca371 100644
--- a/src/mol-model/structure/export/categories/modified-residues.ts
+++ b/src/mol-model/structure/export/categories/modified-residues.ts
@@ -28,7 +28,7 @@ const pdbx_struct_mod_residue_fields: CifField<number, StructureElement[]>[] = [
 ];
 
 function getModifiedResidues({ structures }: CifExportContext): StructureElement[] {
-    // TODO: can different models have differnt modified residues?
+    // TODO: can different models (in the same mmCIF file) have different modified residues?
     const structure = structures[0], model = structure.model;
     const map = model.properties.modifiedResidues.parentId;
     if (!map.size) return [];
diff --git a/src/mol-model/structure/model/types.ts b/src/mol-model/structure/model/types.ts
index 1d48173bf56c23c33555852bf3ba4e43d33a2708..036fd11d0cab18c9d66591b80ae058ced91e8e1d 100644
--- a/src/mol-model/structure/model/types.ts
+++ b/src/mol-model/structure/model/types.ts
@@ -178,11 +178,11 @@ export const AminoAcidNames = new Set([
 export const RnaBaseNames = new Set([ 'A', 'C', 'T', 'G', 'I', 'U' ])
 export const DnaBaseNames = new Set([ 'DA', 'DC', 'DT', 'DG', 'DI', 'DU' ])
 export const PeptideBaseNames = new Set([ 'APN', 'CPN', 'TPN', 'GPN' ])
-export const PurinBaseNames = new Set([ 'A', 'G', 'DA', 'DG', 'DI', 'APN', 'GPN' ])
+export const PurineBaseNames = new Set([ 'A', 'G', 'DA', 'DG', 'DI', 'APN', 'GPN' ])
 export const PyrimidineBaseNames = new Set([ 'C', 'T', 'U', 'DC', 'DT', 'DU', 'CPN', 'TPN' ])
 export const BaseNames = SetUtils.unionMany(RnaBaseNames, DnaBaseNames, PeptideBaseNames)
 
-export const isPurinBase = (compId: string) => PurinBaseNames.has(compId.toUpperCase())
+export const isPurineBase = (compId: string) => PurineBaseNames.has(compId.toUpperCase())
 export const isPyrimidineBase = (compId: string) => PyrimidineBaseNames.has(compId.toUpperCase())
 
 /** get the molecule type from component type and id */
diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts
index 2630573e46291f6f11c7ddc05807f52c2afdff48..4b85cdc0fed86bc65f7ba2433634ee14a254b682 100644
--- a/src/mol-model/structure/structure/element.ts
+++ b/src/mol-model/structure/structure/element.ts
@@ -16,7 +16,7 @@ import Structure from './structure';
 import Unit from './unit';
 import { Boundary } from './util/boundary';
 import { StructureProperties } from '../structure';
-import { sortArray } from '../../../mol-data/util';
+import { sortArray, hashFnv32a, hash2 } from '../../../mol-data/util';
 import Expression from '../../../mol-script/language/expression';
 
 interface StructureElement<U = Unit> {
@@ -141,12 +141,32 @@ namespace StructureElement {
         export function remap(loci: Loci, structure: Structure): Loci {
             if (structure === loci.structure) return loci
 
-            return Loci(structure, loci.elements.map(e => ({
-                unit: structure.unitMap.get(e.unit.id)!,
-                indices: e.indices
-            })));
+            const elements: Loci['elements'][0][] = [];
+            loci.elements.forEach(e => {
+                const unit = structure.unitMap.get(e.unit.id)
+                if (!unit) return
+
+                if (SortedArray.areEqual(e.unit.elements, unit.elements)) {
+                    elements.push({ unit, indices: e.indices })
+                } else {
+                    // TODO optimize
+                    const indices: UnitIndex[] = []
+                    OrderedSet.forEach(e.indices, (v) => {
+                        const eI = e.unit.elements[v]
+                        const uI = SortedArray.indexOf(unit.elements, eI) as UnitIndex | -1
+                        if (uI !== -1) indices.push(uI)
+                    })
+                    elements.push({
+                        unit,
+                        indices: SortedArray.ofSortedArray(indices)
+                    })
+                }
+            });
+
+            return Loci(structure, elements);
         }
 
+        /** Create union of `xs` and `ys` */
         export function union(xs: Loci, ys: Loci): Loci {
             if (xs.elements.length > ys.elements.length) return union(ys, xs);
             if (xs.elements.length === 0) return ys;
@@ -172,6 +192,7 @@ namespace StructureElement {
             return Loci(xs.structure, elements);
         }
 
+        /** Subtract `ys` from `xs` */
         export function subtract(xs: Loci, ys: Loci): Loci {
             const map = new Map<number, OrderedSet<UnitIndex>>();
             for (const e of ys.elements) map.set(e.unit.id, e.indices);
@@ -309,24 +330,24 @@ namespace StructureElement {
         }
 
         export function toScriptExpression(loci: Loci) {
-            if (loci.structure.models.length > 1) {
-                console.warn('toScriptExpression is only supported for Structure with single model, returning empty expression.');
-                return MS.struct.generator.empty();
-            }
             if (loci.elements.length === 0) return MS.struct.generator.empty();
 
-            const sourceIndexMap = new Map<string, UniqueArray<number, number>>();
+            const models = loci.structure.models;
+            const sourceIndexMap = new Map<string, { modelLabel: string, modelIndex: number, xs: UniqueArray<number, number> }>();
             const el = StructureElement.create(), p = StructureProperties.atom.sourceIndex;
             for (const e of loci.elements) {
                 const { indices } = e;
                 const { elements } = e.unit;
-                const opName = e.unit.conformation.operator.name;
+
+                const key = models.length === 1
+                    ? e.unit.conformation.operator.name
+                    : `${e.unit.conformation.operator.name} ${e.unit.model.label} ${e.unit.model.modelNum}`;
 
                 let sourceIndices: UniqueArray<number, number>;
-                if (sourceIndexMap.has(opName)) sourceIndices = sourceIndexMap.get(opName)!;
+                if (sourceIndexMap.has(key)) sourceIndices = sourceIndexMap.get(key)!.xs;
                 else {
                     sourceIndices = UniqueArray.create<number, number>();
-                    sourceIndexMap.set(opName, sourceIndices);
+                    sourceIndexMap.set(key, { modelLabel: e.unit.model.label, modelIndex: e.unit.model.modelNum, xs: sourceIndices });
                 }
 
                 el.unit = e.unit;
@@ -337,20 +358,81 @@ namespace StructureElement {
                 }
             }
 
-            const byOpName: Expression[] = [];
+            const opData: OpData[] = [];
             const keys = sourceIndexMap.keys();
             while (true) {
                 const k = keys.next();
                 if (k.done) break;
-                byOpName.push(getOpNameQuery(k.value, sourceIndexMap.get(k.value)!.array));
+                const e = sourceIndexMap.get(k.value)!;
+                opData.push(getOpData(k.value, e.xs.array, models.length > 1, e.modelLabel, e.modelIndex));
             }
 
+            const opGroups = new Map<string, OpData>();
+            for (let i = 0, il = opData.length; i < il; ++i) {
+                const d = opData[i]
+                const hash = hash2(hashFnv32a(d.atom.ranges), hashFnv32a(d.atom.set))
+                const key = `${hash}|${d.entity ? (d.entity.modelLabel + d.entity.modelIndex) : ''}`
+                if (opGroups.has(key)) {
+                    opGroups.get(key)!.chain.opName.push(...d.chain.opName)
+                } else {
+                    opGroups.set(key, d)
+                }
+            }
+
+            const opQueries: Expression[] = [];
+            opGroups.forEach(d => {
+                const { ranges, set } = d.atom
+                const { opName } = d.chain
+
+                const opProp = MS.struct.atomProperty.core.operatorName()
+                const siProp = MS.struct.atomProperty.core.sourceIndex();
+                const tests: Expression[] = [];
+
+                // TODO: add set.ofRanges constructor to MolQL???
+                if (set.length > 0) {
+                    tests[tests.length] = MS.core.set.has([MS.set.apply(null, set), siProp]);
+                }
+                for (let rI = 0, _rI = ranges.length / 2; rI < _rI; rI++) {
+                    tests[tests.length] = MS.core.rel.inRange([siProp, ranges[2 * rI], ranges[2 * rI + 1]]);
+                }
+
+                if (d.entity) {
+                    const { modelLabel, modelIndex } = d.entity
+                    opQueries.push(MS.struct.generator.atomGroups({
+                        'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0],
+                        'chain-test': opName.length > 1
+                            ? MS.core.set.has([MS.set.apply(null, opName), opProp])
+                            : MS.core.rel.eq([opProp, opName[0]]),
+                        'entity-test': MS.core.logic.and([
+                            MS.core.rel.eq([MS.struct.atomProperty.core.modelLabel(), modelLabel]),
+                            MS.core.rel.eq([MS.struct.atomProperty.core.modelIndex(), modelIndex]),
+                        ])
+                    }))
+                } else {
+                    opQueries.push(MS.struct.generator.atomGroups({
+                        'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0],
+                        'chain-test': opName.length > 1
+                            ? MS.core.set.has([MS.set.apply(null, opName), opProp])
+                            : MS.core.rel.eq([opProp, opName[0]])
+                    }))
+                }
+            })
+
             return MS.struct.modifier.union([
-                byOpName.length === 1 ? byOpName[0] : MS.struct.combinator.merge.apply(null, byOpName)
+                opQueries.length === 1
+                    ? opQueries[0]
+                    // Need to union before merge for fast performance
+                    : MS.struct.combinator.merge(opQueries.map(q => MS.struct.modifier.union([ q ])))
             ]);
         }
 
-        function getOpNameQuery(opName: string, xs: number[]) {
+        type OpData = {
+            atom: { set: number[], ranges: number[] },
+            chain: { opName: string[] },
+            entity?: { modelLabel: string, modelIndex: number }
+        }
+
+        function getOpData(opName: string, xs: number[], multimodel: boolean, modelLabel: string, modelIndex: number): OpData {
             sortArray(xs);
 
             const ranges: number[] = [];
@@ -373,21 +455,17 @@ namespace StructureElement {
                 }
             }
 
-            const siProp = MS.struct.atomProperty.core.sourceIndex();
-            const tests: Expression[] = [];
-
-            // TODO: add set.ofRanges constructor to MolQL???
-            if (set.length > 0) {
-                tests[tests.length] = MS.core.set.has([MS.set.apply(null, set), siProp]);
-            }
-            for (let rI = 0, _rI = ranges.length / 2; rI < _rI; rI++) {
-                tests[tests.length] = MS.core.rel.inRange([siProp, ranges[2 * rI], ranges[2 * rI + 1]]);
-            }
+            return multimodel
+                ? {
+                    atom: { set, ranges },
+                    chain: { opName: [ opName ] },
+                    entity: { modelLabel, modelIndex }
+                }
+                : {
+                    atom: { set, ranges },
+                    chain: { opName: [ opName ] },
+                }
 
-            return MS.struct.generator.atomGroups({
-                'atom-test': tests.length > 1 ? MS.core.logic.or.apply(null, tests) : tests[0],
-                'chain-test': MS.core.rel.eq([MS.struct.atomProperty.core.operatorName(), opName])
-            });
         }
     }
 }
diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts
index 17aa1c75a40ec49304eb821a16f9159c912e2bcc..5c6a70a0d22a6e395eb26ed31c53c639cd9c77b6 100644
--- a/src/mol-model/structure/structure/properties.ts
+++ b/src/mol-model/structure/structure/properties.ts
@@ -113,6 +113,8 @@ const entity = {
 const unit = {
     id: StructureElement.property(l => l.unit.id),
     operator_name: StructureElement.property(l => l.unit.conformation.operator.name),
+    model_index: StructureElement.property(l => l.unit.model.modelNum),
+    model_label: StructureElement.property(l => l.unit.model.label),
     hkl: StructureElement.property(l => l.unit.conformation.operator.hkl),
     spgrOp: StructureElement.property(l => l.unit.conformation.operator.spgrOp),
 
diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts
index e2080382764082168d00aca15fbf801eafdb121f..ef528356144093a8dbf9b2d333421c529330eb11 100644
--- a/src/mol-model/structure/structure/structure.ts
+++ b/src/mol-model/structure/structure/structure.ts
@@ -218,6 +218,10 @@ class Structure {
         return SortedArray.has(this.unitMap.get(e.unit.id).elements, e.element);
     }
 
+    getModelIndex(m: Model) {
+        return this.model
+    }
+
     private initUnits(units: ArrayLike<Unit>) {
         const map = IntMap.Mutable<Unit>();
         let elementCount = 0;
diff --git a/src/mol-model/structure/structure/unit/links.ts b/src/mol-model/structure/structure/unit/links.ts
index 3c115c376c6db742802dc8ec80d2ef5ab37ba2bc..8ea138d4d1d8fc8ef0d9e634513c5bb6a4064c13 100644
--- a/src/mol-model/structure/structure/unit/links.ts
+++ b/src/mol-model/structure/structure/unit/links.ts
@@ -62,6 +62,29 @@ namespace Link {
         return true
     }
 
+    export function remapLoci(loci: Loci, structure: Structure): Loci {
+        if (structure === loci.structure) return loci
+
+        const links: Loci['links'][0][] = [];
+        loci.links.forEach(l => {
+            const unitA = structure.unitMap.get(l.aUnit.id)
+            if (!unitA) return
+            const unitB = structure.unitMap.get(l.bUnit.id)
+            if (!unitB) return
+
+            const elementA = l.aUnit.elements[l.aIndex]
+            const indexA = SortedArray.indexOf(unitA.elements, elementA) as StructureElement.UnitIndex | -1
+            if (indexA === -1) return
+            const elementB = l.bUnit.elements[l.bIndex]
+            const indexB = SortedArray.indexOf(unitB.elements, elementB) as StructureElement.UnitIndex | -1
+            if (indexB === -1) return
+
+            links.push(Location(unitA, indexA, unitB, indexB))
+        });
+
+        return Loci(structure, links);
+    }
+
     export function toStructureElementLoci(loci: Loci): StructureElement.Loci {
         const elements: StructureElement.Loci['elements'][0][] = []
         const map = new Map<number, number[]>()
diff --git a/src/mol-plugin/skin/base/icons.scss b/src/mol-plugin/skin/base/icons.scss
index 2c4c0590c5916943e5db8569171199a3ecd82eee..7c642ee711538fcf36bd3a881feb6d163d119e6f 100644
--- a/src/mol-plugin/skin/base/icons.scss
+++ b/src/mol-plugin/skin/base/icons.scss
@@ -213,3 +213,7 @@
 .msp-icon-floppy:before {
 	content: "\e8d0";
 }
+
+.msp-icon-tape:before {
+	content: "\e8c8";
+}
diff --git a/src/mol-plugin/state/transforms/helpers.ts b/src/mol-plugin/state/transforms/helpers.ts
index 509c96410d860437597a43007b448c761a8e971e..a68fe1cd0c5a7ba4fdbfeb577dd096c730a7181d 100644
--- a/src/mol-plugin/state/transforms/helpers.ts
+++ b/src/mol-plugin/state/transforms/helpers.ts
@@ -25,11 +25,11 @@ function scriptToLoci(structure: Structure, script: Script) {
     return StructureSelection.toLoci2(result)
 }
 
-export function getStructureOverpaint(structure: Structure, scriptLayers: { script: Script, color: Color }[], alpha: number): Overpaint {
+export function getStructureOverpaint(structure: Structure, scriptLayers: { script: Script, color: Color, clear: boolean }[], alpha: number): Overpaint {
     const layers: Overpaint.Layer[] = []
     for (let i = 0, il = scriptLayers.length; i < il; ++i) {
-        const { script, color } = scriptLayers[i]
-        layers.push({ loci: scriptToLoci(structure, script), color })
+        const { script, color, clear } = scriptLayers[i]
+        layers.push({ loci: scriptToLoci(structure, script), color, clear })
     }
     return { layers, alpha }
 }
diff --git a/src/mol-plugin/state/transforms/representation.ts b/src/mol-plugin/state/transforms/representation.ts
index 90136b1fd25b6f7c3fa797c8b6ebe513ec9948bc..35ede9697349cd7eae3a13c5d2dcd7023dc8c5c0 100644
--- a/src/mol-plugin/state/transforms/representation.ts
+++ b/src/mol-plugin/state/transforms/representation.ts
@@ -300,9 +300,9 @@ const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
     canAutoUpdate() {
         return true;
     },
-    apply({ a, params, spine }) {
-        const rootStructure = spine.getRootOfType(SO.Molecule.Structure)!.data;
+    apply({ a, params }) {
         const structure = a.data.source.data;
+        const rootStructure = structure.parent || structure;
         const unitTransforms = new StructureUnitTransforms(rootStructure);
         explodeStructure(structure, unitTransforms, params.t);
         return new SO.Molecule.Structure.Representation3DState({
@@ -312,8 +312,9 @@ const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
             source: a
         }, { label: `Explode T = ${params.t.toFixed(2)}` });
     },
-    update({ a, b, newParams, oldParams, spine }) {
-        const rootStructure = spine.getRootOfType(SO.Molecule.Structure)!.data;
+    update({ a, b, newParams, oldParams }) {
+        const structure = a.data.source.data;
+        const rootStructure = structure.parent || structure;
         if (b.data.info !== rootStructure) return StateTransformer.UpdateResult.Recreate;
         if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
         const unitTransforms = b.data.state.unitTransforms!;
@@ -332,25 +333,15 @@ const OverpaintStructureRepresentation3D = PluginStateTransform.BuiltIn({
     to: SO.Molecule.Structure.Representation3DState,
     params: {
         layers: PD.ObjectList({
-            script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))' }),
-            color: PD.Color(ColorNames.blueviolet)
-        }, e => `${Color.toRgbString(e.color)}`, {
-            defaultValue: [
-                {
-                    script: {
-                        language: 'mol-script',
-                        expression: '(sel.atom.atom-groups :residue-test (= atom.resname LYS))'
-                    },
-                    color: ColorNames.blueviolet
-                },
-                {
-                    script: {
-                        language: 'mol-script',
-                        expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))'
-                    },
-                    color: ColorNames.chartreuse
-                }
-            ]
+            script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.all)' }),
+            color: PD.Color(ColorNames.blueviolet),
+            clear: PD.Boolean(false)
+        }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, {
+            defaultValue: [{
+                script: { language: 'mol-script', expression: '(sel.atom.all)' },
+                color: ColorNames.blueviolet,
+                clear: false
+            }]
         }),
         alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
     }
@@ -390,7 +381,7 @@ const TransparencyStructureRepresentation3D = PluginStateTransform.BuiltIn({
     from: SO.Molecule.Structure.Representation3D,
     to: SO.Molecule.Structure.Representation3DState,
     params: {
-        script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.atom-groups :chain-test (= atom.label_asym_id A))' }),
+        script: PD.ScriptExpression({ language: 'mol-script', expression: '(sel.atom.all)' }),
         value: PD.Numeric(0.75, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
         variant: PD.Select('single', [['single', 'Single-layer'], ['multi', 'Multi-layer']])
     }
diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx
index 2758c3d29871a14f3964791b9324948fd2933e79..31c64b94e133d76048cf17aed55e7694e8b4510e 100644
--- a/src/mol-plugin/ui/controls.tsx
+++ b/src/mol-plugin/ui/controls.tsx
@@ -1,7 +1,8 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 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 * as React from 'react';
@@ -9,12 +10,15 @@ import { PluginCommands } from '../../mol-plugin/command';
 import { UpdateTrajectory } from '../../mol-plugin/state/actions/structure';
 import { PluginUIComponent } from './base';
 import { LociLabelEntry } from '../../mol-plugin/util/loci-label-manager';
-import { IconButton } from './controls/common';
+import { IconButton, Icon } from './controls/common';
 import { PluginStateObject } from '../../mol-plugin/state/objects';
 import { StateTransforms } from '../../mol-plugin/state/transforms';
 import { StateTransformer } from '../../mol-state';
 import { ModelFromTrajectory } from '../../mol-plugin/state/transforms/model';
 import { AnimationControls } from './state/animation';
+import { StructureOverpaintControls } from './structure/overpaint';
+import { StructureRepresentationControls } from './structure/representation';
+import { StructureSelectionControls } from './structure/selection';
 
 export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: boolean, label: string }> {
     state = { show: false, label: '' }
@@ -225,7 +229,7 @@ export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty:
         const isAnimating = this.state.isAnimating;
 
         return <div className='msp-animation-viewport-controls'>
-            <IconButton icon={isAnimating || isPlaying ? 'stop' : 'play'} title={isAnimating ? 'Stop' : 'Select Animation'}
+            <IconButton icon={isAnimating || isPlaying ? 'stop' : 'tape'} title={isAnimating ? 'Stop' : 'Select Animation'}
                 onClick={isAnimating || isPlaying ? this.stop : this.toggleExpanded}
                 disabled={isAnimating|| isPlaying ? false : this.state.isUpdating || this.state.isPlaying || this.state.isEmpty} />
             {(this.state.isExpanded && !this.state.isUpdating) && <div className='msp-animation-viewport-controls-select'>
@@ -249,4 +253,16 @@ export class LociLabelControl extends PluginUIComponent<{}, { entries: ReadonlyA
             {this.state.entries.map((e, i) => <div key={'' + i}>{e}</div>)}
         </div>;
     }
+}
+
+export class StructureToolsWrapper extends PluginUIComponent {
+    render() {
+        return <div>
+            <div className='msp-section-header'><Icon name='code' /> Structure Tools</div>
+
+            <StructureSelectionControls />
+            <StructureOverpaintControls />
+            <StructureRepresentationControls />
+        </div>;
+    }
 }
\ No newline at end of file
diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx
index 615a35103d95d108f0119246232854868d70d5d2..e432dc420209d350293cd1480a6b3de64f9756ac 100644
--- a/src/mol-plugin/ui/plugin.tsx
+++ b/src/mol-plugin/ui/plugin.tsx
@@ -12,7 +12,7 @@ import { LogEntry } from '../../mol-util/log-entry';
 import * as React from 'react';
 import { PluginContext } from '../context';
 import { PluginReactContext, PluginUIComponent } from './base';
-import { LociLabelControl, TrajectoryViewportControls, StateSnapshotViewportControls, AnimationViewportControls } from './controls';
+import { LociLabelControl, TrajectoryViewportControls, StateSnapshotViewportControls, AnimationViewportControls, StructureToolsWrapper } from './controls';
 import { StateSnapshots } from './state';
 import { StateObjectActions } from './state/actions';
 import { StateTree } from './state/tree';
@@ -109,6 +109,7 @@ export class ControlsWrapper extends PluginUIComponent {
             <CurrentObject />
             {/* <AnimationControlsWrapper /> */}
             {/* <CameraSnapshots /> */}
+            <StructureToolsWrapper />
             <StateSnapshots />
         </div>;
     }
diff --git a/src/mol-plugin/ui/sequence.tsx b/src/mol-plugin/ui/sequence.tsx
index 82cbdd76bee45b604aba4ba7e1903a80ff289975..e1a3b8cbd059cd7103d66046a87bfce969d1bf41 100644
--- a/src/mol-plugin/ui/sequence.tsx
+++ b/src/mol-plugin/ui/sequence.tsx
@@ -10,7 +10,7 @@ import { PluginUIComponent } from './base';
 import { StateTreeSpine } from '../../mol-state/tree/spine';
 import { PluginStateObject as SO } from '../state/objects';
 import { Sequence } from './sequence/sequence';
-import { Structure, StructureElement, StructureProperties as SP } from '../../mol-model/structure';
+import { Structure, StructureElement, StructureProperties as SP, Unit } from '../../mol-model/structure';
 import { SequenceWrapper } from './sequence/wrapper';
 import { PolymerSequenceWrapper } from './sequence/polymer';
 import { StructureElementSelectionManager } from '../util/structure-element-selection';
@@ -75,7 +75,12 @@ function getChainOptions(structure: Structure, entityId: string) {
         const id = unit.invariantId
         if (seen.has(id)) continue
 
-        let label = `${SP.chain.label_asym_id(l)}: ${SP.chain.auth_asym_id(l)}`
+        let label = ''
+        if (Unit.isAtomic(unit)) {
+            label = `${SP.chain.label_asym_id(l)}: ${SP.chain.auth_asym_id(l)}`
+        } else {
+            label = `${SP.coarse.asym_id(l)}`
+        }
         if (SP.entity.type(l) === 'water') {
             const count = water.get(label) || 1
             water.set(label, count + 1)
@@ -135,7 +140,7 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
 
         this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => {
             const current = this.spine.current;
-            if (!current || current.sourceRef !== ref || current.state !== state) return;
+            if (!current || current.sourceRef !== ref) return;
             this.setState(this.getInitialState())
         });
     }
diff --git a/src/mol-plugin/ui/sequence/polymer.ts b/src/mol-plugin/ui/sequence/polymer.ts
index 14e8bf64d921a55fe673ebb3e2188ea01587388d..d31ff0c4caef1326ed2016545b6af968f053e1d6 100644
--- a/src/mol-plugin/ui/sequence/polymer.ts
+++ b/src/mol-plugin/ui/sequence/polymer.ts
@@ -72,7 +72,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
         this.missing = data.unit.model.properties.missingResidues
 
         this.modelNum = data.unit.model.modelNum
-        this.asymId = SP.chain.label_asym_id(l)
+        this.asymId = Unit.isAtomic(data.unit) ? SP.chain.label_asym_id(l) : SP.coarse.asym_id(l)
 
         const missing: number[] = []
         for (let i = 0; i < length; ++i) {
diff --git a/src/mol-plugin/ui/structure/overpaint.tsx b/src/mol-plugin/ui/structure/overpaint.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c158f2801f564f0f155caadacce44b05ae4298c5
--- /dev/null
+++ b/src/mol-plugin/ui/structure/overpaint.tsx
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react';
+import { PluginUIComponent } from '../base';
+import { PluginStateObject } from '../../../mol-plugin/state/objects';
+import { StateTransforms } from '../../../mol-plugin/state/transforms';
+import { StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../../mol-state';
+import { ParamDefinition as PD} from '../../../mol-util/param-definition';
+import { ColorNames } from '../../../mol-util/color/tables';
+import { ParameterControls } from '../controls/parameters';
+import { Structure } from '../../../mol-model/structure';
+import { isEmptyLoci } from '../../../mol-model/loci';
+import { PluginContext } from '../../context';
+import { getExpression } from './util';
+
+
+type OverpaintEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, rootStructure: Structure, overpaint?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.OverpaintStructureRepresentation3D>>) => void
+const OverpaintManagerTag = 'overpaint-controls'
+
+export class StructureOverpaintControls extends PluginUIComponent<{}, { params: PD.Values<ReturnType<typeof StructureOverpaintControls.getParams>> }> {
+    state = { params: PD.getDefaultValues(StructureOverpaintControls.getParams(this.plugin)) }
+
+    static getParams = (plugin: PluginContext) => {
+        const { types } = plugin.structureRepresentation.registry
+        return {
+            color: PD.Color(ColorNames.cyan),
+            type: PD.MultiSelect(types.map(t => t[0]), types)
+        }
+    }
+
+    componentDidMount() {
+
+    }
+
+    private async eachRepr(callback: OverpaintEachReprCallback) {
+        const state = this.plugin.state.dataState;
+        const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3D));
+
+        const update = state.build();
+        for (const r of reprs) {
+            const overpaint = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.OverpaintStructureRepresentation3D, r.transform.ref).withTag(OverpaintManagerTag));
+
+            const structure = r.obj!.data.source.data
+            const rootStructure = structure.parent || structure
+
+            callback(update, r, rootStructure, overpaint[0])
+        }
+
+        await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }));
+    }
+
+    set = async (clear: boolean) => {
+        await this.eachRepr((update, repr, rootStructure, overpaint) => {
+            if (!this.state.params.type.includes(repr.params!.values.type.name)) return
+
+            const loci = this.plugin.helpers.structureSelection.get(rootStructure)
+            if (isEmptyLoci(loci) || loci.elements.length === 0) return
+            const expression = getExpression(loci)
+
+            const layer = {
+                script: { language: 'mol-script', expression },
+                color: this.state.params.color,
+                clear
+            }
+
+            if (overpaint) {
+                update.to(overpaint).update({ layers: [ ...overpaint.params!.values.layers, layer ], alpha: 1 })
+            } else {
+                update.to(repr.transform.ref)
+                    .apply(StateTransforms.Representation.OverpaintStructureRepresentation3D, { layers: [ layer ], alpha: 1 }, { tags: OverpaintManagerTag });
+            }
+        })
+    }
+
+    add = async () => {
+        this.set(false)
+    }
+
+    clear = async () => {
+        this.set(true)
+    }
+
+    clearAll = async () => {
+        await this.eachRepr((update, repr, rootStructure, overpaint) => {
+            if (overpaint) update.delete(overpaint.transform.ref)
+        })
+    }
+
+    render() {
+        return <div className='msp-transform-wrapper'>
+            <div className='msp-transform-header'>
+                <button className='msp-btn msp-btn-block'>Current Selection Overpaint</button>
+            </div>
+            <div>
+                <ParameterControls params={StructureOverpaintControls.getParams(this.plugin)} values={this.state.params} onChange={p => {
+                    const params = { ...this.state.params, [p.name]: p.value };
+                    this.setState({ params });
+                }}/>
+
+                <div className='msp-btn-row-group'>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.add}>Add</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.clear}>Clear</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.clearAll}>Clear All</button>
+                </div>
+            </div>
+        </div>
+    }
+}
\ No newline at end of file
diff --git a/src/mol-plugin/ui/structure/representation.tsx b/src/mol-plugin/ui/structure/representation.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..54e06dfce717987bc7a47d76234ecdfae2dae352
--- /dev/null
+++ b/src/mol-plugin/ui/structure/representation.tsx
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react';
+import { PluginUIComponent } from '../base';
+import { PluginStateObject } from '../../../mol-plugin/state/objects';
+import { StateTransforms } from '../../../mol-plugin/state/transforms';
+import { StateTransformer, StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../../mol-state';
+import { ParamDefinition as PD} from '../../../mol-util/param-definition';
+import { ParameterControls } from '../controls/parameters';
+import { StructureElement, QueryContext, StructureSelection } from '../../../mol-model/structure';
+import { isEmptyLoci } from '../../../mol-model/loci';
+import { PluginContext } from '../../context';
+import { getExpression } from './util';
+import { parseMolScript } from '../../../mol-script/language/parser';
+import { transpileMolScript } from '../../../mol-script/script/mol-script/symbols';
+import { compile } from '../../../mol-script/runtime/query/compiler';
+import { StructureRepresentation3DHelpers } from '../../state/transforms/representation';
+
+type RepresentationEachStructureCallback = (update: StateBuilder.Root, structure: StateObjectCell<PluginStateObject.Molecule.Structure, StateTransform<StateTransformer<any, PluginStateObject.Molecule.Structure, any>>>) => void
+const RepresentationManagerTag = 'representation-controls'
+
+function getRepresentationManagerTag(type: string) {
+    return `${RepresentationManagerTag}-${type}`
+}
+
+function getCombinedLoci(mode: 'add' | 'remove' | 'only' | 'all', loci: StructureElement.Loci, currentLoci: StructureElement.Loci): StructureElement.Loci {
+    switch (mode) {
+        case 'add': return StructureElement.Loci.union(loci, currentLoci)
+        case 'remove': return StructureElement.Loci.subtract(currentLoci, loci)
+        case 'only': return loci
+        case 'all': return StructureElement.Loci.all(loci.structure)
+    }
+}
+
+export class StructureRepresentationControls extends PluginUIComponent<{}, { params: PD.Values<ReturnType<typeof StructureRepresentationControls.getParams>> }> {
+    state = { params: PD.getDefaultValues(StructureRepresentationControls.getParams(this.plugin)) }
+
+    static getParams = (plugin: PluginContext) => {
+        const { types } = plugin.structureRepresentation.registry
+        return {
+            type: PD.Select(types[0][0], types)
+        }
+    }
+
+    componentDidMount() {
+
+    }
+
+    private async eachStructure(callback: RepresentationEachStructureCallback) {
+        const state = this.plugin.state.dataState;
+        const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure));
+
+        const update = state.build();
+        for (const s of structures) {
+            callback(update, s)
+        }
+
+        await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }));
+    }
+
+    set = async (mode: 'add' | 'remove' | 'only' | 'all') => {
+        const state = this.plugin.state.dataState
+        const { type } = this.state.params
+
+        await this.eachStructure((update, structure) => {
+            const s = structure.obj!.data
+            const _loci = this.plugin.helpers.structureSelection.get(s)
+            const loci = isEmptyLoci(_loci) ? StructureElement.Loci(s, []) : _loci
+
+            const selections = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure, structure.transform.ref).withTag(getRepresentationManagerTag(type)));
+
+            if (selections.length > 0) {
+                const parsed = parseMolScript(selections[0].params!.values.query.expression)
+                if (parsed.length === 0) return
+
+                const query = transpileMolScript(parsed[0])
+                const compiled = compile(query)
+                const result = compiled(new QueryContext(structure.obj!.data))
+                const currentLoci = StructureSelection.toLoci2(result)
+
+                const combinedLoci = getCombinedLoci(mode, loci, currentLoci)
+
+                update.to(selections[0]).update({
+                    ...selections[0].params!.values,
+                    query: { language: 'mol-script', expression: getExpression(combinedLoci) }
+                })
+            } else {
+                const combinedLoci = getCombinedLoci(mode, loci, StructureElement.Loci(loci.structure, []))
+
+                update.to(structure.transform.ref)
+                    .apply(
+                        StateTransforms.Model.UserStructureSelection,
+                        {
+                            query: { language: 'mol-script', expression: getExpression(combinedLoci) },
+                            label: type
+                        },
+                        { tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] }
+                    )
+                    .apply(
+                        StateTransforms.Representation.StructureRepresentation3D,
+                        StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s)
+                    )
+            }
+        })
+    }
+
+    show = async () => { this.set('add') }
+    hide = async () => { this.set('remove') }
+    only = async () => { this.set('only') }
+    showAll = async () => { this.set('all') }
+
+    hideAll = async () => {
+        const { type } = this.state.params
+        const state = this.plugin.state.dataState;
+        const update = state.build();
+
+        state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure).withTag(getRepresentationManagerTag(type))).forEach(structure => update.delete(structure.transform.ref));
+
+        await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }));
+    }
+
+    render() {
+        return <div className='msp-transform-wrapper'>
+            <div className='msp-transform-header'>
+                <button className='msp-btn msp-btn-block'>Current Selection Representation</button>
+            </div>
+            <div>
+                <ParameterControls params={StructureRepresentationControls.getParams(this.plugin)} values={this.state.params} onChange={p => {
+                    const params = { ...this.state.params, [p.name]: p.value };
+                    this.setState({ params });
+                }}/>
+
+                <div className='msp-btn-row-group'>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.show}>Show</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.hide}>Hide</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.only}>Only</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.showAll}>Show All</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={this.hideAll}>Hide All</button>
+                </div>
+            </div>
+        </div>
+    }
+}
\ No newline at end of file
diff --git a/src/mol-plugin/ui/structure/selection.tsx b/src/mol-plugin/ui/structure/selection.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ea60e71c2582596f255cbe2a99d7026f0cd7a5d9
--- /dev/null
+++ b/src/mol-plugin/ui/structure/selection.tsx
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react';
+import { PluginUIComponent } from '../base';
+import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
+import { StateSelection } from '../../../mol-state';
+import { PluginStateObject } from '../../state/objects';
+import { QueryContext, StructureSelection, QueryFn, Queries as _Queries } from '../../../mol-model/structure';
+import { compile } from '../../../mol-script/runtime/query/compiler';
+import { ButtonsType } from '../../../mol-util/input/input-observer';
+import { EmptyLoci } from '../../../mol-model/loci';
+
+const Queries = {
+    all: () => compile<StructureSelection>(MS.struct.generator.all()),
+    polymers: () => _Queries.internal.atomicSequence(),
+    water: () => _Queries.internal.water(),
+    ligands: () => _Queries.internal.atomicHet(),
+    coarse: () => _Queries.internal.spheres(),
+}
+
+export class StructureSelectionControls extends PluginUIComponent<{}, {}> {
+    state = {}
+
+    select = (query: QueryFn<StructureSelection>) => {
+        const state = this.plugin.state.dataState
+        const structures = state.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Structure))
+        const { structureSelection } = this.plugin.helpers
+
+        structureSelection.clear()
+        for (const so of structures) {
+            const s = so.obj!.data
+            const result = query(new QueryContext(s))
+            const loci = StructureSelection.toLoci2(result)
+
+            // TODO use better API when available
+            this.plugin.interactivity.lociSelections.apply({
+                current: { loci },
+                buttons: ButtonsType.Flag.Secondary,
+                modifiers: { shift: false, alt: false, control: true, meta: false }
+            })
+        }
+    }
+
+    clear = () => {
+        // TODO use better API when available
+        this.plugin.interactivity.lociSelections.apply({
+            current: { loci: EmptyLoci },
+            buttons: ButtonsType.Flag.Secondary,
+            modifiers: { shift: false, alt: false, control: true, meta: false }
+        })
+    }
+
+    render() {
+        return <div className='msp-transform-wrapper'>
+            <div className='msp-transform-header'>
+                <button className='msp-btn msp-btn-block'>Current Selection</button>
+            </div>
+            <div>
+                <div className='msp-btn-row-group'>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.all())}>All</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.clear()}>None</button>
+                </div>
+                <div className='msp-btn-row-group'>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.polymers())}>Polymers</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.ligands())}>Ligands</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.water())}>Water</button>
+                    <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.select(Queries.coarse())}>Coarse</button>
+                </div>
+            </div>
+        </div>
+    }
+}
\ No newline at end of file
diff --git a/src/mol-plugin/ui/structure/util.ts b/src/mol-plugin/ui/structure/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca17b74fe8825f36142e789862c146b48650c2a9
--- /dev/null
+++ b/src/mol-plugin/ui/structure/util.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { StructureElement } from '../../../mol-model/structure';
+import { EmptyLoci, isEmptyLoci } from '../../../mol-model/loci';
+import { MolScriptBuilder } from '../../../mol-script/language/builder';
+import { formatMolScript } from '../../../mol-script/language/expression-formatter';
+
+export function getExpression(loci: StructureElement.Loci | EmptyLoci) {
+    const scriptExpression = isEmptyLoci(loci)
+        ? MolScriptBuilder.struct.generator.empty()
+        : StructureElement.Loci.toScriptExpression(loci)
+    return formatMolScript(scriptExpression)
+}
\ No newline at end of file
diff --git a/src/mol-plugin/util/interactivity.ts b/src/mol-plugin/util/interactivity.ts
index 87031ce0e1c0f76fe4f3189267579a5fd662ba1e..bce545a963a815be2635ba2e8336bafa57f66b4d 100644
--- a/src/mol-plugin/util/interactivity.ts
+++ b/src/mol-plugin/util/interactivity.ts
@@ -163,6 +163,7 @@ namespace Interactivity {
             }
         }
 
+        // TODO create better API that is independent of a `ClickEvent`
         apply(e: ClickEvent) {
             const { current, buttons, modifiers } = e
             const normalized: Loci<ModelLoci> = this.normalizedLoci(current)
diff --git a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
index 7124442af98c2d3847ca631ec7982364b17e318b..711e98eb429477d22f20cc1ce97e8212823dfe26 100644
--- a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
@@ -120,6 +120,7 @@ function eachCarbohydrateLink(loci: Loci, structure: Structure, apply: (interval
     let changed = false
     if (Link.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         const { getLinkIndex } = structure.carbohydrates
         for (const l of loci.links) {
             const idx = getLinkIndex(l.aUnit, l.aUnit.elements[l.aIndex], l.bUnit, l.bUnit.elements[l.bIndex])
@@ -129,6 +130,7 @@ function eachCarbohydrateLink(loci: Loci, structure: Structure, apply: (interval
         }
     } else if (StructureElement.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = StructureElement.Loci.remap(loci, structure)
         // TODO mark link only when both of the link elements are in a StructureElement.Loci
         const { getElementIndex, getLinkIndices, elements } = structure.carbohydrates
         for (const e of loci.elements) {
diff --git a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
index 5c35ab7216125842e4cccc7a43963e91f5543651..b5adcfe5c757b0c2cfa0c53f962283722c7e1492 100644
--- a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
@@ -197,6 +197,7 @@ function eachCarbohydrate(loci: Loci, structure: Structure, apply: (interval: In
     let changed = false
     if (!StructureElement.isLoci(loci)) return false
     if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+    loci = StructureElement.Loci.remap(loci, structure)
     for (const e of loci.elements) {
         // TODO make more efficient by handling/grouping `e.indices` by residue index
         // TODO only call apply when the full alt-residue of the unit is part of `e`
diff --git a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
index 30aae0eb7795003e57fe2a236054605a2ebf8a4c..b540d9d49a79f89f98d0d1f3ff0e7bd24a66d467 100644
--- a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
@@ -134,6 +134,7 @@ function eachTerminalLink(loci: Loci, structure: Structure, apply: (interval: In
     let changed = false
     if (Link.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         for (const l of loci.links) {
             const idx = getTerminalLinkIndex(l.aUnit, l.aUnit.elements[l.aIndex], l.bUnit, l.bUnit.elements[l.bIndex])
             if (idx !== undefined) {
@@ -142,6 +143,7 @@ function eachTerminalLink(loci: Loci, structure: Structure, apply: (interval: In
         }
     } else if (StructureElement.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = StructureElement.Loci.remap(loci, structure)
         // TODO mark link only when both of the link elements are in a StructureElement.Loci
         const { getElementIndex, getTerminalLinkIndices, elements } = structure.carbohydrates
         for (const e of loci.elements) {
diff --git a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
index 155ca3c165cd57a55e49277423f8aa57e68a8d16..b90daa355fa7a586ed872f7d689db3cc334318d5 100644
--- a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
@@ -108,6 +108,7 @@ function eachCrossLink(loci: Loci, structure: Structure, apply: (interval: Inter
     let changed = false
     if (Link.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         for (const b of loci.links) {
             const indices = crossLinks.getPairIndices(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
             if (indices) {
diff --git a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
index 37f0bb0ba619ecbb911ccc7928097534a55827b9..204015f873524fc7e1d8a6f83fd264ca189a2a6b 100644
--- a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
@@ -98,6 +98,7 @@ function eachLink(loci: Loci, structure: Structure, apply: (interval: Interval)
     let changed = false
     if (Link.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         for (const b of loci.links) {
             const idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
             if (idx !== -1) {
@@ -106,6 +107,7 @@ function eachLink(loci: Loci, structure: Structure, apply: (interval: Interval)
         }
     } else if (StructureElement.isLoci(loci)) {
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = StructureElement.Loci.remap(loci, structure)
         // TODO mark link only when both of the link elements are in a StructureElement.Loci
         for (const e of loci.elements) {
             OrderedSet.forEach(e.indices, v => {
diff --git a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
index af8f091cd3620a2716a0172a14b15ec53fd38b62..55fe6843a1468b8be77a3b04168ad96e43e38d62 100644
--- a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
@@ -122,6 +122,7 @@ function eachLink(loci: Loci, structureGroup: StructureGroup, apply: (interval:
     if (Link.isLoci(loci)) {
         const { structure, group } = structureGroup
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         const unit = group.units[0]
         if (!Unit.isAtomic(unit)) return false
         const groupCount = unit.links.edgeCount * 2
@@ -137,6 +138,7 @@ function eachLink(loci: Loci, structureGroup: StructureGroup, apply: (interval:
     } else if (StructureElement.isLoci(loci)) {
         const { structure, group } = structureGroup
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = StructureElement.Loci.remap(loci, structure)
         const unit = group.units[0]
         if (!Unit.isAtomic(unit)) return false
         const groupCount = unit.links.edgeCount * 2
diff --git a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
index 885e01805c39cc7a2ec860e6d23035650b537729..55c8dfa68e7329e6d071048260f871e9ba58bfa1 100644
--- a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
+++ b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
@@ -14,7 +14,7 @@ import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { Segmentation } from '../../../mol-data/int';
 import { CylinderProps } from '../../../mol-geo/primitive/cylinder';
-import { isNucleic, isPurinBase, isPyrimidineBase } from '../../../mol-model/structure/model/types';
+import { isNucleic, isPurineBase, isPyrimidineBase } from '../../../mol-model/structure/model/types';
 import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
 import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual';
 import { NucleotideLocationIterator, getNucleotideElementLoci, eachNucleotideElement } from './util/nucleotide';
@@ -78,7 +78,21 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St
                 let idx1: ElementIndex | -1 = -1, idx2: ElementIndex | -1 = -1, idx3: ElementIndex | -1 = -1, idx4: ElementIndex | -1 = -1, idx5: ElementIndex | -1 = -1, idx6: ElementIndex | -1 = -1
                 let width = 4.5, height = 4.5, depth = 2.5 * sizeFactor
 
-                if (isPurinBase(compId)) {
+                let isPurine = isPurineBase(compId)
+                let isPyrimidine = isPyrimidineBase(compId)
+
+                if (!isPurine && !isPyrimidine) {
+                    // detect Purine or Pyrimidin based on geometry
+                    const idxC4 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
+                    const idxN9 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
+                    if (idxC4 !== -1 && idxN9 !== -1 && Vec3.distance(pos(idxC4, p1), pos(idxN9, p2)) < 1.6) {
+                        isPurine = true
+                    } else {
+                        isPyrimidine = true
+                    }
+                }
+
+                if (isPurine) {
                     height = 4.5
                     idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
                     idx2 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
@@ -86,13 +100,17 @@ function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: St
                     idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
                     idx6 = traceElementIndex[residueIndex]
-                } else if (isPyrimidineBase(compId)) {
+                } else if (isPyrimidine) {
                     height = 3.0
                     idx1 = atomicIndex.findAtomOnResidue(residueIndex, 'N3')
                     idx2 = atomicIndex.findAtomOnResidue(residueIndex, 'C6')
                     idx3 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
                     idx4 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
+                    if (idx5 === -1) {
+                        // modified ring, e.g. DZ
+                        idx5 = atomicIndex.findAtomOnResidue(residueIndex, 'C1')
+                    }
                     idx6 = traceElementIndex[residueIndex]
                 }
 
diff --git a/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts b/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts
index 992b107cf2ee1e219370aee2d3f1b6c0fb20434f..871fe5364818ef593b93ed25e1634f56a7545377 100644
--- a/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts
+++ b/src/mol-repr/structure/visual/nucleotide-ring-mesh.ts
@@ -14,7 +14,7 @@ import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
 import { Segmentation } from '../../../mol-data/int';
 import { CylinderProps } from '../../../mol-geo/primitive/cylinder';
-import { isNucleic, isPurinBase, isPyrimidineBase } from '../../../mol-model/structure/model/types';
+import { isNucleic, isPurineBase, isPyrimidineBase } from '../../../mol-model/structure/model/types';
 import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder';
 import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere';
 import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual';
@@ -101,15 +101,37 @@ function createNucleotideRingMesh(ctx: VisualContext, unit: Unit, structure: Str
 
                 builderState.currentGroup = i
 
-                if (isPurinBase(compId)) {
+                let isPurine = isPurineBase(compId)
+                let isPyrimidine = isPyrimidineBase(compId)
+
+                if (!isPurine && !isPyrimidine) {
+                    // detect Purine or Pyrimidin based on geometry
+                    const idxC4 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
+                    const idxN9 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
+                    if (idxC4 !== -1 && idxN9 !== -1 && Vec3.distance(pos(idxC4, pC4), pos(idxN9, pN9)) < 1.6) {
+                        isPurine = true
+                    } else {
+                        isPyrimidine = true
+                    }
+                }
+
+                if (isPurine) {
                     idxTrace = traceElementIndex[residueIndex]
                     idxN1 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
                     idxC2 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idxN3 = atomicIndex.findAtomOnResidue(residueIndex, 'N3')
                     idxC4 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
                     idxC5 = atomicIndex.findAtomOnResidue(residueIndex, 'C5')
+                    if (idxC5 === -1) {
+                        // modified ring, e.g. DP
+                        idxC5 = atomicIndex.findAtomOnResidue(residueIndex, 'N5')
+                    }
                     idxC6 = atomicIndex.findAtomOnResidue(residueIndex, 'C6')
                     idxN7 = atomicIndex.findAtomOnResidue(residueIndex, 'N7')
+                    if (idxN7 === -1) {
+                        // modified ring, e.g. DP
+                        idxN7 = atomicIndex.findAtomOnResidue(residueIndex, 'C7')
+                    }
                     idxC8 = atomicIndex.findAtomOnResidue(residueIndex, 'C8')
                     idxN9 = atomicIndex.findAtomOnResidue(residueIndex, 'N9')
 
@@ -131,9 +153,13 @@ function createNucleotideRingMesh(ctx: VisualContext, unit: Unit, structure: Str
                         MeshBuilder.addTriangleFan(builderState, positionsRing5_6, fanIndicesTopRing5_6)
                         MeshBuilder.addTriangleFan(builderState, positionsRing5_6, fanIndicesBottomRing5_6)
                     }
-                } else if (isPyrimidineBase(compId)) {
+                } else if (isPyrimidine) {
                     idxTrace = traceElementIndex[residueIndex]
                     idxN1 = atomicIndex.findAtomOnResidue(residueIndex, 'N1')
+                    if (idxN1 === -1) {
+                        // modified ring, e.g. DZ
+                        idxN1 = atomicIndex.findAtomOnResidue(residueIndex, 'C1')
+                    }
                     idxC2 = atomicIndex.findAtomOnResidue(residueIndex, 'C2')
                     idxN3 = atomicIndex.findAtomOnResidue(residueIndex, 'N3')
                     idxC4 = atomicIndex.findAtomOnResidue(residueIndex, 'C4')
diff --git a/src/mol-repr/structure/visual/util/element.ts b/src/mol-repr/structure/visual/util/element.ts
index ec1a76717826923089a8fad264e99288d3c05742..e492772ed8429eb1c37bf32a7265ebda46f3345a 100644
--- a/src/mol-repr/structure/visual/util/element.ts
+++ b/src/mol-repr/structure/visual/util/element.ts
@@ -73,6 +73,7 @@ export function eachElement(loci: Loci, structureGroup: StructureGroup, apply: (
     if (!StructureElement.isLoci(loci)) return false
     const { structure, group } = structureGroup
     if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+    loci = StructureElement.Loci.remap(loci, structure)
     const elementCount = group.elements.length
     for (const e of loci.elements) {
         const unitIdx = group.unitIndexMap.get(e.unit.id)
diff --git a/src/mol-repr/structure/visual/util/nucleotide.ts b/src/mol-repr/structure/visual/util/nucleotide.ts
index dd3d32c503b3d28b347b95799a03906b14346ec9..5406facc48d095ae677a67a8642349011c59acb2 100644
--- a/src/mol-repr/structure/visual/util/nucleotide.ts
+++ b/src/mol-repr/structure/visual/util/nucleotide.ts
@@ -46,6 +46,7 @@ export function eachNucleotideElement(loci: Loci, structureGroup: StructureGroup
     if (!StructureElement.isLoci(loci)) return false
     const { structure, group } = structureGroup
     if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+    loci = StructureElement.Loci.remap(loci, structure)
     const unit = group.units[0]
     if (!Unit.isAtomic(unit)) return false
     const { nucleotideElements, model, elements } = unit
diff --git a/src/mol-repr/structure/visual/util/polymer.ts b/src/mol-repr/structure/visual/util/polymer.ts
index 325e631ca49d9e920bb6fa73bf83221c8e02edd2..b185f488350c3ea99ec4eacd04e32d1f1cb39419 100644
--- a/src/mol-repr/structure/visual/util/polymer.ts
+++ b/src/mol-repr/structure/visual/util/polymer.ts
@@ -96,6 +96,7 @@ export function eachPolymerElement(loci: Loci, structureGroup: StructureGroup, a
     if (!StructureElement.isLoci(loci)) return false
     const { structure, group } = structureGroup
     if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+    loci = StructureElement.Loci.remap(loci, structure)
     const { polymerElements, model, elements } = group.units[0]
     const { index, offsets } = model.atomicHierarchy.residueAtomSegments
     const { traceElementIndex } = model.atomicHierarchy.derived.residue
@@ -157,6 +158,7 @@ export function eachPolymerGapElement(loci: Loci, structureGroup: StructureGroup
     if (Link.isLoci(loci)) {
         const { structure, group } = structureGroup
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = Link.remapLoci(loci, structure)
         const groupCount = group.units[0].gapElements.length
         for (const b of loci.links) {
             const unitIdx = group.unitIndexMap.get(b.aUnit.id)
@@ -171,6 +173,7 @@ export function eachPolymerGapElement(loci: Loci, structureGroup: StructureGroup
     } else if (StructureElement.isLoci(loci)) {
         const { structure, group } = structureGroup
         if (!Structure.areParentsEquivalent(loci.structure, structure)) return false
+        loci = StructureElement.Loci.remap(loci, structure)
         const groupCount = group.units[0].gapElements.length
         for (const e of loci.elements) {
             const unitIdx = group.unitIndexMap.get(e.unit.id)
diff --git a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
index c510f7c3b61ea818b3561948d2156aa23dc60c41..5c7b3b535de15bb94b680c1ed937543b44509a77 100644
--- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
+++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
@@ -5,7 +5,7 @@
  */
 
 import { Unit, StructureElement, ElementIndex, ResidueIndex, Structure } from '../../../../../mol-model/structure';
-import { Segmentation } from '../../../../../mol-data/int';
+import { Segmentation, SortedArray } from '../../../../../mol-data/int';
 import { MoleculeType, SecondaryStructureType } from '../../../../../mol-model/structure/model/types';
 import Iterator from '../../../../../mol-data/iterator';
 import { Vec3 } from '../../../../../mol-math/linear-algebra';
@@ -69,12 +69,14 @@ const tmpVecB = Vec3()
 
 export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> {
     private value: PolymerTraceElement
-    private polymerIt: SortedRanges.Iterator<ElementIndex, ResidueIndex>
+    private polymerIt: SortedRanges.Iterator<ElementIndex, number>
     private residueIt: Segmentation.SegmentIterator<ResidueIndex>
-    private polymerSegment: Segmentation.Segment<ResidueIndex>
+    private polymerSegment: Segmentation.Segment<number>
     private cyclicPolymerMap: Map<ResidueIndex, ResidueIndex>
     private secondaryStructureType: SecondaryStructure['type']
     private secondaryStructureGetIndex: SecondaryStructure['getIndex']
+    private residueSegmentBeg: ResidueIndex
+    private residueSegmentEnd: ResidueIndex
     private residueSegmentMin: ResidueIndex
     private residueSegmentMax: ResidueIndex
     private prevSecStrucType: SecondaryStructureType
@@ -84,6 +86,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
     private currCoarseBackbone: boolean
     private nextCoarseBackbone: boolean
     private state: AtomicPolymerTraceIteratorState = AtomicPolymerTraceIteratorState.nextPolymer
+    private polymerRanges: SortedArray<ElementIndex>
     private residueAtomSegments: Segmentation<ElementIndex, ResidueIndex>
     private traceElementIndex: ArrayLike<ElementIndex>
     private directionFromElementIndex: ArrayLike<ElementIndex | -1>
@@ -114,10 +117,12 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         }
     }
 
-    private updateResidueSegmentRange(polymerSegment: Segmentation.Segment<ResidueIndex>) {
+    private updateResidueSegmentRange(polymerSegment: Segmentation.Segment<number>) {
         const { index } = this.residueAtomSegments
-        this.residueSegmentMin = index[this.unit.elements[polymerSegment.start]]
-        this.residueSegmentMax = index[this.unit.elements[polymerSegment.end - 1]]
+        this.residueSegmentBeg = index[this.unit.elements[polymerSegment.start]]
+        this.residueSegmentEnd = index[this.unit.elements[polymerSegment.end - 1]]
+        this.residueSegmentMin = index[this.polymerRanges[polymerSegment.index * 2]]
+        this.residueSegmentMax = index[this.polymerRanges[polymerSegment.index * 2 + 1] - 1]
     }
 
     private getResidueIndex(residueIndex: number) {
@@ -153,9 +158,13 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
     }
 
     private setFromToVector(out: Vec3, residueIndex: ResidueIndex) {
-        this.pos(tmpVecA, this.directionFromElementIndex[residueIndex])
-        this.pos(tmpVecB, this.directionToElementIndex[residueIndex])
-        Vec3.sub(out, tmpVecB, tmpVecA)
+        if (this.value.isCoarseBackbone) {
+            Vec3.set(out, 1, 0, 0)
+        } else {
+            this.pos(tmpVecA, this.directionFromElementIndex[residueIndex])
+            this.pos(tmpVecB, this.directionToElementIndex[residueIndex])
+            Vec3.sub(out, tmpVecB, tmpVecA)
+        }
     }
 
     private setDirection(out: Vec3, v1: Vec3, v2: Vec3, v3: Vec3) {
@@ -177,9 +186,9 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
                 if (residueIt.hasNext) {
                     this.state = AtomicPolymerTraceIteratorState.nextResidue
                     this.currSecStrucType = SecStrucTypeNA
-                    this.nextSecStrucType = this.getSecStruc(this.residueSegmentMin)
+                    this.nextSecStrucType = this.getSecStruc(this.residueSegmentBeg)
                     this.currCoarseBackbone = false
-                    this.nextCoarseBackbone = this.directionFromElementIndex[this.residueSegmentMin] === -1 || this.directionToElementIndex[this.residueSegmentMin] === -1
+                    this.nextCoarseBackbone = this.directionFromElementIndex[this.residueSegmentBeg] === -1 || this.directionToElementIndex[this.residueSegmentBeg] === -1
                     break
                 }
             }
@@ -201,9 +210,10 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
             value.isCoarseBackbone = this.currCoarseBackbone
             value.coarseBackboneFirst = this.prevCoarseBackbone !== this.currCoarseBackbone
             value.coarseBackboneLast = this.currCoarseBackbone !== this.nextCoarseBackbone
-            value.first = residueIndex === this.residueSegmentMin
-            value.last = residueIndex === this.residueSegmentMax
+            value.first = residueIndex === this.residueSegmentBeg
+            value.last = residueIndex === this.residueSegmentEnd
             value.moleculeType = this.moleculeType[residueIndex]
+            value.isCoarseBackbone = this.directionFromElementIndex[residueIndex] === -1 || this.directionToElementIndex[residueIndex] === -1
 
             const residueIndexPrev3 = this.getResidueIndex(residueIndex - 3)
             const residueIndexPrev2 = this.getResidueIndex(residueIndex - 2)
@@ -212,39 +222,22 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
             const residueIndexNext2 = this.getResidueIndex(residueIndex + 2)
             const residueIndexNext3 = this.getResidueIndex(residueIndex + 3)
 
-            if (value.first) {
-                value.centerPrev.element = this.traceElementIndex[residueIndexPrev1]
-                value.center.element = this.traceElementIndex[residueIndex]
-
-                this.pos(this.p0, this.traceElementIndex[residueIndexPrev3])
-                this.pos(this.p1, this.traceElementIndex[residueIndexPrev2])
-                this.pos(this.p2, this.traceElementIndex[residueIndexPrev1])
-                this.pos(this.p3, this.traceElementIndex[residueIndex])
-                this.pos(this.p4, this.traceElementIndex[residueIndexNext1])
-                this.pos(this.p5, this.traceElementIndex[residueIndexNext2])
-
-                this.setFromToVector(this.d01, residueIndexPrev1)
-                this.setFromToVector(this.d12, residueIndex)
-                this.setFromToVector(this.d23, residueIndexNext1)
-            } else {
-                value.centerPrev.element = value.center.element
-                value.center.element = value.centerNext.element
-
-                Vec3.copy(this.p0, this.p1)
-                Vec3.copy(this.p1, this.p2)
-                Vec3.copy(this.p2, this.p3)
-                Vec3.copy(this.p3, this.p4)
-                Vec3.copy(this.p4, this.p5)
-                Vec3.copy(this.p5, this.p6)
-
-                Vec3.copy(this.d01, this.d12)
-                Vec3.copy(this.d12, this.d23)
-                Vec3.copy(this.d23, this.d34)
-            }
+            value.centerPrev.element = this.traceElementIndex[residueIndexPrev1]
+            value.center.element = this.traceElementIndex[residueIndex]
             value.centerNext.element = this.traceElementIndex[residueIndexNext1]
+
+            this.pos(this.p0, this.traceElementIndex[residueIndexPrev3])
+            this.pos(this.p1, this.traceElementIndex[residueIndexPrev2])
+            this.pos(this.p2, this.traceElementIndex[residueIndexPrev1])
+            this.pos(this.p3, this.traceElementIndex[residueIndex])
+            this.pos(this.p4, this.traceElementIndex[residueIndexNext1])
+            this.pos(this.p5, this.traceElementIndex[residueIndexNext2])
             this.pos(this.p6, this.traceElementIndex[residueIndexNext3])
+
+            this.setFromToVector(this.d01, residueIndexPrev1)
+            this.setFromToVector(this.d12, residueIndex)
+            this.setFromToVector(this.d23, residueIndexNext1)
             this.setFromToVector(this.d34, residueIndexNext2)
-            value.isCoarseBackbone = this.directionFromElementIndex[residueIndex] === -1 || this.directionToElementIndex[residueIndex] === -1
 
             this.setControlPoint(value.p0, this.p0, this.p1, this.p2, residueIndexPrev2)
             this.setControlPoint(value.p1, this.p1, this.p2, this.p3, residueIndexPrev1)
@@ -268,12 +261,13 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
     constructor(private unit: Unit.Atomic, structure: Structure) {
         this.atomicConformation = unit.model.atomicConformation
         this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments
+        this.polymerRanges = unit.model.atomicHierarchy.polymerRanges
         this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues
         this.directionFromElementIndex = unit.model.atomicHierarchy.derived.residue.directionFromElementIndex
         this.directionToElementIndex = unit.model.atomicHierarchy.derived.residue.directionToElementIndex
         this.moleculeType = unit.model.atomicHierarchy.derived.residue.moleculeType
         this.cyclicPolymerMap = unit.model.atomicHierarchy.cyclicPolymerMap
-        this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
+        this.polymerIt = SortedRanges.transientSegments(this.polymerRanges, unit.elements)
         this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
         this.value = createPolymerTraceElement(unit)
         this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
@@ -359,6 +353,8 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
     constructor(private unit: Unit.Spheres | Unit.Gaussians, structure: Structure) {
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
         this.value = createPolymerTraceElement(unit)
+        Vec3.set(this.value.d12, 1, 0, 0)
+        Vec3.set(this.value.d23, 1, 0, 0)
         switch (unit.kind) {
             case Unit.Kind.Spheres: this.conformation = unit.model.coarseConformation.spheres; break
             case Unit.Kind.Gaussians: this.conformation = unit.model.coarseConformation.gaussians; break
diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts
index 1b6f2530b7bf6f6b4ba3146045092d3c0ca8495f..263dcaada8036131c5f521b7534bcc7c6a7353f5 100644
--- a/src/mol-repr/visual.ts
+++ b/src/mol-repr/visual.ts
@@ -82,15 +82,17 @@ namespace Visual {
         // ensure texture has right size
         createOverpaint(overpaint.layers.length ? count : 0, renderObject.values)
 
-        // clear if requested
+        // clear all if requested
         if (clear) clearOverpaint(tOverpaint.ref.value.array, 0, count)
 
         for (let i = 0, il = overpaint.layers.length; i < il; ++i) {
-            const { loci, color } = overpaint.layers[i]
+            const { loci, color, clear } = overpaint.layers[i]
             const apply = (interval: Interval) => {
                 const start = Interval.start(interval)
                 const end = Interval.end(interval)
-                return applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, overpaint.alpha)
+                return clear
+                    ? clearOverpaint(tOverpaint.ref.value.array, start, end)
+                    : applyOverpaintColor(tOverpaint.ref.value.array, start, end, color, overpaint.alpha)
             }
             lociApply(loci, apply)
         }
diff --git a/src/mol-script/language/symbol-table/structure-query.ts b/src/mol-script/language/symbol-table/structure-query.ts
index 3a6ed18461deb903c8d0fc3acfec873e61ad8bf6..b129ba31909cceaf9586e0eab21723e277ffdf3e 100644
--- a/src/mol-script/language/symbol-table/structure-query.ts
+++ b/src/mol-script/language/symbol-table/structure-query.ts
@@ -254,6 +254,8 @@ 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.'),
+        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.')
     },
 
     topology: {
diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts
index e2d75927f1b7e559f70b67f4650f59aa5f7d77a3..ee5e68247bfed35e8c6bd476824246240ec86352 100644
--- a/src/mol-script/runtime/query/table.ts
+++ b/src/mol-script/runtime/query/table.ts
@@ -247,6 +247,8 @@ 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.modelIndex, atomProp(StructureProperties.unit.model_index)),
+    D(MolScript.structureQuery.atomProperty.core.modelLabel, atomProp(StructureProperties.unit.model_label)),
     D(MolScript.structureQuery.atomProperty.core.atomKey, (ctx, _) => cantorPairing(ctx.element.unit.id, ctx.element.element)),
 
     // TODO:
diff --git a/src/mol-script/script/mol-script/symbols.ts b/src/mol-script/script/mol-script/symbols.ts
index 9893d843b4cdfec6a42314a131f0a3d7c8efcfdc..eaa70e1332f235b08ae73c021fdf2620897ccd1c 100644
--- a/src/mol-script/script/mol-script/symbols.ts
+++ b/src/mol-script/script/mol-script/symbols.ts
@@ -101,6 +101,7 @@ export const SymbolTable = [
             Alias(MolScript.structureQuery.generator.queryInSelection, 'sel.atom.query-in-selection'),
             Alias(MolScript.structureQuery.generator.rings, 'sel.atom.rings'),
             Alias(MolScript.structureQuery.generator.empty, 'sel.atom.empty'),
+            Alias(MolScript.structureQuery.generator.all, 'sel.atom.all'),
 
             // Macro(MSymbol('sel.atom.atoms', Arguments.Dictionary({
             //     0: Argument(Type.Bool, { isOptional: true, defaultValue: true, description: 'Test applied to each atom.' })
@@ -198,6 +199,8 @@ 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.modelIndex, 'atom.model-index'),
+            Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'),
             Alias(MolScript.structureQuery.atomProperty.core.atomKey, 'atom.key'),
             Alias(MolScript.structureQuery.atomProperty.core.bondCount, 'atom.bond-count'),
 
diff --git a/src/mol-theme/overpaint.ts b/src/mol-theme/overpaint.ts
index 9f566a056773e2b873b64dd393ce1128dc82cbe3..d885a5de8273907f5606fb76c5b910f75842d17e 100644
--- a/src/mol-theme/overpaint.ts
+++ b/src/mol-theme/overpaint.ts
@@ -12,7 +12,7 @@ export { Overpaint }
 type Overpaint = { layers: ReadonlyArray<Overpaint.Layer>, readonly alpha: number }
 
 namespace Overpaint {
-    export type Layer = { readonly loci: Loci, readonly color: Color }
+    export type Layer = { readonly loci: Loci, readonly color: Color, readonly clear: boolean }
     export const Empty: Overpaint = { layers: [], alpha: 1 }
 
     export function areEqual(oA: Overpaint, oB: Overpaint) {
@@ -20,6 +20,7 @@ namespace Overpaint {
         if (oA.layers.length !== oB.layers.length) return false
         if (oA.alpha !== oB.alpha) return false
         for (let i = 0, il = oA.layers.length; i < il; ++i) {
+            if (oA.layers[i].clear !== oB.layers[i].clear) return false
             if (oA.layers[i].color !== oB.layers[i].color) return false
             if (!Loci.areEqual(oA.layers[i].loci, oB.layers[i].loci)) return false
         }