diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv
index 61649087a630b56eaacafc345338f2b8b37a9584..bd1f2d51bdfe8257abd257a27b09b9f4b6afc390 100644
--- a/data/mmcif-field-names.csv
+++ b/data/mmcif-field-names.csv
@@ -1,3 +1,7 @@
+atom_sites.entry_id
+atom_sites.fract_transf_matrix
+atom_sites.fract_transf_vector
+
 atom_site.group_PDB
 atom_site.id
 atom_site.type_symbol
diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts
index 373fa1ae8401ea1043707cebc355e0760cf321dc..8f67d33bfa2b3c7ff56c2358ba004197450eb8ca 100644
--- a/src/mol-io/reader/cif/schema/mmcif.ts
+++ b/src/mol-io/reader/cif/schema/mmcif.ts
@@ -21,6 +21,11 @@ const Vector = Schema.Vector;
 const List = Schema.List;
 
 export const mmCIF_Schema = {
+    atom_sites: {
+        entry_id: str,
+        fract_transf_matrix: Matrix(3, 3),
+        fract_transf_vector: Vector(3)
+    },
     atom_site: {
         auth_asym_id: str,
         auth_atom_id: str,
diff --git a/src/mol-math/geometry/spacegroup/construction.ts b/src/mol-math/geometry/spacegroup/construction.ts
index 65aa22e849283499982fbf6fefdd71ade328105b..0492fc297ba0ddf35af83d5a4757d5bc3ec0518f 100644
--- a/src/mol-math/geometry/spacegroup/construction.ts
+++ b/src/mol-math/geometry/spacegroup/construction.ts
@@ -5,11 +5,12 @@
  */
 
 import { Vec3, Mat4 } from '../../linear-algebra'
-import { SpacegroupName, TransformData, GroupData, SpacegroupNumbers, SpacegroupNames, OperatorData } from './tables'
+import { SpacegroupName, TransformData, GroupData, getSpacegroupIndex, OperatorData, SpacegroupNames } from './tables'
 import { SymmetryOperator } from '../../geometry';
 
 interface SpacegroupCell {
-    readonly number: number,
+    // zero based spacegroup number
+    readonly index: number,
     readonly size: Vec3,
     readonly anglesInRadians: Vec3,
     /** Transfrom cartesian -> fractional coordinates within the cell */
@@ -26,16 +27,18 @@ interface Spacegroup {
 
 namespace SpacegroupCell {
     // Create a 'P 1' with cellsize [1, 1, 1]
-    export function zero() {
-        return create(0, Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2));
-    }
+    export const Zero: SpacegroupCell = create('P 1', Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2));
 
     // True if 'P 1' with cellsize [1, 1, 1]
     export function isZero(cell: SpacegroupCell) {
-        return cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1;
+        return cell.index === 0 && cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1;
     }
 
-    export function create(nameOrNumber: number | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell {
+    // returns Zero cell if the spacegroup does not exist
+    export function create(nameOrNumber: number | string | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell {
+        const index = getSpacegroupIndex(nameOrNumber);
+        if (index < 0) return Zero;
+
         const alpha = anglesInRadians[0];
         const beta = anglesInRadians[1];
         const gamma = anglesInRadians[2];
@@ -58,23 +61,18 @@ namespace SpacegroupCell {
         ]);
         const toFractional = Mat4.invert(Mat4.zero(), fromFractional)!;
 
-        const num = typeof nameOrNumber === 'number' ? nameOrNumber : SpacegroupNumbers[nameOrNumber];
-        return { number: num, size, anglesInRadians, toFractional, fromFractional };
+        return { index, size, anglesInRadians, toFractional, fromFractional };
     }
 }
 
-namespace Spacegroup {
-    export function create(nameOrNumber: number | SpacegroupName, cell: SpacegroupCell): Spacegroup {
-        const num = typeof nameOrNumber === 'number' ? nameOrNumber : SpacegroupNumbers[nameOrNumber];
-        const name = typeof nameOrNumber === 'number' ? SpacegroupNames[nameOrNumber] : nameOrNumber;
 
-        if (typeof num === 'undefined' || typeof name === 'undefined') {
-            throw new Error(`Spacegroup '${nameOrNumber}' is not defined.`);
-        }
-
-        const operators = GroupData[num].map(i => getOperatorMatrix(OperatorData[i]));
+namespace Spacegroup {
+    // P1 with [1, 1, 1] cell.
+    export const ZeroP1 = create(SpacegroupCell.Zero);
 
-        return { name, cell, operators };
+    export function create(cell: SpacegroupCell): Spacegroup {
+        const operators = GroupData[cell.index].map(i => getOperatorMatrix(OperatorData[i]));
+        return { name: SpacegroupNames[cell.index], cell, operators };
     }
 
     const _tempVec = Vec3.zero(), _tempMat = Mat4.zero();
@@ -89,6 +87,8 @@ namespace Spacegroup {
 
     export function getSymmetryOperator(spacegroup: Spacegroup, index: number, i: number, j: number, k: number): SymmetryOperator {
         const operator = updateOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero());
+        console.log(Mat4.makeTable(operator));
+        console.log({ index, i, j, k });
         return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, Vec3.create(i, j, k));
     }
 
diff --git a/src/mol-math/geometry/spacegroup/tables.ts b/src/mol-math/geometry/spacegroup/tables.ts
index de3ec97927b441f1fc80b0a8d665650b628bf331..91ae4bc0273ed99cc361e047c64684c0cacea90f 100644
--- a/src/mol-math/geometry/spacegroup/tables.ts
+++ b/src/mol-math/geometry/spacegroup/tables.ts
@@ -985,7 +985,7 @@ export const GroupData = [
     [0, 22, 57, 3, 159, 279, 654, 655, 158, 274, 656, 657, 29, 18, 25, 27, 284, 658, 262, 269, 280, 659, 257, 267],
 ];
 
-export const SpacegroupNumbers = {
+export const ZeroBasedSpacegroupNumbers = {
     'P 1': 0,
     'P -1': 1,
     'P 1 2 1': 2,
@@ -1333,12 +1333,19 @@ export const SpacegroupNumbers = {
     'I 2 3a': 265,
 };
 
-export type SpacegroupName = keyof typeof SpacegroupNumbers
+export type SpacegroupName = keyof typeof ZeroBasedSpacegroupNumbers
 
 export const SpacegroupNames: { [num: number]: SpacegroupName } = (function () {
     const names = Object.create(null);
-    for (const n of Object.keys(SpacegroupNumbers)) {
-        names[(SpacegroupNumbers as any)[n]] = n;
+    for (const n of Object.keys(ZeroBasedSpacegroupNumbers)) {
+        names[(ZeroBasedSpacegroupNumbers as any)[n]] = n;
     }
     return names;
-}());
\ No newline at end of file
+}());
+
+// return -1 if the spacegroup does not exist.
+export function getSpacegroupIndex(nameOrNumber: number | string | SpacegroupName): number {
+    const index = typeof nameOrNumber === 'number' ? nameOrNumber - 1 : ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName];
+    if (typeof index === 'undefined' || typeof SpacegroupNames[index] === 'undefined') return -1;
+    return index;
+}
\ No newline at end of file
diff --git a/src/mol-math/linear-algebra/3d/common.ts b/src/mol-math/linear-algebra/3d/common.ts
index eebaeb2901c60d1213d8e5963a5c44fb3fc2842f..4276dd8d60b1e254621bfb87e8dbd5302b023d0e 100644
--- a/src/mol-math/linear-algebra/3d/common.ts
+++ b/src/mol-math/linear-algebra/3d/common.ts
@@ -17,4 +17,8 @@
  * furnished to do so, subject to the following conditions:
  */
 
-export const enum EPSILON { Value = 0.000001 }
\ No newline at end of file
+export const enum EPSILON { Value = 0.000001 }
+
+export function equalEps(a: number, b: number, eps: number) {
+    return Math.abs(a - b) <= eps;
+}
diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts
index 930eaf8518e425ff451b638830264510a8c5d97b..a23c5503950401af6c82307b0cbcbdce23bfbadd 100644
--- a/src/mol-math/linear-algebra/3d/mat4.ts
+++ b/src/mol-math/linear-algebra/3d/mat4.ts
@@ -17,7 +17,7 @@
  * furnished to do so, subject to the following conditions:
  */
 
-import { EPSILON } from './common'
+import { EPSILON, equalEps } from './common'
 import Vec3 from './vec3';
 import Quat from './quat';
 
@@ -539,11 +539,11 @@ namespace Mat4 {
             a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
             /* a30 = a[12], a31 = a[13], a32 = a[14],*/ a33 = a[15];
 
-        if (a33 !== 1 || a03 !== 0 || a13 !== 0 || a23 !== 0) {
+        if (!equalEps(a33, 1, eps) || !equalEps(a03, 0, eps) || !equalEps(a13, 0, eps) || !equalEps(a23, 0, eps)) {
             return false;
         }
         const det3x3 = a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20);
-        if (det3x3 < 1 - eps || det3x3 > 1 + eps) {
+        if (!equalEps(det3x3, 1, eps)) {
             return false;
         }
         return true;
diff --git a/src/mol-model/structure/model.ts b/src/mol-model/structure/model.ts
index c53fd0986639873923cf30fbceba2d6010233741..1704ab09282eb09ec1e30a442532e52dc80def63 100644
--- a/src/mol-model/structure/model.ts
+++ b/src/mol-model/structure/model.ts
@@ -7,6 +7,6 @@
 import Model from './model/model'
 import * as Types from './model/types'
 import Format from './model/format'
-import ModelSymmetry from './model/properties/symmetry'
+import { ModelSymmetry } from './model/properties/symmetry'
 
 export { Model, Types, Format, ModelSymmetry }
\ No newline at end of file
diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model/structure/model/formats/gro.ts
index d2348b1207db8147026b466363c4d593bfc0a89f..46a9cd1652087d59eb597918ac25fa29f34d68a1 100644
--- a/src/mol-model/structure/model/formats/gro.ts
+++ b/src/mol-model/structure/model/formats/gro.ts
@@ -10,8 +10,7 @@ import { Interval, Segmentation } from 'mol-data/int'
 import { Atoms } from 'mol-io/reader/gro/schema'
 import Format from '../format'
 import Model from '../model'
-import * as AtomicHierarchy from '../properties/atomic/hierarchy'
-import { AtomicConformation } from '../properties/atomic/conformation'
+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'
@@ -21,6 +20,7 @@ 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> }
 
@@ -44,9 +44,9 @@ function guessElementSymbol (value: string) {
     return ElementSymbol(guessElement(value));
 }
 
-function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicHierarchy.AtomicData {
+function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicData {
     console.log(atomsData.atomName)
-    const atoms = Table.ofColumns(AtomicHierarchy.AtomsSchema, {
+    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,
@@ -54,14 +54,14 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Atomi
         pdbx_formal_charge: Column.Undefined(atomsData.count, Column.Schema.int)
     });
 
-    const residues = Table.view(Table.ofColumns(AtomicHierarchy.ResiduesSchema, {
+    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),
-    }), AtomicHierarchy.ResiduesSchema, offsets.residues);
+    }), ResiduesSchema, offsets.residues);
     // Optimize the numeric columns
     Table.columnToArray(residues, 'label_seq_id', Int32Array);
     Table.columnToArray(residues, 'auth_seq_id', Int32Array);
@@ -72,7 +72,7 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Atomi
     //     label_entity_id: Column.Undefined(atomsData.count, Column.Schema.str)
     // });
 
-    const chains = Table.ofUndefinedColumns(AtomicHierarchy.ChainsSchema, 0);
+    const chains = Table.ofUndefinedColumns(ChainsSchema, 0);
 
     return { atoms, residues, chains };
 }
@@ -89,10 +89,10 @@ function getConformation(atoms: Atoms): AtomicConformation {
     }
 }
 
-function isHierarchyDataEqual(a: AtomicHierarchy.AtomicData, b: AtomicHierarchy.AtomicData) {
+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<AtomicHierarchy.ResiduesSchema>, b.residues as Table<AtomicHierarchy.ResiduesSchema>)
-        && Table.areEqual(a.atoms as Table<AtomicHierarchy.AtomsSchema>, b.atoms as Table<AtomicHierarchy.AtomsSchema>)
+    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 {
@@ -109,7 +109,7 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo
         };
     }
 
-    const hierarchySegments: AtomicHierarchy.AtomicSegments = {
+    const hierarchySegments: AtomicSegments = {
         residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds),
         chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds),
     }
@@ -135,7 +135,7 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo
         sequence: Sequence.fromAtomicHierarchy(hierarchy),
         atomSiteConformation: getConformation(structure.atoms),
         coarseGrained: CoarseGrainedHierarchy.Empty,
-        symmetry: { assemblies: [] },
+        symmetry: ModelSymmetry.Default,
         atomCount: structure.atoms.count
     };
 }
diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts
index c9a962266f50706a26e346198420b01837db5212..22644e2fa2ef1fdbb7909b6a825275110fa5e24b 100644
--- a/src/mol-model/structure/model/formats/mmcif.ts
+++ b/src/mol-model/structure/model/formats/mmcif.ts
@@ -9,9 +9,8 @@ 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 } from '../properties/atomic/hierarchy'
-import { AtomicConformation } from '../properties/atomic/conformation'
-import Symmetry from '../properties/symmetry'
+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'
@@ -20,6 +19,8 @@ import mmCIF_Format = Format.mmCIF
 import { getSequence } from './mmcif/sequence';
 import { Entities } from '../properties/common';
 import { coarseGrainedFromIHM } from './mmcif/ihm';
+import { Spacegroup, SpacegroupCell } from 'mol-math/geometry';
+import { Vec3 } from 'mol-math/linear-algebra';
 
 function findModelBounds({ data }: mmCIF_Format, startIndex: number) {
     const num = data.atom_site.pdbx_PDB_model_num;
@@ -83,8 +84,29 @@ function getConformation({ data }: mmCIF_Format, bounds: Interval): AtomicConfor
     }
 }
 
-function getSymmetry(format: mmCIF_Format): Symmetry {
-    return { assemblies: createAssemblies(format) };
+function getSymmetry(format: mmCIF_Format): ModelSymmetry {
+    const assemblies = createAssemblies(format);
+    const spacegroup = getSpacegroup(format);
+    const isNonStandardCrytalFrame = checkNonStandardCrystalFrame(format, spacegroup);
+    return { assemblies, spacegroup, isNonStandardCrytalFrame };
+}
+
+function checkNonStandardCrystalFrame(format: mmCIF_Format, spacegroup: Spacegroup) {
+    const { atom_sites } = format.data;
+    if (atom_sites._rowCount === 0) return false;
+    // TODO: parse atom_sites transform and check if it corresponds to the toFractional matrix
+    return false;
+}
+
+function getSpacegroup(format: mmCIF_Format): Spacegroup {
+    const { symmetry, cell } = format.data;
+    if (symmetry._rowCount === 0 || cell._rowCount === 0) return Spacegroup.ZeroP1;
+    const groupName = symmetry['space_group_name_H-M'].value(0);
+    const spaceCell = SpacegroupCell.create(groupName,
+        Vec3.create(cell.length_a.value(0), cell.length_b.value(0), cell.length_c.value(0)),
+        Vec3.scale(Vec3.zero(), Vec3.create(cell.angle_alpha.value(0), cell.angle_beta.value(0), cell.angle_gamma.value(0)), Math.PI / 180));
+
+    return Spacegroup.create(spaceCell);
 }
 
 function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
diff --git a/src/mol-model/structure/model/formats/mmcif/sequence.ts b/src/mol-model/structure/model/formats/mmcif/sequence.ts
index 88da71446301e460c390e91de8771d54b093ca6f..3b30419696303332c6c91fec2fbc66b6de3f2451 100644
--- a/src/mol-model/structure/model/formats/mmcif/sequence.ts
+++ b/src/mol-model/structure/model/formats/mmcif/sequence.ts
@@ -7,7 +7,7 @@
 import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif'
 import Sequence from '../../properties/sequence'
 import { Column } from 'mol-data/db';
-import { AtomicHierarchy } from '../../properties/atomic/hierarchy';
+import { AtomicHierarchy } from '../../properties/atomic';
 import { Entities } from '../../properties/common';
 
 export function getSequence(cif: mmCIF, entities: Entities, hierarchy: AtomicHierarchy): Sequence {
diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts
index 8a0f009c2677eb068e8e2b69b4609d4ad3a13dd9..eb7d5024a4a0d524f59bee9a2328c2eae4cbeec2 100644
--- a/src/mol-model/structure/model/model.ts
+++ b/src/mol-model/structure/model/model.ts
@@ -7,9 +7,8 @@
 import UUID from 'mol-util/uuid'
 import Format from './format'
 import Sequence from './properties/sequence'
-import { AtomicHierarchy } from './properties/atomic/hierarchy'
-import { AtomicConformation } from './properties/atomic/conformation'
-import Symmetry from './properties/symmetry'
+import { AtomicHierarchy, AtomicConformation } from './properties/atomic'
+import { ModelSymmetry } from './properties/symmetry'
 import { CoarseGrainedHierarchy } from './properties/coarse-grained/hierarchy'
 import { Entities } from './properties/common';
 
@@ -28,7 +27,7 @@ interface Model extends Readonly<{
 
     sourceData: Format,
 
-    symmetry: Symmetry,
+    symmetry: ModelSymmetry,
     entities: Entities,
     sequence: Sequence,
 
diff --git a/src/mol-model/structure/model/properties/atomic.ts b/src/mol-model/structure/model/properties/atomic.ts
new file mode 100644
index 0000000000000000000000000000000000000000..65b7bb2cbbf97f583911af6c54409be3f3e89718
--- /dev/null
+++ b/src/mol-model/structure/model/properties/atomic.ts
@@ -0,0 +1,9 @@
+/**
+ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+export * from './atomic/conformation'
+export * from './atomic/hierarchy'
+export * from './atomic/measures'
\ No newline at end of file
diff --git a/src/mol-model/structure/model/properties/symmetry.ts b/src/mol-model/structure/model/properties/symmetry.ts
index d459241b4a7ff1174933c8e410e4fa5e841eddad..3b2659e9e49541c16c1e47f8ae229905a0d1ca92 100644
--- a/src/mol-model/structure/model/properties/symmetry.ts
+++ b/src/mol-model/structure/model/properties/symmetry.ts
@@ -8,6 +8,7 @@ import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator'
 import { arrayFind } from 'mol-data/util'
 import { Query } from '../../query'
 import { Model } from '../../model'
+import { Spacegroup } from 'mol-math/geometry';
 
 /** Determine an atom set and a list of operators that should be applied to that set  */
 export interface OperatorGroup {
@@ -40,12 +41,14 @@ export namespace Assembly {
     }
 }
 
-interface Symmetry {
+interface ModelSymmetry {
     readonly assemblies: ReadonlyArray<Assembly>,
+    readonly spacegroup: Spacegroup,
+    readonly isNonStandardCrytalFrame: boolean
 }
 
-namespace Symmetry {
-    export const Empty: Symmetry = { assemblies: [] };
+namespace ModelSymmetry {
+    export const Default: ModelSymmetry = { assemblies: [], spacegroup: Spacegroup.ZeroP1, isNonStandardCrytalFrame: false };
 
     export function findAssembly(model: Model, id: string): Assembly | undefined {
         const _id = id.toLocaleLowerCase();
@@ -53,4 +56,4 @@ namespace Symmetry {
     }
 }
 
-export default Symmetry
\ No newline at end of file
+export { ModelSymmetry }
\ No newline at end of file
diff --git a/src/mol-model/structure/model/utils/hierarchy-keys.ts b/src/mol-model/structure/model/utils/hierarchy-keys.ts
index e825df9dd4a52de9158a946269434fd5fe8b5097..aabfe1b4a6bb9bed1633ab748d382aa935316588 100644
--- a/src/mol-model/structure/model/utils/hierarchy-keys.ts
+++ b/src/mol-model/structure/model/utils/hierarchy-keys.ts
@@ -5,7 +5,7 @@
  */
 
 import { Column } from 'mol-data/db'
-import { AtomicData, AtomicSegments, AtomicKeys } from '../properties/atomic/hierarchy'
+import { AtomicData, AtomicSegments, AtomicKeys } from '../properties/atomic'
 import { Interval, Segmentation } from 'mol-data/int'
 import { Entities } from '../properties/common';
 
diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts
index 6fe1eed352a7c9f8b52716522cf17f976401f4b5..fc2a066ad957730fe49ec9a884ffb47a822156e6 100644
--- a/src/mol-model/structure/query/properties.ts
+++ b/src/mol-model/structure/query/properties.ts
@@ -5,7 +5,7 @@
  */
 
 import { Element, Unit } from '../structure'
-import { VdwRadius } from '../model/properties/atomic/measures';
+import { VdwRadius } from '../model/properties/atomic';
 
 const constant = {
     true: Element.property(l => true),
diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts
index 2bf71bda6742746edf52621bb930f204ac70418d..bf17c9013887a9f910257183ee3a2bfc99870826 100644
--- a/src/mol-model/structure/structure/symmetry.ts
+++ b/src/mol-model/structure/structure/symmetry.ts
@@ -11,15 +11,17 @@ import { Task } from 'mol-task';
 import { SortedArray } from 'mol-data/int';
 import Unit from './unit';
 import { EquivalenceClasses, hash2 } from 'mol-data/util';
+import { Vec3 } from 'mol-math/linear-algebra';
+import { SymmetryOperator, Spacegroup, SpacegroupCell } from 'mol-math/geometry';
 
 namespace StructureSymmetry {
-    export function buildAssembly(structure: Structure, name: string) {
-        return Task.create('Build Symmetry', async ctx => {
+    export function buildAssembly(structure: Structure, asmName: string) {
+        return Task.create('Build Assembly', async ctx => {
             const models = Structure.getModels(structure);
             if (models.length !== 1) throw new Error('Can only build assemblies from structures based on 1 model.');
 
-            const assembly = ModelSymmetry.findAssembly(models[0], name);
-            if (!assembly) throw new Error(`Assembly '${name}' is not defined.`);
+            const assembly = ModelSymmetry.findAssembly(models[0], asmName);
+            if (!assembly) throw new Error(`Assembly '${asmName}' is not defined.`);
 
             const assembler = Structure.Builder();
 
@@ -41,6 +43,38 @@ namespace StructureSymmetry {
         });
     }
 
+    export function buildSymmetryRange(structure: Structure, ijkMin: Vec3, ijkMax: Vec3) {
+        return Task.create('Build Assembly', async ctx => {
+            const models = Structure.getModels(structure);
+            if (models.length !== 1) throw new Error('Can only build symmetries from structures based on 1 model.');
+
+            const { spacegroup } = models[0].symmetry;
+            if (SpacegroupCell.isZero(spacegroup.cell)) return structure;
+
+            const operators: SymmetryOperator[] = [];
+            for (let op = 0; op < spacegroup.operators.length; op++) {
+                for (let i = ijkMin[0]; i < ijkMax[0]; i++) {
+                    for (let j = ijkMin[1]; j < ijkMax[1]; j++) {
+                        for (let k = ijkMin[2]; k < ijkMax[2]; k++) {
+                            operators[operators.length] = Spacegroup.getSymmetryOperator(spacegroup, op, i, j, k);
+                        }
+                    }
+                }
+            }
+
+            const assembler = Structure.Builder();
+
+            const { units } = structure;
+            for (const oper of operators) {
+                for (const unit of units) {
+                    assembler.addWithOperator(unit, oper);
+                }
+            }
+
+            return assembler.getStructure();
+        });
+    }
+
     function hashUnit(u: Unit) {
         return hash2(u.invariantId, SortedArray.hashCode(u.elements));
     }
diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts
index d8e1037a964b2ba6083c2e33ab07365c1c1abdbd..4070cca0b782bfbc4a2d6f71ae7deb41b155f181 100644
--- a/src/perf-tests/structure.ts
+++ b/src/perf-tests/structure.ts
@@ -16,6 +16,7 @@ import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry,
 
 import to_mmCIF from 'mol-model/structure/export/mmcif'
 import { Run } from 'mol-task';
+import { Vec3 } from 'mol-math/linear-algebra';
 //import { EquivalenceClasses } from 'mol-data/util';
 
 require('util.promisify').shim();
@@ -302,6 +303,19 @@ export namespace PropertyAccess {
         console.log('exported');
     }
 
+    export async function testSymmetry(id: string, s: Structure) {
+        console.time('symmetry')
+        const a = await Run(StructureSymmetry.buildSymmetryRange(s, Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)));
+        //const auth_comp_id = Q.props.residue.auth_comp_id;
+        //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' }));
+        //const alas = await query(q1, a);
+
+        console.timeEnd('symmetry')
+        fs.writeFileSync(`${DATA_DIR}/${id}_symm.bcif`, to_mmCIF(id, a, true));
+        //fs.writeFileSync(`${DATA_DIR}/${id}_assembly.bcif`, to_mmCIF(id, Selection.unionStructure(alas), true));
+        console.log('exported');
+    }
+
     // export async function testGrouping(structure: Structure) {
     //     const { elements, units } = await Run(Symmetry.buildAssembly(structure, '1'));
     //     console.log('grouping', units.length);
@@ -329,7 +343,7 @@ export namespace PropertyAccess {
         //const { structures, models/*, mmcif*/ } = await getBcif('1cbs');
         // const { structures, models } = await getBcif('3j3q');
 
-        const { structures, models /*, mmcif*/ } = await readCIF('e:/test/quick/1hrv_updated.cif');
+        const { structures, models /*, mmcif*/ } = await readCIF('e:/test/quick/1cbs_updated.cif');
         //const { structures: s1, /*, mmcif*/ } = await readCIF('e:/test/quick/1tqn_updated.cif');
 
         // testGrouping(structures[0]);
@@ -340,7 +354,8 @@ export namespace PropertyAccess {
         //console.log(mmcif.pdbx_struct_oper_list.matrix.toArray());
         // console.log(mmcif.pdbx_struct_oper_list.vector.toArray());
 
-        await testAssembly('1hrv', structures[0]);
+        //await testAssembly('1hrv', structures[0]);
+        await testSymmetry('1cbs', structures[0]);
         // throw '';
 
         // console.log(models[0].symmetry.assemblies);