From 720b23c174ecf14ee092e3cbdb85bb57806c355b Mon Sep 17 00:00:00 2001
From: David Sehnal <david.sehnal@gmail.com>
Date: Sat, 28 Oct 2017 13:27:46 +0200
Subject: [PATCH] structure data model

---
 .../collections/_spec/segmentation.spec.ts    |  13 +-
 .../collections/_spec/sorted-array.spec.ts    |   2 +-
 src/mol-base/collections/column.ts            |  25 +++-
 .../collections/integer/impl/segmentation.ts  |  19 ++-
 .../collections/integer/segmentation.ts       |  11 +-
 .../collections/integer/sorted-array.ts       |   4 +-
 src/mol-data/_spec/atom-set.spec.ts           |  30 ++--
 src/mol-data/atom-set.ts                      |  23 ++--
 src/mol-data/atom-set/base.ts                 | 108 +++++++--------
 src/mol-data/atom-set/builder.ts              |   2 +-
 src/mol-data/atom-set/properties.ts           |   6 +
 src/mol-data/atom.ts                          |  21 +++
 src/mol-data/model/formats.ts                 |   4 +-
 src/mol-data/model/formats/mmcif.ts           |  70 +++++++++-
 .../connected-components.ts => base.ts}       |   0
 src/mol-data/structure/data.ts                |  65 ---------
 src/mol-data/structure/selectors/common.ts    |  47 -------
 src/mol-data/structure/selectors/mmcif.ts     |  34 -----
 .../structure/topology/secondary-structure.ts | 128 ------------------
 src/mol-io/reader/{cif/index.ts => cif.ts}    |  12 +-
 src/perf-tests/sets.ts                        |  11 +-
 src/script.ts                                 |   9 +-
 22 files changed, 258 insertions(+), 386 deletions(-)
 create mode 100644 src/mol-data/atom-set/properties.ts
 create mode 100644 src/mol-data/atom.ts
 rename src/mol-data/structure/{topology/connected-components.ts => base.ts} (100%)
 delete mode 100644 src/mol-data/structure/data.ts
 delete mode 100644 src/mol-data/structure/selectors/common.ts
 delete mode 100644 src/mol-data/structure/selectors/mmcif.ts
 delete mode 100644 src/mol-data/structure/topology/secondary-structure.ts
 rename src/mol-io/reader/{cif/index.ts => cif.ts} (53%)

diff --git a/src/mol-base/collections/_spec/segmentation.spec.ts b/src/mol-base/collections/_spec/segmentation.spec.ts
index 27b3708fd..005b0bbe8 100644
--- a/src/mol-base/collections/_spec/segmentation.spec.ts
+++ b/src/mol-base/collections/_spec/segmentation.spec.ts
@@ -7,17 +7,26 @@
 import OrderedSet from '../integer/ordered-set'
 import Interval from '../integer/interval'
 import Segmentation from '../integer/segmentation'
-import SortedArray from '../integer/sorted-array'
 
 describe('segments', () => {
     const data = OrderedSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]);
-    const segs = Segmentation.create(SortedArray.ofSortedArray([0, 4, 10, 12, 13, 15, 25]), [])
+    const segs = Segmentation.create([0, 4, 10, 12, 13, 15, 25]);
+
+    it('size', () => expect(Segmentation.count(segs)).toBe(6));
 
     it('project', () => {
         const p = Segmentation.projectValue(segs, data, 4);
         expect(p).toBe(Interval.ofBounds(0, 2))
     });
 
+    it('map', () => {
+        const segs = Segmentation.create([1, 2, 3]);
+        expect(segs.segmentMap).toEqual(new Int32Array([0, 1]));
+        expect(segs.offset).toEqual(1);
+        expect(Segmentation.getSegment(segs, 1)).toBe(0);
+        expect(Segmentation.getSegment(segs, 2)).toBe(1);
+    })
+
     it('iteration', () => {
         const it = Segmentation.segments(segs, data);
 
diff --git a/src/mol-base/collections/_spec/sorted-array.spec.ts b/src/mol-base/collections/_spec/sorted-array.spec.ts
index 9a8871e64..7aa731c9d 100644
--- a/src/mol-base/collections/_spec/sorted-array.spec.ts
+++ b/src/mol-base/collections/_spec/sorted-array.spec.ts
@@ -37,7 +37,7 @@ describe('sortedArray', () => {
     test('getAt', SortedArray.getAt(a2468, 1), 4);
 
     test('areEqual', SortedArray.areEqual(a2468, a2468), true);
-    test('areEqual1', SortedArray.areEqual(a2468, SortedArray.create([4, 2, 8, 6])), true);
+    test('areEqual1', SortedArray.areEqual(a2468, SortedArray.ofUnsortedArray([4, 2, 8, 6])), true);
     test('areEqual2', SortedArray.areEqual(a1234, a2468), false);
 
     test('predIndex1', SortedArray.findPredecessorIndex(a1234, 5), 4);
diff --git a/src/mol-base/collections/column.ts b/src/mol-base/collections/column.ts
index acedf1d92..0a9ec96f6 100644
--- a/src/mol-base/collections/column.ts
+++ b/src/mol-base/collections/column.ts
@@ -35,6 +35,7 @@ export interface Column<T> {
 }
 
 export function UndefinedColumn<T extends ColumnType>(rowCount: number, type: T): Column<T['@type']> {
+    const v = type.isString ? '' : 0;
     const value: Column<T['@type']>['value'] = type.isString ? row => '' : row => 0;
     return {
         '@type': type,
@@ -44,7 +45,7 @@ export function UndefinedColumn<T extends ColumnType>(rowCount: number, type: T)
         isValueDefined: row => false,
         toArray: params => {
             const { array } = createArray(rowCount, params);
-            for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(0)
+            for (let i = 0, _i = array.length; i < _i; i++) array[i] = v;
             return array;
         },
         stringEquals: (row, value) => !value,
@@ -52,6 +53,28 @@ export function UndefinedColumn<T extends ColumnType>(rowCount: number, type: T)
     }
 }
 
+export function SingleValueColumn<T extends ColumnType>(v: T['@type'], rowCount: number, type: T): Column<T['@type']> {
+    const value: Column<T['@type']>['value'] = row => v;
+    return {
+        '@type': type,
+        isDefined: true,
+        rowCount,
+        value,
+        isValueDefined: row => false,
+        toArray: params => {
+            const { array } = createArray(rowCount, params);
+            for (let i = 0, _i = array.length; i < _i; i++) array[i] = v;
+            return array;
+        },
+        stringEquals: type.isString
+            ? (row, value) => value === v
+            : type.isScalar
+            ? (row, value) => +value === v
+            : (row, value) => false,
+        areValuesEqual: (rowA, rowB) => true
+    }
+}
+
 export interface ArrayColumnSpec<T extends ColumnType> {
     array: ArrayLike<T['@type']>,
     type: T,
diff --git a/src/mol-base/collections/integer/impl/segmentation.ts b/src/mol-base/collections/integer/impl/segmentation.ts
index 542b4daf2..e24f4757d 100644
--- a/src/mol-base/collections/integer/impl/segmentation.ts
+++ b/src/mol-base/collections/integer/impl/segmentation.ts
@@ -10,15 +10,22 @@ import Interval from '../interval'
 import SortedArray from '../sorted-array'
 import Segs from '../segmentation'
 
-type Segmentation = { segments: OrderedSet, segmentIndex: ArrayLike<number> }
+type Segmentation = { segments: SortedArray, segmentMap: ArrayLike<number>, offset: number, count: number }
 
-export function create(segments: SortedArray, segmentIndex: ArrayLike<number>): Segmentation {
-    return { segments, segmentIndex };
+export function create(values: ArrayLike<number>): Segmentation {
+    const segments = SortedArray.ofSortedArray(values);
+    const min = SortedArray.min(segments), max = SortedArray.max(segments);
+    const segmentMap = new Int32Array(max - min);
+    for (let i = 0, _i = values.length - 1; i < _i; i++) {
+        for (let j = values[i] - min, _j = values[i + 1] - min; j < _j; j++) {
+            segmentMap[j] = i;
+        }
+    }
+    return { segments, segmentMap, offset: min, count: values.length - 1 };
 }
 
-export function getSegment({ segmentIndex }: Segmentation, value: number) {
-    return segmentIndex[value];
-}
+export function count({ count }: Segmentation) { return count; }
+export function getSegment({ segmentMap, offset }: Segmentation, value: number) { return segmentMap[value - offset]; }
 
 export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval {
     const last = OrderedSet.max(segments);
diff --git a/src/mol-base/collections/integer/segmentation.ts b/src/mol-base/collections/integer/segmentation.ts
index 6225478c9..5d1e6c3bb 100644
--- a/src/mol-base/collections/integer/segmentation.ts
+++ b/src/mol-base/collections/integer/segmentation.ts
@@ -7,20 +7,25 @@
 import Iterator from '../iterator'
 import Interval from './interval'
 import OrderedSet from './ordered-set'
-import SortedArray from './sorted-array'
 import * as Impl from './impl/segmentation'
 
 namespace Segmentation {
     export interface Segment { index: number, start: number, end: number }
 
-    export const create: (segs: SortedArray, segIndex: ArrayLike<number>) => Segmentation = Impl.create as any;
+    export const create: (segs: ArrayLike<number>) => Segmentation = Impl.create as any;
 
+    export const count: (segs: Segmentation) => number = Impl.count as any;
     export const getSegment: (segs: Segmentation, value: number) => number = Impl.getSegment as any;
     export const projectValue: (segs: Segmentation, set: OrderedSet, value: number) => Interval = Impl.projectValue as any;
 
     export const segments: (segs: Segmentation, set: OrderedSet, range?: Interval) => Iterator<Segment> = Impl.segments as any;
 }
 
-interface Segmentation { '@type': 'segmentation' }
+interface Segmentation {
+    '@type': 'segmentation',
+    readonly segmentMap: ArrayLike<number>,
+    readonly offset: number,
+    readonly count: number
+}
 
 export default Segmentation
\ No newline at end of file
diff --git a/src/mol-base/collections/integer/sorted-array.ts b/src/mol-base/collections/integer/sorted-array.ts
index a98b9a0f9..d30f572c8 100644
--- a/src/mol-base/collections/integer/sorted-array.ts
+++ b/src/mol-base/collections/integer/sorted-array.ts
@@ -8,9 +8,7 @@ import * as Impl from './impl/sorted-array'
 import Interval from './interval'
 
 namespace SortedArray {
-    /** Create interval [min, max] */
-    export const create: (xs: ArrayLike<number>) => SortedArray = Impl.ofUnsortedArray as any;
-    /** Create interval [min, max) */
+    export const ofUnsortedArray: (xs: ArrayLike<number>) => SortedArray = Impl.ofUnsortedArray as any;
     export const ofSortedArray: (xs: ArrayLike<number>) => SortedArray = Impl.ofSortedArray as any;
     export const is: (v: any) => v is Interval = Impl.is as any;
 
diff --git a/src/mol-data/_spec/atom-set.spec.ts b/src/mol-data/_spec/atom-set.spec.ts
index f5c1d8e78..2c265acc9 100644
--- a/src/mol-data/_spec/atom-set.spec.ts
+++ b/src/mol-data/_spec/atom-set.spec.ts
@@ -4,15 +4,15 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import IntTuple from '../../mol-base/collections/integer/tuple'
 import OrderedSet from '../../mol-base/collections/integer/ordered-set'
 import AtomSet from '../atom-set'
+import Atom from '../atom'
 
 describe('atom set', () => {
-    const p = (i: number, j: number) => IntTuple.create(i, j);
+    const p = (i: number, j: number) => Atom.create(i, j);
 
-    function setToPairs(set: AtomSet): ArrayLike<IntTuple> {
-        const ret: IntTuple[] = [];
+    function setToPairs(set: AtomSet): ArrayLike<Atom> {
+        const ret: Atom[] = [];
         const it = AtomSet.atoms(set);
         while (it.hasNext) {
             ret[ret.length] = it.move();
@@ -23,9 +23,9 @@ describe('atom set', () => {
     it('singleton pair', () => {
         const set = AtomSet.create(p(10, 11));
         expect(setToPairs(set)).toEqual([p(10, 11)]);
-        expect(AtomSet.hasAtom(set, p(10, 11))).toBe(true);
-        expect(AtomSet.hasAtom(set, p(11, 11))).toBe(false);
-        expect(AtomSet.getAtomAt(set, 0)).toBe(p(10, 11));
+        expect(AtomSet.atomHas(set, p(10, 11))).toBe(true);
+        expect(AtomSet.atomHas(set, p(11, 11))).toBe(false);
+        expect(AtomSet.atomGetAt(set, 0)).toBe(p(10, 11));
         expect(AtomSet.atomCount(set)).toBe(1);
     });
 
@@ -42,16 +42,16 @@ describe('atom set', () => {
         const ret = [p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)];
         expect(AtomSet.atomCount(set)).toBe(ret.length);
         expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]);
-        expect(AtomSet.hasAtom(set, p(10, 11))).toBe(false);
-        expect(AtomSet.hasAtom(set, p(3, 0))).toBe(true);
-        expect(AtomSet.hasAtom(set, p(1, 7))).toBe(true);
+        expect(AtomSet.atomHas(set, p(10, 11))).toBe(false);
+        expect(AtomSet.atomHas(set, p(3, 0))).toBe(true);
+        expect(AtomSet.atomHas(set, p(1, 7))).toBe(true);
         for (let i = 0; i < AtomSet.atomCount(set); i++) {
-            expect(IntTuple.areEqual(AtomSet.getAtomAt(set, i), ret[i])).toBe(true);
+            expect(Atom.areEqual(AtomSet.atomGetAt(set, i), ret[i])).toBe(true);
         }
     });
 
     it('element at / index of', () => {
-        const control: IntTuple[] = [];
+        const control: Atom[] = [];
         const sets = Object.create(null);
         for (let i = 1; i < 10; i++) {
             const set = [];
@@ -63,11 +63,11 @@ describe('atom set', () => {
         }
         const ms = AtomSet.create(sets);
         for (let i = 0; i < control.length; i++) {
-            expect(IntTuple.areEqual(AtomSet.getAtomAt(ms, i), control[i])).toBe(true);
+            expect(Atom.areEqual(AtomSet.atomGetAt(ms, i), control[i])).toBe(true);
         }
 
         for (let i = 0; i < control.length; i++) {
-            expect(AtomSet.indexOfAtom(ms, control[i])).toBe(i);
+            expect(AtomSet.atomIndexOf(ms, control[i])).toBe(i);
         }
     });
 
@@ -129,6 +129,7 @@ describe('atom set', () => {
         const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]);
         const c = AtomSet.create([p(1, 3)]);
         const d = AtomSet.create([p(2, 3)]);
+        const e = AtomSet.create([p(0, 2)]);
         expect(setToPairs(AtomSet.subtract(a, a))).toEqual([]);
         expect(setToPairs(AtomSet.subtract(a, a1))).toEqual([]);
         expect(setToPairs(AtomSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]);
@@ -136,6 +137,7 @@ describe('atom set', () => {
         expect(setToPairs(AtomSet.subtract(a, c))).toEqual([p(0, 1), p(0, 2), p(0, 6)]);
         expect(setToPairs(AtomSet.subtract(c, a))).toEqual([]);
         expect(setToPairs(AtomSet.subtract(d, a))).toEqual([p(2, 3)]);
+        expect(setToPairs(AtomSet.subtract(a, e))).toEqual([p(0, 1), p(0, 6), p(1, 3)]);
     });
 
     it('union', () => {
diff --git a/src/mol-data/atom-set.ts b/src/mol-data/atom-set.ts
index d638c30fc..7f3fd55ab 100644
--- a/src/mol-data/atom-set.ts
+++ b/src/mol-data/atom-set.ts
@@ -6,7 +6,7 @@
 
 import OrderedSet from '../mol-base/collections/integer/ordered-set'
 import Iterator from '../mol-base/collections/iterator'
-import Tuple from '../mol-base/collections/integer/tuple'
+import Atom from './atom'
 import * as Base from './atom-set/base'
 import createBuilder from './atom-set/builder'
 
@@ -14,22 +14,21 @@ import createBuilder from './atom-set/builder'
 namespace AtomSet {
     export const Empty: AtomSet = Base.Empty as any;
 
-    export const create: (data: Tuple | ArrayLike<Tuple> | Tuple | { [id: number]: OrderedSet }) => AtomSet = Base.create as any;
+    export const create: (data: Atom | ArrayLike<Atom> | { [unitId: number]: OrderedSet }) => AtomSet = Base.create as any;
 
-    export const units: (set: AtomSet) => OrderedSet = Base.getKeys as any;
     export const unitCount: (set: AtomSet) => number = Base.keyCount as any;
-    export const hasUnit: (set: AtomSet, id: number) => boolean = Base.hasKey as any;
-    export const getUnitId: (set: AtomSet, i: number) => number = Base.getKey as any;
+    export const unitIds: (set: AtomSet) => OrderedSet = Base.getKeys as any;
+    export const unitHas: (set: AtomSet, id: number) => boolean = Base.hasKey as any;
+    export const unitGetId: (set: AtomSet, i: number) => number = Base.getKey as any;
 
-    export const getByKey: (set: AtomSet, key: number) => OrderedSet = Base.getByKey as any;
-    export const getByIndex: (set: AtomSet, i: number) => OrderedSet = Base.getByIndex as any;
-
-    export const hasAtom: (set: AtomSet, x: Tuple) => boolean = Base.hasTuple as any;
-    export const indexOfAtom: (set: AtomSet, x: Tuple) => number = Base.indexOf as any;
-    export const getAtomAt: (set: AtomSet, i: number) => Tuple = Base.getAt as any;
-    export const atoms: (set: AtomSet) => Iterator<Tuple> = Base.values as any;
+    export const unitGetById: (set: AtomSet, key: number) => OrderedSet = Base.getByKey as any;
+    export const unitGetByIndex: (set: AtomSet, i: number) => OrderedSet = Base.getByIndex as any;
 
     export const atomCount: (set: AtomSet) => number = Base.size as any;
+    export const atomHas: (set: AtomSet, x: Atom) => boolean = Base.hasAtom as any;
+    export const atomIndexOf: (set: AtomSet, x: Atom) => number = Base.indexOf as any;
+    export const atomGetAt: (set: AtomSet, i: number) => Atom = Base.getAt as any;
+    export const atoms: (set: AtomSet) => Iterator<Atom> = Base.values as any;
 
     export const hashCode: (set: AtomSet) => number = Base.hashCode as any;
     export const areEqual: (a: AtomSet, b: AtomSet) => boolean = Base.areEqual as any;
diff --git a/src/mol-data/atom-set/base.ts b/src/mol-data/atom-set/base.ts
index eeec45449..e457f8dc9 100644
--- a/src/mol-data/atom-set/base.ts
+++ b/src/mol-data/atom-set/base.ts
@@ -6,21 +6,21 @@
 
 import OrderedSet from '../../mol-base/collections/integer/ordered-set'
 import Iterator from '../../mol-base/collections/iterator'
-import Tuple from '../../mol-base/collections/integer/tuple'
 import Interval from '../../mol-base/collections/integer/interval'
 import { sortArray } from '../../mol-base/collections/sort'
 import { hash1 } from '../../mol-base/collections/hash-functions'
+import Atom from '../atom'
 
 /** Long and painful implementation starts here */
 
 export interface AtomSetElements { [id: number]: OrderedSet, offsets: number[], hashCode: number, keys: OrderedSet }
-export type AtomSetImpl = Tuple | AtomSetElements
+export type AtomSetImpl = Atom | AtomSetElements
 
 export const Empty: AtomSetImpl = { offsets: [0], hashCode: 0, keys: OrderedSet.Empty };
 
-export function create(data: Tuple | ArrayLike<Tuple> | { [id: number]: OrderedSet }): AtomSetImpl {
-    if (typeof data === 'number' || Tuple.is(data)) return data;
-    if (isArrayLike(data)) return ofTuples(data);
+export function create(data: Atom | ArrayLike<Atom> | { [id: number]: OrderedSet }): AtomSetImpl {
+    if (typeof data === 'number' || Atom.is(data)) return data;
+    if (isArrayLike(data)) return ofAtoms(data);
     return ofObject(data as { [id: number]: OrderedSet });
 }
 
@@ -39,42 +39,42 @@ export function keyCount(set: AtomSetImpl): number {
 }
 
 export function hasKey(set: AtomSetImpl, key: number): boolean {
-    if (typeof set === 'number') return Tuple.fst(set) === key;
+    if (typeof set === 'number') return Atom.unit(set) === key;
     return OrderedSet.has((set as AtomSetElements).keys, key);
 }
 
 export function getKey(set: AtomSetImpl, index: number): number {
-    if (typeof set === 'number') return Tuple.fst(set);
+    if (typeof set === 'number') return Atom.unit(set);
     return OrderedSet.getAt((set as AtomSetElements).keys, index);
 }
 
-export function hasTuple(set: AtomSetImpl, t: Tuple): boolean {
-    if (typeof set === 'number') return Tuple.areEqual(t, set);
-    const unit = Tuple.fst(t);
+export function hasAtom(set: AtomSetImpl, t: Atom): boolean {
+    if (typeof set === 'number') return Atom.areEqual(t, set);
+    const unit = Atom.unit(t);
     return OrderedSet.has((set as AtomSetElements).keys, unit)
-        ? OrderedSet.has((set as AtomSetElements)[unit], Tuple.snd(t)) : false;
+        ? OrderedSet.has((set as AtomSetElements)[unit], Atom.index(t)) : false;
 }
 
 export function getByKey(set: AtomSetImpl, key: number): OrderedSet {
     if (typeof set === 'number') {
-        return Tuple.fst(set) === key ? OrderedSet.ofSingleton(Tuple.snd(set)) : OrderedSet.Empty;
+        return Atom.unit(set) === key ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty;
     }
     return OrderedSet.has((set as AtomSetElements).keys, key) ? (set as AtomSetElements)[key] : OrderedSet.Empty;
 }
 
 export function getByIndex(set: AtomSetImpl, index: number): OrderedSet {
-    if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(Tuple.snd(set)) : OrderedSet.Empty;
+    if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty;
     const key = OrderedSet.getAt((set as AtomSetElements).keys, index);
     return (set as AtomSetElements)[key] || OrderedSet.Empty;
 }
 
-export function getAt(set: AtomSetImpl, i: number): Tuple {
+export function getAt(set: AtomSetImpl, i: number): Atom {
     if (typeof set === 'number') return set;
     return getAtE(set as AtomSetElements, i);
 }
 
-export function indexOf(set: AtomSetImpl, t: Tuple) {
-    if (typeof set === 'number') return Tuple.areEqual(set, t) ? 0 : -1;
+export function indexOf(set: AtomSetImpl, t: Atom) {
+    if (typeof set === 'number') return Atom.areEqual(set, t) ? 0 : -1;
     return indexOfE(set as AtomSetElements, t);
 }
 
@@ -85,14 +85,14 @@ export function size(set: AtomSetImpl) {
 }
 
 export function hashCode(set: AtomSetImpl) {
-    if (typeof set === 'number') return Tuple.hashCode(set);
+    if (typeof set === 'number') return Atom.hashCode(set);
     if ((set as AtomSetElements).hashCode !== -1) return (set as AtomSetElements).hashCode;
     return computeHash((set as AtomSetElements));
 }
 
 export function areEqual(a: AtomSetImpl, b: AtomSetImpl) {
     if (typeof a === 'number') {
-        if (typeof b === 'number') return Tuple.areEqual(a, b);
+        if (typeof b === 'number') return Atom.areEqual(a, b);
         return false;
     }
     if (typeof b === 'number') return false;
@@ -101,7 +101,7 @@ export function areEqual(a: AtomSetImpl, b: AtomSetImpl) {
 
 export function areIntersecting(a: AtomSetImpl, b: AtomSetImpl) {
     if (typeof a === 'number') {
-        if (typeof b === 'number') return Tuple.areEqual(a, b);
+        if (typeof b === 'number') return Atom.areEqual(a, b);
         return areIntersectingNE(a, b as AtomSetElements);
     }
     if (typeof b === 'number') return areIntersectingNE(b, a as AtomSetElements);
@@ -110,7 +110,7 @@ export function areIntersecting(a: AtomSetImpl, b: AtomSetImpl) {
 
 export function intersect(a: AtomSetImpl, b: AtomSetImpl) {
     if (typeof a === 'number') {
-        if (typeof b === 'number') return Tuple.areEqual(a, b) ? a : Empty;
+        if (typeof b === 'number') return Atom.areEqual(a, b) ? a : Empty;
         return intersectNE(a, b as AtomSetElements);
     }
     if (typeof b === 'number') return intersectNE(b, a as AtomSetElements);
@@ -119,7 +119,7 @@ export function intersect(a: AtomSetImpl, b: AtomSetImpl) {
 
 export function subtract(a: AtomSetImpl, b: AtomSetImpl) {
     if (typeof a === 'number') {
-        if (typeof b === 'number') return Tuple.areEqual(a, b) ? Empty : a;
+        if (typeof b === 'number') return Atom.areEqual(a, b) ? Empty : a;
         return subtractNE(a, b as AtomSetElements);
     }
     if (typeof b === 'number') return subtractEN(a as AtomSetElements, b);
@@ -134,7 +134,7 @@ export function unionMany(sets: ArrayLike<AtomSetImpl>) {
     return findUnion(sets);
 }
 
-class ElementsIterator implements Iterator<Tuple> {
+class ElementsIterator implements Iterator<Atom> {
     private unit: number = 0;
     private keyCount: number;
     private setIndex = -1;
@@ -145,8 +145,8 @@ class ElementsIterator implements Iterator<Tuple> {
     hasNext: boolean = false;
 
     move() {
-        if (!this.hasNext) return Tuple.Zero;
-        const ret = Tuple.create(this.unit, OrderedSet.getAt(this.currentSet, this.currentIndex++));
+        if (!this.hasNext) return Atom.Zero;
+        const ret = Atom.create(this.unit, OrderedSet.getAt(this.currentSet, this.currentIndex++));
         if (this.currentIndex >= this.currentSize) this.advance();
         return ret;
     }
@@ -170,12 +170,12 @@ class ElementsIterator implements Iterator<Tuple> {
     }
 }
 
-export function values(set: AtomSetImpl): Iterator<Tuple> {
-    if (typeof set === 'number') return Iterator.Value(set as Tuple);
+export function values(set: AtomSetImpl): Iterator<Atom> {
+    if (typeof set === 'number') return Iterator.Value(set as Atom);
     return new ElementsIterator(set as AtomSetElements);
 }
 
-function isArrayLike(x: any): x is ArrayLike<Tuple> {
+function isArrayLike(x: any): x is ArrayLike<Atom> {
     return x && (typeof x.length === 'number' && (x instanceof Array || !!x.buffer));
 }
 
@@ -188,7 +188,7 @@ function ofObject(data: { [id: number]: OrderedSet }) {
     if (!keys.length) return Empty;
     if (keys.length === 1) {
         const set = data[keys[0]];
-        if (OrderedSet.size(set) === 1) return Tuple.create(keys[0], OrderedSet.getAt(set, 0));
+        if (OrderedSet.size(set) === 1) return Atom.create(keys[0], OrderedSet.getAt(set, 0));
     }
     return ofObject1(keys, data);
 }
@@ -197,7 +197,7 @@ function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) {
     if (keys.length === 1) {
         const k = keys[0];
         const set = data[k];
-        if (OrderedSet.size(set) === 1) return Tuple.create(k, OrderedSet.getAt(set, 0));
+        if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0));
     }
     sortArray(keys);
     return _createObjectOrdered(OrderedSet.ofSortedArray(keys), data);
@@ -207,7 +207,7 @@ function ofObjectOrdered(keys: OrderedSet, data: { [id: number]: OrderedSet }) {
     if (OrderedSet.size(keys) === 1) {
         const k = OrderedSet.getAt(keys, 0);
         const set = data[k];
-        if (OrderedSet.size(set) === 1) return Tuple.create(k, OrderedSet.getAt(set, 0));
+        if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0));
     }
     return _createObjectOrdered(keys, data);
 }
@@ -251,12 +251,12 @@ function normalizeArray(xs: number[]) {
     return xs;
 }
 
-function ofTuples(xs: ArrayLike<Tuple>) {
+function ofAtoms(xs: ArrayLike<Atom>) {
     if (xs.length === 0) return Empty;
     const sets: { [key: number]: number[] } = Object.create(null);
     for (let i = 0, _i = xs.length; i < _i; i++) {
         const x = xs[i];
-        const u = Tuple.fst(x), v = Tuple.snd(x);
+        const u = Atom.unit(x), v = Atom.index(x);
         const set = sets[u];
         if (set) set[set.length] = v;
         else sets[u] = [v];
@@ -286,21 +286,21 @@ function getOffsetIndex(xs: ArrayLike<number>, value: number) {
     return value < xs[min] ? min - 1 : min;
 }
 
-function getAtE(set: AtomSetElements, i: number): Tuple {
+function getAtE(set: AtomSetElements, i: number): Atom {
     const { offsets, keys } = set;
     const o = getOffsetIndex(offsets, i);
     if (o >= offsets.length - 1) return 0 as any;
     const k = OrderedSet.getAt(keys, o);
     const e = OrderedSet.getAt(set[k], i - offsets[o]);
-    return Tuple.create(k, e);
+    return Atom.create(k, e);
 }
 
-function indexOfE(set: AtomSetElements, t: Tuple) {
+function indexOfE(set: AtomSetElements, t: Atom) {
     const { keys } = set;
-    const u = Tuple.fst(t);
+    const u = Atom.unit(t);
     const setIdx = OrderedSet.indexOf(keys, u);
     if (setIdx < 0) return -1;
-    const o = OrderedSet.indexOf(set[u], Tuple.snd(t));
+    const o = OrderedSet.indexOf(set[u], Atom.index(t));
     if (o < 0) return -1;
     return set.offsets[setIdx] + o;
 }
@@ -332,9 +332,9 @@ function areEqualEE(a: AtomSetElements, b: AtomSetElements) {
     return true;
 }
 
-function areIntersectingNE(a: Tuple, b: AtomSetElements) {
-    const u = Tuple.fst(a);
-    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Tuple.snd(a));
+function areIntersectingNE(a: Atom, b: AtomSetElements) {
+    const u = Atom.unit(a);
+    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Atom.index(a));
 }
 
 function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) {
@@ -350,9 +350,9 @@ function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) {
     return false;
 }
 
-function intersectNE(a: Tuple, b: AtomSetElements) {
-    const u = Tuple.fst(a);
-    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Tuple.snd(a)) ? a : Empty;
+function intersectNE(a: Atom, b: AtomSetElements) {
+    const u = Atom.unit(a);
+    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Atom.index(a)) ? a : Empty;
 }
 
 function intersectEE(a: AtomSetElements, b: AtomSetElements) {
@@ -377,14 +377,14 @@ function intersectEE(a: AtomSetElements, b: AtomSetElements) {
     return ofObjectOrdered(OrderedSet.ofSortedArray(keys), ret);
 }
 
-function subtractNE(a: Tuple, b: AtomSetElements) {
-    const u = Tuple.fst(a);
-    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Tuple.snd(a)) ? Empty : a;
+function subtractNE(a: Atom, b: AtomSetElements) {
+    const u = Atom.unit(a);
+    return OrderedSet.has(b.keys, u) && OrderedSet.has(b[u], Atom.index(a)) ? Empty : a;
 }
 
-function subtractEN(a: AtomSetElements, b: Tuple): AtomSetImpl {
+function subtractEN(a: AtomSetElements, b: Atom): AtomSetImpl {
     const aKeys =  a.keys;
-    const u = Tuple.fst(b), v = Tuple.snd(b);
+    const u = Atom.unit(b), v = Atom.index(b);
     if (!OrderedSet.has(aKeys, u) || !OrderedSet.has(a[u], v)) return a;
     const set = a[u];
     if (OrderedSet.size(set) === 1) {
@@ -465,14 +465,14 @@ function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) {
     }
     eCount.count = countE;
     if (!countN) return Empty;
-    if (countN === sets.length) return ofTuples(sets as ArrayLike<Tuple>);
+    if (countN === sets.length) return ofAtoms(sets as ArrayLike<Atom>);
     const packed = new Float64Array(countN);
     let offset = 0;
     for (let i = 0, _i = sets.length; i < _i; i++) {
         const s = sets[i];
         if (typeof s === 'number') packed[offset++] = s;
     }
-    return ofTuples(packed as any);
+    return ofAtoms(packed as any);
 }
 
 function unionInto(data: { [key: number]: OrderedSet }, a: AtomSetElements) {
@@ -485,12 +485,12 @@ function unionInto(data: { [key: number]: OrderedSet }, a: AtomSetElements) {
     }
 }
 
-function unionIntoN(data: { [key: number]: OrderedSet }, a: Tuple) {
-    const u = Tuple.fst(a);
+function unionIntoN(data: { [key: number]: OrderedSet }, a: Atom) {
+    const u = Atom.unit(a);
     const set = data[u];
     if (set) {
-        data[u] = OrderedSet.union(set, OrderedSet.ofSingleton(Tuple.snd(a)));
+        data[u] = OrderedSet.union(set, OrderedSet.ofSingleton(Atom.index(a)));
     } else {
-        data[u] = OrderedSet.ofSingleton(Tuple.snd(a));
+        data[u] = OrderedSet.ofSingleton(Atom.index(a));
     }
 }
\ No newline at end of file
diff --git a/src/mol-data/atom-set/builder.ts b/src/mol-data/atom-set/builder.ts
index d28cbe641..568236286 100644
--- a/src/mol-data/atom-set/builder.ts
+++ b/src/mol-data/atom-set/builder.ts
@@ -40,7 +40,7 @@ class Builder {
                 sets[k] = OrderedSet.ofSingleton(unit[0]);
             } else {
                 const set = OrderedSet.ofSortedArray(unit);
-                const parentSet = AtomSet.getByKey(this.parent, k);
+                const parentSet = AtomSet.unitGetById(this.parent, k);
                 sets[k] = OrderedSet.areEqual(set, parentSet) ? parentSet : set;
             }
         }
diff --git a/src/mol-data/atom-set/properties.ts b/src/mol-data/atom-set/properties.ts
new file mode 100644
index 000000000..d13a9ee15
--- /dev/null
+++ b/src/mol-data/atom-set/properties.ts
@@ -0,0 +1,6 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
diff --git a/src/mol-data/atom.ts b/src/mol-data/atom.ts
new file mode 100644
index 000000000..30f225fa7
--- /dev/null
+++ b/src/mol-data/atom.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import Tuple from '../mol-base/collections/integer/tuple'
+
+interface Atom { '@type': Tuple['@type'] }
+
+namespace Atom {
+    export const Zero: Atom = Tuple.Zero;
+    export const create: (unit: number, index: number) => Atom = Tuple.create;
+    export const is: (x: any) => x is Atom = Tuple.is;
+    export const unit: (a: Atom) => number = Tuple.fst;
+    export const index: (a: Atom) => number = Tuple.snd;
+    export const areEqual: (a: Atom, b: Atom) => boolean = Tuple.areEqual;
+    export const hashCode: (a: Atom) => number = Tuple.hashCode;
+}
+
+export default Atom
\ No newline at end of file
diff --git a/src/mol-data/model/formats.ts b/src/mol-data/model/formats.ts
index d0243888b..1f98e96f5 100644
--- a/src/mol-data/model/formats.ts
+++ b/src/mol-data/model/formats.ts
@@ -4,5 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import mmCIF from '../../mol-io/reader/cif/schema/mmcif'
 
-export type RawData = {}
\ No newline at end of file
+export type RawData =
+    | { source: 'mmCIF', data: mmCIF }
\ No newline at end of file
diff --git a/src/mol-data/model/formats/mmcif.ts b/src/mol-data/model/formats/mmcif.ts
index 96788f75b..c1cc176dd 100644
--- a/src/mol-data/model/formats/mmcif.ts
+++ b/src/mol-data/model/formats/mmcif.ts
@@ -4,4 +4,72 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-// TODO: implement interfaces for mmCIF source data format
\ No newline at end of file
+import { RawData } from '../formats'
+import mmCIF from '../../../mol-io/reader/cif/schema/mmcif'
+import Model from '../../model'
+import Interval from '../../../mol-base/collections/integer/interval'
+import Segmentation from '../../../mol-base/collections/integer/segmentation'
+
+function findModelBounds(data: mmCIF, startIndex: number) {
+    const num = data.atom_site.pdbx_PDB_model_num;
+    const atomCount = num.rowCount;
+    if (!num.isDefined) return Interval.ofBounds(startIndex, atomCount);
+    let endIndex = startIndex + 1;
+    while (endIndex < atomCount && num.areValuesEqual(startIndex, endIndex)) endIndex++;
+    return Interval.ofBounds(startIndex, endIndex);
+}
+
+function segment(data: mmCIF, bounds: Interval) {
+    const start = Interval.start(bounds), end = Interval.end(bounds);
+    const residues = [start], chains = [start];
+
+    const { label_entity_id, auth_asym_id, auth_seq_id, pdbx_PDB_ins_code } = data.atom_site;
+
+    for (let i = start + 1; i < end; i++) {
+        const newEntity = !label_entity_id.areValuesEqual(i - 1, i);
+        const newChain = newEntity || !auth_asym_id.areValuesEqual(i - 1, i);
+        const newResidue = newChain
+            || !auth_seq_id.areValuesEqual(i - 1, i)
+            || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i);
+
+        if (newResidue) residues[residues.length] = i;
+        if (newChain) chains[chains.length] = i;
+    }
+
+    residues[residues.length] = end;
+    chains[chains.length] = end;
+
+    return { residues: Segmentation.create(residues), chains: Segmentation.create(chains) };
+}
+
+function createModel(raw: RawData, data: mmCIF, bounds: Interval): Model {
+    const segments = segment(data, bounds);
+    return {
+        data: raw,
+        common: 0 as any,
+        macromolecule: 0 as any,
+        residues: segments.residues,
+        chains: segments.chains
+    };
+}
+
+function getModels(data: mmCIF): ArrayLike<Model> {
+    const raw: RawData = { source: 'mmCIF', data };
+    const models: Model[] = [];
+    const atomCount = data.atom_site._rowCount;
+    let modelStart = 0;
+    while (modelStart < atomCount) {
+        const bounds = findModelBounds(data, modelStart);
+        const model = createModel(raw, data, bounds);
+        models.push(model);
+        modelStart = Interval.end(bounds);
+    }
+    return models;
+}
+
+export default getModels;
+
+// function createStructure() {
+
+// }
+
diff --git a/src/mol-data/structure/topology/connected-components.ts b/src/mol-data/structure/base.ts
similarity index 100%
rename from src/mol-data/structure/topology/connected-components.ts
rename to src/mol-data/structure/base.ts
diff --git a/src/mol-data/structure/data.ts b/src/mol-data/structure/data.ts
deleted file mode 100644
index d162fc1b3..000000000
--- a/src/mol-data/structure/data.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-export type Table<Data> = { rowCount: number } & { [E in keyof Data]: ArrayLike<Data[E]> }
-export type DataTable<Data> = { data: any } & Table<Data & { dataIndex: number }>
-
-export interface Position { x: number, y: number, z: number }
-export interface Positions extends Table<Position> {}
-
-export interface Atom {
-    name: string,
-    elementSymbol: string,
-    altLoc: string | null,
-}
-export interface Atoms extends DataTable<Atom> { }
-
-export interface Residue {
-    key: number,
-    name: string,
-    seqNumber: number,
-    insCode: string | null,
-    isHet: number
-}
-export interface Residues extends DataTable<Residue> { }
-
-export interface Chain { key: number, id: string }
-export interface Chains extends DataTable<Chain> { }
-
-export interface Entity { key: number, id: string }
-export interface Entities extends DataTable<Entity> { }
-
-export interface Bonds extends Readonly<{
-    /**
-     * Where bonds for atom A start and end.
-     * Start at idx, end at idx + 1
-     */
-    offset: ArrayLike<number>,
-    neighbor: ArrayLike<number>,
-
-    order: ArrayLike<number>,
-    flags: ArrayLike<number>,
-
-    count: number
-}> { }
-
-export type SourceData =
-    | { kind: 'mmCIF', data: any  } // TODO
-    | { kind: 'custom', data: any  } // TODO
-
-export interface Structure {
-    atoms: Atoms,
-    residues: Residues,
-    chains: Chains,
-    entities: Entities
-}
-
-export interface SecondaryStructure {
-
-}
-export interface SecondaryStructures extends Table<SecondaryStructure> {
-
-}
\ No newline at end of file
diff --git a/src/mol-data/structure/selectors/common.ts b/src/mol-data/structure/selectors/common.ts
deleted file mode 100644
index 76b4c876f..000000000
--- a/src/mol-data/structure/selectors/common.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// /**
-//  * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
-//  *
-//  * @author David Sehnal <david.sehnal@gmail.com>
-//  */
-
-// import { Selector } from '../model'
-// //import * as Data from '../data'
-// //import { Vec3 } from '../../utils/linear-algebra'
-
-// export const atom = {
-//     name: Selector((m, u) => {
-//         const unit = m.units[u].structure;
-//         const atoms = unit.atoms;
-//         const name = unit.data.atoms.name;
-//         return a => name[atoms[a]];
-//     }),
-//     // position: Selector<Vec3>((m, u) => {
-//     //     const unit = m.units[u].structure;
-//     //     const atoms = unit.atoms;
-//     //     const { positions: { x, y, z } } = m.conformation[u];
-//     //     const { operator: { transform } } = unit;
-//     //     return (atom, position) => {
-//     //         const a = atoms[atom];
-//     //         const p = position || Vec3.zero();
-//     //         Vec3.set(p, x[a], y[a], z[a]);
-//     //         return Vec3.transformMat4(p, p, transform);
-//     //     };
-//     // }),
-//     // inversePosition: Selector<Vec3>((m, u) => {
-//     //     const unit = m.structure[u];
-//     //     const atoms = unit.atoms;
-//     //     const { positions: { x, y, z } } = m.conformation[u];
-//     //     const { operator: { inverse } } = unit;
-
-//     //     return (atom, position) => {
-//     //         const a = atoms[atom];
-//     //         const p = position || Vec3.zero();
-//     //         Vec3.set(p, x[a], y[a], z[a]);
-//     //         return Vec3.transformMat4(p, p, inverse);
-//     //     };
-//     // })
-// }
-
-// export const residue = {
-
-// }
\ No newline at end of file
diff --git a/src/mol-data/structure/selectors/mmcif.ts b/src/mol-data/structure/selectors/mmcif.ts
deleted file mode 100644
index 08a95c232..000000000
--- a/src/mol-data/structure/selectors/mmcif.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// /**
-//  * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
-//  *
-//  * @author David Sehnal <david.sehnal@gmail.com>
-//  */
-
-// import { Model, Unit, PropertyGetterProvider } from '../model'
-// import * as Data from '../data'
-// //import { Vec3 } from '../../utils/linear-algebra'
-
-// // function provider<T>(p: (model: Model) => Property<T>): PropertyProvider<T> {
-// //     return p;
-// // }
-
-// function atomPropertyProvider<T>(property: (ps: Model['structure']['properties']) => ArrayLike<T>): PropertyGetterProvider<T> {
-//     return m => {
-//         const a = m.structure.properties.atoms;
-//         const p = property(m.structure.properties);
-//         return ({ unit, atom }) => p[a[unit][atom]];
-//     };
-// }
-
-// function unitStructureProvider<T>(p: (structure: Unit.Structure, data: Data.Structure, atom: number) => T): PropertyGetterProvider<T> {
-//     return m => {
-//         const units = m.structure.units;
-//         const data = m.structure.properties.data;
-//         return ({ unit, atom }) => p(units[unit], data[unit], atom);
-//     };
-// }
-
-// export const atom_site = {
-//     label_atom_id: atomPropertyProvider(ps => ps.),
-//     label_comp_id: unitStructureProvider((u, d, a) => d.residues.name[u.atomResidue[a]])
-// }
\ No newline at end of file
diff --git a/src/mol-data/structure/topology/secondary-structure.ts b/src/mol-data/structure/topology/secondary-structure.ts
deleted file mode 100644
index 75d1f4b89..000000000
--- a/src/mol-data/structure/topology/secondary-structure.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-const enum SSF {
-    None = 0x0,
-
-    // category
-    DoubleHelix = 0x1,
-    Helix = 0x2,
-    Beta = 0x4,
-    Turn = 0x8,
-
-    // category variant
-    LeftHanded = 0x10,  // helix
-    RightHanded = 0x20,
-
-    ClassicTurn = 0x40,  // turn
-    InverseTurn = 0x80,
-
-    // sub-category
-    HelixOther = 0x100,  // protein
-    Helix27 = 0x200,
-    Helix3Ten = 0x400,
-    HelixAlpha = 0x800,
-    HelixGamma = 0x1000,
-    HelixOmega = 0x2000,
-    HelixPi = 0x4000,
-    HelixPolyproline = 0x8000,
-
-    DoubleHelixOther = 0x10000,  // nucleic
-    DoubleHelixZ = 0x20000,
-    DoubleHelixA = 0x40000,
-    DoubleHelixB = 0x80000,
-
-    BetaOther = 0x100000,  // protein
-    BetaStrand = 0x200000,  // single strand
-    BetaSheet = 0x400000,  // multiple hydrogen bonded strands
-    BetaBarell = 0x800000,  // closed series of sheets
-
-    TurnOther = 0x1000000,  // protein
-    Turn1 = 0x2000000,
-    Turn2 = 0x4000000,
-    Turn3 = 0x8000000,
-
-    NA = 0x10000000,  // not applicable/available
-}
-export { SSF as SecondaryStructureFlag }
-
-export const SecondaryStructureMmcif: { [value: string]: number } = {
-    HELX_LH_27_P: SSF.Helix | SSF.LeftHanded | SSF.Helix27,  // left-handed 2-7 helix (protein)
-    HELX_LH_3T_P: SSF.Helix | SSF.LeftHanded | SSF.Helix3Ten,  // left-handed 3-10 helix (protein)
-    HELX_LH_AL_P: SSF.Helix | SSF.LeftHanded | SSF.HelixAlpha,  // left-handed alpha helix (protein)
-    HELX_LH_A_N: SSF.DoubleHelix | SSF.LeftHanded | SSF.DoubleHelixA,  // left-handed A helix (nucleic acid)
-    HELX_LH_B_N: SSF.DoubleHelix | SSF.LeftHanded | SSF.DoubleHelixB,  // left-handed B helix (nucleic acid)
-    HELX_LH_GA_P: SSF.Helix | SSF.LeftHanded | SSF.HelixGamma,  // left-handed gamma helix (protein)
-    HELX_LH_N: SSF.DoubleHelix | SSF.LeftHanded,  // left-handed helix with type not specified (nucleic acid)
-    HELX_LH_OM_P: SSF.Helix | SSF.LeftHanded | SSF.HelixOmega,  // left-handed omega helix (protein)
-    HELX_LH_OT_N: SSF.DoubleHelix | SSF.LeftHanded | SSF.DoubleHelixOther,  // left-handed helix with type that does not conform to an accepted category (nucleic acid)
-    HELX_LH_OT_P: SSF.Helix | SSF.LeftHanded | SSF.HelixOther,  // left-handed helix with type that does not conform to an accepted category (protein)
-    HELX_LH_P: SSF.Helix | SSF.LeftHanded,  // left-handed helix with type not specified (protein)
-    HELX_LH_PI_P: SSF.Helix | SSF.LeftHanded | SSF.HelixPi,  // left-handed pi helix (protein)
-    HELX_LH_PP_P: SSF.Helix | SSF.LeftHanded | SSF.HelixPolyproline,  // left-handed polyproline helix (protein)
-    HELX_LH_Z_N: SSF.DoubleHelix | SSF.LeftHanded | SSF.DoubleHelixZ,  // left-handed Z helix (nucleic acid)
-    HELX_N: SSF.DoubleHelix,  // helix with handedness and type not specified (nucleic acid)
-    HELX_OT_N: SSF.DoubleHelix,  // helix with handedness and type that do not conform to an accepted category (nucleic acid)
-    HELX_OT_P: SSF.Helix,  // helix with handedness and type that do not conform to an accepted category (protein)
-    HELX_P: SSF.Helix,  // helix with handedness and type not specified (protein)
-    HELX_RH_27_P: SSF.Helix | SSF.RightHanded | SSF.Helix27,  // right-handed 2-7 helix (protein)
-    HELX_RH_3T_P: SSF.Helix | SSF.RightHanded | SSF.Helix3Ten,  // right-handed 3-10 helix (protein)
-    HELX_RH_AL_P: SSF.Helix | SSF.RightHanded | SSF.HelixAlpha,  // right-handed alpha helix (protein)
-    HELX_RH_A_N: SSF.DoubleHelix | SSF.RightHanded | SSF.DoubleHelixA,  // right-handed A helix (nucleic acid)
-    HELX_RH_B_N: SSF.DoubleHelix | SSF.RightHanded | SSF.DoubleHelixB,  // right-handed B helix (nucleic acid)
-    HELX_RH_GA_P: SSF.Helix | SSF.RightHanded | SSF.HelixGamma,  // right-handed gamma helix (protein)
-    HELX_RH_N: SSF.DoubleHelix | SSF.RightHanded,  // right-handed helix with type not specified (nucleic acid)
-    HELX_RH_OM_P: SSF.Helix | SSF.RightHanded | SSF.HelixOmega,  // right-handed omega helix (protein)
-    HELX_RH_OT_N: SSF.DoubleHelix | SSF.RightHanded | SSF.DoubleHelixOther,  // right-handed helix with type that does not conform to an accepted category (nucleic acid)
-    HELX_RH_OT_P: SSF.Helix | SSF.RightHanded | SSF.HelixOther,  // right-handed helix with type that does not conform to an accepted category (protein)
-    HELX_RH_P: SSF.Helix | SSF.RightHanded,  // right-handed helix with type not specified (protein)
-    HELX_RH_PI_P: SSF.Helix | SSF.RightHanded | SSF.HelixPi,  // right-handed pi helix (protein)
-    HELX_RH_PP_P: SSF.Helix | SSF.RightHanded | SSF.HelixPolyproline,  // right-handed polyproline helix (protein)
-    HELX_RH_Z_N: SSF.DoubleHelix | SSF.RightHanded | SSF.DoubleHelixZ,  // right-handed Z helix (nucleic acid)
-    STRN: SSF.Beta | SSF.BetaStrand,  // beta strand (protein)
-    TURN_OT_P: SSF.Turn | SSF.TurnOther,  // turn with type that does not conform to an accepted category (protein)
-    TURN_P: SSF.Turn,  // turn with type not specified (protein)
-    TURN_TY1P_P: SSF.Turn | SSF.InverseTurn | SSF.Turn1,  // type I prime turn (protein)
-    TURN_TY1_P: SSF.Turn | SSF.ClassicTurn | SSF.Turn1,  // type I turn (protein)
-    TURN_TY2P_P: SSF.Turn | SSF.InverseTurn | SSF.Turn2,  // type II prime turn (protein)
-    TURN_TY2_P: SSF.Turn | SSF.ClassicTurn | SSF.Turn2,  // type II turn (protein)
-    TURN_TY3P_P: SSF.Turn | SSF.InverseTurn | SSF.Turn3,  // type III prime turn (protein)
-    TURN_TY3_P: SSF.Turn | SSF.ClassicTurn | SSF.Turn3,  // type III turn (protein)
-}
-
-export const SecondaryStructurePdb: { [value: string]: number } = {
-    1: SSF.Helix | SSF.RightHanded | SSF.HelixAlpha,  // Right-handed alpha (default)
-    2: SSF.Helix | SSF.RightHanded | SSF.HelixOmega,  // Right-handed omega
-    3: SSF.Helix | SSF.RightHanded | SSF.HelixPi,  // Right-handed pi
-    4: SSF.Helix | SSF.RightHanded | SSF.HelixGamma,  // Right-handed gamma
-    5: SSF.Helix | SSF.RightHanded | SSF.Helix3Ten,  // Right-handed 310
-    6: SSF.Helix | SSF.LeftHanded | SSF.HelixAlpha,  // Left-handed alpha
-    7: SSF.Helix | SSF.LeftHanded | SSF.HelixOmega,  // Left-handed omega
-    8: SSF.Helix | SSF.LeftHanded | SSF.HelixGamma,  // Left-handed gamma
-    9: SSF.Helix | SSF.Helix27,  // 27 ribbon/helix
-    10: SSF.Helix | SSF.HelixPolyproline,  // Polyproline
-}
-
-export const SecondaryStructureStride: { [value: string]: number } = {
-    H: SSF.Helix | SSF.HelixAlpha,  // Alpha helix
-    G: SSF.Helix | SSF.Helix3Ten,  // 3-10 helix
-    I: SSF.Helix | SSF.HelixPi,  // PI-helix
-    E: SSF.Beta | SSF.BetaSheet,  // Extended conformation
-    B: SSF.Beta | SSF.BetaStrand,  // Isolated bridge
-    b: SSF.Beta | SSF.BetaStrand,  // Isolated bridge
-    T: SSF.Turn,  // Turn
-    C: SSF.NA,  // Coil (none of the above)
-}
-
-export const SecondaryStructureDssp: { [value: string]: number } = {
-    H: SSF.Helix | SSF.HelixAlpha,  // alpha-helix
-    B: SSF.Beta | SSF.BetaStrand,  // residue in isolated beta-bridge
-    E: SSF.Beta | SSF.BetaSheet,  // extended strand, participates in beta ladder
-    G: SSF.Helix | SSF.Helix3Ten,  // 3-helix (310 helix)
-    I: SSF.Helix | SSF.HelixPi,  // 5 helix (pi-helix)
-    T: SSF.Turn,  // hydrogen bonded turn
-    S: SSF.Turn,  // bend
-}
\ No newline at end of file
diff --git a/src/mol-io/reader/cif/index.ts b/src/mol-io/reader/cif.ts
similarity index 53%
rename from src/mol-io/reader/cif/index.ts
rename to src/mol-io/reader/cif.ts
index aa10d1ffa..0d44a5482 100644
--- a/src/mol-io/reader/cif/index.ts
+++ b/src/mol-io/reader/cif.ts
@@ -4,11 +4,11 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import parseText from './text/parser'
-import parseBinary from './binary/parser'
-import { Block } from './data-model'
-import { toTypedFrame as applySchema } from './schema'
-import mmCIF from './schema/mmcif'
+import parseText from './cif/text/parser'
+import parseBinary from './cif/binary/parser'
+import { Block } from './cif/data-model'
+import { toTypedFrame as applySchema } from './cif/schema'
+import mmCIF from './cif/schema/mmcif'
 
 export default {
     parseText,
@@ -19,4 +19,4 @@ export default {
     }
 }
 
-export * from './data-model'
\ No newline at end of file
+export * from './cif/data-model'
\ No newline at end of file
diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts
index 178a9d9fe..4794a55cf 100644
--- a/src/perf-tests/sets.ts
+++ b/src/perf-tests/sets.ts
@@ -3,7 +3,6 @@ import Tuple from '../mol-base/collections/integer/tuple'
 import OrdSet from '../mol-base/collections/integer/ordered-set'
 import AtomSet from '../mol-data/atom-set'
 import Segmentation from '../mol-base/collections/integer/segmentation'
-import SortedArray from '../mol-base/collections/integer/sorted-array'
 
 export namespace Iteration {
     const U = 1000, V = 2500;
@@ -38,15 +37,15 @@ export namespace Iteration {
 
     export function elementAt() {
         let s = 0;
-        for (let i = 0, _i = AtomSet.atomCount(ms); i < _i; i++) s += Tuple.snd(AtomSet.getAtomAt(ms, i));
+        for (let i = 0, _i = AtomSet.atomCount(ms); i < _i; i++) s += Tuple.snd(AtomSet.atomGetAt(ms, i));
         return s;
     }
 
     export function manual() {
         let s = 0;
-        const keys = AtomSet.units(ms);
+        const keys = AtomSet.unitIds(ms);
         for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) {
-            const set = AtomSet.getByKey(ms, OrdSet.getAt(keys, i));
+            const set = AtomSet.unitGetById(ms, OrdSet.getAt(keys, i));
             for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
                 s += OrdSet.getAt(set, j);
             }
@@ -57,7 +56,7 @@ export namespace Iteration {
     export function manual1() {
         let s = 0;
         for (let i = 0, _i = AtomSet.unitCount(ms); i < _i; i++) {
-            const set = AtomSet.getByIndex(ms, i);
+            const set = AtomSet.unitGetByIndex(ms, i);
             for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
                 s += OrdSet.getAt(set, j);
             }
@@ -274,7 +273,7 @@ export namespace Tuples {
 
 export function testSegments() {
     const data = OrdSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]);
-    const segs = Segmentation.create(SortedArray.ofSortedArray([0, 4, 10, 12, 13, 15, 25]), []);
+    const segs = Segmentation.create([0, 4, 10, 12, 13, 15, 25]);
     const it = Segmentation.segments(segs, data);
 
     while (it.hasNext) {
diff --git a/src/script.ts b/src/script.ts
index 0870d4429..345e177e6 100644
--- a/src/script.ts
+++ b/src/script.ts
@@ -13,10 +13,12 @@ const readFileAsync = util.promisify(fs.readFile);
 const writeFileAsync = util.promisify(fs.writeFile);
 
 import Gro from './mol-io/reader/gro/parser'
-import CIF from './mol-io/reader/cif/index'
+import CIF from './mol-io/reader/cif'
 
 import Computation from './mol-base/computation'
 
+import createModels from './mol-data/model/formats/mmcif'
+
 // import { toTypedFrame as applySchema } from './reader/cif/schema'
 import { generateSchema } from './mol-io/reader/cif/schema/utils'
 
@@ -112,6 +114,11 @@ async function runCIF(input: string | Uint8Array) {
     console.log(mmcif.entity.type.toArray());
     console.log(mmcif.pdbx_struct_oper_list.matrix.value(0));
 
+    console.time('createModels');
+    const models = createModels(mmcif);
+    console.timeEnd('createModels');
+    console.log(models[0].common);
+
     // const schema = await _dic()
     // if (schema) {
     //     const mmcif2 = applySchema(schema, data)
-- 
GitLab