diff --git a/src/mol-base/collections/_spec/table.spec.ts b/src/mol-base/collections/_spec/table.spec.ts
index a41ce9f115f998ce3197f9f11ed0b696443a5b12..35eaacb85e3695341f292e91f6c2444063380ac5 100644
--- a/src/mol-base/collections/_spec/table.spec.ts
+++ b/src/mol-base/collections/_spec/table.spec.ts
@@ -69,6 +69,27 @@ describe('table', () => {
         expect(t.n.toArray()).toEqual(['row1', 'row2']);
     });
 
+    it('ofArrays', () => {
+        const t = Table.ofArrays<typeof schema>(schema, {
+            x: [10, -1],
+            n: ['row1', 'row2'],
+        });
+        expect(t.x.toArray()).toEqual([10, -1]);
+        expect(t.n.toArray()).toEqual(['row1', 'row2']);
+    });
+
+    it('pickColumns', () => {
+        const t = Table.ofColumns<typeof schema>({
+            x: Column.ofArray({ array: [10, -1], type: Column.Type.int }),
+            n: Column.ofArray({ array: ['row1', 'row2'], type: Column.Type.str }),
+        });
+        const s = { x: Column.Type.int };
+        const picked = Table.pickColumns(s, t);
+        expect(picked._columns).toEqual(['x']);
+        expect(picked._rowCount).toEqual(2);
+        expect(picked.x.toArray()).toEqual([10, -1]);
+    });
+
     it('sort', () => {
         const t = Table.ofColumns<typeof schema>({
             x: Column.ofArray({ array: [10, -1], type: Column.Type.int }),
diff --git a/src/mol-base/collections/column.ts b/src/mol-base/collections/column.ts
index 4650e8a3739bc0fe6a4a24e5e17763ab89f767bb..4ae3ad6f6179aa7cd6b11de1997640a05292a77b 100644
--- a/src/mol-base/collections/column.ts
+++ b/src/mol-base/collections/column.ts
@@ -17,18 +17,24 @@ interface Column<T> {
 }
 
 namespace Column {
-    export type Type = typeof Type.str | typeof Type.int | typeof Type.float | Type.Vector | Type.Matrix
+    export type Type<T = any> = Type.Str | Type.Int | Type.Float | Type.Vector | Type.Matrix | Type.Aliased<T>
 
     export namespace Type {
-        export const str = { T: '' as string, kind: 'str' as 'str' };
-        export const int = { T: 0 as number, kind: 'int' as 'int' };
-        export const float = { T: 0 as number, kind: 'float' as 'float' };
-
+        export type Str = { T: string, kind: 'str' }
+        export type Int = { T: number, kind: 'int' }
+        export type Float = { T: number, kind: 'float' }
         export type Vector = { T: number[], dim: number, kind: 'vector' };
         export type Matrix = { T: number[][], rows: number, cols: number, kind: 'matrix' };
+        export type Aliased<T> = { T: T } & { kind: 'str' | 'int' | 'float' }
+
+        export const str: Str = { T: '', kind: 'str' };
+        export const int: Int = { T: 0, kind: 'int' };
+        export const float: Float = { T: 0, kind: 'float' };
+
 
         export function vector(dim: number): Vector { return { T: [] as number[], dim, kind: 'vector' }; }
         export function matrix(rows: number, cols: number): Matrix { return { T: [] as number[][], rows, cols, kind: 'matrix' }; }
+        export function aliased<T>(t: Type): Aliased<T> { return t as any as Aliased<T>; }
     }
 
     export interface ToArrayParams {
diff --git a/src/mol-base/collections/table.ts b/src/mol-base/collections/table.ts
index 152a894a28d0152fb0d73603c7c3ff6022c863ba..a7f7aed5af70b183ff39dd91460f0c2ab046b246 100644
--- a/src/mol-base/collections/table.ts
+++ b/src/mol-base/collections/table.ts
@@ -9,10 +9,21 @@ import { sortArray } from './sort'
 
 type Table<Schema extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & Table.Columns<Schema>
 
+/** An immutable table */
 namespace Table {
     export type Schema = { [field: string]: Column.Type }
     export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
     export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
+    export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
+
+    export function pickColumns<S extends Schema, T extends S>(schema: S, table: Table<T>): Table<S> {
+        const ret = Object.create(null);
+        const keys = Object.keys(schema);
+        ret._rowCount = table._rowCount;
+        ret._columns = keys;
+        for (const k of keys) ret[k] = table[k];
+        return ret;
+    }
 
     export function ofColumns<S extends Schema, R extends Table<S> = Table<S>>(columns: Columns<S>): R {
         const _columns = Object.keys(columns);
@@ -26,7 +37,7 @@ namespace Table {
         const columns = Object.keys(schema);
         ret._rowCount = rowCount;
         ret._columns = columns;
-        for (const k of Object.keys(schema)) {
+        for (const k of columns) {
             (ret as any)[k] = Column.ofLambda({
                 rowCount,
                 type: schema[k],
@@ -37,6 +48,17 @@ namespace Table {
         return ret as R;
     }
 
+    export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R {
+        const ret = Object.create(null);
+        const columns = Object.keys(schema);
+        ret._rowCount = arrays[columns[0]].length;
+        ret._columns = columns;
+        for (const k of Object.keys(schema)) {
+            (ret as any)[k] = Column.ofArray({ array: arrays[k], type: schema[k] })
+        }
+        return ret as R;
+    }
+
     /** Sort and return a new table */
     export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) {
         const indices = new Int32Array(table._rowCount);
diff --git a/src/mol-data/model/properties/hierarchy.ts b/src/mol-data/model/properties/hierarchy.ts
index 427b2a1bc5e6745467e094b54c2b959076f52b0c..5d54f59ba5995e11418fcb9add0a4721e72d31d1 100644
--- a/src/mol-data/model/properties/hierarchy.ts
+++ b/src/mol-data/model/properties/hierarchy.ts
@@ -5,7 +5,8 @@
  */
 
 import Column from '../../../mol-base/collections/column'
-import { Shape as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif'
+import Table from '../../../mol-base/collections/table'
+import { Schema as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif'
 
 export interface ElementSymbol extends String { '@type': 'element-symbol' }
 export function ElementSymbol(s: string): ElementSymbol {
@@ -13,50 +14,55 @@ export function ElementSymbol(s: string): ElementSymbol {
     return s.toUpperCase() as any;
 }
 
-type Key = { key: Column<number> }
-
-type _Atoms = Pick<mmCIF['atom_site'],
-    | 'type_symbol'
-    | 'label_atom_id'
-    | 'auth_atom_id'
-    | 'label_alt_id'
-    | 'pdbx_formal_charge'
-    | 'occupancy'
-    | 'B_iso_or_equiv'>
-    & Key
-export interface Atoms extends _Atoms {
-    source_row: Column<number>
-}
+export const AtomsSchema = {
+    type_symbol: Column.Type.aliased<ElementSymbol>(mmCIF.atom_site.type_symbol),
+    label_atom_id: mmCIF.atom_site.label_atom_id,
+    auth_atom_id: mmCIF.atom_site.auth_atom_id,
+    label_alt_id: mmCIF.atom_site.label_alt_id,
+    pdbx_formal_charge: mmCIF.atom_site.pdbx_formal_charge,
+    occupancy: mmCIF.atom_site.occupancy,
+    B_iso_or_equiv: mmCIF.atom_site.B_iso_or_equiv,
+
+    key: Column.Type.int,
+    source_row: Column.Type.int,
+};
+
+export interface Atoms extends Table<typeof AtomsSchema> { }
+
+export const ResiduesSchema = {
+    group_PDB: mmCIF.atom_site.group_PDB,
+    label_comp_id: mmCIF.atom_site.label_comp_id,
+    auth_comp_id: mmCIF.atom_site.auth_comp_id,
+    label_seq_id: mmCIF.atom_site.label_seq_id,
+    auth_seq_id: mmCIF.atom_site.auth_seq_id,
+    pdbx_PDB_ins_code: mmCIF.atom_site.pdbx_PDB_ins_code,
 
-type _Residues = Pick<mmCIF['atom_site'],
-    | 'group_PDB'
-    | 'label_comp_id'
-    | 'auth_comp_id'
-    | 'label_seq_id'
-    | 'auth_seq_id'
-    | 'pdbx_PDB_ins_code'>
-    & Key
-export interface Residues extends _Residues { }
-
-type _Chains = Pick<mmCIF['atom_site'],
-    | 'label_asym_id'
-    | 'auth_asym_id'
-    | 'auth_comp_id'
-    | 'label_entity_id'
-    | 'pdbx_PDB_model_num'>
-    & Key
-export interface Chains extends _Chains {
-    enityDataIndex: Column<number>
+    key: Column.Type.int
+};
+
+export interface Residues extends Table<typeof AtomsSchema> { }
+
+export const ChainsSchema = {
+    label_asym_id: mmCIF.atom_site.label_asym_id,
+    auth_asym_id: mmCIF.atom_site.auth_asym_id,
+    auth_comp_id: mmCIF.atom_site.auth_comp_id,
+    label_entity_id: mmCIF.atom_site.label_entity_id,
+    pdbx_PDB_model_num: mmCIF.atom_site.pdbx_PDB_model_num,
+
+    key: Column.Type.int,
+    entityIndex: Column.Type.int
 }
 
-type _EntityData = mmCIF['entity']
-export interface EntityData extends _EntityData { }
+export interface Chains extends Table<typeof ChainsSchema> { }
+
+export const EntitySchema = mmCIF['entity']
+export interface Entities extends Table<typeof EntitySchema> { }
 
 export interface Macromolecule {
     atoms: Atoms,
     residues: Residues,
     chains: Chains,
-    entityData: EntityData
+    entities: Entities
 }
 
 export default Macromolecule
\ No newline at end of file
diff --git a/src/mol-io/reader/_spec/cif.spec.ts b/src/mol-io/reader/_spec/cif.spec.ts
index accb0ab6abf1d8df80344aed155d013653f9435a..553c01917afae6eb1343754471d78ecefb158f9d 100644
--- a/src/mol-io/reader/_spec/cif.spec.ts
+++ b/src/mol-io/reader/_spec/cif.spec.ts
@@ -21,7 +21,7 @@ const testBlock = Data.Block({
 }, 'test');
 
 namespace TestSchema {
-    export const atoms = { x: Schema.Field.int(), name: Schema.Field.str() }
+    export const atoms = { x: Schema.Types.int, name: Schema.Types.str }
     export const schema = { atoms }
 }
 
diff --git a/src/mol-io/reader/cif/schema.ts b/src/mol-io/reader/cif/schema.ts
index adf421e3bb3df8159ed5ddbfc212841d5a744551..113ed90849f66613882cfa45e440054c3dd66243 100644
--- a/src/mol-io/reader/cif/schema.ts
+++ b/src/mol-io/reader/cif/schema.ts
@@ -6,93 +6,56 @@
 
 import * as Data from './data-model'
 import Column, { createAndFillArray } from '../../../mol-base/collections/column'
-
-/**
- * A schema defines the shape of categories and fields.
- *
- * @example:
- * const atom_site = {
- *   '@alias': '_atom_site',
- *   label_atom_id: Field.str(),
- *   Cartn_x: Field.float(),
- *   Cartn_y: Field.float(),
- *   Cartn_z: Field.float(),
- * }
- *
- * const mmCIF = { atom_site };
- */
-
-//////////////////////////////////////////////
+import Table from '../../../mol-base/collections/table'
 
 export function toTypedFrame<Schema extends FrameSchema, Frame extends TypedFrame<Schema> = TypedFrame<Schema>>(schema: Schema, frame: Data.Frame): Frame {
     return createTypedFrame(schema, frame) as Frame;
 }
 
-export function toTypedCategory<Schema extends CategorySchema>(schema: Schema, category: Data.Category): TypedCategory<Schema> {
-    return new _TypedCategory(category, schema, true) as TypedCategory<any>;
+export function toTable<Schema extends Table.Schema, R extends Table<Schema> = Table<Schema>>(schema: Schema, category: Data.Category): R {
+    return new _TypedCategory(category, schema, true) as any;
 }
 
-export type FrameSchema = { [category: string]: CategorySchema }
-export type TypedFrameShape<Schema extends FrameSchema> = { [C in keyof Schema]: TypedCategoryShape<Schema[C]> }
+export const Types = Column.Type
+
+export type FrameSchema = { [category: string]: Table.Schema }
 export type TypedFrame<Schema extends FrameSchema> = {
     readonly _header?: string,
     readonly _frame: Data.Frame
-} & { [C in keyof Schema]: TypedCategory<Schema[C]> }
-
-export type CategorySchema = { [field: string]: Field.Schema<any> }
-export type TypedCategoryShape<Schema extends CategorySchema> = { [F in keyof Schema]: Column<Schema[F]['T']> }
-export type TypedCategory<Schema extends CategorySchema> = {
-    readonly _rowCount: number,
-    readonly _isDefined: boolean,
-    readonly _category: Data.Category
-} & { [F in keyof Schema]: Column<Schema[F]['T']> }
-
-export namespace Field {
-    export interface Schema<T> { T: T, ctor: (field: Data.Field, category: Data.Category, key: string) => Column<T>, undefinedField: (c: number) => Data.Field, alias?: string };
-    export interface Spec { undefinedField?: (c: number) => Data.Field, alias?: string }
-
-    export function alias(name: string): Schema<any> { return { alias: name } as any; }
-    export function str(spec?: Spec) { return createSchema(spec, Str); }
-    export function int(spec?: Spec) { return createSchema(spec, Int); }
-    export function float(spec?: Spec) { return createSchema(spec, Float); }
-    export function vector(rows: number, spec?: Spec) { return createSchema(spec, Vector(rows)); }
-    export function matrix(rows: number, cols: number, spec?: Spec) { return createSchema(spec, Matrix(rows, cols)); }
-
-    function create<T>(type: Column.Type, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
-        return {
-            '@type': type,
-            '@array': field['@array'],
-            isDefined: field.isDefined,
-            rowCount: field.rowCount,
-            value,
-            valueKind: field.valueKind,
-            areValuesEqual: field.areValuesEqual,
-            toArray
-        };
-    }
-
-    function Str(field: Data.Field) { return create(Column.Type.str, field, field.str, field.toStringArray); }
-    function Int(field: Data.Field) { return create(Column.Type.int, field, field.int, field.toIntArray); }
-    function Float(field: Data.Field) { return create(Column.Type.float, field, field.float, field.toFloatArray); }
-
-    function Vector(rows: number) {
-        return function(field: Data.Field, category: Data.Category, key: string) {
-            const value = (row: number) => Data.getVector(category, key, rows, row);
-            return create(Column.Type.vector(rows), field, value, params => createAndFillArray(field.rowCount, value, params));
+} & { [C in keyof Schema]: Table<Schema[C]> }
+
+type ColumnCtor = (field: Data.Field, category: Data.Category, key: string) => Column<any>
+
+function getColumnCtor(t: Column.Type): ColumnCtor {
+    switch (t.kind) {
+        case 'str': return (f, c, k) => createColumn(Column.Type.str, f, f.str, f.toStringArray);
+        case 'int': return (f, c, k) => createColumn(Column.Type.int, f, f.int, f.toIntArray);
+        case 'float': return (f, c, k) => createColumn(Column.Type.float, f, f.float, f.toFloatArray);
+        case 'vector': return (f, c, k) => {
+            const dim = t.dim;
+            const value = (row: number) => Data.getVector(c, k, dim, row);
+            return createColumn(t, f, value, params => createAndFillArray(f.rowCount, value, params));
         }
-    }
-
-    function Matrix(rows: number, cols: number) {
-        return function(field: Data.Field, category: Data.Category, key: string) {
-            const value = (row: number) => Data.getMatrix(category, key, rows, cols, row);
-            return create(Column.Type.matrix(rows, cols), field, value, params => createAndFillArray(field.rowCount, value, params));
+        case 'matrix': return (f, c, k) => {
+            const rows = t.rows, cols = t.cols;
+            const value = (row: number) => Data.getMatrix(c, k, rows, cols, row);
+            return createColumn(t, f, value, params => createAndFillArray(f.rowCount, value, params));
         }
     }
+}
 
-    // spec argument is to allow for specialised implementation for undefined fields
-    function createSchema<T>(spec: Spec | undefined, ctor: (field: Data.Field, category: Data.Category, key: string) => Column<T>): Schema<T> {
-        return { T: 0 as any, ctor, undefinedField: (spec && spec.undefinedField) || Data.DefaultUndefinedField, alias: spec && spec.alias };
-    }
+
+function createColumn<T>(type: Column.Type, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
+    return {
+        '@type': type,
+        '@array': field['@array'],
+        isDefined: field.isDefined,
+        rowCount: field.rowCount,
+        value,
+        valueKind: field.valueKind,
+        areValuesEqual: field.areValuesEqual,
+        toArray
+    };
 }
 
 class _TypedFrame implements TypedFrame<any> { // tslint:disable-line:class-name
@@ -104,19 +67,21 @@ class _TypedFrame implements TypedFrame<any> { // tslint:disable-line:class-name
     }
 }
 
-class _TypedCategory implements TypedCategory<any> { // tslint:disable-line:class-name
+class _TypedCategory implements Table<any> { // tslint:disable-line:class-name
     _rowCount = this._category.rowCount;
-    constructor(public _category: Data.Category, schema: CategorySchema, public _isDefined: boolean) {
-        const fieldKeys = Object.keys(schema).filter(k => k !== '@alias');
+    _columns: ReadonlyArray<string>;
+    constructor(public _category: Data.Category, schema: Table.Schema, public _isDefined: boolean) {
+        const fieldKeys = Object.keys(schema);
+        this._columns = fieldKeys;
         const cache = Object.create(null);
         for (const k of fieldKeys) {
-            const s = schema[k];
+            const cType = schema[k];
+            const ctor = getColumnCtor(cType);
             Object.defineProperty(this, k, {
                 get: function() {
                     if (cache[k]) return cache[k];
-                    const name = s.alias || k;
-                    const field = _category.getField(name) || s.undefinedField(_category.rowCount);
-                    cache[k] = s.ctor(field, _category, name);
+                    const field = _category.getField(k);
+                    cache[k] = !!field ? ctor(field, _category, k) : Column.Undefined(_category.rowCount, cType);
                     return cache[k];
                 },
                 enumerable: true,
@@ -130,9 +95,7 @@ function createTypedFrame(schema: FrameSchema, frame: Data.Frame): any {
     return new _TypedFrame(frame, schema);
 }
 
-function createTypedCategory(key: string, schema: CategorySchema, frame: Data.Frame) {
-    const alias = (schema['@alias'] && schema['@alias'].alias) || key;
-    const name = alias[0] === '_' ? alias : '_' + alias;
-    const cat = frame.categories[name];
+function createTypedCategory(key: string, schema: Table.Schema, frame: Data.Frame) {
+    const cat = frame.categories[key[0] === '_' ? key : '_' + key];
     return new _TypedCategory(cat || Data.Category.Empty, schema, !!cat);
 }
\ No newline at end of file
diff --git a/src/mol-io/reader/cif/schema/dic.ts b/src/mol-io/reader/cif/schema/dic.ts
index 77e4dac7e9c74c0e26ce0cbe242dae88a538c11f..4d20eccc89ad032c556fdbe8df256109a330cb15 100644
--- a/src/mol-io/reader/cif/schema/dic.ts
+++ b/src/mol-io/reader/cif/schema/dic.ts
@@ -4,10 +4,10 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Field, TypedFrame } from '../schema'
+import { Types, TypedFrame } from '../schema'
 
-const str = Field.str()
-const float = Field.float()
+const str = Types.str
+const float = Types.float
 
 const datablock = {
     id: str,
@@ -58,7 +58,7 @@ const item_units_conversion = {
 
 // TODO save frame dic schema
 
-const dic = {
+export const Schema = {
     datablock,
     dictionary,
     dictionary_history,
@@ -69,5 +69,7 @@ const dic = {
     item_units_conversion
 }
 
-type dic = TypedFrame<typeof dic>
-export default dic
+export interface Frame extends TypedFrame<typeof Schema> { }
+
+// type dic = TypedFrame<typeof dic>
+//export default dic
diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts
index a5d3ba1567e63dc17b42123231f1e3195c2a3141..839ee23e8c7b5e39cf5294b71634c96e5e6dbd98 100644
--- a/src/mol-io/reader/cif/schema/mmcif.ts
+++ b/src/mol-io/reader/cif/schema/mmcif.ts
@@ -4,11 +4,11 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Field, TypedFrame, TypedFrameShape } from '../schema'
+import { Types, TypedFrame } from '../schema'
 
-const str = Field.str();
-const int = Field.int();
-const float = Field.float();
+const str = Types.str;
+const int = Types.int;
+const float = Types.float;
 
 const entry = {
     id: str
@@ -18,7 +18,7 @@ type EntityType = 'polymer' | 'non-polymer' | 'water'
 
 const entity = {
     id: str,
-    type: str as Field.Schema<EntityType>,
+    type: Types.aliased<EntityType>(str),
     src_method: str,
     pdbx_description: str,
     formula_weight: float,
@@ -48,8 +48,8 @@ const cell = {
 
 const symmetry = {
     entry_id: str,
-    space_group_name_HM: Field.str({ alias: 'space_group_name_H-M' }),
-    pdbx_full_space_group_name_HM: Field.str({ alias: 'pdbx_full_space_group_name_H-M' }),
+    'space_group_name_H-M': str,
+    'pdbx_full_space_group_name_H': str,
     cell_setting: str,
     Int_Tables_number: int,
     space_group_name_Hall: str
@@ -117,7 +117,7 @@ type BondValueOrder =
 
 const struct_conn = {
     id: str,
-    conn_type_id: str as Field.Schema<StructConnTypeId>,
+    conn_type_id: Types.aliased<StructConnTypeId>(str),
     pdbx_PDB_id: str,
     ptnr1_label_asym_id: str,
     ptnr1_label_comp_id: str,
@@ -148,11 +148,11 @@ const struct_conn = {
     pdbx_ptnr3_PDB_ins_code: str,
     details: str,
     pdbx_dist_value: float,
-    pdbx_value_order: str as Field.Schema<BondValueOrder>
+    pdbx_value_order: Types.aliased<BondValueOrder>(str)
 }
 
 const struct_conn_type = {
-    id: str as Field.Schema<StructConnTypeId>,
+    id: Types.aliased<StructConnTypeId>(str),
     criteria: str,
     reference: str
 }
@@ -161,10 +161,10 @@ const chem_comp_bond = {
     comp_id: str,
     pdbx_stereo_config: str,
     pdbx_ordinal: int,
-    pdbx_aromatic_flag: str as Field.Schema<'Y' | 'N'>,
+    pdbx_aromatic_flag: Types.aliased<'Y' | 'N'>(str),
     atom_id_1: str,
     atom_id_2: str,
-    value_order: str as Field.Schema<BondValueOrder>
+    value_order: Types.aliased<BondValueOrder>(str)
 }
 
 const pdbx_struct_assembly = {
@@ -186,8 +186,8 @@ const pdbx_struct_oper_list = {
     type: str,
     name: str,
     symmetry_operation: str,
-    matrix: Field.matrix(3, 3),
-    vector: Field.vector(3)
+    matrix: Types.matrix(3, 3),
+    vector: Types.vector(3)
 }
 
 const pdbx_struct_mod_residue = {
@@ -245,5 +245,4 @@ export const Schema = {
     atom_site
 };
 
-export interface Frame extends TypedFrame<typeof Schema> { }
-export interface Shape extends TypedFrameShape<typeof Schema> { }
\ No newline at end of file
+export interface Frame extends TypedFrame<typeof Schema> { }
\ No newline at end of file