diff --git a/package-lock.json b/package-lock.json
index 2d93002f4e7b77be351e8e4eb333e62b3f5807b4..c1e108171140e7de69fdc590f15d660d717faef1 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index fc7b94070b8354b925e1ea4564eef34fa45161db..23e18880b61b541eae8b34c8e21c38539d3e9c2b 100644
--- a/package.json
+++ b/package.json
@@ -85,7 +85,7 @@
     "argparse": "^1.0.10",
     "express": "^4.16.3",
     "gl": "^4.0.4",
-    "material-ui": "^1.0.0-beta.46",
+    "material-ui": "~1.0.0-beta.43",
     "node-fetch": "^2.1.2",
     "react": "^16.3.2",
     "react-dom": "^16.3.2",
diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts
index 1bc3b500b28e524f48acd0ed3df60cd0414e4266..15ee55fd8578f3d636fe1173b980fd6990d42f38 100644
--- a/src/apps/structure-info/model.ts
+++ b/src/apps/structure-info/model.ts
@@ -30,7 +30,7 @@ async function readPdbFile(path: string) {
 }
 
 export function atomLabel(model: Model, aI: number) {
-    const { atoms, residues, chains, residueSegments, chainSegments } = model.hierarchy
+    const { atoms, residues, chains, residueSegments, chainSegments } = model.atomicHierarchy
     const { label_atom_id } = atoms
     const { label_comp_id, label_seq_id } = residues
     const { label_asym_id } = chains
@@ -92,10 +92,10 @@ export function printUnits(structure: Structure) {
         } else if (Unit.isCoarse(l.unit)) {
             console.log(`Coarse unit ${unit.id} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`);
 
-            const props = Queries.props.coarse_grained;
+            const props = Queries.props.coarse;
             const seq = l.unit.model.sequence;
 
-            for (let j = 0, _j = Math.min(size, 10); j < _j; j++) {
+            for (let j = 0, _j = Math.min(size, 3); j < _j; j++) {
                 l.element = OrderedSet.getAt(elements, j);
 
                 const residues: string[] = [];
@@ -104,16 +104,16 @@ export function printUnits(structure: Structure) {
                 for (let e = start; e <= end; e++) residues.push(compId(e));
                 console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`);
             }
-            if (size > 10) console.log(`...`);
+            if (size > 3) console.log(`...`);
         }
     }
 }
 
 
 export function printIHMModels(model: Model) {
-    if (!model.coarseGrained.isDefined) return false;
+    if (!model.coarseHierarchy.isDefined) return false;
     console.log('IHM Models\n=============');
-    console.log(Table.formatToString(model.coarseGrained.modelList));
+    console.log(Table.formatToString(model.coarseHierarchy.models));
 }
 
 async function run(mmcif: mmCIF_Database) {
diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts
index 1946f810388add6a316106575e1393a5c3798d44..db82405148603f4167fc66e3a9f7deaba9f872f4 100644
--- a/src/mol-geo/representation/structure/spacefill.ts
+++ b/src/mol-geo/representation/structure/spacefill.ts
@@ -39,7 +39,7 @@ function createSpacefillMesh(unit: Unit, detail: number) {
         if (Unit.isAtomic(unit)) {
             radius = Queries.props.atom.vdw_radius
         } else if (Unit.isSpheres(unit)) {
-            radius = Queries.props.coarse_grained.sphere_radius
+            radius = Queries.props.coarse.sphere_radius
         } else {
             console.warn('Unsupported unit type')
             return meshBuilder.getMesh()
diff --git a/src/mol-geo/theme/structure/color/chain-id.ts b/src/mol-geo/theme/structure/color/chain-id.ts
index 95e305be302a3a1d0c86c2175cec389cde5d50b7..d521d6036c9f12edaa560e8ba87be14b9bc76fb6 100644
--- a/src/mol-geo/theme/structure/color/chain-id.ts
+++ b/src/mol-geo/theme/structure/color/chain-id.ts
@@ -18,11 +18,11 @@ function createChainIdMap(unit: Unit) {
     let count: number
     let asym_id: Column<string>
     if (Unit.isAtomic(unit)) {
-        asym_id = unit.model.hierarchy.chains.label_asym_id
-        count = unit.model.hierarchy.chains._rowCount
+        asym_id = unit.model.atomicHierarchy.chains.label_asym_id
+        count = unit.model.atomicHierarchy.chains._rowCount
     } else if (Unit.isCoarse(unit)) {
-        asym_id = unit.sites.asym_id
-        count = unit.sites.count
+        asym_id = unit.coarseElements.asym_id
+        count = unit.coarseElements.count
     } else {
         console.warn('Unknown unit type')
         return { map, count: index }
@@ -51,7 +51,7 @@ export function chainIdColorData(props: StructureColorDataProps) {
     if (Unit.isAtomic(unit)) {
         asym_id = Queries.props.chain.label_asym_id
     } else if (Unit.isCoarse(unit)) {
-        asym_id = Queries.props.coarse_grained.asym_id
+        asym_id = Queries.props.coarse.asym_id
     }
 
     const l = Element.Location()
diff --git a/src/mol-geo/theme/structure/color/element-symbol.ts b/src/mol-geo/theme/structure/color/element-symbol.ts
index 815985b3347c57eb88bf4c47ad219939f2be1c7c..affedfac08a2ca695894d76ff1177370a827b2e8 100644
--- a/src/mol-geo/theme/structure/color/element-symbol.ts
+++ b/src/mol-geo/theme/structure/color/element-symbol.ts
@@ -23,7 +23,7 @@ export function elementSymbolColor(element: ElementSymbol): Color {
 
 export function elementSymbolColorData(props: StructureColorDataProps) {
     const { group: { units, elements }, vertexMap } = props
-    const { type_symbol } = units[0].model.hierarchy.atoms
+    const { type_symbol } = units[0].model.atomicHierarchy.atoms
     return createAttributeOrElementColor(vertexMap, {
         colorFn: (elementIdx: number) => {
             const e = elements[elementIdx]
diff --git a/src/mol-geo/theme/structure/size/element.ts b/src/mol-geo/theme/structure/size/element.ts
index 8bf99acc04942e56183eeb840b458222ac3668ad..741ba136f322274b9e72abee37168e56d481fb62 100644
--- a/src/mol-geo/theme/structure/size/element.ts
+++ b/src/mol-geo/theme/structure/size/element.ts
@@ -17,7 +17,7 @@ export function elementSizeData(props: StructureSizeDataProps) {
     if (Unit.isAtomic(unit)) {
         radius = Queries.props.atom.vdw_radius
     } else if (Unit.isSpheres(unit)) {
-        radius = Queries.props.coarse_grained.sphere_radius
+        radius = Queries.props.coarse.sphere_radius
     }
     const l = Element.Location()
     l.unit = unit
diff --git a/src/mol-math/linear-algebra/3d/mat3.ts b/src/mol-math/linear-algebra/3d/mat3.ts
index cd577776b23b29e6abbd21049ae009ce42ce3a50..c72dc713dba2f2c51e76ae986e1f39474f09bdc5 100644
--- a/src/mol-math/linear-algebra/3d/mat3.ts
+++ b/src/mol-math/linear-algebra/3d/mat3.ts
@@ -104,6 +104,10 @@ namespace Mat3 {
         return Mat3.copy(Mat3.zero(), a);
     }
 
+    export function setValue(a: Mat3, i: number, j: number, value: number) {
+        a[3 * j + i] = value;
+    }
+
     /**
      * Copy the values from one Mat3 to another
      */
diff --git a/src/mol-math/linear-algebra/tensor.ts b/src/mol-math/linear-algebra/tensor.ts
index 976e410e1b0fb96055c435cabd873cb868791be9..4bcae46f7bd0db8987d0036ed8590ba804cd1d9c 100644
--- a/src/mol-math/linear-algebra/tensor.ts
+++ b/src/mol-math/linear-algebra/tensor.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Mat4, Vec3, Vec4 } from './3d'
+import { Mat4, Vec3, Vec4, Mat3 } from './3d'
 
 export interface Tensor { data: Tensor.Data, space: Tensor.Space }
 
@@ -65,6 +65,16 @@ export namespace Tensor {
         return mat;
     }
 
+    export function toMat3(space: Space, data: Tensor.Data): Mat3 {
+        if (space.rank !== 2) throw new Error('Invalid tensor rank');
+        const mat = Mat3.zero();
+        const d0 = Math.min(3, space.dimensions[0]), d1 = Math.min(3, space.dimensions[1]);
+        for (let i = 0; i < d0; i++) {
+            for (let j = 0; j < d1; j++) Mat3.setValue(mat, i, j, space.get(data, i, j));
+        }
+        return mat;
+    }
+
     export function toVec3(space: Space, data: Tensor.Data): Vec3 {
         if (space.rank !== 1) throw new Error('Invalid tensor rank');
         const vec = Vec3.zero();
diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts
index 65ded0cbbcb3c48f51824ca0649699f9c3f350e7..d7f4491a64738c7009e719bd0672ce8d8741bb75 100644
--- a/src/mol-model/structure/export/mmcif.ts
+++ b/src/mol-model/structure/export/mmcif.ts
@@ -96,7 +96,7 @@ const atom_site: Encoder.CategoryDefinition<Element.Location> = {
         str('auth_asym_id', P.chain.auth_asym_id),
 
         int('pdbx_PDB_model_num', P.unit.model_num),
-        str('pdbx_operator_name', P.unit.operator_name)
+        str('operator_name', P.unit.operator_name)
     ]
 };
 
diff --git a/src/mol-model/structure/model/format.ts b/src/mol-model/structure/model/format.ts
index d463c30d70af897445b2293aca0726a14d53119a..2fa1f8ae1880d87643f6923b8a266c6693a151f5 100644
--- a/src/mol-model/structure/model/format.ts
+++ b/src/mol-model/structure/model/format.ts
@@ -4,15 +4,15 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { File as GroFile } from 'mol-io/reader/gro/schema'
+// import { File as GroFile } from 'mol-io/reader/gro/schema'
 import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'
 
 type Format =
-    | Format.gro
+    // | Format.gro
     | Format.mmCIF
 
 namespace Format {
-    export interface gro { kind: 'gro', data: GroFile }
+    // export interface gro { kind: 'gro', data: GroFile }
     export interface mmCIF { kind: 'mmCIF', data: mmCIF_Database }
 }
 
diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model/structure/model/formats/gro.ts
index 46a9cd1652087d59eb597918ac25fa29f34d68a1..5e53509740ac866421ad7f736d9e91057b6d23be 100644
--- a/src/mol-model/structure/model/formats/gro.ts
+++ b/src/mol-model/structure/model/formats/gro.ts
@@ -1,153 +1,154 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import UUID from 'mol-util/uuid'
-import { Column, Table } from 'mol-data/db'
-import { Interval, Segmentation } from 'mol-data/int'
-import { Atoms } from 'mol-io/reader/gro/schema'
-import Format from '../format'
-import Model from '../model'
-import { AtomicConformation, AtomicData, AtomsSchema, ResiduesSchema, ChainsSchema, AtomicSegments } from '../properties/atomic'
-import { CoarseGrainedHierarchy } from '../properties/coarse-grained/hierarchy'
-import findHierarchyKeys from '../utils/hierarchy-keys'
-import { guessElement } from '../utils/guess-element'
-import { ElementSymbol} from '../types'
-import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
-
-import gro_Format = Format.gro
-import Sequence from '../properties/sequence';
-import { Entities } from '../properties/common';
-import { ModelSymmetry } from '../properties/symmetry';
-
-type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> }
-
-function findHierarchyOffsets(atomsData: Atoms, bounds: Interval) {
-    const start = Interval.start(bounds), end = Interval.end(bounds);
-    const residues = [start], chains = [start];
-
-    const { residueName, residueNumber } = atomsData;
-
-    for (let i = start + 1; i < end; i++) {
-        const newResidue = !residueNumber.areValuesEqual(i - 1, i)
-            || !residueName.areValuesEqual(i - 1, i);
-        console.log(residueName.value(i - 1), residueName.value(i), residueNumber.value(i - 1), residueNumber.value(i), newResidue)
-        if (newResidue) residues[residues.length] = i;
-    }
-    console.log(residues, residues.length)
-    return { residues, chains };
-}
-
-function guessElementSymbol (value: string) {
-    return ElementSymbol(guessElement(value));
-}
-
-function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicData {
-    console.log(atomsData.atomName)
-    const atoms = Table.ofColumns(AtomsSchema, {
-        type_symbol: Column.ofArray({ array: Column.mapToArray(atomsData.atomName, guessElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
-        label_atom_id: atomsData.atomName,
-        auth_atom_id: atomsData.atomName,
-        label_alt_id: Column.Undefined(atomsData.count, Column.Schema.str),
-        pdbx_formal_charge: Column.Undefined(atomsData.count, Column.Schema.int)
-    });
-
-    const residues = Table.view(Table.ofColumns(ResiduesSchema, {
-        group_PDB: Column.Undefined(atomsData.count, Column.Schema.Aliased<'ATOM' | 'HETATM'>(Column.Schema.str)),
-        label_comp_id: atomsData.residueName,
-        auth_comp_id: atomsData.residueName,
-        label_seq_id: atomsData.residueNumber,
-        auth_seq_id: atomsData.residueNumber,
-        pdbx_PDB_ins_code: Column.Undefined(atomsData.count, Column.Schema.str),
-    }), ResiduesSchema, offsets.residues);
-    // Optimize the numeric columns
-    Table.columnToArray(residues, 'label_seq_id', Int32Array);
-    Table.columnToArray(residues, 'auth_seq_id', Int32Array);
-
-    // const chains = Table.ofColumns(Hierarchy.ChainsSchema, {
-    //     label_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
-    //     auth_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
-    //     label_entity_id: Column.Undefined(atomsData.count, Column.Schema.str)
-    // });
-
-    const chains = Table.ofUndefinedColumns(ChainsSchema, 0);
-
-    return { atoms, residues, chains };
-}
-
-function getConformation(atoms: Atoms): AtomicConformation {
-    return {
-        id: UUID.create(),
-        atomId: atoms.atomNumber,
-        occupancy: Column.Undefined(atoms.count, Column.Schema.int),
-        B_iso_or_equiv: Column.Undefined(atoms.count, Column.Schema.float),
-        x: Column.mapToArray(atoms.x, x => x * 10, Float32Array),
-        y: Column.mapToArray(atoms.y, y => y * 10, Float32Array),
-        z: Column.mapToArray(atoms.z, z => z * 10, Float32Array)
-    }
-}
-
-function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
-    // need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
-    return Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>)
-        && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>)
-}
-
-function createModel(format: gro_Format, modelNum: number, previous?: Model): Model {
-    const structure = format.data.structures[modelNum];
-    const bounds = Interval.ofBounds(0, structure.atoms.count);
-
-    const hierarchyOffsets = findHierarchyOffsets(structure.atoms, bounds);
-    const hierarchyData = createHierarchyData(structure.atoms, hierarchyOffsets);
-
-    if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) {
-        return {
-            ...previous,
-            atomSiteConformation: getConformation(structure.atoms)
-        };
-    }
-
-    const hierarchySegments: AtomicSegments = {
-        residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds),
-        chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds),
-    }
-
-    // TODO: create a better mock entity
-    const entityTable = Table.ofRows<mmCIF['entity']>(mmCIF.entity, [{
-        id: '0',
-        src_method: 'syn',
-        type: 'polymer',
-        pdbx_number_of_molecules: 1
-    }]);
-
-    const entities: Entities = { data: entityTable, getEntityIndex: Column.createIndexer(entityTable.id) };
-
-    const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments);
-    const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments };
-    return {
-        id: UUID.create(),
-        sourceData: format,
-        modelNum,
-        hierarchy,
-        entities,
-        sequence: Sequence.fromAtomicHierarchy(hierarchy),
-        atomSiteConformation: getConformation(structure.atoms),
-        coarseGrained: CoarseGrainedHierarchy.Empty,
-        symmetry: ModelSymmetry.Default,
-        atomCount: structure.atoms.count
-    };
-}
-
-function buildModels(format: gro_Format): ReadonlyArray<Model> {
-    const models: Model[] = [];
-
-    format.data.structures.forEach((_, i) => {
-        const model = createModel(format, i, models.length > 0 ? models[models.length - 1] : void 0);
-        models.push(model);
-    });
-    return models;
-}
-
-export default buildModels;
\ No newline at end of file
+// TODO: make this work when the time comes.
+// /**
+//  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+//  *
+//  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+//  */
+
+// import { Column, Table } from 'mol-data/db';
+// import { Interval, Segmentation } from 'mol-data/int';
+// import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif';
+// import { Atoms } from 'mol-io/reader/gro/schema';
+// import UUID from 'mol-util/uuid';
+// import Format from '../format';
+// import Model from '../model';
+// import { AtomicConformation, AtomicData, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../properties/atomic';
+// import { CoarseHierarchy } from '../properties/coarse';
+// import { Entities } from '../properties/common';
+// import Sequence from '../properties/sequence';
+// import { ModelSymmetry } from '../properties/symmetry';
+// import { guessElement } from '../properties/utils/guess-element';
+// import { getAtomicKeys } from '../properties/utils/keys';
+// import { ElementSymbol } from '../types';
+
+// import gro_Format = Format.gro
+
+// type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> }
+
+// function findHierarchyOffsets(atomsData: Atoms, bounds: Interval) {
+//     const start = Interval.start(bounds), end = Interval.end(bounds);
+//     const residues = [start], chains = [start];
+
+//     const { residueName, residueNumber } = atomsData;
+
+//     for (let i = start + 1; i < end; i++) {
+//         const newResidue = !residueNumber.areValuesEqual(i - 1, i)
+//             || !residueName.areValuesEqual(i - 1, i);
+//         console.log(residueName.value(i - 1), residueName.value(i), residueNumber.value(i - 1), residueNumber.value(i), newResidue)
+//         if (newResidue) residues[residues.length] = i;
+//     }
+//     console.log(residues, residues.length)
+//     return { residues, chains };
+// }
+
+// function guessElementSymbol (value: string) {
+//     return ElementSymbol(guessElement(value));
+// }
+
+// function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicData {
+//     console.log(atomsData.atomName)
+//     const atoms = Table.ofColumns(AtomsSchema, {
+//         type_symbol: Column.ofArray({ array: Column.mapToArray(atomsData.atomName, guessElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
+//         label_atom_id: atomsData.atomName,
+//         auth_atom_id: atomsData.atomName,
+//         label_alt_id: Column.Undefined(atomsData.count, Column.Schema.str),
+//         pdbx_formal_charge: Column.Undefined(atomsData.count, Column.Schema.int)
+//     });
+
+//     const residues = Table.view(Table.ofColumns(ResiduesSchema, {
+//         group_PDB: Column.Undefined(atomsData.count, Column.Schema.Aliased<'ATOM' | 'HETATM'>(Column.Schema.str)),
+//         label_comp_id: atomsData.residueName,
+//         auth_comp_id: atomsData.residueName,
+//         label_seq_id: atomsData.residueNumber,
+//         auth_seq_id: atomsData.residueNumber,
+//         pdbx_PDB_ins_code: Column.Undefined(atomsData.count, Column.Schema.str),
+//     }), ResiduesSchema, offsets.residues);
+//     // Optimize the numeric columns
+//     Table.columnToArray(residues, 'label_seq_id', Int32Array);
+//     Table.columnToArray(residues, 'auth_seq_id', Int32Array);
+
+//     // const chains = Table.ofColumns(Hierarchy.ChainsSchema, {
+//     //     label_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
+//     //     auth_asym_id: Column.ofConst('A', atomsData.count, Column.Schema.str),
+//     //     label_entity_id: Column.Undefined(atomsData.count, Column.Schema.str)
+//     // });
+
+//     const chains = Table.ofUndefinedColumns(ChainsSchema, 0);
+
+//     return { atoms, residues, chains };
+// }
+
+// function getConformation(atoms: Atoms): AtomicConformation {
+//     return {
+//         id: UUID.create(),
+//         atomId: atoms.atomNumber,
+//         occupancy: Column.Undefined(atoms.count, Column.Schema.int),
+//         B_iso_or_equiv: Column.Undefined(atoms.count, Column.Schema.float),
+//         x: Column.mapToArray(atoms.x, x => x * 10, Float32Array),
+//         y: Column.mapToArray(atoms.y, y => y * 10, Float32Array),
+//         z: Column.mapToArray(atoms.z, z => z * 10, Float32Array)
+//     }
+// }
+
+// function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
+//     // need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
+//     return Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>)
+//         && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>)
+// }
+
+// function createModel(format: gro_Format, modelNum: number, previous?: Model): Model {
+//     const structure = format.data.structures[modelNum];
+//     const bounds = Interval.ofBounds(0, structure.atoms.count);
+
+//     const hierarchyOffsets = findHierarchyOffsets(structure.atoms, bounds);
+//     const hierarchyData = createHierarchyData(structure.atoms, hierarchyOffsets);
+
+//     if (previous && isHierarchyDataEqual(previous.atomicHierarchy, hierarchyData)) {
+//         return {
+//             ...previous,
+//             atomicConformation: getConformation(structure.atoms)
+//         };
+//     }
+
+//     const hierarchySegments: AtomicSegments = {
+//         residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds),
+//         chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds),
+//     }
+
+//     // TODO: create a better mock entity
+//     const entityTable = Table.ofRows<mmCIF['entity']>(mmCIF.entity, [{
+//         id: '0',
+//         src_method: 'syn',
+//         type: 'polymer',
+//         pdbx_number_of_molecules: 1
+//     }]);
+
+//     const entities: Entities = { data: entityTable, getEntityIndex: Column.createIndexer(entityTable.id) };
+
+//     const hierarchyKeys = getAtomicKeys(hierarchyData, entities, hierarchySegments);
+//     const atomicHierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments };
+//     return {
+//         id: UUID.create(),
+//         sourceData: format,
+//         modelNum,
+//         atomicHierarchy,
+//         entities,
+//         sequence: Sequence.fromAtomicHierarchy(atomicHierarchy),
+//         atomicConformation: getConformation(structure.atoms),
+//         coarseHierarchy: CoarseHierarchy.Empty,
+//         coarseConformation: void 0 as any,
+//         symmetry: ModelSymmetry.Default
+//     };
+// }
+
+// function buildModels(format: gro_Format): ReadonlyArray<Model> {
+//     const models: Model[] = [];
+
+//     format.data.structures.forEach((_, i) => {
+//         const model = createModel(format, i, models.length > 0 ? models[models.length - 1] : void 0);
+//         models.push(model);
+//     });
+//     return models;
+// }
+
+// export default buildModels;
\ No newline at end of file
diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts
index 22644e2fa2ef1fdbb7909b6a825275110fa5e24b..3f68d54440d673ad55a30bd8b7e6fefec442df8d 100644
--- a/src/mol-model/structure/model/formats/mmcif.ts
+++ b/src/mol-model/structure/model/formats/mmcif.ts
@@ -4,23 +4,23 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import UUID from 'mol-util/uuid'
-import { Column, Table } from 'mol-data/db'
-import { Interval, Segmentation } from 'mol-data/int'
-import Format from '../format'
-import Model from '../model'
-import { AtomsSchema, ResiduesSchema, ChainsSchema, AtomicData, AtomicSegments, AtomicConformation } from '../properties/atomic'
-import { ModelSymmetry } from '../properties/symmetry'
-import findHierarchyKeys from '../utils/hierarchy-keys'
-import { ElementSymbol} from '../types'
-import createAssemblies from './mmcif/assembly'
-
-import mmCIF_Format = Format.mmCIF
-import { getSequence } from './mmcif/sequence';
-import { Entities } from '../properties/common';
-import { coarseGrainedFromIHM } from './mmcif/ihm';
+import { Column, Table } from 'mol-data/db';
+import { Interval, Segmentation } from 'mol-data/int';
 import { Spacegroup, SpacegroupCell } from 'mol-math/geometry';
 import { Vec3 } from 'mol-math/linear-algebra';
+import UUID from 'mol-util/uuid';
+import Format from '../format';
+import Model from '../model';
+import { AtomicConformation, AtomicData, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../properties/atomic';
+import { Entities } from '../properties/common';
+import { ModelSymmetry } from '../properties/symmetry';
+import { getAtomicKeys } from '../properties/utils/atomic-keys';
+import { ElementSymbol } from '../types';
+import { createAssemblies } from './mmcif/assembly';
+import { getIHMCoarse } from './mmcif/ihm';
+import { getSequence } from './mmcif/sequence';
+
+import mmCIF_Format = Format.mmCIF
 
 function findModelBounds({ data }: mmCIF_Format, startIndex: number) {
     const num = data.atom_site.pdbx_PDB_model_num;
@@ -120,10 +120,10 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model):
     const hierarchyOffsets = findHierarchyOffsets(format, bounds);
     const hierarchyData = createHierarchyData(format, bounds, hierarchyOffsets);
 
-    if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) {
+    if (previous && isHierarchyDataEqual(previous.atomicHierarchy, hierarchyData)) {
         return {
             ...previous,
-            atomSiteConformation: getConformation(format, bounds)
+            atomicConformation: getConformation(format, bounds)
         };
     }
 
@@ -134,21 +134,23 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model):
 
     const entities: Entities = { data: format.data.entity, getEntityIndex: Column.createIndexer(format.data.entity.id) };
 
-    const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments);
+    const hierarchyKeys = getAtomicKeys(hierarchyData, entities, hierarchySegments);
+
+    const atomicHierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments };
 
-    const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments };
+    const coarse = getIHMCoarse(format.data, entities);
 
     return {
         id: UUID.create(),
         sourceData: format,
         modelNum: format.data.atom_site.pdbx_PDB_model_num.value(Interval.start(bounds)),
         entities,
-        hierarchy,
-        sequence: getSequence(format.data, entities, hierarchy),
-        atomSiteConformation: getConformation(format, bounds),
-        coarseGrained: coarseGrainedFromIHM(format.data, entities),
-        symmetry: getSymmetry(format),
-        atomCount: Interval.size(bounds)
+        atomicHierarchy,
+        sequence: getSequence(format.data, entities, atomicHierarchy),
+        atomicConformation: getConformation(format, bounds),
+        coarseHierarchy: coarse.hierarchy,
+        coarseConformation: coarse.conformation,
+        symmetry: getSymmetry(format)
     };
 }
 
diff --git a/src/mol-model/structure/model/formats/mmcif/assembly.ts b/src/mol-model/structure/model/formats/mmcif/assembly.ts
index 2315133d4cb83c7219207a9690f5b55ca148db9b..48720596c26741364f2acc6306ae47ba87ad3426 100644
--- a/src/mol-model/structure/model/formats/mmcif/assembly.ts
+++ b/src/mol-model/structure/model/formats/mmcif/assembly.ts
@@ -12,7 +12,7 @@ import { Queries as Q, Query } from '../../../query'
 
 import mmCIF_Format = Format.mmCIF
 
-export default function create(format: mmCIF_Format): ReadonlyArray<Assembly> {
+export function createAssemblies(format: mmCIF_Format): ReadonlyArray<Assembly> {
     const { pdbx_struct_assembly } = format.data;
     if (!pdbx_struct_assembly._rowCount) return [];
 
diff --git a/src/mol-model/structure/model/formats/mmcif/bonds.ts b/src/mol-model/structure/model/formats/mmcif/bonds.ts
index 7bd6ec6f9d732ace52744af95756aa1a80292fc0..95ac32d4d36ffcebcc35064cd69e5d5b790f1ba9 100644
--- a/src/mol-model/structure/model/formats/mmcif/bonds.ts
+++ b/src/mol-model/structure/model/formats/mmcif/bonds.ts
@@ -118,7 +118,7 @@ export namespace StructConn {
         const _p = (row: number, ps: typeof p1) => {
             if (ps.label_asym_id.valueKind(row) !== Column.ValueKind.Present) return void 0;
             const asymId = ps.label_asym_id.value(row)
-            const residueIndex = model.hierarchy.findResidueKey(
+            const residueIndex = model.atomicHierarchy.findResidueKey(
                 findEntityIdByAsymId(model, asymId),
                 ps.label_comp_id.value(row),
                 asymId,
diff --git a/src/mol-model/structure/model/formats/mmcif/ihm.ts b/src/mol-model/structure/model/formats/mmcif/ihm.ts
index 5eab89ab00f1343d56e65a7ce44b630e67e670d4..1dfdc1c1d36a15658b7c88da88f7741382596ad0 100644
--- a/src/mol-model/structure/model/formats/mmcif/ihm.ts
+++ b/src/mol-model/structure/model/formats/mmcif/ihm.ts
@@ -5,50 +5,81 @@
  */
 
 import { mmCIF_Database as mmCIF, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'
-import { CoarseGrainedHierarchy } from '../../properties/coarse-grained/hierarchy'
+import { CoarseHierarchy, CoarseConformation, CoarseElementData, CoarseSphereConformation, CoarseGaussianConformation } from '../../properties/coarse'
 import { Entities } from '../../properties/common';
 import { Column } from 'mol-data/db';
+import { getCoarseKeys } from '../../properties/utils/coarse-keys';
+import { UUID } from 'mol-util';
+import { Segmentation, Interval } from 'mol-data/int';
+import { Mat3, Tensor } from 'mol-math/linear-algebra';
 
-function coarseGrainedFromIHM(data: mmCIF, entities: Entities): CoarseGrainedHierarchy {
-    if (data.ihm_model_list._rowCount === 0) return CoarseGrainedHierarchy.Empty;
+export function getIHMCoarse(data: mmCIF, entities: Entities): { hierarchy: CoarseHierarchy, conformation: CoarseConformation } {
+    if (data.ihm_model_list._rowCount === 0) return { hierarchy: CoarseHierarchy.Empty, conformation: void 0 as any };
 
     const { ihm_model_list, ihm_sphere_obj_site, ihm_gaussian_obj_site } = data;
     const modelIndex = Column.createIndexer(ihm_model_list.model_id);
 
+    const sphereData = getData(ihm_sphere_obj_site);
+    const sphereConformation = getSphereConformation(ihm_sphere_obj_site);
+    const sphereKeys = getCoarseKeys(sphereData, modelIndex, entities);
+
+    const gaussianData = getData(ihm_gaussian_obj_site);
+    const gaussianConformation = getGaussianConformation(ihm_gaussian_obj_site);
+    const gaussianKeys = getCoarseKeys(gaussianData, modelIndex, entities);
+
     return {
-        isDefined: true,
-        modelList: ihm_model_list,
-        spheres: getSpheres(ihm_sphere_obj_site, entities, modelIndex),
-        gaussians: getGaussians(ihm_gaussian_obj_site, entities, modelIndex)
+        hierarchy: {
+            isDefined: true,
+            models: ihm_model_list,
+            spheres: { ...sphereData, ...sphereKeys },
+            gaussians: { ...gaussianData, ...gaussianKeys },
+        },
+        conformation: {
+            id: UUID.create(),
+            spheres: sphereConformation,
+            gaussians: gaussianConformation
+        }
     };
 }
 
-function getSpheres(data: mmCIF['ihm_sphere_obj_site'], entities: Entities, modelIndex: (id: number) => number): CoarseGrainedHierarchy.Spheres {
-    const { Cartn_x, Cartn_y, Cartn_z, object_radius: radius, rmsf } = data;
-    const x = Cartn_x.toArray({ array: Float32Array });
-    const y = Cartn_y.toArray({ array: Float32Array });
-    const z = Cartn_z.toArray({ array: Float32Array });
-    return { count: x.length, ...getCommonColumns(data, entities, modelIndex), x, y, z, radius, rmsf };
+function getSphereConformation(data: mmCIF['ihm_sphere_obj_site']): CoarseSphereConformation {
+    return {
+        x: data.Cartn_x.toArray({ array: Float32Array }),
+        y: data.Cartn_y.toArray({ array: Float32Array }),
+        z: data.Cartn_z.toArray({ array: Float32Array }),
+        radius: data.object_radius.toArray({ array: Float32Array }),
+        rmsf: data.rmsf.toArray({ array: Float32Array })
+    };
 }
 
-function getGaussians(data: mmCIF['ihm_gaussian_obj_site'], entities: Entities, modelIndex: (id: number) => number): CoarseGrainedHierarchy.Gaussians {
-    const { mean_Cartn_x, mean_Cartn_y, mean_Cartn_z, weight, covariance_matrix  } = data;
-    const x = mean_Cartn_x.toArray({ array: Float32Array });
-    const y = mean_Cartn_y.toArray({ array: Float32Array });
-    const z = mean_Cartn_z.toArray({ array: Float32Array });
-    return { count: x.length, ...getCommonColumns(data, entities, modelIndex), x, y, z, weight, covariance_matrix, matrix_space: mmCIF_Schema.ihm_gaussian_obj_site.covariance_matrix.space };
-}
+function getGaussianConformation(data: mmCIF['ihm_gaussian_obj_site']): CoarseGaussianConformation {
+    const matrix_space = mmCIF_Schema.ihm_gaussian_obj_site.covariance_matrix.space;
+    const covariance_matrix: Mat3[] = [];
+    const { covariance_matrix: cm } = data;
 
-function getCommonColumns(data: mmCIF['ihm_sphere_obj_site'] | mmCIF['ihm_gaussian_obj_site'], entities: Entities, modelIndex: (id: number) => number) {
-    const { model_id, entity_id, seq_id_begin, seq_id_end, asym_id } = data;
+    for (let i = 0, _i = cm.rowCount; i < _i; i++) {
+        covariance_matrix[i] = Tensor.toMat3(matrix_space, cm.value(i));
+    }
 
     return {
-        entityKey: Column.mapToArray(entity_id, id => entities.getEntityIndex(id), Int32Array),
-        modelKey: Column.mapToArray(model_id, modelIndex, Int32Array),
-        asym_id,
-        seq_id_begin,
-        seq_id_end
+        x: data.mean_Cartn_x.toArray({ array: Float32Array }),
+        y: data.mean_Cartn_y.toArray({ array: Float32Array }),
+        z: data.mean_Cartn_z.toArray({ array: Float32Array }),
+        weight: data.weight.toArray({ array: Float32Array }),
+        covariance_matrix
     };
 }
 
-export { coarseGrainedFromIHM }
\ No newline at end of file
+function getChainSegments(asym_id: Column<string>) {
+    const offsets = [0];
+    for (let i = 1, _i = asym_id.rowCount; i < _i; i++) {
+        if (!asym_id.areValuesEqual(i - 1, i)) offsets[offsets.length] = i;
+    }
+
+    return Segmentation.ofOffsets(offsets, Interval.ofBounds(0, asym_id.rowCount));
+}
+
+function getData(data: mmCIF['ihm_sphere_obj_site'] | mmCIF['ihm_gaussian_obj_site']): CoarseElementData {
+    const { model_id, entity_id, seq_id_begin, seq_id_end, asym_id } = data;
+    return { count: model_id.rowCount, entity_id, model_id, asym_id, seq_id_begin, seq_id_end, chainSegments: getChainSegments(asym_id) };
+}
\ No newline at end of file
diff --git a/src/mol-model/structure/model/formats/mmcif/util.ts b/src/mol-model/structure/model/formats/mmcif/util.ts
index a03d511f54dd47643b4cc8b2189fdb532e994e7e..1817e1c22868410ce6529934aa279e3096654189 100644
--- a/src/mol-model/structure/model/formats/mmcif/util.ts
+++ b/src/mol-model/structure/model/formats/mmcif/util.ts
@@ -16,9 +16,9 @@ export function findEntityIdByAsymId(model: Model, asymId: string) {
 }
 
 export function findAtomIndexByLabelName(model: Model, residueIndex: number, atomName: string, altLoc: string | null) {
-    const { segmentMap, segments } = model.hierarchy.residueSegments
+    const { segmentMap, segments } = model.atomicHierarchy.residueSegments
     const idx = segmentMap[residueIndex]
-    const { label_atom_id, label_alt_id } = model.hierarchy.atoms;
+    const { label_atom_id, label_alt_id } = model.atomicHierarchy.atoms;
     for (let i = segments[idx], n = segments[idx + 1]; i <= n; ++i) {
         if (label_atom_id.value(i) === atomName && (!altLoc || label_alt_id.value(i) === altLoc)) return i;
     }
diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts
index eb7d5024a4a0d524f59bee9a2328c2eae4cbeec2..0a1725d48fa174c42fb08d30cbc0d45b6b990610 100644
--- a/src/mol-model/structure/model/model.ts
+++ b/src/mol-model/structure/model/model.ts
@@ -9,10 +9,10 @@ import Format from './format'
 import Sequence from './properties/sequence'
 import { AtomicHierarchy, AtomicConformation } from './properties/atomic'
 import { ModelSymmetry } from './properties/symmetry'
-import { CoarseGrainedHierarchy } from './properties/coarse-grained/hierarchy'
+import { CoarseHierarchy, CoarseConformation } from './properties/coarse'
 import { Entities } from './properties/common';
 
-import from_gro from './formats/gro'
+//import from_gro from './formats/gro'
 import from_mmCIF from './formats/mmcif'
 
 /**
@@ -31,11 +31,11 @@ interface Model extends Readonly<{
     entities: Entities,
     sequence: Sequence,
 
-    hierarchy: AtomicHierarchy,
-    atomSiteConformation: AtomicConformation,
-    coarseGrained: CoarseGrainedHierarchy,
+    atomicHierarchy: AtomicHierarchy,
+    atomicConformation: AtomicConformation,
 
-    atomCount: number,
+    coarseHierarchy: CoarseHierarchy,
+    coarseConformation: CoarseConformation
 }> {
 
 } { }
@@ -43,22 +43,10 @@ interface Model extends Readonly<{
 namespace Model {
     export function create(format: Format) {
         switch (format.kind) {
-            case 'gro': return from_gro(format);
+            //case 'gro': return from_gro(format);
             case 'mmCIF': return from_mmCIF(format);
         }
     }
-    // export function spatialLookup(model: Model): GridLookup {
-    //     if (model['@spatialLookup']) return model['@spatialLookup']!;
-    //     const lookup = GridLookup(model.conformation);
-    //     model['@spatialLookup'] = lookup;
-    //     return lookup;
-    // }
-    // export function bonds(model: Model): Bonds {
-    //     if (model['@bonds']) return model['@bonds']!;
-    //     const bonds = computeBonds(model);
-    //     model['@bonds'] = bonds;
-    //     return bonds;
-    // }
 }
 
 export default Model
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/atomic/hierarchy.ts b/src/mol-model/structure/model/properties/atomic/hierarchy.ts
index 02ef4dd5d7666fd8956d12d14b62bd3c664c7b6d..fe50e990b242ece24021f6f8eee514388e95d6da 100644
--- a/src/mol-model/structure/model/properties/atomic/hierarchy.ts
+++ b/src/mol-model/structure/model/properties/atomic/hierarchy.ts
@@ -8,7 +8,6 @@ import { Column, Table } from 'mol-data/db'
 import { Segmentation } from 'mol-data/int'
 import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
 import { ElementSymbol} from '../../types'
-import { Keys } from '../common';
 
 export const AtomsSchema = {
     type_symbol: Column.Schema.Aliased<ElementSymbol>(mmCIF.atom_site.type_symbol),
@@ -52,9 +51,21 @@ export interface AtomicSegments {
     chainSegments: Segmentation
 }
 
-export interface AtomicKeys extends Keys {
+export interface AtomicKeys {
+    // indicate whether the keys form an increasing sequence and within each chain, sequence numbers
+    // are in increasing order.
+    // monotonous sequences enable for example faster secondary structure assignment.
+    isMonotonous: boolean,
+
     // assign a key to each residue index.
-    residueKey: Column<number>,
+    residueKey: ArrayLike<number>,
+    // assign a key to each chain index
+    chainKey: ArrayLike<number>,
+    // assigne a key to each chain index
+    // also index to the Entities table.
+    entityKey: ArrayLike<number>,
+
+    findChainKey(entityId: string, label_asym_id: string): number
     findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number
 }
 
diff --git a/src/mol-model/structure/model/properties/coarse-grained/conformation.ts b/src/mol-model/structure/model/properties/coarse-grained/conformation.ts
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/mol-model/structure/model/properties/coarse-grained/hierarchy.ts b/src/mol-model/structure/model/properties/coarse-grained/hierarchy.ts
deleted file mode 100644
index 12dd042cd1df9d3d75fc15a17ca4bd621d469495..0000000000000000000000000000000000000000
--- a/src/mol-model/structure/model/properties/coarse-grained/hierarchy.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
-import { Tensor } from 'mol-math/linear-algebra';
-import { Column } from 'mol-data/db';
-
-interface CoarseGrainedHierarchy {
-    isDefined: boolean,
-    modelList: mmCIF['ihm_model_list'],
-    spheres: CoarseGrainedHierarchy.Spheres,
-    gaussians: CoarseGrainedHierarchy.Gaussians
-}
-
-namespace CoarseGrainedHierarchy {
-    export const Empty: CoarseGrainedHierarchy = { isDefined: false } as any;
-
-    export const enum ElementType { Sphere, Gaussian }
-
-    export interface SiteBase {
-        asym_id: string,
-        seq_id_begin: number,
-        seq_id_end: number
-    }
-
-    export interface Sphere extends SiteBase {
-        radius: number,
-        rmsf: number
-    }
-
-    export interface Gaussian extends SiteBase {
-        weight: number,
-        covariance_matrix: Tensor.Data
-    }
-
-    type Common = {
-        count: number,
-        x: ArrayLike<number>,
-        y: ArrayLike<number>,
-        z: ArrayLike<number>,
-        modelKey: ArrayLike<number>,
-        entityKey: ArrayLike<number>
-    }
-
-    export type SitesBase =  Common & { [P in keyof SiteBase]: Column<SiteBase[P]> }
-    export type Spheres =  Common & { [P in keyof Sphere]: Column<Sphere[P]> }
-    export type Gaussians = Common & { matrix_space: Tensor.Space } & { [P in keyof Gaussian]: Column<Gaussian[P]> }
-}
-
-export { CoarseGrainedHierarchy }
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/coarse.ts b/src/mol-model/structure/model/properties/coarse.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1fc3decf25a10a9a54c5924d5c4cdfaa7ab556a
--- /dev/null
+++ b/src/mol-model/structure/model/properties/coarse.ts
@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+export * from './coarse/conformation'
+export * from './coarse/hierarchy'
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/coarse/conformation.ts b/src/mol-model/structure/model/properties/coarse/conformation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4298b80bdb8fd043149520f2b5b50d56aae59212
--- /dev/null
+++ b/src/mol-model/structure/model/properties/coarse/conformation.ts
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import UUID from 'mol-util/uuid'
+import { Mat3 } from 'mol-math/linear-algebra';
+
+export interface CoarseConformation {
+    id: UUID,
+    spheres: CoarseSphereConformation,
+    gaussians: CoarseGaussianConformation
+}
+
+export interface CoarseSphereConformation {
+    x: ArrayLike<number>,
+    y: ArrayLike<number>,
+    z: ArrayLike<number>,
+    radius: ArrayLike<number>,
+    rmsf: ArrayLike<number>
+}
+
+export interface CoarseGaussianConformation {
+    x: ArrayLike<number>,
+    y: ArrayLike<number>,
+    z: ArrayLike<number>,
+    weight: ArrayLike<number>,
+    covariance_matrix: ArrayLike<Mat3>
+}
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/coarse/hierarchy.ts b/src/mol-model/structure/model/properties/coarse/hierarchy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fedb463c9e7b015de3485ef2c2a90b2798df71ec
--- /dev/null
+++ b/src/mol-model/structure/model/properties/coarse/hierarchy.ts
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
+import { Column } from 'mol-data/db'
+import { Segmentation } from 'mol-data/int';
+
+export interface CoarsedElementKeys {
+    // indicate whether the keys form an increasing sequence and within each chain, sequence numbers
+    // are in increasing order.
+    // monotonous sequences enable for example faster secondary structure assignment.
+    isMonotonous: boolean,
+
+    // assign a key to each element
+    chainKey: ArrayLike<number>,
+    // assign a key to each element, index to the Model.entities.data table
+    entityKey: ArrayLike<number>,
+    // assign a key to each element, index to the CoarseHierarchy.models table
+    modelKey: ArrayLike<number>,
+
+    findChainKey(entityId: string, asym_id: string): number
+}
+
+export interface CoarseElementData {
+    count: number,
+    entity_id: Column<string>,
+    model_id: Column<number>,
+    asym_id: Column<string>,
+    seq_id_begin: Column<number>,
+    seq_id_end: Column<number>,
+
+    chainSegments: Segmentation
+}
+
+export type CoarseElements = CoarsedElementKeys & CoarseElementData
+
+export interface CoarseHierarchy {
+    isDefined: boolean,
+    models: mmCIF['ihm_model_list'],
+    spheres: CoarseElements,
+    gaussians: CoarseElements
+}
+
+export namespace CoarseHierarchy {
+    export const Empty: CoarseHierarchy = { isDefined: false } as any;
+}
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/common.ts b/src/mol-model/structure/model/properties/common.ts
index e70712cbbf3e5c68f731da2f70afde1acb5e2846..9dadbc95ac9d951e4d175949655012b0222a52a5 100644
--- a/src/mol-model/structure/model/properties/common.ts
+++ b/src/mol-model/structure/model/properties/common.ts
@@ -5,24 +5,8 @@
  */
 
 import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
-import { Column } from 'mol-data/db';
 
 export interface Entities {
     data: mmCIF['entity'],
     getEntityIndex(id: string): number
-}
-
-export interface Keys {
-    // indicate whether the keys form an increasing sequence and within each chain, sequence numbers
-    // are in increasing order.
-    // monotonous sequences enable for example faster secondary structure assignment.
-    isMonotonous: boolean,
-
-    // assign a key to each chain index
-    chainKey: Column<number>,
-    // assigne a key to each chain index
-    // also index to the Entities table.
-    entityKey: Column<number>,
-
-    findChainKey(entityId: string, label_asym_id: string): number
 }
\ No newline at end of file
diff --git a/src/mol-model/structure/model/utils/hierarchy-keys.ts b/src/mol-model/structure/model/properties/utils/atomic-keys.ts
similarity index 90%
rename from src/mol-model/structure/model/utils/hierarchy-keys.ts
rename to src/mol-model/structure/model/properties/utils/atomic-keys.ts
index aabfe1b4a6bb9bed1633ab748d382aa935316588..1cfec2f0348e902f39787c3f2c0510687caef256 100644
--- a/src/mol-model/structure/model/utils/hierarchy-keys.ts
+++ b/src/mol-model/structure/model/properties/utils/atomic-keys.ts
@@ -4,10 +4,9 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Column } from 'mol-data/db'
-import { AtomicData, AtomicSegments, AtomicKeys } from '../properties/atomic'
+import { AtomicData, AtomicSegments, AtomicKeys } from '../atomic'
 import { Interval, Segmentation } from 'mol-data/int'
-import { Entities } from '../properties/common';
+import { Entities } from '../common'
 
 function getResidueId(comp_id: string, seq_id: number, ins_code: string) {
     return `${comp_id} ${seq_id} ${ins_code}`;
@@ -62,7 +61,7 @@ function missingEntity(k: string) {
     throw new Error(`Missing entity entry for entity id '${k}'.`);
 }
 
-function create(data: AtomicData, entities: Entities, segments: AtomicSegments): AtomicKeys {
+export function getAtomicKeys(data: AtomicData, entities: Entities, segments: AtomicSegments): AtomicKeys {
     const { chains, residues } = data;
 
     const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 };
@@ -110,12 +109,10 @@ function create(data: AtomicData, entities: Entities, segments: AtomicSegments):
 
     return {
         isMonotonous: isMonotonous && checkMonotonous(entityKey) && checkMonotonous(chainKey) && checkMonotonous(residueKey),
-        residueKey: Column.ofIntArray(residueKey),
-        chainKey: Column.ofIntArray(chainKey),
-        entityKey: Column.ofIntArray(entityKey),
+        residueKey: residueKey,
+        chainKey: chainKey,
+        entityKey: entityKey,
         findChainKey,
         findResidueKey
     };
-}
-
-export default create;
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/utils/coarse-keys.ts b/src/mol-model/structure/model/properties/utils/coarse-keys.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ef72039a5f3fa35705364b38d24d2e4bef00553
--- /dev/null
+++ b/src/mol-model/structure/model/properties/utils/coarse-keys.ts
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Entities } from '../common';
+import { CoarseElementData, CoarsedElementKeys } from '../coarse';
+
+function getElementKey(map: Map<string, number>, key: string, counter: { index: number }) {
+    if (map.has(key)) return map.get(key)!;
+    const ret = counter.index++;
+    map.set(key, ret);
+    return ret;
+}
+
+function getElementSubstructureKeyMap(map: Map<number, Map<string, number>>, key: number) {
+    if (map.has(key)) return map.get(key)!;
+    const ret = new Map<string, number>();
+    map.set(key, ret);
+    return ret;
+}
+
+function createLookUp(entities: Entities, chain: Map<number, Map<string, number>>) {
+    const getEntKey = entities.getEntityIndex;
+    const findChainKey: CoarsedElementKeys['findChainKey'] = (e, c) => {
+        let eKey = getEntKey(e);
+        if (eKey < 0) return -1;
+        const cm = chain.get(eKey)!;
+        if (!cm.has(c)) return -1;
+        return cm.get(c)!;
+    }
+    return { findChainKey };
+}
+
+function checkMonotonous(xs: ArrayLike<number>) {
+    for (let i = 1, _i = xs.length; i < _i; i++) {
+        if (xs[i] < xs[i - 1]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+function missingEntity(k: string) {
+    throw new Error(`Missing entity entry for entity id '${k}'.`);
+}
+
+function missingModel(k: string) {
+    throw new Error(`Missing entity entry for model id '${k}'.`);
+}
+
+export function getCoarseKeys(data: CoarseElementData, modelIndex: (id: number) => number, entities: Entities): CoarsedElementKeys {
+    const { model_id, entity_id, asym_id, count, chainSegments } = data;
+
+    const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 };
+    const chainKey = new Int32Array(count);
+    const entityKey = new Int32Array(count);
+    const modelKey = new Int32Array(count);
+
+    for (let i = 0; i < count; i++) {
+        entityKey[i] = entities.getEntityIndex(entity_id.value(i));
+        if (entityKey[i] < 0) missingEntity(entity_id.value(i));
+        modelKey[i] = modelIndex(model_id.value(i));
+        if (modelKey[i] < 0) missingModel('' + model_id.value(i));
+    }
+
+    for (let cI = 0; cI < chainSegments.count; cI++) {
+        const start = chainSegments.segments[cI], end = chainSegments.segments[cI + 1];
+        const map = getElementSubstructureKeyMap(chainMaps, entityKey[start]);
+        const key = getElementKey(map, asym_id.value(start), chainCounter);
+        for (let i = start; i < end; i++) chainKey[i] = key;
+    }
+
+    const { findChainKey } = createLookUp(entities, chainMaps);
+
+    return {
+        isMonotonous: checkMonotonous(entityKey) && checkMonotonous(chainKey),
+        chainKey: chainKey,
+        entityKey: entityKey,
+        modelKey: modelKey,
+        findChainKey
+    };
+}
\ No newline at end of file
diff --git a/src/mol-model/structure/model/utils/guess-element.ts b/src/mol-model/structure/model/properties/utils/guess-element.ts
similarity index 100%
rename from src/mol-model/structure/model/utils/guess-element.ts
rename to src/mol-model/structure/model/properties/utils/guess-element.ts
diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts
index ac9df85096b1680ea2cd0f9a193c7713e69b0843..01445a1dad2933947020a5ab53f7b8d44502232a 100644
--- a/src/mol-model/structure/query/generators.ts
+++ b/src/mol-model/structure/query/generators.ts
@@ -80,8 +80,8 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A
             const elements = unit.elements;
 
             builder.beginUnit(unit.id);
-            const chainsIt = Segmentation.transientSegments(unit.model.hierarchy.chainSegments, elements);
-            const residuesIt = Segmentation.transientSegments(unit.model.hierarchy.residueSegments, elements);
+            const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements);
+            const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements);
             while (chainsIt.hasNext) {
                 const chainSegment = chainsIt.move();
                 l.element = OrderedSet.getAt(elements, chainSegment.start);
@@ -174,8 +174,8 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group
             l.unit = unit;
             const elements = unit.elements;
 
-            const chainsIt = Segmentation.transientSegments(unit.model.hierarchy.chainSegments, elements);
-            const residuesIt = Segmentation.transientSegments(unit.model.hierarchy.residueSegments, elements);
+            const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainSegments, elements);
+            const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, elements);
             while (chainsIt.hasNext) {
                 const chainSegment = chainsIt.move();
                 l.element = OrderedSet.getAt(elements, chainSegment.start);
diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts
index fc2a066ad957730fe49ec9a884ffb47a822156e6..a4d4ad5b3d38a50dcc50cdf596f26373883af42f 100644
--- a/src/mol-model/structure/query/properties.ts
+++ b/src/mol-model/structure/query/properties.ts
@@ -29,61 +29,61 @@ const atom = {
     x: Element.property(l => l.unit.conformation.x(l.element)),
     y: Element.property(l => l.unit.conformation.y(l.element)),
     z: Element.property(l => l.unit.conformation.z(l.element)),
-    id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomSiteConformation.atomId.value(l.element)),
-    occupancy: Element.property(l => !Unit.isAtomic(l.unit) ?  notAtomic() : l.unit.model.atomSiteConformation.occupancy.value(l.element)),
-    B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ?  notAtomic() : l.unit.model.atomSiteConformation.B_iso_or_equiv.value(l.element)),
+    id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.atomId.value(l.element)),
+    occupancy: Element.property(l => !Unit.isAtomic(l.unit) ?  notAtomic() : l.unit.model.atomicConformation.occupancy.value(l.element)),
+    B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ?  notAtomic() : l.unit.model.atomicConformation.B_iso_or_equiv.value(l.element)),
 
     // Hierarchy
-    type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.atoms.type_symbol.value(l.element)),
-    label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.atoms.label_atom_id.value(l.element)),
-    auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.atoms.auth_atom_id.value(l.element)),
-    label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.atoms.label_alt_id.value(l.element)),
-    pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.atoms.pdbx_formal_charge.value(l.element)),
+    type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element)),
+    label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_atom_id.value(l.element)),
+    auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)),
+    label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)),
+    pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)),
 
     // Derived
-    vdw_radius: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.hierarchy.atoms.type_symbol.value(l.element))),
+    vdw_radius: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element))),
 }
 
 const residue = {
-    key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residueKey.value(l.unit.residueIndex[l.element])),
-
-    group_PDB: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])),
-    label_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])),
-    auth_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])),
-    label_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])),
-    auth_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])),
-    pdbx_PDB_ins_code: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element]))
+    key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residueKey[l.unit.residueIndex[l.element]]),
+
+    group_PDB: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])),
+    label_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])),
+    auth_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])),
+    label_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])),
+    auth_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])),
+    pdbx_PDB_ins_code: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element]))
 }
 
 const chain = {
-    key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.chainKey.value(l.unit.chainIndex[l.element])),
+    key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chainKey[l.unit.chainIndex[l.element]]),
 
-    label_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])),
-    auth_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])),
-    label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element]))
+    label_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])),
+    auth_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])),
+    label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element]))
 }
 
-const coarse_grained = {
+const coarse = {
     key: atom.key,
-    modelKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.sites.modelKey[l.element]),
-    entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.sites.entityKey[l.element]),
+    modelKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.modelKey[l.element]),
+    entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entityKey[l.element]),
 
     x: atom.x,
     y: atom.y,
     z: atom.z,
 
-    asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.sites.asym_id.value(l.element)),
-    seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.sites.seq_id_begin.value(l.element)),
-    seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.sites.seq_id_end.value(l.element)),
+    asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)),
+    seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)),
+    seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)),
 
-    sphere_radius: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.sites.radius.value(l.element)),
-    sphere_rmsf: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.sites.rmsf.value(l.element)),
+    sphere_radius: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.radius[l.element]),
+    sphere_rmsf: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.rmsf[l.element]),
 
-    gaussian_weight: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.sites.weight.value(l.element)),
-    gaussian_covariance_matrix: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.sites.covariance_matrix.value(l.element))
+    gaussian_weight: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.weight[l.element]),
+    gaussian_covariance_matrix: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.covariance_matrix[l.element])
 }
 
-function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.hierarchy.entityKey.value(l.unit.chainIndex[l.element]); }
+function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.entityKey[l.unit.chainIndex[l.element]]; }
 
 const entity = {
     key: eK,
@@ -112,7 +112,7 @@ const Properties = {
     chain,
     entity,
     unit,
-    coarse_grained
+    coarse
 }
 
 type Properties = typeof Properties
diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts
index c2a2ecd38aea3292c2adc63de607733040cd0aa7..efe4e8bde3ba92dd75c98953f01ccc5ec4e3370a 100644
--- a/src/mol-model/structure/structure/structure.ts
+++ b/src/mol-model/structure/structure/structure.ts
@@ -13,6 +13,7 @@ import Element from './element'
 import Unit from './unit'
 import { StructureLookup3D } from './util/lookup3d';
 import StructureSymmetry from './symmetry';
+import { CoarseElements } from '../model/properties/coarse';
 
 class Structure {
     readonly unitMap: IntMap<Unit>;
@@ -90,7 +91,7 @@ namespace Structure {
     }
 
     export function ofModel(model: Model): Structure {
-        const chains = model.hierarchy.chainSegments;
+        const chains = model.atomicHierarchy.chainSegments;
         const builder = new StructureBuilder();
 
         for (let c = 0; c < chains.count; c++) {
@@ -98,21 +99,27 @@ namespace Structure {
             builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, elements);
         }
 
-        const cs = model.coarseGrained;
+        const cs = model.coarseHierarchy;
         if (cs.isDefined) {
             if (cs.spheres.count > 0) {
-                const elements = SortedArray.ofBounds(0, cs.spheres.count);
-                builder.addUnit(Unit.Kind.Spheres, model, SymmetryOperator.Default, elements);
+                addCoarseUnits(builder, model, model.coarseHierarchy.spheres, Unit.Kind.Spheres);
             }
             if (cs.gaussians.count > 0) {
-                const elements = SortedArray.ofBounds(0, cs.spheres.count);
-                builder.addUnit(Unit.Kind.Gaussians, model, SymmetryOperator.Default, elements);
+                addCoarseUnits(builder, model, model.coarseHierarchy.gaussians, Unit.Kind.Gaussians);
             }
         }
 
         return builder.getStructure();
     }
 
+    function addCoarseUnits(builder: StructureBuilder, model: Model, elements: CoarseElements, kind: Unit.Kind) {
+        const { chainSegments } = elements;
+        for (let cI = 0; cI < chainSegments.count; cI++) {
+            const elements = SortedArray.ofBounds(chainSegments.segments[cI], chainSegments.segments[cI + 1]);
+            builder.addUnit(kind, model, SymmetryOperator.Default, elements);
+        }
+    }
+
     export class StructureBuilder {
         private units: Unit[] = [];
 
diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts
index 7ede3f8a7568f207ea00d2766f88bb2f0f7a6495..4ea70f014d413e7d14967ae769c2d500cb0cdf24 100644
--- a/src/mol-model/structure/structure/unit.ts
+++ b/src/mol-model/structure/structure/unit.ts
@@ -7,10 +7,10 @@
 import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator'
 import { Model } from '../model'
 import { GridLookup3D, Lookup3D } from 'mol-math/geometry'
-import { CoarseGrainedHierarchy } from '../model/properties/coarse-grained/hierarchy';
 import { SortedArray } from 'mol-data/int';
 import { idFactory } from 'mol-util/id-factory';
 import { IntraUnitBonds, computeIntraUnitBonds } from './unit/bonds'
+import { CoarseElements, CoarseSphereConformation, CoarseGaussianConformation } from '../model/properties/coarse';
 
 // A building block of a structure that corresponds to an atomic or a coarse grained representation
 // 'conveniently grouped together'.
@@ -26,9 +26,9 @@ namespace Unit {
 
     export function create(id: number, kind: Kind, model: Model, operator: SymmetryOperator, elements: SortedArray): Unit {
         switch (kind) {
-            case Kind.Atomic: return new Atomic(id, unitIdFactory(), model, elements, SymmetryOperator.createMapping(operator, model.atomSiteConformation));
-            case Kind.Spheres: return createCoarse(id, unitIdFactory(), model, Kind.Spheres, model.coarseGrained.spheres, elements, SymmetryOperator.createMapping(operator, model.atomSiteConformation));
-            case Kind.Gaussians: return createCoarse(id, unitIdFactory(), model, Kind.Gaussians, model.coarseGrained.gaussians, elements, SymmetryOperator.createMapping(operator, model.atomSiteConformation));
+            case Kind.Atomic: return new Atomic(id, unitIdFactory(), model, elements, SymmetryOperator.createMapping(operator, model.atomicConformation));
+            case Kind.Spheres: return createCoarse(id, unitIdFactory(), model, Kind.Spheres, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.spheres));
+            case Kind.Gaussians: return createCoarse(id, unitIdFactory(), model, Kind.Gaussians, elements, SymmetryOperator.createMapping(operator, model.coarseConformation.gaussians));
         }
     }
 
@@ -78,13 +78,13 @@ namespace Unit {
 
         applyOperator(id: number, operator: SymmetryOperator, dontCompose = false): Unit {
             const op = dontCompose ? operator : SymmetryOperator.compose(this.conformation.operator, operator);
-            return new Atomic(id, this.invariantId, this.model, this.elements, SymmetryOperator.createMapping(op, this.model.atomSiteConformation));
+            return new Atomic(id, this.invariantId, this.model, this.elements, SymmetryOperator.createMapping(op, this.model.atomicConformation));
         }
 
         private _lookup3d?: Lookup3D = void 0;
         get lookup3d() {
             if (this._lookup3d) return this._lookup3d;
-            const { x, y, z } = this.model.atomSiteConformation;
+            const { x, y, z } = this.model.atomicConformation;
             this._lookup3d = GridLookup3D({ x, y, z, indices: this.elements });
             return this._lookup3d;
         }
@@ -103,62 +103,64 @@ namespace Unit {
             this.elements = elements;
             this.conformation = conformation;
 
-            this.residueIndex = model.hierarchy.residueSegments.segmentMap;
-            this.chainIndex = model.hierarchy.chainSegments.segmentMap;
+            this.residueIndex = model.atomicHierarchy.residueSegments.segmentMap;
+            this.chainIndex = model.atomicHierarchy.chainSegments.segmentMap;
         }
     }
 
-    // Coarse grained representations.
-    export interface CoarseBase<S extends CoarseGrainedHierarchy.SitesBase> extends Base  {
-        readonly sites: S
-    }
-
-    class Coarse<S extends CoarseGrainedHierarchy.SitesBase> implements CoarseBase<S> {
-        readonly kind: Kind;
+    class Coarse<K extends Kind.Gaussians | Kind.Spheres, C extends CoarseSphereConformation | CoarseGaussianConformation> implements Base {
+        readonly kind: K;
 
         readonly id: number;
         readonly invariantId: number;
         readonly elements: SortedArray;
         readonly model: Model;
         readonly conformation: SymmetryOperator.ArrayMapping;
-        readonly sites: S;
+
+        readonly coarseElements: CoarseElements;
+        readonly coarseConformation: C;
 
         getChild(elements: SortedArray): Unit {
             if (elements.length === this.elements.length) return this as any as Unit /** lets call this an ugly temporary hack */;
-            return createCoarse(this.id, this.invariantId, this.model, this.kind, this.sites, elements, this.conformation);
+            return createCoarse(this.id, this.invariantId, this.model, this.kind, elements, this.conformation);
         }
 
         applyOperator(id: number, operator: SymmetryOperator, dontCompose = false): Unit {
             const op = dontCompose ? operator : SymmetryOperator.compose(this.conformation.operator, operator);
-            return createCoarse(id, this.invariantId, this.model, this.kind, this.sites, this.elements, SymmetryOperator.createMapping(op, this.sites));
+            return createCoarse(id, this.invariantId, this.model, this.kind, this.elements, SymmetryOperator.createMapping(op, this.getCoarseElements()));
         }
 
         private _lookup3d?: Lookup3D = void 0;
         get lookup3d() {
             if (this._lookup3d) return this._lookup3d;
-            const { x, y, z } = this.sites;
-            // TODO: support sphere radius
+            const { x, y, z } = this.getCoarseElements();
+            // TODO: support sphere radius?
             this._lookup3d = GridLookup3D({ x, y, z, indices: this.elements });
             return this._lookup3d;
         }
 
-        constructor(id: number, invariantId: number, model: Model, kind: Kind, sites: S, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping) {
+        private getCoarseElements() {
+            return this.kind === Kind.Spheres ? this.model.coarseConformation.spheres : this.model.coarseConformation.gaussians;
+        }
+
+        constructor(id: number, invariantId: number, model: Model, kind: K, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping) {
             this.kind = kind;
             this.id = id;
             this.invariantId = invariantId;
             this.model = model;
             this.elements = elements;
             this.conformation = conformation;
-            this.sites = sites;
+            this.coarseElements = kind === Kind.Spheres ? model.coarseHierarchy.spheres : model.coarseHierarchy.gaussians;
+            this.coarseConformation = (kind === Kind.Spheres ? model.coarseConformation.spheres : model.coarseConformation.gaussians) as C;
         }
     }
 
-    function createCoarse<S extends CoarseGrainedHierarchy.SitesBase>(id: number, invariantId: number, model: Model, kind: Kind, sites: S, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping): Unit {
-        return new Coarse(id, invariantId, model, kind, sites, elements, conformation) as any as Unit /** lets call this an ugly temporary hack */;
+    function createCoarse<K extends Kind.Gaussians | Kind.Spheres>(id: number, invariantId: number, model: Model, kind: K, elements: SortedArray, conformation: SymmetryOperator.ArrayMapping): Unit {
+        return new Coarse(id, invariantId, model, kind, elements, conformation) as any as Unit /** lets call this an ugly temporary hack */;
     }
 
-    export interface Spheres extends CoarseBase<CoarseGrainedHierarchy.Spheres> { kind: Kind.Spheres }
-    export interface Gaussians extends CoarseBase<CoarseGrainedHierarchy.Gaussians> { kind: Kind.Gaussians }
+    export class Spheres extends Coarse<Kind.Spheres, CoarseSphereConformation> { }
+    export class Gaussians extends Coarse<Kind.Gaussians, CoarseGaussianConformation> { }
 }
 
 export default Unit;
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
index 2d8f9af0240a0a0fb13bff6f5354c0fcd78b9cc3..9cd4a9ed1839ec43517ce9e9c46ecb06eb8d31f7 100644
--- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
+++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
@@ -109,11 +109,11 @@ function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[],
 function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): IntraUnitBonds {
     const MAX_RADIUS = 3;
 
-    const { x, y, z } = unit.model.atomSiteConformation;
+    const { x, y, z } = unit.model.atomicConformation;
     const atomCount = unit.elements.length;
     const { elements: atoms, residueIndex } = unit;
-    const { type_symbol, label_atom_id, label_alt_id } = unit.model.hierarchy.atoms;
-    const { label_comp_id } = unit.model.hierarchy.residues;
+    const { type_symbol, label_atom_id, label_alt_id } = unit.model.atomicHierarchy.atoms;
+    const { label_comp_id } = unit.model.atomicHierarchy.residues;
     const query3d = unit.lookup3d;
 
     const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.create(unit.model) : void 0
diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts
index 4070cca0b782bfbc4a2d6f71ae7deb41b155f181..92831adec5440d24c6a0eee1e44491ccfca12211 100644
--- a/src/perf-tests/structure.ts
+++ b/src/perf-tests/structure.ts
@@ -371,7 +371,7 @@ export namespace PropertyAccess {
         // return;
 
         console.log('bs', baseline(models[0]));
-        console.log('sp', sumProperty(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element)));
+        console.log('sp', sumProperty(structures[0], l => l.unit.model.atomicConformation.atomId.value(l.element)));
         //console.log(sumPropertySegmented(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element)));
 
         //console.log(sumPropertySegmentedMutable(structures[0], l => l.unit.model.conformation.atomId.value(l.element));
@@ -418,7 +418,7 @@ export namespace PropertyAccess {
         console.log(Selection.structureCount(q2r));
         //console.log(q1(structures[0]));
 
-        const col = models[0].atomSiteConformation.atomId.value;
+        const col = models[0].atomicConformation.atomId.value;
         const suite = new B.Suite();
         suite
             //.add('test q', () => q1(structures[0]))